[JAVA] 一文带你了解SpringBoot中常用注解的原理和使用

1898 0
Honkers 2022-11-8 17:21:20 | 显示全部楼层 |阅读模式
目录

    @AutoConfiguration@Import@ConfigurationProperties


@AutoConfiguration

读取所有jar包下的 /META-INF/spring.factories 并追加到一个 LinkedMultiValueMap 中。每一个url中记录的文件路径如下:
file:/C:/Users/wangchao/apache-maven-3.5.0/repo/com/baomidou/mybatis-plus-boot-starter/3.5.1/mybatis-plus-boot-starter-3.5.1.jar!/META-INF/spring.factories
按照如下路径查看
  1. // 1. @EnableAutoConfiguration
  2. @Target(ElementType.TYPE)
  3. @Retention(RetentionPolicy.RUNTIME)
  4. @Documented
  5. @Inherited
  6. @AutoConfigurationPackage
  7. @Import(AutoConfigurationImportSelector.class)
  8. public @interface EnableAutoConfiguration {
  9. }
  10. // 2. AutoConfigurationImportSelector.class#selectImports()
  11. public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
  12.                 ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
  13.         @Override
  14.         public String[] selectImports(AnnotationMetadata annotationMetadata) {
  15.                 if (!isEnabled(annotationMetadata)) {
  16.                         return NO_IMPORTS;
  17.                 }
  18.                 AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
  19.                 return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
  20.         }
  21. }
  22. // 3. AutoConfigurationImportSelector.class#getAutoConfigurationEntry()
  23. protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
  24.         if (!isEnabled(annotationMetadata)) {
  25.                 return EMPTY_ENTRY;
  26.         }
  27.         AnnotationAttributes attributes = getAttributes(annotationMetadata);
  28.         List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
  29.         configurations = removeDuplicates(configurations);
  30.         Set<String> exclusions = getExclusions(annotationMetadata, attributes);
  31.         checkExcludedClasses(configurations, exclusions);
  32.         configurations.removeAll(exclusions);
  33.         configurations = getConfigurationClassFilter().filter(configurations);
  34.         fireAutoConfigurationImportEvents(configurations, exclusions);
  35.         return new AutoConfigurationEntry(configurations, exclusions);
  36. }
  37. // 4. AutoConfigurationImportSelector.class#getCandidateConfigurations()
  38. protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
  39.         List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
  40.                         getBeanClassLoader());
  41.         Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
  42.                         + "are using a custom packaging, make sure that file is correct.");
  43.         return configurations;
  44. }
  45. // 5. org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames()
  46. public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
  47.         String factoryTypeName = factoryType.getName();
  48.         return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
  49. }
复制代码
最终使用 loadSpringFactories(@Nullable ClassLoader classLoader) 方法读取所有配置文件。
  1. // 6. org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories()
  2. private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
  3.                 MultiValueMap<String, String> result = cache.get(classLoader);
  4.                 if (result != null) {
  5.                         return result;
  6.                 }
  7.                 try {
  8.                         Enumeration<URL> urls = (classLoader != null ?
  9.                                         classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
  10.                                         ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
  11.                         result = new LinkedMultiValueMap<>();
  12.                         while (urls.hasMoreElements()) {
  13.                             // 读取所有jar包下的 /META-INF/spring.factories
  14.                                 URL url = urls.nextElement();
  15.                                 UrlResource resource = new UrlResource(url);
  16.                                 Properties properties = PropertiesLoaderUtils.loadProperties(resource);
  17.                                 for (Map.Entry<?, ?> entry : properties.entrySet()) {
  18.                                         String factoryTypeName = ((String) entry.getKey()).trim();
  19.                                         for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
  20.                                                 result.add(factoryTypeName, factoryImplementationName.trim());
  21.                                         }
  22.                                 }
  23.                         }
  24.                         cache.put(classLoader, result);
  25.                         return result;
  26.                 }
  27.                 catch (IOException ex) {
  28.                         throw new IllegalArgumentException("Unable to load factories from location [" +
  29.                                         FACTORIES_RESOURCE_LOCATION + "]", ex);
  30.                 }
  31.         }
复制代码
tomcat的自动配置内置于springboot的autoconfiguration中。参考tomcat的自动配置:SpringBoot如何实现Tomcat自动配置
mybatis-plus的配置没有被springboot包括。因此mybatis-stater中包含一个包mybatis-spring-boot-autoconfigure,这其中配置了需要自动配置的类。
因此我们也可以在自己的项目下新建 /META-INF/spring.factories ,并配置自动配置类。

@Import

@Import 用于导入配置类或需要前置加载的类。被导入的类会注册为Bean,可直接作为Bean被引用。它的 value 属性可以支持三种类型:
    被 @Configuration 修饰的配置类、或普通类(4.2版本之后可以)。ImportSelector 接口的实现。ImportBeanDefinitionRegistrar 接口的实现。
