首页 > 技术文章 > spring注解驱动开发(1)——组件注册

kehao 2021-03-26 09:32 原文

给容器中注册组件有以下方法

  1. 包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的类]
  2. @Bean[导入的第三方包里面的组件]
  3. @Import[快速给容器中导入一个组件]
    1. @Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名
    2. ImportSelector:返回需要导入的组件的全类名数组;
    3. ImportBeanDefinitionRegistrar:手动注册bean到容器中
  4. 使用Spring提供的 FactoryBean(工厂Bean);
    1. 默认获取到的是工厂bean调用getObject创建的对象
    2. 要获取工厂Bean本身,我们需要给id前面加一个&,具体看后面的例子

1. @Configuration&@Bean给容器中注册组件

用一个配置类来代替beans.xml文件

//MainConfig.java
@Configuration//代表这个是个配置类
public class MainConfig {
    @Bean//对函数进行标记 表示该函数用来返回一个Bean
    public Person person(){
        return new Person();
    }
}
主函数
public class MainTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
        Person person = (Person) context.getBean("person");//利用上面的函数名来获取Bean
        System.out.println(person.toString());
    }
}

运行结果
Person{name='null', age=0}

2. @ComponentScan-自动扫描&指定扫描规则

在xml中配置自动扫描是在beans.xml文件上的
所以同理,在注解开发中则将注解标记在配置类上


//配置类==配置文件
@Configuration  //告诉Spring这是一个配置类
@ComponentScans(
      value = {
            @ComponentScan(value="com.atguigu",includeFilters = {
                //按照注解来过滤
                  @Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
                  //按照类型过滤
                  @Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.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("person")
   public Person person01(){
      return new Person("lisi", 20);
   }

}
@Test
public void test01(){
    //加载配置
   AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
   String[] definitionNames = applicationContext.getBeanDefinitionNames();
   for (String name : definitionNames) {
      System.out.println(name);
   }
}

3. 自定义TypeFilter指定过滤规则

@Configuration  //告诉Spring这是一个配置类
@ComponentScan(value="com.atguigu",
    includeFilters = {@Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})},
    useDefaultFilters = false)   
//FilterType.CUSTOM:使用自定义规则,MyTypeFilter类里面配置了过滤的方法(match方法)
public class MainConfig {
   
   //给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
   @Bean("person")
   public Person person01(){
      return new Person("lisi", 20);
   }

}
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")){//类名里包含er的类
         return true;//返回true会在列表里
      }
      return false;//返回false的Bean会从列表里取出
   }

}

4. @Scope设置组件的作用域

作用域的概念

@Scope:调整作用域

  • prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。
    每次获取的时候才会调用方法创建对象;
  • singleton:单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中。以后每次获取就是直接从容器(map.get())中拿,
  • request:同一次请求创建一个实例
  • session:同一个session创建一个实例

5. @Lazy懒加载

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

@Configuration
public class MainConfig2 {
   
   @Lazy
   @Bean("person")
   public Person person(){
      System.out.println("给容器中添加Person....");
      return new Person("张三", 25);
   }
}

6. @Conditional按照条件注册Bean

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

  1. 如果配置在类上则这个配置类只有符合条件的情况下,他里面配置的方法才能生效(里面的bean可以被注册)
  2. 如果配置在方法上,则只有符合条件的情况下,方法才能生效(里面的bean可以被注册)

7. @Import给容器快速导入一个组件

import注解的源码:

public @interface Import {

   /**
    * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
    * or regular component classes to import.
    */
   Class<?>[] value();

}

说明import注解中参数可以是以下几种类型

  1. 配置类(意思就是将其他的配置类在这个配置类上声明,加载了这个配置类就同时将import了的配置类一同加载了)
  2. ImportSelector
  3. ImportBeanDefinitionRegistrar
  4. 组件,即普通的类.class
    @Import导入组件(类:如Color.class))时,id默认是组件的全类名

eg: 假设Color Red Blue这些类已经存在

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

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

}

8. 使用Factory Bean注册组件

  1. 写一个工厂类
public class ColorFactoryBean implements FactoryBean<Color> {
   //返回一个Color对象,这个对象会添加到容器中
   @Override
   public Color getObject() throws Exception {
      System.out.println("ColorFactoryBean...getObject...");
      return new Color();
   }

   @Override
   public Class<?> getObjectType() {
      return Color.class;
   }

   //是单例?
   //true:这个bean是单实例,在容器中保存一份
   //false:多实例,每次获取都会创建一个新的bean;
   @Override
   public boolean isSingleton() {
      return false;
   }

}
  1. 将工厂类注册为Bean
@Configuration
//@Import导入组件,id默认是组件的全类名
public class MainConfig2 {
   @Bean
   public ColorFactoryBean colorFactoryBean(){
      return new ColorFactoryBean();
   }
}

此时获取这个Bean获取到的并不是工厂类的对象,而是工厂类的getObject方法返回的对象
如果要获取工厂类对象,则需要在id上加上&

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
@Test
public void testImport(){
   Object bean4 = applicationContext.getBean("&colorFactoryBean");
   System.out.println(bean4.getClass());
}

推荐阅读