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

作者:twilight0402 时间:2022-07-22 14:27:43 

@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. @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<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> 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<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> 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<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

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

// 6. org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories()
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}

try {
Enumeration<URL> 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的自动配置: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 的配置

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

}

导入一个普通类

package com.example.ssmpdemo.entity;

public class TestA {
   public void fun(){
       System.out.println("testA");
   }
}

导入一个配置类

package com.example.ssmpdemo.entity;

import org.springframework.context.annotation.Configuration;

@Configuration
public class TestB {
   public void fun(){
       System.out.println("testB");
   }
}

通过实现 ImportSelector 接口

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"};
   }
}

通过重写 ImportBeanDefinitionRegistrar 的 registerBeanDefinitions 方法。

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中。

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

2.使用@EnableConfigurationProperties(JDBCProperties.class) 将 ConfigData 注册为Bean,并提供给ConfigurationTest使用 。可将ConfigData作为参数注入到构造函数和普通函数中。

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

可以直接把 ConfigData 当成Bean使用

/**
    * 可直接被注入
    */
   @Autowired
   private ConfigData configData;

可以用构造函数传入进来

@Data
@Configuration
@EnableConfigurationProperties(value = ConfigData.class )
public class ConfigurationTest {
   private ConfigData configData2;
   /**
    * 作为构造函数的参数注入
    * @param data
    */
   ConfigurationTest(ConfigData data){
       this.configData2 = data;
   }

也可以作为@Bean的方法函数的参数。只有当前类(ConfigurationTest)才可

/**
    * 直接作为函数的参数
    * @param data
    * @return
    */
   @Bean(name = "configData2")
   HashMap<String, String> getBean(ConfigData data){
       return new HashMap<>(0);
   }

可以省略ConfigData直接将字段注入到返回结果中。

@Bean
@ConfigurationProperties(value = "spring.datasource.druid")
HashMap<String, String> getBean2(ConfigData data){
   // 会自动为hashMap赋值,或使用set方法为对象赋值
   return new HashMap<>();
}

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

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EnableConfigurationPropertiesRegistrar.class)
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);
   }

来源:https://www.cnblogs.com/twilight0402/p/16846942.html

标签:SpringBoot,常用,注解
0
投稿

猜你喜欢

  • C#文件后缀名的详细介绍

    2022-06-03 15:07:41
  • Android UI效果之绘图篇(四)

    2022-08-07 19:26:12
  • java语言基础之标识符和命名规则详解

    2023-04-21 16:50:18
  • C#警惕匿名方法造成的变量共享实例分析

    2021-08-26 19:35:22
  • 基于spring AOP @Around @Before @After的区别说明

    2023-12-15 03:08:25
  • Java设计通用的返回数据格式过程讲解

    2023-11-09 00:16:40
  • C# 大数据导出word的假死报错的处理方法

    2022-09-25 07:19:48
  • C#获取日期的星期名称实例代码

    2022-10-22 13:25:53
  • 如何利用反射生成 MyBatisPlus中QueryWrapper动态条件

    2021-10-20 14:59:40
  • JAVA提高第八篇 动态代理技术

    2023-07-19 07:13:12
  • Intellij IDEA 的maven项目通过Java代码实现Jetty的Http服务器(推荐)

    2022-02-19 20:29:13
  • Android自定义View制作仪表盘界面

    2021-10-05 06:45:01
  • MyBatis全局映射文件实现原理解析

    2021-06-21 17:01:29
  • Java一维数组和二维数组元素默认初始化值的判断方式

    2022-03-08 21:17:49
  • C语言实现简单版三子棋

    2023-03-01 22:36:30
  • SpringBoot获取配置文件内容的几种方式总结

    2023-11-24 18:10:48
  • 详解c# 数组(Array)

    2022-04-15 14:19:47
  • 详解Kotlin中的面向对象(二)

    2021-12-29 03:10:00
  • Java cookie和session会话技术介绍

    2021-12-30 06:51:22
  • Android startService的使用与Service生命周期案例详解

    2021-07-14 01:51:24
  • asp之家 软件编程 m.aspxhome.com