@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加载。