@Импортировать аннотацию
@Import
даSpring
Основной компонент конфигурации на основе аннотаций Java.@Import
Аннотации обеспечивают@Bean
Функция аннотации, как и оригиналаSpring
На основе файла конфигурации xml<import>
Функция тегов для организации нескольких разбросанных файлов xml, конечно, здесь заключается в организации нескольких разбросанных файлов xml.@Configuration
тип.
Далее будет описано соответственно@Import
Функция аннотации.
1. Введите другие @Configuration
Предположим, у вас есть следующий интерфейс и два класса реализации:
package com.test
interface ServiceInterface {
void test();
}
class ServiceA implements ServiceInterface {
@Override
public void test() {
System.out.println("ServiceA");
}
}
class ServiceB implements ServiceInterface {
@Override
public void test() {
System.out.println("ServiceB");
}
}
два@Configuration
,вConfigA``@Import``ConfigB
:
package com.test
@Import(ConfigB.class)
@Configuration
class ConfigA {
@Bean
@ConditionalOnMissingBean
public ServiceInterface getServiceA() {
return new ServiceA();
}
}
@Configuration
class ConfigB {
@Bean
@ConditionalOnMissingBean
public ServiceInterface getServiceB() {
return new ServiceB();
}
}
пройти черезConfigA
СоздайтеAnnotationConfigApplicationContext
,ПолучатьServiceInterface
, чтобы увидеть, какая реализация:
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigA.class);
ServiceInterface bean = ctx.getBean(ServiceInterface.class);
bean.test();
}
Результат:ServiceB
.доказывать@Import
Определение класса загружается вместо собственного.
2. Напрямую инициализировать bean-компоненты других классов
существуетSpring 4.2после,@Import
Вы можете напрямую указать класс сущности и загрузить определение класса вcontext
середина. Например, в приведенном выше кодеConfigA
из@Import
превратиться в@Import(ServiceB.class)
, будет генерироватьServiceB
изBean
в контекст контейнера, а затем запуститеmain
метод, вывод:ServiceB
.доказывать@Import
определения классов загружаются в первую очередь, чем их собственные.
3. Укажите класс, реализующий ImportSelector (и DeferredServiceImportSelector) для персонализированной загрузки.
указанная реализацияImportSelector
класс, черезAnnotationMetadata
свойства внутри динамически загружаемых классов.AnnotationMetadata
даImport
Атрибут класса аннотации (если класс является классом аннотации, он распространяется на класс, не являющийся аннотацией, к которому применяется этот класс аннотации).
необходимо реализоватьselectImports
метод, который возвращает@Configuation
или конкретныйBean
полное имя классаString
множество.
package com.test;
class ServiceImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//可以是@Configuration注解修饰的类,也可以是具体的Bean类的全限定名称
return new String[]{"com.test.ConfigB"};
}
}
@Import(ServiceImportSelector.class)
@Configuration
class ConfigA {
@Bean
@ConditionalOnMissingBean
public ServiceInterface getServiceA() {
return new ServiceA();
}
}
бежать сноваmain
метод, вывод:ServiceB
.доказывать@Import
Определение класса загружается вместо собственного. Как правило, если структура основана наAnnotationMetadata
Параметры класса динамической загрузки, вообще пишут доп.Enable
Аннотация, используемая вместе. Например:
package com.test;
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
@Import(ServiceImportSelector.class)
@interface EnableService {
String name();
}
class ServiceImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//这里的importingClassMetadata针对的是使用@EnableService的非注解类
//因为`AnnotationMetadata`是`Import`注解所在的类属性,如果所在类是注解类,则延伸至应用这个注解类的非注解类为止
Map<String , Object> map = importingClassMetadata.getAnnotationAttributes(EnableService.class.getName(), true);
String name = (String) map.get("name");
if (Objects.equals(name, "B")) {
return new String[]{"com.test.ConfigB"};
}
return new String[0];
}
}
После этого вConfigA
добавить заметки в@EnableService(name = "B")
package com.test;
@EnableService(name = "B")
@Configuration
class ConfigA {
@Bean
@ConditionalOnMissingBean
public ServiceInterface getServiceA() {
return new ServiceA();
}
}
бежать сноваmain
метод, вывод:ServiceB
.
также может быть достигнутоDeferredImportSelector
интерфейс, такselectImports
Все возвращенные классы загружаются последними, а не как@Import
Как уже говорилось, сначала загрузите. Например:
package com.test;
class DefferredServiceImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
Map<String, Object> map = importingClassMetadata.getAnnotationAttributes(EnableService.class.getName(), true);
String name = (String) map.get("name");
if (Objects.equals(name, "B")) {
return new String[]{"com.test.ConfigB"};
}
return new String[0];
}
}
ИсправлятьEnableService
аннотация:
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
@Import(DefferredServiceImportSelector.class)
@interface EnableService {
String name();
}
такConfigA
иметь приоритет надDefferredServiceImportSelector
возвращениеConfigB
загрузить, выполнитьmain
метод, вывод:ServiceA
4. Укажите класс, реализующий ImportBeanDefinitionRegistrar для персонализированной загрузки.
иImportSelector
Использование похоже на Цель, но если мы хотим переопределитьBean
, такие как динамическое внедрение свойств, изменениеBean
тип иScope
Подождите, это нужно реализовать, указавImportBeanDefinitionRegistrar
реализация класса. Например:
определениеServiceC
package com.test;
class ServiceC implements ServiceInterface {
private final String name;
ServiceC(String name) {
this.name = name;
}
@Override
public void test() {
System.out.println(name);
}
}
определениеServiceImportBeanDefinitionRegistrar
динамическая регистрацияServiceC
,ИсправлятьEnableService
package com.test;
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
@Import(ServiceImportBeanDefinitionRegistrar.class)
@interface EnableService {
String name();
}
class ServiceImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Map<String, Object> map = importingClassMetadata.getAnnotationAttributes(EnableService.class.getName(), true);
String name = (String) map.get("name");
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(ServiceC.class)
//增加构造参数
.addConstructorArgValue(name);
//注册Bean
registry.registerBeanDefinition("serviceC", beanDefinitionBuilder.getBeanDefinition());
}
}
И, согласно следующему анализу исходного кода, мы можем знать,ImportBeanDefinitionRegistrar
существует@Bean
После того, как аннотация загружена, ее необходимо изменитьConfigA
удалить@ConditionalOnMissingBean
аннотированныйBean
, иначе он обязательно сгенерируетConfigA
изServiceInterface
package com.test;
@EnableService(name = "TestServiceC")
@Configuration
class ConfigA {
// @Bean
// @ConditionalOnMissingBean
// public ServiceInterface getServiceA() {
// return new ServiceA();
// }
}
бежать заmain
, вывод:TestServiceC
Анализ исходного кода, связанный с @Import
Разбор нагрузки@Import
Аннотации расположены по адресуBeanFactoryPostProcessor
При обработке:
AbstractApplicationContext
изrefresh
метод
-> invokeBeanFactoryPostProcessors(beanFactory);
-> PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
-> registryProcessor.postProcessBeanDefinitionRegistry(registry);
здесьregistryProcessor
, мы ссылаемся наConfigurationClassPostProcessor
ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(registry)
-> processConfigBeanDefinitions(registry)
:
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
//省略一些配置检查与设置的逻辑
//根据@Order注解,排序所有的@Configuration类
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// 创建ConfigurationClassParser解析@Configuration类
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
//剩余没有解析的@Configuration类
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
//已经解析的@Configuration类
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
//解析
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// 生成类定义读取器读取类定义
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
if (registry.getBeanDefinitionCount() > candidateNames.length) {
//省略检查是否有其他需要加载的配置的逻辑
}
}
while (!candidates.isEmpty());
//省略后续清理逻辑
}
вparser.parse(candidates)
Логика состоит в основном изorg.springframework.context.annotation.ConfigurationClassParser
реализация, функция заключается в загрузке@Import
Аннотация и непосредственно@Import
аннотация.reader.loadBeanDefinitions(configClasses);
Логика состоит в основном изorg.springframework.context.annotation.ConfigurationClassBeanDefinitionReader
изloadBeanDefinitionsForConfigurationClass
Метод реализован, и функция состоит в том, чтобы преобразовать разобранную выше конфигурацию вBeanDefinition
этоBean
определение.
1. Загрузите аннотацию @Import
org.springframework.context.annotation.ConfigurationClassParser
прежде всегоparse
метод
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
//这里的parse实际上就是调用下面即将分析的doProcessConfigurationClass
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
//最后处理所有的`DeferredImportSelector`,符合上面提到的`DeferredImportSelector`的功能
this.deferredImportSelectorHandler.process();
}
@Nullable
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
//处理`@Component`注解的MemberClass相关代码...
//处理`@PropertySource`注解相关代码...
//处理`@ComponentScan`注解相关代码...
//处理`@Import`注解:
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
//处理`@ImportResource`注解相关代码...
//处理`@Bean`注解相关代码...
//处理接口方法相关代码...
//处理父类相关代码...
}
пройти черезgetImports
метод, собирая соответствующие@Import
класс внутри.
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
Set<SourceClass> imports = new LinkedHashSet<>();
Set<SourceClass> visited = new LinkedHashSet<>();
//递归查询所有注解以及注解的注解是否包含@Import
collectImports(sourceClass, imports, visited);
return imports;
}
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
throws IOException {
//记录是否已经扫描过这个类,如果扫描过就不重复添加,防止重复或者死循环
if (visited.add(sourceClass)) {
for (SourceClass annotation : sourceClass.getAnnotations()) {
String annName = annotation.getMetadata().getClassName();
//对于非@Import注解,递归查找其内部是否包含@Import注解
if (!annName.equals(Import.class.getName())) {
collectImports(annotation, imports, visited);
}
}
//添加@Import注解里面的所有配置类
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
После сбора его можно проанализировать.
2. Разберите аннотацию @Import
Способ разбора:processImports
//在解析时,入栈,解析结束后,出栈,通过检查栈中是否有当前类,判断是否有循环依赖
private final ImportStack importStack = new ImportStack();
//记录所有的ImportBeanDefinitionRegistrar
private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars = new LinkedHashMap<>();
//解析也是递归方法
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
//通过importStack检查循环依赖
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
//入栈
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
//处理ImportSelector接口的实现类
Class<?> candidateClass = candidate.loadClass();
//创建这些Selector实例
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
//查看是否有过滤器
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null) {
exclusionFilter = exclusionFilter.or(selectorFilter);
}
//如果是DeferredImportSelector,则用deferredImportSelectorHandler处理
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
//如果不是DeferredImportSelector,调用selectImports方法获取要加载的类全限定名称,递归调用本方法继续解析
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// 处理ImportBeanDefinitionRegistrar接口的实现类
Class<?> candidateClass = candidate.loadClass();
//同样的,创建这些ImportBeanDefinitionRegistrar实例
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
//放入importBeanDefinitionRegistrar,用于后面加载
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
//处理@Configuration注解类,或者是普通类(直接生成Bean)
//在栈加上这个类
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
//递归回到doProcessConfigurationClass处理@Configuration注解类 processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}
Таким образом, все@Conditional
связанный с классом@Import
Аннотация загружается и анализируется, что представляет собой большой рекурсивный процесс.
3. Преобразование в BeanDefinition и регистрация в контейнере
org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader
изloadBeanDefinitionsForConfigurationClass
метод:
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
//对Import完成的,加载其Import的BeanDefinition
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
//加载@Bean注解的方法生成的Bean的Definition
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
//@ImportResource 注解加载的
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
//加载ImportBeanDefinitionRegistrar加载的Bean的Definition
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
Отсюда мы можем понять, почему мы сказали ранее@Bean
аннотированныйBean
будет иметь приоритет надImportBeanDefinitionRegistrar
возвращениеBean
нагрузка.