@Import注解

@Import注解
@Import是Spring基于 Java 注解配置的主要组成部分。@Import注解提供了@Bean注解的功能,同时还有原来Spring基于 xml 配置文件里的<import>标签组织多个分散的xml文件的功能,当然在这里是组织多个分散的@Configuration的类。

下面将分别说明@Import注解的功能。

1. 引入其他的@Configuration

 1 //假设有如下接口和两个实现类:
 2 package com.test
 3 interface ServiceInterface {
 4     void test();
 5 }
 6 
 7 class ServiceA implements ServiceInterface {
 8 
 9     @Override
10     public void test() {
11         System.out.println("ServiceA");
12     }
13 }
14 
15 class ServiceB implements ServiceInterface {
16 
17     @Override
18     public void test() {
19         System.out.println("ServiceB");
20     }
21 }
22 //两个@Configuration,其中ConfigA``@Import``ConfigB:
23 
24 package com.test
25 @Import(ConfigB.class)
26 @Configuration
27 class ConfigA {
28     @Bean
29     @ConditionalOnMissingBean
30     public ServiceInterface getServiceA() {
31         return new ServiceA();
32     }
33 }
34 
35 @Configuration
36 class ConfigB {
37     @Bean
38     @ConditionalOnMissingBean
39     public ServiceInterface getServiceB() {
40         return new ServiceB();
41     }
42 }
43 //通过ConfigA创建AnnotationConfigApplicationContext,获取ServiceInterface,看是哪种实现:
44 
45 public static void main(String[] args) {
46     ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigA.class);
47     ServiceInterface bean = ctx.getBean(ServiceInterface.class);
48     bean.test();
49 }
50 //输出为:ServiceB.证明@Import的优先于本身的的类定义加载。

2. 直接初始化其他类的Bean
在Spring 4.2之后,@Import可以直接指定实体类,加载这个类定义到context中。 例如把上面代码中的ConfigA的@Import修改为@Import(ServiceB.class),就会生成ServiceB的Bean到容器上下文中,之后运行main方法,输出为:ServiceB.证明@Import的优先于本身的的类定义加载.

3. 指定实现ImportSelector(以及DefferredServiceImportSelector)的类,用于个性化加载
指定实现ImportSelector的类,通过AnnotationMetadata里面的属性,动态加载类。AnnotationMetadata是Import注解所在的类属性(如果所在类是注解类,则延伸至应用这个注解类的非注解类为止)。

 1 //需要实现selectImports方法,返回要加载的@Configuation或者具体Bean类的全限定名的String数组。
 2 package com.test;
 3 class ServiceImportSelector implements ImportSelector {
 4     @Override
 5     public String[] selectImports(AnnotationMetadata importingClassMetadata) {
 6         //可以是@Configuration注解修饰的类,也可以是具体的Bean类的全限定名称
 7         return new String[]{"com.test.ConfigB"};
 8     }
 9 }
10 
11 @Import(ServiceImportSelector.class)
12 @Configuration
13 class ConfigA {
14     @Bean
15     @ConditionalOnMissingBean
16     public ServiceInterface getServiceA() {
17         return new ServiceA();
18     }
19 }
20 //再次运行main方法,输出:ServiceB.证明@Import的优先于本身的的类定义加载。 一般的,框架中如果基于AnnotationMetadata的参数实现动态加载类,一般会写一个额外的Enable注解,配合使用。例如:
21 
22 package com.test;
23 
24 @Retention(RetentionPolicy.RUNTIME)
25 @Documented
26 @Target(ElementType.TYPE)
27 @Import(ServiceImportSelector.class)
28 @interface EnableService {
29     String name();
30 }
31 
32 class ServiceImportSelector implements ImportSelector {
33     @Override
34     public String[] selectImports(AnnotationMetadata importingClassMetadata) {
35         //这里的importingClassMetadata针对的是使用@EnableService的非注解类
36         //因为`AnnotationMetadata`是`Import`注解所在的类属性,如果所在类是注解类,则延伸至应用这个注解类的非注解类为止
37         Map<String , Object> map = importingClassMetadata.getAnnotationAttributes(EnableService.class.getName(), true);
38         String name = (String) map.get("name");
39         if (Objects.equals(name, "B")) {
40             return new String[]{"com.test.ConfigB"};
41         }
42         return new String[0];
43     }
44 }
45 //之后,在ConfigA中增加注解@EnableService(name = "B")
46 
47 package com.test;
48 @EnableService(name = "B")
49 @Configuration
50 class ConfigA {
51     @Bean
52     @ConditionalOnMissingBean
53     public ServiceInterface getServiceA() {
54         return new ServiceA();
55     }
56 }
57 //再次运行main方法,输出:ServiceB.
58 
59 //还可以实现DeferredImportSelector接口,这样selectImports返回的类就都是最后加载的,而不是像@Import注解那样,先加载。 例如:
60 
61 package com.test;
62 class DefferredServiceImportSelector implements DeferredImportSelector {
63     @Override
64     public String[] selectImports(AnnotationMetadata importingClassMetadata) {
65         Map<String, Object> map = importingClassMetadata.getAnnotationAttributes(EnableService.class.getName(), true);
66         String name = (String) map.get("name");
67         if (Objects.equals(name, "B")) {
68             return new String[]{"com.test.ConfigB"};
69         }
70         return new String[0];
71     }
72 }
73 //修改EnableService注解:
74 
75 @Retention(RetentionPolicy.RUNTIME)
76 @Documented
77 @Target(ElementType.TYPE)
78 @Import(DefferredServiceImportSelector.class)
79 @interface EnableService {
80     String name();
81 }
82 //这样ConfigA就优先于DefferredServiceImportSelector返回的ConfigB加载,执行main方法,输出:ServiceA

