• Spring注解


    Spring Boot的出现极大的简化了我们的开发,让我们无需再写繁杂的配置文件,其正是利用了注解的便捷性,而Spring Boot又依赖于Spring,因此深入学习Spring的注解是十分必要的。

    组件注册相关注解

    @Configuration

    写在类上,声明此类是一个配置类,替代xml文件

    @Bean

    作用:

    ​ 给 IOC 容器中注册一个Bean,一般添加在方法上,组件类型为方法的返回值,id默认为方法名称

    常用属性:

    • value / name:指定组件的名称,如果不指定,默认是方法名
    • initMethod:指定初始化方法
    • destroyMethod:指定销毁方法

    @ComponentScan

    作用:

    根据自定义的规则,自动扫描 IOC 容器中所有组件,在 jdk1.8 之后可以在一个类上定义多个@ComponentScan

    还有一个@ComponentScans注解,也可以在里面定义多个@ComponentScan

    常用属性:

    • value / basePackages:指定要扫描的包名
    • @Filter:用于指定过滤的规则
      • type:过滤类型
        • FilterType.ANNOTATION:按照注解的方式
        • FilterType.ASSIGNABLE_TYPE:按照给定的类型
        • FilterType.ASPECTJ:使用ASPECTJ表达式
        • FilterType.REGEX:使用正则表达式
        • FilterType.CUSTOM:自定义类型
      • value / classes:过滤值
      • pattern:过滤规则,根据不同的过滤类型配置不同的规则
    • useDefaultFilters:是否使用默认过滤规则, 默认是 true
    • includeFilters:指定扫描的时候只包含什么组件,需要配置 useDefaultFilters 属性为false
    • excludeFilters:指定扫描的时候按照什么规则排除哪些组件
    • lazyInit:懒加载

    如何使用FilterType.CUSTOM自定义过滤规则?

    public class MyTypeFilter implements TypeFilter {
    
        /**
         * 匹配方法,确定此过滤器是否与给定元数据描述的类匹配
         * @param metadataReader 读取到的当前正在扫描的类的信息
         * @param metadataReaderFactory 可以获取到其他任何类的信息
         * @return true:匹配, false:不匹配
         * @throws IOException
         */
        @Override
        public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
            //获取当前类的注解信息
            AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
            //获取当前正在扫描的类的信息
            ClassMetadata classMetadata = metadataReader.getClassMetadata();
            //获取当前类资源(类的路劲)
            Resource resource = metadataReader.getResource();
    
            //自定义匹配规则
            String className = classMetadata.getClassName();
            if(className.contains("Controller")){
                return true;
            }
            return false;
        }
    }
    

    @Scope

    作用:

    ​ 调整作用域

    常用参数:

    • value / scopeName
      • singleton:单实例,ioc容器启动的时候就会调用方法,创建bean对象,以后每次获取都是直接从ioc容器中取(map.get())。
      • prototype:多实例,ioc容器启动的时候不会去调用,当从ioc容器中获取bean对象的时候才会创建。
      • request:同一次请求创建一个实例。
      • session:同一个session范围创建一个实例。
      • global session:全局session范围创建一个实例,一般用于集群。

    @Lazy

    懒加载,一般用于单例模式,容器启动的时候不会创建bean,第一次调用的时候才创建

    @Conditional

    作用:

    ​ 按照一定条件进行判断,满足条件才注册bean,可以放在方法或类上,此注解在Spring Boot底层大量使用

    常用参数:

    • value:传入一个继承了Condition接口的类(可传入多个),类中定义自己需要的条件

    @Import

    作用:

    ​ 给ioc容器中导入指定的组件

    常用参数:

    • value:传入指定类,id默认是全类名。可传入自定义类、ImportSelector 和 ImportBeanDefinitionRegistrar

    用法:

    ​ 在配置类上添加如下形式的注解即可

    @Import({Red.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
    

    ImportSelector

    传入实现了 ImportSelector 接口的类,返回一个全类名数组,好处就是可以自定义需要导入的组件

    public class MyImportSelector implements ImportSelector {
    
        /**
         * 自定义导入的组件
         * @param importingClassMetadata 当前标注了@Import注解的类的所有注解信息
         * @return 返回全类名
         */
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{"com.spring.color.Blue", "com.spring.color.Yellow"};
        }
    }
    

    打断点debug一下,可以看到参数的信息,的确是当前标注@Import的类上的注解信息

    ImportBeanDefinitionRegistrar

    手动注册bean到容器中,调用 registerBeanDefinition() 方法

    public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
        /**
         *
         * @param importingClassMetadata 当前标注了@Import注解的类的所有注解信息
         * @param registry 容器中已注册组件的信息
         */
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            int count = registry.getBeanDefinitionCount();
            if(count > 10){
                BeanDefinition beanDefinition = new RootBeanDefinition(Ten.class);
                registry.registerBeanDefinition("ten", beanDefinition);
            }
        }
    }
    

    使用 FactoryBean 注册组件

    自定义一个类,实现 FactoryBean 接口

    public class FoodFactoryBean implements FactoryBean<Food> {
    
        /**
         * 获取实例对象
         * @return
         * @throws Exception
         */
        @Override
        public Food getObject() throws Exception {
            return new Food();
        }
    
        /**
         * 获取实例类型
         * @return
         */
        @Override
        public Class<?> getObjectType() {
            return Food.class;
        }
    
        /**
         * 是否单例,true:单例  false:多例
         * @return
         */
        @Override
        public boolean isSingleton() {
            return true;
        }
    }
    

    使用@Bean注册到容器

    @Bean
    public FoodFactoryBean foodFactoryBean(){
        return new FoodFactoryBean();
    }
    

    测试一下

    @Test
    public void test2(){
        Object bean = applicationContext.getBean("foodFactoryBean");
        System.out.println("foodFactoryBean 的类型:" + bean.getClass());
    }
    

    运行结果如下,发现类型竟然不是 FoodFactoryBean ,使用@Bean注册的组件类型不是方法的返回值吗?实际上,FoodFactoryBean注册的时候调用的了 getObject() 方法,所以注册的是 Food 类

    foodFactoryBean 的类型:class com.spring.bean.Food
    

    那么如果想要获得 FoodFactoryBean 类怎么办呢?

    看一下 BeanFactory 的源码,定义了一个成员变量 FACTORY_BEAN_PREFIX

    这个变量用于取消引用 FactoryBean 实例,并将其与由 FactoryBean 创建的bean区别开。

    例如,如果名为 test 的 bean 是 FactoryBean,则获取 &test 将返回工厂,而不是工厂返回的实例。

    所以在getBean的时候,在 id 前加上 & 即可

    @Test
    public void test2(){
        Object bean = applicationContext.getBean("&foodFactoryBean");
        System.out.println("foodFactoryBean 的类型:" + bean.getClass());
    }
    

    总结

    注册组件的方式:

    1. 包扫描 (@ComponentScan) + 组件注解(@Component / @Controller / @Service / @Repository)
    2. @Bean [导入第三方包里的组件]
    3. @Import [快速给容器中导入组件]
      • 普通类:直接注册,id默认是全类名
      • ImportSelector:返回需要注册组件的全类名数组
      • ImportBeanDefinitionRegistrar: 手动注册组件到容器中
    4. 使用 spring 提供的 FactoryBean(工厂Bean)
      • 默认获取的是 FactoryBean 调用getObject方法返回的对象
      • 可以在获取Bean的时候在 id 前面加上 & 符号,获取 FactoryBean 本身
  • 相关阅读:
    spring 之 AOP
    spring 之 动态代理
    为 NSDate 添加扩展类 判断时间
    iOS 日期相关总结
    iOS 请求出现 "Request failed: bad request (400)"
    NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802) 解决办法
    poj-3302
    辽宁省赛——杨鲁斯卡尔专场-J
    辽宁省赛——杨鲁斯卡尔专场 -F
    zzuli训练赛_05_13-D
  • 原文地址:https://www.cnblogs.com/songjilong/p/12513501.html
Copyright © 2020-2023  润新知