spring之组件注解探索(ComponentScan)

spring的组件注册相关注解探索

本文参考博客(基本摘自以下博客内容)

https://www.jianshu.com/p/81880251a700
https://www.jianshu.com/p/a00d2b43e160

本节主要内容介绍spring注解的组件注册,具体包括下面的几个注解:

  • @ComponentScan自动扫面组件
  • @Scope设置组件的作用域
  • @Lazy bean组件懒加载
  • @Conditional按照条件注册Bean
  • @Import给容器中导入一个组件

1、@ComponentScan自动扫面组件

  • 源码
@Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
//可以重复使用
    @Repeatable(ComponentScans.class)
    public @interface ComponentScan {
//使用包名
        @AliasFor("basePackages")
        String[] value() default {};
        @AliasFor("value")
        String[] basePackages() default {};
//使用具体类型名称
        Class<?>[] basePackageClasses() default {};
        ....其他属性
//指定扫描的时候只需要包含哪些组件
        Filter[] includeFilters() default {};
//指定扫描的时候按照什么规则排除那些组件
        Filter[] excludeFilters() default {};
        boolean lazyInit() default false;
//过滤规则类
        @Retention(RetentionPolicy.RUNTIME)
        @Target({})
        @interface Filter {
//FilterType定义按什么过滤类型来进行过滤,
/**
FilterType.ANNOTATION:按照注解
FilterType.ASSIGNABLE_TYPE:按照给定的类型;
FilterType.ASPECTJ:使用ASPECTJ表达式
FilterType.REGEX:使用正则指定
FilterType.CUSTOM:使用自定义规则
*/
            FilterType type() default FilterType.ANNOTATION;
            @AliasFor("classes")
            Class<?>[] value() default {};
            @AliasFor("value")
            Class<?>[] classes() default {};
            String[] pattern() default {};

        }

    }
  • 使用说明
1、只包含有Controller注解bean
@ComponentScan(value="com.qiu",includeFilters = {
                @Filter(type=FilterType.ANNOTATION,classes={Controller.class}),}
,useDefaultFilters = false)
2、排除含有controller注解的bean
@ComponentScan(value="com.qiu",excludeFilters = {
    @Filter(type=FilterType.ANNOTATION,classes={Controller.class})
})  

2、@Scope设置组件的作用域

  • 源码
//作用与类或者方法上
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Scope {
        @AliasFor("scopeName")
        String value() default "";
        @AliasFor("value")
        String scopeName() default "";
        ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
    }
  • 使用
*默认是单实例的,ConfigurableBeanFactory接口含有者两个属性
*单实例和原型实例说明:prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。 
每次获取的时候才会调用方法创建对象;singleton:单实例的(默认值):ioc容器启动会调用方法创建
对象放到ioc容器中。 以后每次获取就是直接从容器(map.get())中拿
1、原型案例
@Scope("prototype")
    @Bean()
    public User user() {
        System.out.println("给容器中添加user....");
        return new User();
    }
2、测试
ConfigurableApplicationContext context2=new 
AnnotationConfigApplicationContext(ScopeConfig.class);
        //测试scope:获取容器中的bean,singleton单例时候相等,prototype时候不相等
        User user2=(User) context2.getBean("user");
        User user1=(User) context2.getBean("user");
        System.out.println(user1==user2);

3、@Lazy bean组件懒加载

  • @Lazy注解用于标识bean是否需要延迟加载,默认是true。当没加注解之前主要容器启动就会实例化bean,加上@Lazy注解则必须在第一次调用的时候才会加载如下:
1、没加注解之前主要容器启动就会实例化bean
        ConfigurableApplicationContext context2=new 
AnnotationConfigApplicationContext(ScopeConfig.class);
2、加入@Lazy后必须第一次调用的时候才会加载
User user3=(User) context2.getBean("user1");

4、@Conditional按照条件注册Bean

  • 源码
//作用于方法和类
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
//参数是:继承于Condition的类数组
    Class<? extends Condition>[] value();
}
//condition接口,自定义的condition类需要实现该接口
public interface Condition {
/**
     * ConditionContext:判断条件能使用的上下文(环境)
     * AnnotatedTypeMetadata:注释信息
     */
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
  • 案例说明
    需求:按照当前操作系统来注入相应的Bean,如果系统是windows,给容器中注册("bill"),如果是linux系统,给容器中注册("linus")
1、判断是否是linux系统
public class LinuxCondition implements Condition{
    /**
     * ConditionContext:判断条件能使用的上下文(环境)
     * AnnotatedTypeMetadata:注释信息
     */
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) 
{
        //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");
        if (property.contains("linux")) {
            return true;//放行
        }
        return false;
    }

}

