Spring使用注解驱动开发一组件添加/Bean的注入

简介: Spring使用注解驱动开发一组件添加/Bean的注入

【1】@Configuration和@Bean

@Configuration


从Spring3.0开始,@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。


@Configuration标注在类上,相当于把该类作为spring的xml配置文件中的<beans>,作用为:配置spring容器(应用上下文)。


AnnotationConfigWebApplicationContext类继承图如下所示:


AnnotationConfigApplicationContext类继承图如下所示:


配置类实例如下:

package com.web.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.web.bean.Person;
//配置类==配置文件
@Configuration  //告诉Spring这是一个配置类
public class MainConfig {
  //给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
  @Bean("person")
  public Person person01(){
    return new Person("lisi", 20);
  }
}



测试类如下:

package com.web.test;
import java.util.Map;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import com.web.config.MainConfig;
public class IOCTest {
/*用AnnotationConfigApplicationContext 替换ClassPathXmlApplicationContext。
如果加载spring-context.xml文件:
ApplicationContext context = new
ClassPathXmlApplicationContext("applicationContext.xml");
*/
  AnnotationConfigApplicationContext applicationContext = 
  new AnnotationConfigApplicationContext(MainConfig.class);
  @SuppressWarnings("resource")
  @Test
  public void test01(){
    AnnotationConfigApplicationContext applicationContext = 
    new AnnotationConfigApplicationContext(MainConfig.class);
    // 获取容器中注册的bean的信息
    String[] definitionNames = applicationContext.getBeanDefinitionNames();
    for (String name : definitionNames) {
      System.out.println(name);
    }
  }
}

@Bean 注解

@Bean是一个方法级别上的注解,效果等同于在xml中配置一个bean,并设置其属性。

xml配置如下:

<bean id="person" 
  class="com.core.Person" scope="singleton"  
  init-method="init"  destroy-method="cleanUp"
  autowire="byName" lazy-init="true" >  
</bean> 



等同于如下:

//给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
@Scope("singleton")
@Lazy
@Bean(name="person",initMethod="init",destroyMethod="cleanUp",
autowire=Autowire.BY_NAME)
public Person person01(){
  return new Person("lisi", 20);
}

Spring容器中注册的bean,默认是单实例的。其作用域说明如下:

ConfigurableBeanFactory#SCOPE_PROTOTYPE    
ConfigurableBeanFactory#SCOPE_SINGLETON  
org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST  request
org.springframework.web.context.WebApplicationContext#SCOPE_SESSION  sesssion



@Scope注解调整作用域

prototype:


多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。每次获取的时候才会调用方法创建对象。


singleton:

单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中。以后每次获取就是直接从容器(map.get())中拿。


request: 同一次请求创建一个实例。

session: 同一个session创建一个实例。


需要注意的是,单实例bean 初始化方法在容器创建时被调用,销毁方法在容器销毁时被调用。多实例bean,初始化方法在第一次获取bean的时候调用(非容器创建时,容器创建时会调用构造方法),销毁方法容Spring 容器不负责管理。


懒加载说明如下:

单实例bean:默认在容器启动的时候创建对象;
懒加载:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化。


多实例懒加载测试如下:

@Test
public void test02(){
  AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
  System.out.println("ioc容器创建完成....");
  Object bean = applicationContext.getBean("person");
  Object bean2 = applicationContext.getBean("person");
  System.out.println(bean == bean2);
}


result as follows :

ioc容器创建完成....
给容器中添加Person....
给容器中添加Person....
false//两个bean不同哦

【2】@ComponentScan注解

xml配置中必不可少的标签,用来扫描bean并注册到IOC容器中。


xml配置示例如下:

<!-- 包扫描、只要标注了@Controller、@Service、@Repository,@Component 都被注入-->
 <context:component-scan base-package="com.web" use-default-filters="false">
  <context:include-filter type="annotation" expression=""/>
  <context:exclude-filter type="annotation" expression=""/>
 </context:component-scan>


其中,type有五种形式:


  • annotation-注解,
  • assignable-给定的类型,
  • regex-正则指定,
  • custom-自定义规则
  • aspectj-ASPECTJ表达式。



配置类如下:

//配置类==配置文件
@Configuration  //告诉Spring这是一个配置类
@ComponentScans(
  value = {
    @ComponentScan(value="com.web",includeFilters = {
      @Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
      @Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}),
      @Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})
    },useDefaultFilters = false)  
  }
)
//@ComponentScan  value:指定要扫描的包
//excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件
//includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件
//FilterType.ANNOTATION:按照注解
//FilterType.ASSIGNABLE_TYPE:按照给定的类型;
//FilterType.ASPECTJ:使用ASPECTJ表达式
//FilterType.REGEX:使用正则指定
//FilterType.CUSTOM:使用自定义规则
public class MainConfig {
  //给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
  @Bean(name="person")
  public Person person01(){
    return new Person("lisi", 20);
  }
}


