• SpringBoot 常用注解的原理和使用


    @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

    按照如下路径查看

    Copy
    // 1. @EnableAutoConfiguration @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { } // 2. AutoConfigurationImportSelector.class#selectImports() public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } } // 3. AutoConfigurationImportSelector.class#getAutoConfigurationEntry() protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); List configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); Set exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = getConfigurationClassFilter().filter(configurations); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); } // 4. AutoConfigurationImportSelector.class#getCandidateConfigurations() protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " + "are using a custom packaging, make sure that file is correct."); return configurations; } // 5. org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames() public static List loadFactoryNames(Class factoryType, @Nullable ClassLoader classLoader) { String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); }

    最终使用 loadSpringFactories(@Nullable ClassLoader classLoader) 方法读取所有配置文件。

    Copy
    // 6. org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories() private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap result = cache.get(classLoader); if (result != null) { return result; } try { Enumeration urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap<>(); while (urls.hasMoreElements()) { // 读取所有jar包下的 /META-INF/spring.factories URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim(); for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { result.add(factoryTypeName, factoryImplementationName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }

    tomcat的自动配置内置于springboot的autoconfiguration中。参考tomcat的自动配置 https://www.cnblogs.com/zhaokejin/p/15626392.html

    mybatis-plus的配置没有被springboot包括。因此mybatis-stater中包含一个包mybatis-spring-boot-autoconfigure,这其中配置了需要自动配置的类。

    因此我们也可以在自己的项目下新建 /META-INF/spring.factories ,并配置自动配置类。

    @Import#

    @Import 用于导入配置类或需要前置加载的类。被导入的类会注册为Bean,可直接作为Bean被引用。它的 value 属性可以支持三种类型:

    • @Configuration 修饰的配置类、或普通类(4.2版本之后可以)。
    • ImportSelector 接口的实现。
    • ImportBeanDefinitionRegistrar 接口的实现。

    @Import 的配置

    Copy
    @Configuration @Import(value = {TestA.class, TestB.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class}) public class ConfigurationTest { }

    导入一个普通类

    Copy
    package com.example.ssmpdemo.entity; public class TestA { public void fun(){ System.out.println("testA"); } }

    导入一个配置类

    Copy
    package com.example.ssmpdemo.entity; import org.springframework.context.annotation.Configuration; @Configuration public class TestB { public void fun(){ System.out.println("testB"); } }

    通过实现 ImportSelector 接口

    Copy
    import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{"com.example.ssmpdemo.entity.TestC"}; } }

    通过重写 ImportBeanDefinitionRegistrarregisterBeanDefinitions 方法。

    Copy
    import com.example.ssmpdemo.entity.TestD; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.type.AnnotationMetadata; public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { RootBeanDefinition root = new RootBeanDefinition(TestD.class); registry.registerBeanDefinition("testD", root); } }

    @ConfigurationProperties#

    • 支持常见的下划线、中划线和驼峰的转换。支持对象引导。比如:user.friend.name 代表的是user对象中的friend对象中的name
    • 需要有set()方法
    • 只添加 @ConfigurationProperties(prefix = "xxx") 并不会生效,需要配合 @Configuration 让容器识别到。
    • @EnableConfigurationProperties(value = ConfigData.class ) 会将value中指定的类注册为Bean,可直接用 @AutoWired 引用。
    1. 定义一个类用来记录所有字段,并使用@ConfigurationProperties(prefix = "xxx")将数据注入到ConfigData中。
    Copy
    package com.example.ssmpdemo.entity; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; /** * 用来记录Configuration的数据 * @author wangc */ @Data @ConfigurationProperties(value = "spring.datasource.druid") public class ConfigData { private String driverClassName; private String url; private String username; private String password; } # 对应的yml文件 spring: datasource: druid: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:5506/ssmpdemo?serverTimezone=UTC username: root password: xxxx
    1. 使用@EnableConfigurationProperties(JDBCProperties.class)ConfigData 注册为Bean,并提供给ConfigurationTest使用 。可将ConfigData作为参数注入到构造函数和普通函数中。

    2. 可以用以下方式引用被@ConfigurationProperties(value = "spring.datasource.druid")修饰的ConfigData

    • 可以直接把 ConfigData 当成Bean使用
      Copy
      /** * 可直接被注入 */ @Autowired private ConfigData configData;
    • 可以用构造函数传入进来
      Copy
      @Data @Configuration @EnableConfigurationProperties(value = ConfigData.class ) public class ConfigurationTest { private ConfigData configData2; /** * 作为构造函数的参数注入 * @param data */ ConfigurationTest(ConfigData data){ this.configData2 = data; }
    • 也可以作为@Bean的方法函数的参数。只有当前类(ConfigurationTest)才可
      Copy
      /** * 直接作为函数的参数 * @param data * @return */ @Bean(name = "configData2") HashMap getBean(ConfigData data){ return new HashMap<>(0); }
    • 可以省略ConfigData直接将字段注入到返回结果中。
      Copy
      @Bean @ConfigurationProperties(value = "spring.datasource.druid") HashMap getBean2(ConfigData data){ // 会自动为hashMap赋值,或使用set方法为对象赋值 return new HashMap<>(); }

    EnableConfigurationProperties注解的内部如下,它导入了一个实现了 ImportBeanDefinitionRegistrar 接口的类。

    Copy
    @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(EnableConfigurationPropertiesRegistrar.class)
    Copy
    class EnableConfigurationPropertiesRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { registerInfrastructureBeans(registry); ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry); // 获得@EnableConfigurationProperties的value指向的对象,并注册。 getTypes(metadata).forEach(beanRegistrar::register); }
  • 相关阅读:
    【计网】(一) 集线器、网桥、交换机、路由器等概念
    UI自动化测试实践
    让照片人物开口说话,SadTalker 安装及使用(避坑指南)
    HTML5简介
    git:一个本地仓库绑定多个远程的方法以及遇到的问题
    【Python基础入门3】转义字符和原字符
    基于静电放电算法优化概率神经网络PNN的分类预测 - 附代码
    HPC集群自动弹性扩缩的两种实现方式
    京东API商品数据获取方法详解
    6.基于蜻蜓优化算法 (DA)优化的VMD参数(DA-VMD)
  • 原文地址:https://www.cnblogs.com/twilight0402/p/16846942.html