spring 全家族相关注解

粗略解析记录spring系列常用注解,该篇暂不探寻源码原理阶段。主要记录作用

 

1.@ConfigurationProperties和@Value

两个注解都是可以从配置文件中属性自动赋值到实体类Bean中的属性值,但是@Value只能一个一个属性的修饰,而ConfigurationProperties适合很多属性的情况下,并且还可以指定配置文件中的某一段来配置导入,需要指定perfix前缀属性,并且还支持_ - 自动转换驼峰

2.@EnableConfigurationProperties

让使用了 @ConfigurationProperties 注解的类生效,并且将该类(注解里面的value参数修饰的类)注入到 IOC 容器中,交由 IOC 容器进行管理。因为单纯的在一个Java类使用注解无法生效,前提是(需要被spring容器来管理才可以,或者可以不用该注解,而用@Configuration或@Componet 也可以让ConfigurationProperties注解生效)而该注解里面还有一个属性,该属性能设置让选中ConfigurationProperties修饰类生效

Class<?>[] value() default {} 

3.条件注解@ConditionalOnClass,@ConditionalOnProperty,@ConditionalOnMissingBean,@ConditionalOnBean

常见的@Conditionalxxx开头的注解我们称之为条件注解,常见的条件注解有如上这些,这几个注解通常会结合使用,一般都是在配置类中使用,SpringBoot各种xxxxAutoCconfiguration都用到了这些注解,这也是SpringBoot自动装配的重要注解

3.1@ConditionalOnProperty

该注解表示在spring boot中有时候需要控制配置类是否生效,可以使用该注解来控制配置类@Configuration是否生效,简单来讲,一般是在配置类上或者是@Bean修饰的方法上,添加此注解表示一个类是否要被Spring上下文加载,若满足条件则加载,若不满足条件则不加载。

通过其两个属性name以及havingValue来实现的,其中name用来从application.properties或者yml,总之配置文件中读取某个属性值。prefix为配置文件中的前缀。(value是name属性的别名,作用都是一样的。)
如果该值(prefix.name)为空,则返回false;
如果值不为空,则将该值与havingValue指定的值进行比较,如果一样则返回true;否则返回false
如果返回值为false,则该属性修饰的Java对象不被spring管理所以不生效;true生效。该逻辑优先级最高;但是还有一种特殊情况,比如不配置havingValue属性,或者故意把havingValue值写错。则会有两种不同的结果。

不配置havingValue属性matchIfMissing为true则一定会加载配置类,matchIfMissing为false,有配置(name/prefix)加载类,无配置不加载类。

配置错误的havingValue的值:无论matchIfMissing怎么设置,都不会加载。假如havingValue值正确(有配置的name/prefix)配置文件的值相等正确。那么就是正确情况下的true,则必然加载

3.2@ConditionalOnClass,@ConditionalOnMissingClass

@ConditionalOnClass:某个class(或者注解中属性设置的所有的class)位于类路径上,才会实例化一个修饰当前Bean。即判断当前classpath下是否存在指定类(所有),若是则将当前的配置装载入spring容器。也可以这么理解该注解表明输入(xxx.class)几个class,相当于给配置文件添加了一个开关,当检测存在输入类的时候,被改注解修饰的配置类会生效;否则不会将不会实例化。也就是说,如果项目中存在该依赖,会自动开启配置类,这也是spring boot自动装配原理中很重要的注解

@ConditionalOnMissingClass:当指定的类([]所有)不存在时才会被加载

3.2@ConditionalOnBean,@ConditionalOnMissingBean

@ConditionalOnBean:当前spring容器中存在指定的实例对象时,被修饰的对应的@bean才会被spring加载

@ConditionalOnMissingBean:同上正好相反,当不存在时才会被加载。

-------------相关类似的注解还有

@ConditionalOnBean(仅仅在当前上下文中存在某个对象时,才会实例化一个Bean)
@ConditionalOnClass(某个class位于类路径上,才会实例化一个Bean)
@ConditionalOnExpression(当表达式为true的时候,才会实例化一个Bean)
@ConditionalOnMissingBean(仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean)
@ConditionalOnMissingClass(某个class类路径上不存在的时候,才会实例化一个Bean)
@ConditionalOnNotWebApplication(不是web应用)

4.@import

4.1、@Import注解须知

  • @Import只能用在类上 ,@Import通过快速导入的方式实现把实例加入spring的IOC容器中

  • 加入IOC容器的方式有很多种,@Import注解就相对很牛皮了,@Import注解可以用于导入第三方包 ,当然@Bean注解也可以,但是@Import注解快速导入的方式更加便捷

  • @Import注解有三种用法

4.2、@Import的三种用法

@Import的三种用法主要包括:

1、直接填class数组方式
2、ImportSelector方式【重点】
3、ImportBeanDefinitionRegistrar方式

4.2.1、第一种用法:直接填class数组

直接填对应的class数组,class数组可以有0到多个。

语法如下:

@Import({ 类名.class , 类名.class... })
public class TestDemo {

}

对应的import的bean都将加入到spring容器中,这些在容器中bean名称是该类的全类名 ,比如com.yc.类名

4.2.2、第二种用法:ImportSelector方式【重点】

这种方式的前提就是一个类要实现ImportSelector接口,假如我要用这种方法,目标对象是Myclass这个类,分析具体如下:

