Spring Import 三种用法与源码解读
最近在看Spring Cloud相关的源码,每次引入一个新的starter,发现都会加一些enable的注解,比如:@EnableDiscoveryClient,用于将应用注册到Eureka Server并将Eureka Server有的服务拉取到微服务系统。点开EnableDiscoveryClient源码,便会发现里面用到了@import注解。源码...
最近在看Spring Cloud相关的源码,每次引入一个新的starter,发现都会加一些enable的注解,比如:@EnableDiscoveryClient,用于将应用注册到Eureka Server并将Eureka Server有的服务拉取到微服务系统。点开EnableDiscoveryClient源码,便会发现里面用到了@import注解。源码如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(EnableDiscoveryClientImportSelector.class)
public @interface EnableDiscoveryClient {
/**
* If true, the ServiceRegistry will automatically register the local server.
*/
boolean autoRegister() default true;
}
这个@import的作用是什么呢,它是如何工作的呢?我们在项目里如何应用@import导入我们自定义的类?
Import的用法
Spring 3.0之前,我们的Bean可以通过xml配置文件与扫描特定包下面的类来将类注入到Spring IOC容器内。Spring 3.0之后提供了JavaConfig的方式,也就是将IOC容器里Bean的元信息以java代码的方式进行描述。我们可以通过@Configuration与@Bean这两个注解配合使用来将原来配置在xml文件里的bean通过java代码的方式进行描述。@Import注解提供了@Bean注解的功能,同时还有xml配置文件里<import>标签组织多个分散的xml文件的功能,当然在这里是组织多个分散的@Configuration。先看看@Import注解的源码吧:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
//这里说了可以配合 Configuration , ImportSelector, ImportBeanDefinitionRegistrar 来使用噢 或者常用的(regular component classes )也就是Bean
/**
* {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
* or regular component classes to import.
*/
Class<?>[] value();
}
从源码里可以看出Import可以配合 Configuration , ImportSelector, ImportBeanDefinitionRegistrar 来使用,下面的or表示也可以把Import当成普通的Bean来使用,只是使用方式上有点区别,@Import只允许放到类上面,不能放到方法上。下面我们来看具体的使用方式。在使用之前我们有几个类在下面几种方式里都会用到,下面把这些公共的类放到一起。
- 首先这里有个User的实体类,做为Bean里的参数,在实际的测试中也可以没有这个类,为了例子的完整性,我还是把代码贴到下面:
package com.ivan.entity;
public class User {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- 定义了一个UserService接口类,为了是体现Spring的面向接口编程。代码如下:
package com.ivan.service;
import com.ivan.entity.User;
public interface UserService {
public int save(User user);
}
- UserService接口对应的实现类,也是我们想注入到Spring IOC容器里的类,代码如下:
package com.ivan.service.impl;
import com.ivan.entity.User;
import com.ivan.service.UserService;
public class UserServiceImpl implements UserService {
public int save(User user) {
System.out.println("调用了当前方法");
return 1;
}
}
- 测试类:
package com.ivan;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.ivan.config.Config;
import com.ivan.service.UserService;
import com.ivan.service.impl.UserServiceImpl;
/**
*
* 功能描述:
*
* @version 2.0.0
* @author zhiminchen
*/
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
UserService userService = (UserService)context.getBean(UserServiceImpl.class);
userService.save(null);
context.close();
}
}
在测试类App里,用到了Config.class,不同的Import使用方式,这里的代码是不一样的,下面来看具体Import在使用上的区别:
1:最简单的导入类的方式
这是最简单的一种将类加入到Spring IOC容器的方式,直接将类的class加到Import的value里,Config的代码如下:
package com.ivan.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import com.ivan.service.impl.UserServiceImpl;
@Configuration
@Import(value={UserServiceImpl.class})
public class Config {
}
启动测试类,发现调用到了我们的UserServiceImpl里的save方法,这样将类注入的方式有个问题就是没法注入参数。也就是说UserServiceImpl提供的应该是无参的构造方法。这种方式注入类在Spring内部用的并不多。最多的使用方式还是下面两种。
2.通过ImportBeanDefinitionRegistrar将类注入到Spring IOC容器
Import注解通过配合ImportBeanDefinitionRegistrar类将类注入Spring IOC容器里。ImportBeanDefinitionRegistrar类的源码如下:
public interface ImportBeanDefinitionRegistrar {
/**
* Register bean definitions as necessary based on the given annotation metadata of
* the importing {@code @Configuration} class.
* <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
* registered here, due to lifecycle constraints related to {@code @Configuration}
* class processing.
* @param importingClassMetadata annotation metadata of the importing class
* @param registry current bean definition registry
*/
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}
从上面的代码可以看出在注入Spring IOC容器的时候,我们肯定是通过registry这个变量了,而importingClassMetadata可以得到类的元数据信息。我们自定义的UserServiceBeanDefinitionRegistrar 类定义如下:
package com.ivan.bean.registrar;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import com.ivan.service.impl.UserServiceImpl;
public class UserServiceBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
BeanDefinitionBuilder userService = BeanDefinitionBuilder.rootBeanDefinition(UserServiceImpl.class);
//通过registry就可以注入到容器里啦
registry.registerBeanDefinition("userService", userService.getBeanDefinition());
}
}
Config类的Import class需要改成UserServiceBeanDefinitionRegistrar.classs。代码如下:
package com.ivan.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import com.ivan.bean.registrar.UserServiceBeanDefinitionRegistrar;
@Configuration(value="ivan_test")
@Import(value={UserServiceBeanDefinitionRegistrar.class})
public class Config {
}
可以明显的看出通过ImportBeanDefinitionRegistrar的方式我们可以对类进行个性化的定制,比如对需要传入的参数进行修改,也可以通过ImportBeanDefinitionRegistrar注入一批相似的类。有BeanDefinitionRegistry对象也有可以控制Spring IOC容器里Bean的定义,想做些什么也就方便很多了。
3.通过ImportSelector方式注入Bean
上面通过ImportBeanDefinitionRegistrar的方式注入的实例需要我们操作BeanDefinitionRegistry 对象,而通过ImportSelector方式我们可以不操作BeanDefinitionRegistry 对象,只需要告诉容器我们需要注入类的完整类名就好。ImportSelector类的源码如下:
public interface ImportSelector {
/**
* Select and return the names of which class(es) should be imported based on
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
上面的代码中可以看出需要返回一个String数组给到容器,传入的参数可以得到注解的元数据信息,现在我们来模拟下我们在Cloud代码里看到的enable都是如何通过ImportSelector方式来实现的。首先我们需要自定义一个注解,源码如下:
package com.ivan.enable;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
import com.ivan.select.UserServiceImportSelect;
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
@Import(UserServiceImportSelect.class)
public @interface EnableUserService {
String name ();
}
EnableUserService 是个注解,里面通过@Import引入了UserServiceImportSelect,具体注入的逻辑在UserServiceImportSelect这个类里面,我们的注解同时定义了一个name属性,这里只是为了测试,在实际中你可以定义你需要的属性,然后在具体的ImportSelect里根据属性的值进行不同的配置。UserServiceImportSelect属性的代码如下:
package com.ivan.select;
import java.util.Map;
import java.util.Map.Entry;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import com.ivan.enable.EnableUserService;
import com.ivan.service.impl.UserServiceImpl;
public class UserServiceImportSelect implements ImportSelector{
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
Map<String , Object> map = importingClassMetadata.getAnnotationAttributes(EnableUserService.class.getName(), true);
for(Entry<String, Object> entry : map.entrySet()){
System.out.println("key is : " + entry.getKey() + " value is : " + entry.getValue());
}
return new String[]{UserServiceImpl.class.getName()};
}
}
可以通过importingClassMetadata这个属性得到用了EnableUserService注解的元信息。同时返回了一个我们需要注入的类的名称,这样就可以注入到容器里啦,Config类的代码如下:
package com.ivan.config;
import org.springframework.context.annotation.Configuration;
import com.ivan.enable.EnableUserService;
@Configuration()
@EnableUserService(name="ivan_test")
public class Config {
}
通过上面三步我们就可以完成自定义enable注解并完成相应Bean的注入,顺带提一下通过ImportBeanDefinitionRegistrar的方式同样可以自定义注解然后注入我们需要的类。必竟ImportBeanDefinitionRegistrar这种方式从灵活性上来说应该是最全的。
源码解读
通过上面几种方式的应用,应该可以掌握@Import注解的应用了。但Spring IOC内部是如何做到类的注入的呢,下面就来读一下Spring IOC内部类的源码。读源码如何找到源码的入口是关键,这里有个小技巧,通过IDE的Call Hierarchy调用链我们可以找到关键的代码,我们知道@Import这个注解无论它多么神,一定会调用到ImportSelector类里的selectImports方法的。我们通过这个方法就能找到如下的调用关系,如下图所示:
在Spring IOC容器加载过程中会调用BeanFactoryPostProcessor方法,以处理如对BeanDefinition进行修改等操作。从调用链上看我们需要关注ConfigurationClassPostProcessor这个类,从名字中我们也可以看出他一定跟@Configuration这个注解有关。下面对ConfigurationClassPostProcessor的关键代码进行注释:
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
/**
*在容器初使化的时候会调用到这个方法里,
**/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
int factoryId = System.identityHashCode(beanFactory);
if (this.factoriesPostProcessed.contains(factoryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + beanFactory);
}
this.factoriesPostProcessed.add(factoryId);
if (!this.registriesPostProcessed.contains(factoryId)) {
// BeanDefinitionRegistryPostProcessor hook apparently not supported...
// Simply call processConfigurationClasses lazily at this point then.
//Configuration肯定是在这个方法里处理的
processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
}
enhanceConfigurationClasses(beanFactory);
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
/** 这里的注释已经说了是处理Configuration的啦
* Build and validate a configuration model based on the registry of
* {@link Configuration} classes.
*/
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<BeanDefinitionHolder>();
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
//这里会将类里有Configuration, Component, ComponentScan, Import, ImportResource 这些注解过的类加到configCandidates容器里
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
//如果没有上面提到的类,则直接返回
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
return;
}
//对晒出来的类根据Order进行排序
// Sort by previously determined @Order value, if applicable
Collections.sort(configCandidates, new Comparator<BeanDefinitionHolder>() {
@Override
public int compare(BeanDefinitionHolder bd1, BeanDefinitionHolder bd2) {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
}
});
// Detect any custom bean name generation strategy supplied through the enclosing application context
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet && sbr.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
//具体的解析是通过ConfigurationClassParser
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
do {
//调用ConfigurationClassParser的parse方法进行解析
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
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) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<String>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<String>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (sbr != null) {
if (!sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
}
上面的分析最终解析Import类是在ConfigurationClassParser这个类上面,从上面的调用图上我们也可以看出会调用到ConfigurationClassParser这个类上,下面对这个类的关键代码进行注释(代码只截取了部分源码,且没按源码里的顺序,只是为了阅读的方便):
class ConfigurationClassParser {
/**
*这是解析的入口方法,传入的参数是有特殊注解的Class类
**/
public void parse(Set<BeanDefinitionHolder> configCandidates) {
this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
//无论bd属于下面的那三种情况,都会调用processConfigurationClass方法
try {
if (bd instanceof AnnotatedBeanDefinition) {
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);
}
}
processDeferredImportSelectors();
}
//下面三种方法用于处理不同的BeanDefinition 类型,但最终都会调用到processConfigurationClass方法
protected final void parse(String className, String beanName) throws IOException {
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
processConfigurationClass(new ConfigurationClass(reader, beanName));
}
protected final void parse(Class<?> clazz, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(clazz, beanName));
}
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName));
}
//这里面处理Configuration 标注的class类
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
//在这里处理Cconfiguration重复import
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
existingClass.mergeImportedBy(configClass);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
}
else {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
for (Iterator<ConfigurationClass> it = this.knownSuperclasses.values().iterator(); it.hasNext();) {
if (configClass.equals(it.next())) {
it.remove();
}
}
}
}
// Recursively process the configuration class and its superclass hierarchy.
//这里通过循环处理上面的注解标注的类,得到SourceClass对象
SourceClass sourceClass = asSourceClass(configClass);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
/**
**这个方法里会处理具体的各种注解类,@Configuration @Import等
*/
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass);
// Process any @PropertySource annotations
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
//在这个方法里处理Import注解,
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
// Process any @ImportResource annotations
if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process default methods on interfaces
processInterfaces(configClass, sourceClass);
// Process superclass, if any
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
// No superclass -> processing is complete
return null;
}
//在这里处理具体的Import
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
//这里看到了熟悉的ImportSelector了吧
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
//这里会把DeferredImportSelector 加入到deferredImportSelectors集合类中,在parse方法里调用接口定义的方法
this.deferredImportSelectors.add(
new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
//在这里处理ImportBeanDefinitionRegistrar,
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
//将import当成Configuration来使用也是我们最简单的一种应用的方式
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
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();
}
}
}
}
上面整体上分析了一下Spring里是如何处理@Import这个注解的,只跟踪了处理ImportSelector这种方式的处理逻辑,代码里都有比较详细的注释。其它两种方式分析的方法大同小异。从整体上来理解的话大概就下面几个流程:
- Spring IOC容器初使化的时候会调用AbstractApplicationContext 的refresh方法
- 在refresh里会调用各种BeanFactoryPostProcessor, 其中就包括我们需要关注的ConfigurationClassPostProcessor。
- ConfigurationClassPostProcessor 不但用于处理@Configuration注解,里面也有处理@Import注解。
- 最终的处理是通过 ConfigurationClassParser 这个类完成对Import各种情况的处理
- Import有三种导入方式,从代码里我们可以看到对于不同方式的处理。
更多推荐
所有评论(0)