2、组件注册相关注解

本节主要内容介绍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();
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,539评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,594评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,871评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,963评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,984评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,763评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,468评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,850评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,002评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,144评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,823评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,483评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,026评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,150评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,415评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,092评论 2 355

推荐阅读更多精彩内容