创建Myclass类并实现ImportSelector接口

public class Myclass implements ImportSelector {
//既然是接口肯定要实现这个接口的方法
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[0];
    }
}

分析实现接口的selectImports方法中的:
1、返回值: 就是我们实际上要导入到容器中的组件全类名【重点 】
2、参数: AnnotationMetadata表示当前被@Import注解给标注的所有注解信息【不是重点】
需要注意的是selectImports方法可以返回空数组但是不能返回null,否则会报空指针异常!

以上分析完毕之后,具体用法步骤如下:

第一步:创建Myclass类并实现ImportSelector接口,这里用于演示就添加一个全类名给其返回值

public class Myclass implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"com.yc.Test.TestDemo3"};
    }
}

第二步:使用ImportSelector方式的Myclass类

@Import({Myclass.class})

这种方式也是springboot自动化配置@EnableAutoConfiguration的涉及到的用法。

可详见spring5学习系列之------3 给容器注册组件三 @Conditional 和 @Import 注解用法_xzjayx的博客-CSDN博客

4.2.3、第三种用法:ImportBeanDefinitionRegistrar方式

同样是一个接口,类似于第二种ImportSelector用法,相似度80%,只不过这种用法比较自定义化注册,具体如下:

第一步:创建Myclass2类并实现ImportBeanDefinitionRegistrar接口

public class Myclass2 implements ImportBeanDefinitionRegistrar {
//该实现方法默认为空
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
      
    }
}

参数分析:
第一个参数:annotationMetadata 和之前的ImportSelector参数一样都是表示当前被@Import注解给标注的所有注解信息
第二个参数表示用于注册定义一个bean

第二步:编写代码,自定义注册bean

public class Myclass2 implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        //指定bean定义信息(包括bean的类型、作用域...)
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(TestDemo4.class);
        //注册一个bean指定bean名字(id)
        beanDefinitionRegistry.registerBeanDefinition("TestDemo4444",rootBeanDefinition);
    }
}

5.@EnableAutoConfiguration和@Configuration

5.1@EnableAutoConfiguration

该注解是springboot中的开启自动配置的注解,简单点说就是Spring Boot根据依赖中的jar包,自动选择实例化某些配置@EnableAutoConfiguration注解通过读取spring.factories文件里面的EnableAutoConfiguration指定的类,来初始化指定类下面的所有加了@Bean的方法,并初始化这个bean。

spring-boot-autoconfigure.jar/META-INF/spring.factories 源码中

这里插一嘴如果我们想自定义springboot-starter,一定要记得在自行写resource中写建文件spring.factories ,然后执行EnableAutoConfiguration=\xxx,\xxx

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.study.spring.boot.configuration.auto.config.AutoFirstBeanConfiguration,\
com.study.spring.boot.configuration.auto.config.AutoSecondBeanConfiguration

----------------------------------------------------

这里多说几句,至于为什么是spring.factories文件,是因为在springboot启动类入口中

SpringApplication.run静态方法运行启动之时,会首选会new springApplication(arg1,arg2);在该方法用会有做设置初始化接口和监听器接口时,会有一个重要方法getSpringFactoriesInstances(xx)
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
在该方法中SpringFactoriesLoader.loadFactoryNames(type, classLoader)该方法里面会加载spring.factories,至此就springboot在启动之时就先读取并且加载了spring.factories文件内容。
 private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            try {
                //指定META-INF/spring.factories的文件名和位置并且加载读取
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                    
                LinkedMultiValueMap result = new LinkedMultiValueMap();

                while(urls.hasMoreElements()) {
                   //解析配置文件,会放入到对应的result中做对应缓存起来
                }

                cache.put(classLoader, result);
                return result;
            } catch (IOException var13) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
            }
        }
    }

5.2@Configuration

该注解仅仅就是表示是一个配置类的@Component,然后被spring容器管理

两种方式的差异

  1. 初始化的时机,@Configuration初始化的方式总是在@EnableAutoConfiguration初始化方式之前
  2. @Configuration初始化的顺序和扫描的过程相关,并不能进行有效的进行指定,不方便确定文件加载的顺序
  3. @EnableAutoConfiguration可以通过@AutoConfigureAfter、@AutoConfigureBefore 和 @AutoConfigureOrder来指定类的加载顺序
  4. @Configuration初始化会先初始化所有被扫到加了@Configuration文件的@PostConstruct注解,然后再初始化这些文件里面的@Bean注解,但是@EnableAutoConfiguration是根据文件来进行初始化的,所以会初始化完一个文件的@PostConstruct注解,然后再初始化这个文件的@Bean注解,然后再接着处理另外的文件同一个来回。

所以需要提供bean给其他jar包进行使用的时候,最好使用@EnableAutoConfiguration方式(spring-boot-starters里面的都是通过这种方式来进行提供的,他的所有初始化的过程全部在spring-boot-autoconfigure项目中),因为能更好的控制类文件的加载顺序。有助于维护更佳复杂的项目。

另外需要注意一点的就是,如果@EnableAutoConfiguration提供的类名称在扫描的路径之中,spring-boot会把这些类作为configuration先进行初始化了。然后@AutoConfigureAfter@AutoConfigureBefore 和 @AutoConfigureOrder 这类指定顺序的注解都会失效

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值