← 文章归档27-Spring Boot 注解开关原理
约 16 分钟阅读阅读 — 次
Spring 注解开关原理
1. 如果完成自动化配置
- 定义一个Annotation,让使用了这个Annotaion的应用程序自动化地注入一些类或者做一些底层的事情。
- 我们使用
@Import注解配合一个配置类来完成上面的事情。
- 最简单的注解:
EnableContentService,使用这个注解的程序会自动注入ContentService这个Bean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Import(ContentConfiguration.class) public @interface EnableContentService {}
public interface ContentService { void doSomething(); }
public class SimpleContentService implements ContentService { @Override public void doSomething() { System.out.println("do some simple things"); } }
|
- 之后在应用程序入口使用
@EnableContentService注解,Spring Boot使用了ImportSelector来完成
2. ImportSelector的使用
- 修改
@EnableContentService注解,添加属性policy,并且import一个Selector
1 2 3 4 5 6
| @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Import(ContentImportSelector.class) public @interface EnableContentService { String policy() default "simple"; }
|
- 这个
ContentImportSelector根据EnableContentService注解里的policy加载不同的bean:也就是policy如果是core,则会加载CoreContentService,否则会加载SimpleContentService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class ContentImportSelector implements ImportSelector {
@Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { Class<?> annotationType = EnableContentService.class; AnnotationAttributes attributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes( annotationType.getName(), false)); String policy = attributes.getString("policy"); if ("core".equals(policy)) { return new String[] { CoreContentConfiguration.class.getName() }; } else { return new String[] { SimpleContentConfiguration.class.getName() }; } }
}
|
2.1. CoreContentService
1 2 3 4 5 6
| public class CoreContentService implements ContentService { @Override public void doSomething() { System.out.println("do some import things"); } }
|
2.2. CoreContentConfiguration
1 2 3 4 5 6
| public class CoreContentConfiguration { @Bean public ContentService contentService() { return new CoreContentService(); } }
|
3. ImportSelector在Spring Boot中使用
- SpringBoot里的
ImportSelector是通过SpringBoot提供的@EnableAutoConfiguration这个注解里完成的。
- 这个
@EnableAutoConfiguration注解可以显式地调用,否则它会在@SpringBootApplication注解中隐式地被调用。
@EnableAutoConfiguration注解中使用了EnableAutoConfigurationImportSelector作为ImportSelector。下面这段代码就是EnableAutoConfigurationImportSelector中进行选择的具体代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Override public String[] selectImports(AnnotationMetadata metadata) { try { AnnotationAttributes attributes = getAttributes(metadata); List<String> configurations = getCandidateConfigurations(metadata, attributes); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(metadata, attributes); configurations.removeAll(exclusions); configurations = sort(configurations); recordWithConditionEvaluationReport(configurations, exclusions); return configurations.toArray(new String[configurations.size()]); } catch (IOException ex) { throw new IllegalStateException(ex); } }
|
- 其中
getCandidateConfigurations方法将获取配置类:
1 2 3 4 5
| protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { return SpringFactoriesLoader.loadFactoryNames( getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); }
|
SpringFactoriesLoader.loadFactoryNames方法会根据FACTORIES_RESOURCE_LOCATION这个静态变量从所有的jar包中读取META-INF/spring.factories文件信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); try { Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); List<String> result = new ArrayList<String>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url)); String factoryClassNames = properties.getProperty(factoryClassName); result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames))); } return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }
|
getCandidateConfigurations方法中的getSpringFactoriesLoaderFactoryClass方法返回的是EnableAutoConfiguration.class,所以会过滤出key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的值。
- 之后根据文件内容进行配置初始化
4. 参考
- SpringBoot自动化配置的注解开关原理
评论