FilterType.CUSTOM,classes={MyTypeFilter.class}中的MyTypeFilter类如下:

public class MyTypeFilter implements TypeFilter {
  /**
   * metadataReader:读取到的当前正在扫描的类的信息
   * metadataReaderFactory:可以获取到其他任何类信息的
   */
  @Override
  public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
      throws IOException {
    // TODO Auto-generated method stub
    //获取当前类注解的信息
    AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
    //获取当前正在扫描的类的类信息
    ClassMetadata classMetadata = metadataReader.getClassMetadata();
    //获取当前类资源(类的路径)
    Resource resource = metadataReader.getResource();
    String className = classMetadata.getClassName();
    System.out.println("--->"+className);
    // 这里自定义规则
    if(className.contains("er")){
      return true;
    }
    return false;
  }
}


【3】@Conditional条件判断

@Conditional({Condition}) : 按照一定的条件进行判断,满足条件给容器中注册bean。

配置类如下:

/**
 * @Conditional({Condition}) : 按照一定的条件进行判断,满足条件给容器中注册bean
 * 
 * 如果系统是windows,给容器中注册("bill")
 * 如果是linux系统,给容器中注册("linus")
 */
@Conditional(WindowsCondition.class)
@Bean("bill")
public Person person01(){
  return new Person("Bill Gates",62);
}
@Conditional(LinuxCondition.class)
@Bean("linus")
public Person person02(){
  return new Person("linus", 48);
}



Conditional接口如下:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
  /**
   * All {@link Condition}s that must {@linkplain Condition#matches match}
   * in order for the component to be registered.
   */
  Class<? extends Condition>[] value();
}


既可以作用于方法上面,也可以配置在类上面,参数为Condition的实现类。


Condition接口如下:

public interface Condition {
  /**
   * Determine if the condition matches.
   * @param context the condition context
   * @param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
   * or {@link org.springframework.core.type.MethodMetadata method} being checked.
   * @return {@code true} if the condition matches and the component can be registered
   * or {@code false} to veto registration.
   */
  boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

条件类如下:

//判断是否linux系统
public class LinuxCondition implements Condition {
  /**
   * ConditionContext:判断条件能使用的上下文(环境)
   * AnnotatedTypeMetadata:注释信息
   */
  @Override
  public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    // TODO是否linux系统
    //1、能获取到ioc使用的beanfactory
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    //2、获取类加载器
    ClassLoader classLoader = context.getClassLoader();
    //3、获取当前环境信息
    Environment environment = context.getEnvironment();
    //4、获取到bean定义的注册类
    BeanDefinitionRegistry registry = context.getRegistry();
    String property = environment.getProperty("os.name");
    //可以判断容器中的bean注册情况,也可以给容器中注册bean
    boolean definition = registry.containsBeanDefinition("person");
    if(property.contains("linux")){
      return true;
    }
    return false;
  }
}

如果该注解配置在类上面,则该配置类的所有方法都将使用该注解。示例如下:

@Conditional({WindowsCondition.class})
@Configuration
public class MainConfig2 {
  //...
}

@Confitional扩展如下图:


【4】@import导入组件

@Import ,快速给容器中导入一个组件。


其注解如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
  /**
   * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
   * or regular component classes to import.
   */
  Class<?>[] value();
}



其使用方式如下:

1)@Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名。

@Configuration
@Import({Color.class,Red.class})
public class MainConfig2 {
  //...
}



2)ImportSelector:返回需要导入的组件的全类名数组;

//自定义逻辑返回需要导入的组件
public class MyImportSelector implements ImportSelector {
  //返回值,就是到导入到容器中的组件全类名
  //AnnotationMetadata:当前标注@Import注解的类的所有注解信息
  @Override
  public String[] selectImports(AnnotationMetadata importingClassMetadata) {
    //importingClassMetadata
    //方法不要返回null值
    return new String[]{"com.web.bean.Blue","com.web.bean.Yellow"};
  }
}

此时配置类如下:

@Configuration
@Import({Color.class,Red.class,MyImportSelector.class})
//@Import导入组件,id默认是组件的全类名
public class MainConfig2 {
  //...
}



3)ImportBeanDefinitionRegistrar:手动注册bean到容器中。

ImportBeanDefinitionRegistrar接口类如下:

public interface ImportBeanDefinitionRegistrar {
  /**
   * Register bean definitions as necessary based on the given annotation metadata of
   * the importing {@code @Configuration} class.
   * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
   * registered here, due to lifecycle constraints related to {@code @Configuration}
   * class processing.
   * @param importingClassMetadata annotation metadata of the importing class
   * @param registry current bean definition registry
   */
  public void registerBeanDefinitions(
      AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}



实现类如下:

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
  /**
   * AnnotationMetadata:当前类的注解信息
   * BeanDefinitionRegistry:BeanDefinition注册类;
   *    把所有需要添加到容器中的bean;调用
   *    BeanDefinitionRegistry.registerBeanDefinition手工注册进来
   */
  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    boolean definition = registry.containsBeanDefinition("com.web.bean.Red");
    boolean definition2 = registry.containsBeanDefinition("com.web.bean.Blue");
    if(definition && definition2){
      //指定Bean定义信息;(Bean的类型,Bean。。。)
      RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
      //注册一个Bean,指定bean名
      registry.registerBeanDefinition("rainBow", beanDefinition);
    }
  }
}

