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

按照如下路径查看

// 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的自动配置 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 的配置

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

通过重写 ImportBeanDefinitionRegistrarregisterBeanDefinitions 方法。

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 
  1. 使用@EnableConfigurationProperties(JDBCProperties.class)ConfigData 注册为Bean,并提供给ConfigurationTest使用 。可将ConfigData作为参数注入到构造函数和普通函数中。

  2. 可以用以下方式引用被@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); 	} 

发表评论

评论已关闭。

相关文章