4. 指定实现ImportBeanDefinitionRegistrar的类,用于个性化加载
与ImportSelector用法与用途类似,但是如果我们想重定义Bean,例如动态注入属性,改变Bean的类型和Scope等等,就需要通过指定实现ImportBeanDefinitionRegistrar的类实现。例如:

  1 //定义ServiceC
  2 package com.test;
  3 class ServiceC implements ServiceInterface {
  4 
  5     private final String name;
  6 
  7     ServiceC(String name) {
  8         this.name = name;
  9     }
 10 
 11     @Override
 12     public void test() {
 13         System.out.println(name);
 14     }
 15 }
 16 //定义ServiceImportBeanDefinitionRegistrar动态注册ServiceC,修改EnableService
 17 
 18 package com.test;
 19 
 20 @Retention(RetentionPolicy.RUNTIME)
 21 @Documented
 22 @Target(ElementType.TYPE)
 23 @Import(ServiceImportBeanDefinitionRegistrar.class)
 24 @interface EnableService {
 25     String name();
 26 }
 27 
 28 class ServiceImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
 29     @Override
 30     public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
 31         Map<String, Object> map = importingClassMetadata.getAnnotationAttributes(EnableService.class.getName(), true);
 32         String name = (String) map.get("name");
 33         BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(ServiceC.class)
 34                 //增加构造参数
 35                 .addConstructorArgValue(name);
 36         //注册Bean
 37         registry.registerBeanDefinition("serviceC", beanDefinitionBuilder.getBeanDefinition());
 38     }
 39 }
 40 //并且根据后面的源代码解析可以知道,ImportBeanDefinitionRegistrar 在 @Bean 注解之后加载,所以要修改ConfigA去掉其中被@ConditionalOnMissingBean注解的Bean,否则一定会生成ConfigA的ServiceInterface
 41 
 42 package com.test;
 43 @EnableService(name = "TestServiceC")
 44 @Configuration
 45 class ConfigA {
 46 //    @Bean
 47 //    @ConditionalOnMissingBean
 48 //    public ServiceInterface getServiceA() {
 49 //        return new ServiceA();
 50 //    }
 51 }
 52 //之后运行main,输出:TestServiceC
 53 
 54 //@Import相关源码解析
 55 /*
 56 加载解析@Import注解位于BeanFactoryPostProcessor处理的时候:
 57 
 58 AbstractApplicationContext的refresh方法
 59 
 60 -> invokeBeanFactoryPostProcessors(beanFactory);
 61 
 62 -> PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
 63 
 64 -> registryProcessor.postProcessBeanDefinitionRegistry(registry);
 65 
 66 这里的registryProcessor,我们指ConfigurationClassPostProcessor
 67 
 68 ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(registry)
 69 
 70 -> processConfigBeanDefinitions(registry):
 71 */
 72 public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
 73     //省略一些配置检查与设置的逻辑
 74 
 75     //根据@Order注解,排序所有的@Configuration类
 76     configCandidates.sort((bd1, bd2) -> {
 77             int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
 78             int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
 79             return Integer.compare(i1, i2);
 80         });
 81 
 82     // 创建ConfigurationClassParser解析@Configuration类
 83     ConfigurationClassParser parser = new ConfigurationClassParser(
 84             this.metadataReaderFactory, this.problemReporter, this.environment,
 85             this.resourceLoader, this.componentScanBeanNameGenerator, registry);
 86 
 87     //剩余没有解析的@Configuration类
 88     Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
 89     //已经解析的@Configuration类
 90     Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
 91     do {
 92         //解析
 93         parser.parse(candidates);
 94         parser.validate();
 95 
 96         Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
 97         configClasses.removeAll(alreadyParsed);
 98 
 99         // 生成类定义读取器读取类定义
100         if (this.reader == null) {
101             this.reader = new ConfigurationClassBeanDefinitionReader(
102                     registry, this.sourceExtractor, this.resourceLoader, this.environment,
103                     this.importBeanNameGenerator, parser.getImportRegistry());
104         }
105         this.reader.loadBeanDefinitions(configClasses);
106         alreadyParsed.addAll(configClasses);
107 
108         candidates.clear();
109         if (registry.getBeanDefinitionCount() > candidateNames.length) {
110             //省略检查是否有其他需要加载的配置的逻辑
111         }
112     }
113     while (!candidates.isEmpty());
114 
115     //省略后续清理逻辑
116 }
117 //其中parser.parse(candidates)的逻辑主要由org.springframework.context.annotation.ConfigurationClassParser实现,功能是加载@Import注解还有即系@Import注解。reader.loadBeanDefinitions(configClasses);的逻辑主要由org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader的loadBeanDefinitionsForConfigurationClass方法实现,功能是将上面解析的配置转换为BeanDefinition就是Bean定义。
118 
119 //1. 加载@Import注解
120 //org.springframework.context.annotation.ConfigurationClassParser
121 
122 //首先是parse方法
123 
124 public void parse(Set<BeanDefinitionHolder> configCandidates) {
125     for (BeanDefinitionHolder holder : configCandidates) {
126         BeanDefinition bd = holder.getBeanDefinition();
127         try {
128             if (bd instanceof AnnotatedBeanDefinition) {
129                 //这里的parse实际上就是调用下面即将分析的doProcessConfigurationClass
130                 parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
131             }
132             else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
133                 parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
134             }
135             else {
136                 parse(bd.getBeanClassName(), holder.getBeanName());
137             }
138         }
139         catch (BeanDefinitionStoreException ex) {
140             throw ex;
141         }
142         catch (Throwable ex) {
143             throw new BeanDefinitionStoreException(
144                     "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
145         }
146     }
147     //最后处理所有的`DeferredImportSelector`,符合上面提到的`DeferredImportSelector`的功能
148     this.deferredImportSelectorHandler.process();
149 }
150 @Nullable
151 protected final SourceClass doProcessConfigurationClass(
152             ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
153             throws IOException {
154     //处理`@Component`注解的MemberClass相关代码...
155     //处理`@PropertySource`注解相关代码...
156     //处理`@ComponentScan`注解相关代码...
157     //处理`@Import`注解:
158     processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
159     //处理`@ImportResource`注解相关代码...
160     //处理`@Bean`注解相关代码...
161     //处理接口方法相关代码...
162     //处理父类相关代码...
163 }
164 //通过getImports方法,采集相关的@Import里面的类。
165 
166 private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
167     Set<SourceClass> imports = new LinkedHashSet<>();
168     Set<SourceClass> visited = new LinkedHashSet<>();
169     //递归查询所有注解以及注解的注解是否包含@Import
170     collectImports(sourceClass, imports, visited);
171     return imports;
172 }
173 private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
174             throws IOException {
175     //记录是否已经扫描过这个类,如果扫描过就不重复添加,防止重复或者死循环
176     if (visited.add(sourceClass)) {
177         for (SourceClass annotation : sourceClass.getAnnotations()) {
178             String annName = annotation.getMetadata().getClassName();
179             //对于非@Import注解,递归查找其内部是否包含@Import注解
180             if (!annName.equals(Import.class.getName())) {
181                 collectImports(annotation, imports, visited);
182             }
183         }
184         //添加@Import注解里面的所有配置类
185         imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
186     }
187 }
188 //采集好之后,就可以解析了。
189 
190 //2. 解析@Import注解
191 //解析的方法是:processImports
192 
193 //在解析时,入栈,解析结束后,出栈,通过检查栈中是否有当前类,判断是否有循环依赖
194 private final ImportStack importStack = new ImportStack();
195 
196 //记录所有的ImportBeanDefinitionRegistrar
197 private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars = new LinkedHashMap<>();
198 
199 //解析也是递归方法
200 private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
201             Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
202             boolean checkForCircularImports) {
203 
204     if (importCandidates.isEmpty()) {
205         return;
206     }
207     //通过importStack检查循环依赖
208     if (checkForCircularImports && isChainedImportOnStack(configClass)) {
209         this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
210     }
211     else {
212         //入栈
213         this.importStack.push(configClass);
214         try {
215             for (SourceClass candidate : importCandidates) {
216                 if (candidate.isAssignable(ImportSelector.class)) {
217                     //处理ImportSelector接口的实现类
218                     Class<?> candidateClass = candidate.loadClass();
219                     //创建这些Selector实例
220                     ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
221                             this.environment, this.resourceLoader, this.registry);
222                     //查看是否有过滤器
223                     Predicate<String> selectorFilter = selector.getExclusionFilter();
224                     if (selectorFilter != null) {
225                         exclusionFilter = exclusionFilter.or(selectorFilter);
226                     }
227                     //如果是DeferredImportSelector,则用deferredImportSelectorHandler处理
228                     if (selector instanceof DeferredImportSelector) {
229                         this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
230                     }
231                     else {
232                         //如果不是DeferredImportSelector,调用selectImports方法获取要加载的类全限定名称,递归调用本方法继续解析
233                         String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
234                         Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
235                         processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
236                     }
237                 }
238                 else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
239                     
240                     // 处理ImportBeanDefinitionRegistrar接口的实现类
241                     Class<?> candidateClass = candidate.loadClass();
242                     //同样的,创建这些ImportBeanDefinitionRegistrar实例
243                     ImportBeanDefinitionRegistrar registrar =
244                             ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
245                                     this.environment, this.resourceLoader, this.registry);
246                     //放入importBeanDefinitionRegistrar,用于后面加载
247                 configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
248                 }
249                 else {
250                     //处理@Configuration注解类,或者是普通类(直接生成Bean)
251                     //在栈加上这个类
252                     this.importStack.registerImport(
253                             currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
254                     //递归回到doProcessConfigurationClass处理@Configuration注解类    processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
255                 }
256             }
257         }
258         catch (BeanDefinitionStoreException ex) {
259             throw ex;
260         }
261         catch (Throwable ex) {
262             throw new BeanDefinitionStoreException(
263                     "Failed to process import candidates for configuration class [" +
264                     configClass.getMetadata().getClassName() + "]", ex);
265         }
266         finally {
267             this.importStack.pop();
268         }
269     }
270 }
271 //这样,所有的@Conditional类相关的@Import注解就加载解析完成了,这是一个大的递归过程。
272 
273 //3. 转换为BeanDefinition注册到容器
274 //org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader的loadBeanDefinitionsForConfigurationClass方法:
275 
276 private void loadBeanDefinitionsForConfigurationClass(
277             ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
278 
279     if (trackedConditionEvaluator.shouldSkip(configClass)) {
280         String beanName = configClass.getBeanName();
281         if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
282             this.registry.removeBeanDefinition(beanName);
283         }
284         this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
285         return;
286     }
287 
288     //对Import完成的,加载其Import的BeanDefinition
289     if (configClass.isImported()) {
290         registerBeanDefinitionForImportedConfigurationClass(configClass);
291     }
292     //加载@Bean注解的方法生成的Bean的Definition
293     for (BeanMethod beanMethod : configClass.getBeanMethods()) {
294         loadBeanDefinitionsForBeanMethod(beanMethod);
295     }
296     //@ImportResource 注解加载的
297     loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
298     //加载ImportBeanDefinitionRegistrar加载的Bean的Definition
299     loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
300 }
301 //通过这里可以看出,为啥之前说@Bean注解的Bean会优先于ImportBeanDefinitionRegistrar返回的Bean加载。

本文转自 Spring全解系列 - @Import注解

posted @ 2023-07-07 15:45  dint  阅读(373)  评论(0编辑  收藏  举报