此时配置类如下:

@Import({Color.class,Red.class,MyImportSelector.class,
MyImportBeanDefinitionRegistrar.class})
//@Import导入组件,id默认是组件的全类名
public class MainConfig2 {
  //...
}

【5】FactoryBean

FactoryBean : 是一个Java Bean,但是它是一个能生产对象的工厂Bean,它的实现和工厂模式及修饰器模式很像。


自定义工厂bean实现该接口: 这里为该类添加了@Component注解。

@Component
//创建一个Spring定义的FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {
  //返回一个Color对象,这个对象会添加到容器中
  @Override
  public Color getObject() throws Exception {
    // TODO Auto-generated method stub
    System.out.println("ColorFactoryBean...getObject...");
    return new Color();
  }
  @Override
  public Class<?> getObjectType() {
    // TODO Auto-generated method stub
    return Color.class;
  }
  //是单例?
  //true:这个bean是单实例,在容器中保存一份
  //false:多实例,每次获取都会创建一个新的bean;
  @Override
  public boolean isSingleton() {
    // TODO Auto-generated method stub
    //return true;
    return false;
  }
}

测试如下:

public class IOCTest {
  AnnotationConfigApplicationContext applicationContext =
  new AnnotationConfigApplicationContext(MainConfig2.class);
  @Test
  public void testImport(){
    //工厂Bean获取的是调用getObject创建的对象
    Object bean2 = applicationContext.getBean("colorFactoryBean");
    Object bean3 = applicationContext.getBean("colorFactoryBean");
    System.out.println("bean的类型:"+bean2.getClass());
    System.out.println(bean2 == bean3);
  }

result as follows :

ColorFactoryBean...getObject...
ColorFactoryBean...getObject...
bean的类型:class com.web.bean.Color
false

可以看到根据id colorFactoryBean获取的实际bean为com.web.bean.Color!并且两次获取的bean不等。


如果要获取工厂bean本身,则如下:

Object bean4 = applicationContext.getBean("&colorFactoryBean");
System.out.println(bean4.getClass());

result as follows :

class com.web.bean.ColorFactoryBean

原理如下图:

那么Servlet、Filter和Listener如何使用代码方式注入容器呢?

目录
相关文章
|
10天前
|
Java Spring
【Spring】方法注解@Bean,配置类扫描路径
@Bean方法注解,如何在同一个类下面定义多个Bean对象,配置扫描路径
136 73
|
5天前
|
Java Spring 容器
【SpringFramework】Spring IoC-基于注解的实现
本文主要记录基于Spring注解实现IoC容器和DI相关知识。
39 21
|
10天前
|
存储 Java Spring
【Spring】获取Bean对象需要哪些注解
@Conntroller,@Service,@Repository,@Component,@Configuration,关于Bean对象的五个常用注解
|
7月前
|
Java 开发者 Spring
解析Spring中Bean的生命周期
解析Spring中Bean的生命周期
64 2
|
8月前
|
缓存 Java Spring
Spring 框架中 Bean 的生命周期
Spring 框架中 Bean 的生命周期
82 1
|
7月前
|
XML druid Java
Spring5系列学习文章分享---第二篇(IOC的bean管理factory+Bean作用域与生命周期+自动装配+基于注解管理+外部属性管理之druid)
Spring5系列学习文章分享---第二篇(IOC的bean管理factory+Bean作用域与生命周期+自动装配+基于注解管理+外部属性管理之druid)
76 0
|
3月前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细解析Spring Bean的生命周期及其核心概念,并深入源码分析。Spring Bean是Spring框架的核心,由容器管理其生命周期。从实例化到销毁,共经历十个阶段,包括属性赋值、接口回调、初始化及销毁等。通过剖析`BeanFactory`、`ApplicationContext`等关键接口与类,帮助你深入了解Spring Bean的管理机制。希望本文能助你更好地掌握Spring Bean生命周期。
149 1
|
3月前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细介绍了Spring框架中的核心概念——Spring Bean的生命周期,包括实例化、属性赋值、接口回调、初始化、使用及销毁等10个阶段,并深入剖析了相关源码,如`BeanFactory`、`DefaultListableBeanFactory`和`BeanPostProcessor`等关键类与接口。通过理解这些核心组件,读者可以更好地掌握Spring Bean的管理和控制机制。
116 1
|
6月前
|
Java Spring 容器
Spring Boot 启动源码解析结合Spring Bean生命周期分析
Spring Boot 启动源码解析结合Spring Bean生命周期分析
118 11
|
5月前
|
前端开发 Java 开发者