Spring 3.0之前浇借,創(chuàng)建Bean可以通過xml配置文件與掃描特定包下面的類來將類注入到Spring IOC容器內(nèi)歹嘹。而在Spring 3.0之后提供了JavaConfig的方式恤浪,也就是將IOC容器里Bean的元信息以java代碼的方式進(jìn)行描述扎阶。我們可以通過@Configuration與@Bean這兩個注解配合使用來將原來配置在xml文件里的bean通過java代碼的方式進(jìn)行描述
@Import注解提供了@Bean注解的功能揩晴,同時還有xml配置文件里標(biāo)簽組織多個分散的xml文件的功能筐钟,當(dāng)然在這里是組織多個分散的@Configuration
先看一下@Import注解的源碼:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
{@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
or regular component classes to import.
Class<?>[] value();
}
從源碼里可以看出@Import可以配合 Configuration ,ImportSelector, ImportBeanDefinitionRegistrar 來使用潘拱,下面的or表示也可以把Import當(dāng)成普通的Bean使用
@Import只允許放到類上面疹鳄,不能放到方法上。下面我們來看具體的使用方式
普通使用方法
這種方式可以直接把類加入到Spring IOC容器
@Configuration
@Import(value={UserServiceImpl.class})
public class Config {
}
但是這種方式有一些問題芦岂,那就是只能使用類的無參構(gòu)造方法來創(chuàng)建bean瘪弓,對于有參數(shù)的構(gòu)造方法就無能為力了
結(jié)合ImportBeanDefinitionRegistrar接口
ImportBeanDefinitionRegistrar接口的源碼如下:
public interface ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}
可以看到這個接口唯一的方法是有兩個參數(shù)的
AnnotationMetadata:通過這個參數(shù)可以拿到類的元數(shù)據(jù)信息
BeanDefinitionRegistry:通過這個參數(shù)可以操作IOC容器
我們可以使用一個類來實(shí)現(xiàn)這個接口
public class UserServiceBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {
BeanDefinitionBuilder userService = BeanDefinitionBuilder.rootBeanDefinition(UserServiceImpl.class);
registry.registerBeanDefinition("userService", userService.getBeanDefinition());
}
}
可以看到我們在這個方法里面做一些特殊操作什么的都是可以的,相比較于普通的方式可是靈活了很多
接著我們在@Import注解引入的地方只需要修改為引入UserServiceBeanDefinitionRegistrar就可以了
@Configuration
@Import(value={UserServiceBeanDefinitionRegistrar.class})
public class Config {
}
結(jié)合ImportSelector接口
相比較與實(shí)現(xiàn)ImportBeanDefinitionRegistrar接口之后直接操作Bean容器來說禽最,使用ImportSelector會更加優(yōu)雅一些腺怯,只需要返回需要注入類的全限定名就可以了
ImportSelector接口的源碼如下:
public interface ImportSelector {
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
public class UserServiceImportSelect implements ImportSelector{
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{UserServiceImpl.class.getName()};
}
}
@Configuration()
@Import(value={UserServiceImportSelect.class})
public class Config {
}
相比較三種方式來說可以看到最后這種才是最優(yōu)雅的方式
源碼解析
首先我們就以第三種比較優(yōu)雅的方式出發(fā),使用Call Hierarchy看一下ImportSelector接口的selectImports方法調(diào)用鏈關(guān)系:
看過之前Spring源碼解析文章的同學(xué)都知道川无,refresh方法是用來初始化容器上下文的呛占。跟著這個調(diào)用鏈走下來到中間有一個類是ConfigurationClassPostProcessor,根據(jù)類名我們就可以猜到這個類應(yīng)該是處理配置類(也就是標(biāo)注@Configuration)的懦趋。那么從這開始看吧
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)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
//如果沒有配置類就直接返回
if (configCandidates.isEmpty()) {
return;
}
//對這些配置類根據(jù)Order排序
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;
}
});
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;
}
}
//創(chuàng)建配置類的解析類
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方法進(jìn)行解析晾虑,重點(diǎn)哈
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());
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();
}
}
現(xiàn)在該進(jìn)入ConfigurationClassParser類了
public void parse(Set<BeanDefinitionHolder> configCandidates) {
this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
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));
}
可以看到配置類可能會是三種形式的存在,這三種形式的Bean在操作上有著部分不一樣帜篇,但是大部分又是一樣糙捺,所以Spring用這種模式來處理。不得不感嘆人家設(shè)計的真好
接著往下看
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
//在這里處理Configuration重復(fù)import
//如果同一個配置類被處理兩次笙隙,兩次都屬于被import的則合并導(dǎo)入類继找,返回。如果配置類不是被導(dǎo)入的逃沿,則移除舊使用新的配置類
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
existingClass.mergeImportedBy(configClass);
}
return;
}
else {
this.configurationClasses.remove(configClass);
for (Iterator<ConfigurationClass> it = this.knownSuperclasses.values().iterator(); it.hasNext();) {
if (configClass.equals(it.next())) {
it.remove();
}
}
}
}
SourceClass sourceClass = asSourceClass(configClass);
do {
//接著往下看吧
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
// 處理遞歸類
processMemberClasses(configClass, sourceClass);
// 處理@PropertySource注解
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");
}
}
// 處理 @ComponentScan 注解
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注解,這個是咱們的菜
processImports(configClass, sourceClass, getImports(sourceClass), true);
// 處理@ImportResource 注解
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);
}
}
//處理包含@Bean注解的方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// 處理普通方法
processInterfaces(configClass, sourceClass);
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();
}
}
return null;
}
看到這里好像突然發(fā)現(xiàn)了新大陸呀幻锁,原來我們經(jīng)常見的@Bean凯亮、@ImportResource 、@Import哄尔、@ComponentScan假消、@PropertySource都是在這里處理的呀
咱們的重點(diǎn)還是放在@Import上,對其他幾個注解感興趣的同學(xué)可以自行研究一下
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) {
//如果實(shí)現(xiàn)了ImportSelector接口
if (candidate.isAssignable(ImportSelector.class)) {
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) {
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);
}
}
//如果實(shí)現(xiàn)了ImportBeanDefinitionRegistrar接口
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
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當(dāng)成Configuration來使用就是我們的第一種應(yīng)用的方式
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();
}
}
}
至此@import 源碼閱讀完畢