@Import 的配置
  1. @Configuration
  2. @Import(value = {TestA.class, TestB.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
  3. public class ConfigurationTest {
  4. }
复制代码
导入一个普通类
  1. package com.example.ssmpdemo.entity;
  2. public class TestA {
  3.     public void fun(){
  4.         System.out.println("testA");
  5.     }
  6. }
复制代码
导入一个配置类
  1. package com.example.ssmpdemo.entity;
  2. import org.springframework.context.annotation.Configuration;
  3. @Configuration
  4. public class TestB {
  5.     public void fun(){
  6.         System.out.println("testB");
  7.     }
  8. }
复制代码
通过实现 ImportSelector 接口
  1. import org.springframework.context.annotation.ImportSelector;
  2. import org.springframework.core.type.AnnotationMetadata;
  3. public class MyImportSelector implements ImportSelector {
  4.     @Override
  5.     public String[] selectImports(AnnotationMetadata importingClassMetadata) {
  6.         return new String[]{"com.example.ssmpdemo.entity.TestC"};
  7.     }
  8. }
复制代码
通过重写 ImportBeanDefinitionRegistrar 的 registerBeanDefinitions 方法。
  1. import com.example.ssmpdemo.entity.TestD;
  2. import org.springframework.beans.factory.support.BeanDefinitionRegistry;
  3. import org.springframework.beans.factory.support.RootBeanDefinition;
  4. import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
  5. import org.springframework.core.type.AnnotationMetadata;
  6. public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
  7.     @Override
  8.     public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  9.         RootBeanDefinition root = new RootBeanDefinition(TestD.class);
  10.         registry.registerBeanDefinition("testD", root);
  11.     }
  12. }
复制代码
@ConfigurationProperties

    支持常见的下划线、中划线和驼峰的转换。支持对象引导。比如:user.friend.name 代表的是user对象中的friend对象中的name需要有set()方法只添加 @ConfigurationProperties(prefix = "xxx") 并不会生效,需要配合 @Configuration 让容器识别到。@EnableConfigurationProperties(value = ConfigData.class ) 会将value中指定的类注册为Bean,可直接用 @AutoWired 引用。
1.定义一个类用来记录所有字段,并使用@ConfigurationProperties(prefix = "xxx")将数据注入到ConfigData中。
  1. package com.example.ssmpdemo.entity;
  2. import lombok.Data;
  3. import org.springframework.boot.context.properties.ConfigurationProperties;
  4. /**
  5. * 用来记录Configuration的数据
  6. * @author wangc
  7. */
  8. @Data
  9. @ConfigurationProperties(value = "spring.datasource.druid")
  10. public class ConfigData {
  11.     private String driverClassName;
  12.     private String url;
  13.     private String username;
  14.     private String password;
  15. }
  16. # 对应的yml文件
  17. spring:
  18.   datasource:
  19.     druid:
  20.       driver-class-name: com.mysql.cj.jdbc.Driver
  21.       url: jdbc:mysql://localhost:5506/ssmpdemo?serverTimezone=UTC
  22.       username: root
  23.       password: xxxx
复制代码
2.使用@EnableConfigurationProperties(JDBCProperties.class) 将 ConfigData 注册为Bean,并提供给ConfigurationTest使用 。可将ConfigData作为参数注入到构造函数和普通函数中。
3.可以用以下方式引用被@ConfigurationProperties(value = "spring.datasource.druid")修饰的ConfigData
可以直接把 ConfigData 当成Bean使用
  1.     /**
  2.      * 可直接被注入
  3.      */
  4.     @Autowired
  5.     private ConfigData configData;
复制代码
可以用构造函数传入进来
  1. @Data
  2. @Configuration
  3. @EnableConfigurationProperties(value = ConfigData.class )
  4. public class ConfigurationTest {
  5.     private ConfigData configData2;
  6.     /**
  7.      * 作为构造函数的参数注入
  8.      * @param data
  9.      */
  10.     ConfigurationTest(ConfigData data){
  11.         this.configData2 = data;
  12.     }
复制代码
也可以作为@Bean的方法函数的参数。只有当前类(ConfigurationTest)才可
  1.     /**
  2.      * 直接作为函数的参数
  3.      * @param data
  4.      * @return
  5.      */
  6.     @Bean(name = "configData2")
  7.     HashMap<String, String> getBean(ConfigData data){
  8.         return new HashMap<>(0);
  9.     }
复制代码
可以省略ConfigData直接将字段注入到返回结果中。
  1. @Bean
  2. @ConfigurationProperties(value = "spring.datasource.druid")
  3. HashMap<String, String> getBean2(ConfigData data){
  4.     // 会自动为hashMap赋值,或使用set方法为对象赋值
  5.     return new HashMap<>();
  6. }
复制代码
EnableConfigurationProperties注解的内部如下,它导入了一个实现了 ImportBeanDefinitionRegistrar 接口的类。
  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Import(EnableConfigurationPropertiesRegistrar.class)
复制代码
  1. class EnableConfigurationPropertiesRegistrar implements ImportBeanDefinitionRegistrar {
  2.     @Override
  3.     public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
  4.         registerInfrastructureBeans(registry);
  5.         ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);
  6.         // 获得@EnableConfigurationProperties的value指向的对象,并注册。
  7.         getTypes(metadata).forEach(beanRegistrar::register);
  8.     }
复制代码
到此这篇关于一文带你了解SpringBoot中常用注解的原理和使用的文章就介绍到这了,更多相关SpringBoot注解内容请搜索中国红客联盟以前的文章或继续浏览下面的相关文章希望大家以后多多支持中国红客联盟!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Honkers

荣誉红客

关注
  • 4004
    主题
  • 36
    粉丝
  • 0
    关注
这家伙很懒,什么都没留下!

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行