ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

spring之组件注解探索(ComponentScan)

2022-01-17 10:35:59  阅读:115  来源: 互联网

标签:容器 spring public bean 组件 注解 ComponentScan class


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,public,bean,组件,注解,ComponentScan,class
来源: https://www.cnblogs.com/clover-forever/p/15812386.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有