2、判断是否是windows系统
public class WindowsCondition implements Condition{
    /**
     * ConditionContext:判断条件能使用的上下文(环境)
     * AnnotatedTypeMetadata:注释信息
     */
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata)
 {
        Environment environment = context.getEnvironment();
        String property=environment.getProperty("os.name");
        if (property.contains("Windows")) {
            return true;//放行
        }
        return false;
    }
}
3、@Conditional:按照一定的逻辑进行判断,满足条件给容器注入bean
public class ConditionalConfig {
//注入windows
    @Conditional(value= {WindowsCondition.class})
    @Bean
    public User user1() {
        User user=new User();
        user.setUserName("bill");
        return user;
    }
//注入linux
    @Conditional(value= {LinuxCondition.class})
    @Bean
    public User user2() {
        User user=new User();
        user.setUserName("linus");
        return user;
    }
4、idea中更换操作系统方法:-Dos.name=linux

介绍完了条件注解在spring中的使用,在Springboot中条件注解的分类:

Class conditions:@ConditionalOnClass和@ConditionalOnMissingClass,表示类是否在类路径下的条件注解
Bean conditions:@ConditionalOnBean和@ConditionalOnMissingBean,表示Bean是否被定义的条件注解
Property conditions:@ConditionalOnProperty,使用prefix和name属性用来表示是否有值,默认的话,只要该属性存在值,且不为false,即可匹配
Resource conditions:@ConditionalOnResource表示是否存在指定的resouce的条件注解
Web application conditions:@ConditionalOnWebApplication和@ConditionalOnNotWebApplication,当项目是web项目,或者不是web项目的条件注解
SpEL expression conditions:@ConditionalOnExpression,根据SPEL表达式执行结果作为条件

5、@Import给容器中导入一个组件

  • 源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
//只有一个属性,类型数组
    Class<?>[] value();
}
  • 给容器中注册组件方式:
1、包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的类]
2、@Bean[导入的第三方包里面的组件或者自己写的]
3、@Import[快速给容器中导入一个组件,常用于框架中]
(1)、@Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名
(2)、ImportSelector:返回需要导入的组件的全类名数组;
(3)、ImportBeanDefinitionRegistrar:手动注册bean到容器中
4、使用Spring提供的 FactoryBean(工厂Bean);
(1)默认获取到的是工厂bean调用getObject创建的对象
  • @Import注入组件案例说明:
1、@Import(要导入到容器中的组件)
@Import(value= {Person.class})
2、ImportSelector:返回需要导入的组件的全类名数组;
//先自定义逻辑返回需要导入的组件
public class MyImportSelector implements ImportSelector {
    //返回值,就是到导入到容器中的组件全类名
    //AnnotationMetadata:当前标注@Import注解的类的所有注解信息
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //importingClassMetadata
        //方法不要返回null值
        return new String[]{"com.huya.qiu.model.BlackPerson",
"com.huya.qiu.model.WhitePerson"};
    }
}
//然后注入
@Import(value= {MyImportSelector.class})
3、ImportBeanDefinitionRegistrar:手动注册bean到容器中
//先实现ImportBeanDefinitionRegistrar接口
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    /**
     * AnnotationMetadata:当前类的注解信息
     * BeanDefinitionRegistry:BeanDefinition注册类;
     *      把所有需要添加到容器中的bean;调用
     *      BeanDefinitionRegistry.registerBeanDefinition手工注册进来
     */
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //当同时拥有下面两个注册bean时候也注册yellowPerson
        boolean definition = registry.containsBeanDefinition("com.huya.qiu.model.BlackPerson");
        boolean definition2 = registry.containsBeanDefinition("com.huya.qiu.model.WhitePerson");
        if(definition && definition2){
            //指定Bean定义信息;(Bean的类型,Bean。。。)
            RootBeanDefinition beanDefinition = new RootBeanDefinition(YellowPerson.class);
            //注册一个Bean,指定bean名
            registry.registerBeanDefinition("yellowPerson", beanDefinition);
        }
    }
}
//然后注入
@Import(value= {MyImportBeanDefinitionRegistrar.class})
  • 使用Spring提供的 FactoryBean注入组价案例说明
1、创建一个Spring定义的FactoryBean
public class PersonFactoryBean implements FactoryBean<Person> {

    //返回一个Person对象,这个对象会添加到容器中
    public Person getObject() throws Exception {
        System.out.println("PersonFactoryBean...getObject...");
        return new Person();
    }

    public Class<?> getObjectType() {
        return Person.class;
    }

    //是单例?
    public boolean isSingleton() {
        return false;
    }

}
2、注入相应的bean
@Bean
    public PersonFactoryBean personFactoryBean(){
        return new PersonFactoryBean();
    }
上一篇:Spring系列之基于环境抽象-10


下一篇:探究Spring的Bean生命周期~~