你知道Spring是怎样解析装备类的吗?

你知道Spring是怎样解析装备类的吗?

目录

Spring中的第二行代码
ClassPathBeanDefinitionScanner源码剖析
1、经过findCandidateComponents办法完结扫描
2、履行postProcessBeanDefinition办法
3、履行processCommonDefinitionAnnotations办法
4、注册BeanDefinition
Spring是怎样解析装备类的?
1、解析机遇剖析
2、解析源码剖析
总结
完全读懂Spring(二)你知道Spring是怎样解析装备类的吗?

引荐阅览:

Spring官网阅览系列

完全读懂Spring(一)读源码,咱们能够从榜首行读起

Spring履行流程图如下:

假如图片显现不清楚能够拜访如下链接查看高清大图:

Spring履行流程图

这个流程图会跟着咱们的学习不断的变得越来越具体,也会越来越杂乱,期望在这个进程中咱们都能朝着通晓Spring的方针不断前进!

在上篇文章咱们学习了Spring中的榜首行代码,咱们现已知道了Spring中的榜首行代码其实便是创立了一个AnnotatedBeanDefinitionReader方针,这个方针的首要效果便是注册bd(BeanDefinition)到容器中。而且在创立这个方针的进程中,Spring还为容器注册了开天辟地的几个bd,包含ConfigurationClassPostProcessor,AutowiredAnnotationBeanPostProcessor等等。

那么在本文中,咱们就一同来看看Spring中的第二行代码又做了些什么?

Spring中的第二行代码
第二行代码在上面的流程图中现已标示的十分了解了,便是

this.scanner = new ClassPathBeanDefinitionScanner(this);
仅仅简略的创立了一个ClassPathBeanDefinitionScanner方针。那么这个ClassPathBeanDefinitionScanner有什么效果呢?从姓名上来看如同便是这个方针来完结Spring中的扫描的,真的是这样吗?期望同学们能带着这两个问题往下看

ClassPathBeanDefinitionScanner源码剖析
这个类名直译过来便是:类途径下的BeanDefinition的扫描器,所以咱们就直接重视其扫描相关的办法,便是其间的doScan办法。其代码如下:

// 这个办法会完结对指定包名下的class文件的扫描
// basePackages:指定包名,是一个可变参数
protected Set doScan(String... basePackages) {

Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
// 1.findCandidateComponents这个办法是实践完结扫描的办法,也是接下来咱们要剖析的办法
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {、
// 上篇文章中咱们现已剖析过了,完结了@Scope注解的解析
// 参阅《完全读懂Spring(一)读源码,咱们能够从榜首行读起》
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
// 2.假如你对BeanDefinition有必定了解的话,你必定会知道这个判别必定会建立的,这意味着                // 一切扫描出来的bd都会履行postProcessBeanDefinition办法进行一些后置处理
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
// 3. 是不是一个AnnotatedBeanDefinition,假如是的话,还需求进行额定的处理
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
// 4.查看容器中是否现已有这个bd了,假如有就不进行注册了
if (checkCandidate(beanName, candidate)) {
// 下面这段逻辑在上篇文章中都现已剖析过了,这儿就直接越过了
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;

}
上面这段代码首要做了四件事

经过findCandidateComponents办法完结扫描
判别扫描出来的bd是否是一个AbstractBeanDefinition,假如是的话履行postProcessBeanDefinition办法
判别扫描出来的bd是否是一个AnnotatedBeanDefinition,假如是的话履行processCommonDefinitionAnnotations办法
查看容器中是否现已有这个bd了,假如有就不进行注册了
接下来咱们就一步步剖析这个办法,搞了解ClassPathBeanDefinitionScanner究竟能起到什么效果

1、经过findCandidateComponents办法完结扫描
findCandidateComponents办法源码如下:

public Set findCandidateComponents(String basePackage) {

if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
}
else {
// 正常状况下都是进入这个判别,对classpath下的class文件进行扫描
return scanCandidateComponents(basePackage);
}

}
addCandidateComponentsFromIndex
不必过多重视这个办法。正常状况下Spring都是选用扫描classpath下的class文件来完结扫描,可是尽管依据classpath扫描速度十分快,但经过在编译时创立候选静态列表,能够进步大型运用程序的发动功用。在这种办法下,运用程序的一切模块都必须运用这种机制,由于当 ApplicationContext检测到这样的索引时,它将主动运用它而不是扫描类途径。
要生成索引,只需向包含组件扫描指令方针组件的每个模块增加附加依靠项即可:
Maven:

org.springframework spring-context-indexer 5.0.6.RELEASE true咱们有爱好的话能够参阅官网:https://docs.spring.io/spring/docs/5.1.14.BUILD-SNAPSHOT/spring-framework-reference/core.html#beans-scanning-index

这个依靠真实太大了,半响了拉不下来,我这儿就不演示了

scanCandidateComponents(basePackage)
正常状况下咱们的运用都是经过这个办法完结扫描的,其代码如下:

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
// 用来存储回来的bd的调集
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
// 拼接成这种办法:classpath*:com.dmz.spring
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
// 获取到一切的class文件封装而成的Resource方针
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
// 遍历得到的一切class文件封装而成的Resource方针
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
// 经过Resource构建一个MetadataReader方针,这个MetadataReader方针包含了对应class文件的解析出来的class的元信息以及注解元信息
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
// 并不是一切的class文件文件都要被解析成为bd,只要被增加了注解(@Component,@Controller等)才是Spring中的组件
if (isCandidateComponent(metadataReader)) {
// 解析元信息(class元信息以及注解元信息)得到一个ScannedGenericBeanDefinition
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
}
// 省掉剩余的代码
return candidates;
}

在Spring官网阅览(一)容器及实例化 一文中,我画过这样一张图

从上图中能够看出,java class + configuration metadata 终究会转化为一个BenaDefinition,结合咱们上面的代码剖析能够知道,java class + configuration metadata实践上便是一个MetadataReader方针,而转化成一个BenaDefinition则是指经过这个MetadataReader方针创立一个ScannedGenericBeanDefinition。

2、履行postProcessBeanDefinition办法
protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {

// 为bd中的特点设置默许值
beanDefinition.applyDefaults(this.beanDefinitionDefaults);
// 注解办法下这个值必定为null,运用XML装备时,
if (this.autowireCandidatePatterns != null) {
beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));
}

}

// 设置默许值

public void applyDefaults(BeanDefinitionDefaults defaults) {

setLazyInit(defaults.isLazyInit());
setAutowireMode(defaults.getAutowireMode());
setDependencyCheck(defaults.getDependencyCheck());
setInitMethodName(defaults.getInitMethodName());
setEnforceInitMethod(false);
setDestroyMethodName(defaults.getDestroyMethodName());
setEnforceDestroyMethod(false);

}
能够看出,postProcessBeanDefinition办法最首要的功用便是给扫描出来的bd设置默许值,进一步填充bd中的特点

3、履行processCommonDefinitionAnnotations办法
这句代码将进一步解析class上的注解信息,Spring在创立这个abd的信息时分就现已将当时的class放入其间了,一切这行代码首要做的便是经过class方针获取到上面的注解(包含@Lazy,@Primary,@DependsOn注解等等),然后将得到注解中对应的装备信息并放入到bd中的特点中

4、注册BeanDefinition
跟完全读懂Spring(一)读源码,咱们能够从榜首行读起的注册逻辑是相同的

经过上面的剖析,咱们现已知道了ClassPathBeanDefinitionScanner的效果,毋庸置疑,Spring必定是经过这个类来完结扫描的,可是问题是,Spring是经过第二步创立的这个方针来完结扫描的吗?咱们再来看看这个ClassPathBeanDefinitionScanner的创立进程:

// 榜首步
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {

this(registry, true);

}
// 第二步
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {

this(registry, useDefaultFilters, getOrCreateEnvironment(registry));

}
// 第三步
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,

                                  Environment environment) {
this(registry, useDefaultFilters, environment,
(registry instanceof ResourceLoader ? (ResourceLoader) registry : null));

}
// 第四步
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,

                                  Environment environment, @Nullable ResourceLoader resourceLoader) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
this.registry = registry;
if (useDefaultFilters) {
// 注册默许的扫描过滤规矩(要被@Component注解润饰)
registerDefaultFilters();
}
setEnvironment(environment);
setResourceLoader(resourceLoader);

}
在这个ClassPathBeanDefinitionScanner的创立进程中咱们全程无法干与,不能对这个ClassPathBeanDefinitionScanner进行任何装备。而咱们在装备类上分明是能够对扫描的规矩进行装备的,例如:

@ComponentScan(value = "com.spring.study.springfx.aop.service", useDefaultFilters = true,

           excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {IndexService.class}))

所以Spring中必定不是运用在这儿创立的这个ClassPathBeanDefinitionScanner方针。

实践上真实完结扫描的机遇是在咱们流程图中的3-5-1步。完结扫描这个功用的类便是咱们在上篇文章中所说到的ConfigurationClassPostProcessor。接下来咱们就经过这个类,看看Spring究竟是怎样完结的扫描,这也是本文要点想要阐明的问题

Spring是怎样解析装备类的?
1、解析机遇剖析
解析前Spring做了什么?
注册装备类
在剖析扫描机遇之前咱们先回忆下之前的代码,整个程序的进口如下:

public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {

this();
register(annotatedClasses);
refresh();

}
其间在this()空参结构中Spring实例化了两个方针,一个是AnnotatedBeanDefinitionReader,在上篇文章中现已介绍过了,别的一个是ClassPathBeanDefinitionScanner,在前文中也进行了具体的剖析。

在完结这两个方针的创立后,Spring紧接着就运用榜首步中创立的AnnotatedBeanDefinitionReader去将装备类注册到了容器中。看到这儿不知道咱们有没有一个疑问,已然Spring是直接经过这种办法来注册装备类,为什么咱们还非要在装备类上增加@Configuration注解呢?依照这个代码的话,我不在装备类上增加任何注解,也能将装备类注册到容器中,例如下面这样:

public class Config {
}

public class Main {

public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
System.out.println(ac.getBean("config"));
// 程序输出:com.spring.study.springfx.aop.Config@7b69c6ba
// 意味着Config被注册到了容器中
}

}
咱们仔细想想我这个问题,无妨带着这些疑问持续往下看。

调用refresh办法
在将装备类注册到容器中后,Spring紧接着又调用了refresh办法,其源码如下:

public void refresh() throws BeansException, IllegalStateException {

synchronized (this.startupShutdownMonitor) {
// 这个办法首要做了以下几件事
// 1.记载容器的发动时刻,并将容器状况更改为激活
// 2.调用initPropertySources()办法,首要用于web环境下初始化封装相关的web资源,比方将servletContext封装成为ServletContextPropertySource
// 3.校验环境中必要的特点是否存在
// 4.供给了一个扩展点能够提早放入一些事情,当applicationEventMulticaster这个bean被注册到容器中后就直接发布事情
prepareRefresh();
// 实践上获取的便是一个DefaultListableBeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 为bean工厂设置一些特点
prepareBeanFactory(beanFactory);
try {
// 供给给子类复写的办法,答应子类在这一步对beanFactory做一些后置处理
postProcessBeanFactory(beanFactory);
// 履行现已注册在容器中的bean工厂的后置处理器,在这儿完结的扫描
invokeBeanFactoryPostProcessors(beanFactory);
// 后边的代码跟扫描无关,咱们在之后的文章再介绍
}
// .....
}

}
大部分的代码都写了很具体的注释,关于其间两个比较杂乱的办法咱们独自剖析

prepareBeanFactory
invokeBeanFactoryPostProcessors
prepareBeanFactory做了什么?
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {

// 设置classLoader,一般便是appClassLoader
beanFactory.setBeanClassLoader(getClassLoader());
// 设置el表达式解析器
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
// 容器中增加一个特点修改器注册表,关于特点修改在《Spring官网阅览(十四)Spring中的BeanWrapper及类型转化》有过具体介绍,这儿就不再赘述了
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
//    增加了一个bean的后置处理器,用于履行xxxAware办法
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// 主动注入模型下,假如bean中存在以下类型的依靠,不进行注入
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
// 为什么咱们能直接将ApplicationContext等一些方针直接注入到bean中呢?便是下面这段代码的效果啦!
// Spring在进行特点注入时会从resolvableDependencies的map中查找是否有对应类型的bean存在,假如有的话就直接注入,下面这段代码便是将对应的bean放入到resolvableDependencies这个map中
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// 增加一个后置处理器,用于处理ApplicationListener
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
// 是否装备了LTW,也便是在类加载时期进行织入,一般都不会装备
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// 加载时期织入会装备一个暂时的类加载器
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
// 装备一些默许的环境相关的bean
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}

}
上面这段代码全体来说仍是十分简略的,逻辑也很明晰,便是在为beanFactory做一些装备,咱们需求留意的是跟后置处理器相关的内容,能够看到在这一步总共注册了两个后置处理器

ApplicationContextAwareProcessor,用于履行xxxAware接口中的办法
ApplicationListenerDetector,确保监听器被增加到容器中
关于ApplicationListenerDetector请参阅Spring官网阅览(八)容器的扩展点(三)(BeanPostProcessor)

invokeBeanFactoryPostProcessors做了什么?
这个办法的履行流程在Spring官网阅览(六)容器的扩展点(一)BeanFactoryPostProcessor 现已做过十分具体的剖析了,其履行流程如下

整的来说,它便是将容器中现已注册的bean工厂的后置处理器依照必定的次序进行履行。

那么到这一步停止,容器中现已有哪些bean工厂的后置处理器呢?

还记住咱们在上篇文章中说到的ConfigurationClassPostProcessor吗?在创立AnnotatedBeanDefinitionReader的进程中它对应的BeanDefinition就被注册到容器中了。接下来咱们就来剖析ConfigurationClassPostProcessor这个类的源码

ConfigurationClassPostProcessor源码剖析
它完结了BeanDefinitionRegistryPostProcessor,所以首要履行它的postProcessBeanDefinitionRegistry办法,其源码如下

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {

   // 生成一个注册表ID
int registryId = System.identityHashCode(registry);
//.....
// 标明这个工厂现现已过了后置处理器了
this.registriesPostProcessed.add(registryId);
// 从姓名来看这个办法是再对装备类的bd进行处理
processConfigBeanDefinitions(registry);

}
processConfigBeanDefinitions办法的代码很长,咱们拆分一段段剖析,先看榜首段

榜首段
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {

// ========榜首段代码========
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
// 咱们能够考虑一个问题,当时容器中有哪些BeanDefinition呢?
// 这个当地应该能获取到哪些姓名?
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
// 依据称号获取到对应BeanDefinition
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
// 省掉日志打印
// 查看是否是装备类,在这儿会将对应的bd标记为FullConfigurationClass或许LiteConfigurationClass
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
// 是装备类的话,将这个bd增加到configCandidates中
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// 没有装备类,直接回来
if (configCandidates.isEmpty()) {
return;
}
// 依据@Order注解进行排序
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// .....

上面这段代码有这么几个问题:

当时容器中有哪些BeanDefinition
假如你看过上篇文章的话应该知道,在创立AnnotatedBeanDefinitionReader方针的时分Spring现已往容器中注册了5个BeanDefinition,再加上注册的装备类,那么此刻容器中应该存在6个BeanDefinition,咱们能够打个断点验证

果然如此,确实是6个

checkConfigurationClassCandidate
代码如下:

public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
String className = beanDef.getBeanClassName();
if (className == null || beanDef.getFactoryMethodName() != null) {
return false;
}
// 下面这一段都是为了获取一个AnnotationMetadata
// AnnotationMetadata包含了对应class上的注解元信息以及class元信息
AnnotationMetadata metadata;
if (beanDef instanceof AnnotatedBeanDefinition &&
className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
// 现已解析过了,比方注册的装备类就归于这种,直接从bd中获取
metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
}
else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
// 拿到字节码从头解析获取到一个AnnotationMetadata
Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
metadata = new StandardAnnotationMetadata(beanClass, true);
}
else {
try {
// class特点都没有,就依据className运用ASM字节码技能获取到这个AnnotationMetadata
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
metadata = metadataReader.getAnnotationMetadata();
}
catch (IOException ex) {
return false;
}
}
// 假如被@Configuration注解标示了,阐明是一个FullConfigurationCandidate
if (isFullConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
// 假如被这些注解标示了,@Component,@ComponentScan,@Import,@ImportResource
// 或许办法上有@Bean注解,那么便是一个LiteConfigurationCandidate
// 也便是说你想把这个类当装备类运用,可是没有增加@Configuration注解
else if (isLiteConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
return false;
}
// 解析@Order注解,用于排序
Integer order = getOrder(metadata);
if (order != null) {
beanDef.setAttribute(ORDER_ATTRIBUTE, order);
}
return true;
}

第二段
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {

// 榜首段
// .....
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
// beanName的生成战略,不重要
if (!this.localBeanNameGeneratorSet) {
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
// 中心意图便是创立这个ConfigurationClassParser方针
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);

// 第三段
}
这段代码中心意图便是为了创立一个ConfigurationClassParser,这个类首要用于后续的装备类的解析。

第三段
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {

// 榜首段,第二段
// .....
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
// 在第二段代码中创立了一个ConfigurationClassParser,这儿便是运用这个parser来解析装备类
// 咱们知道扫描便是经过@ComponentScan,@ComponentScans来完结的,那么不出意外必定是在这儿完结的扫描
parser.parse(candidates);
// 校验在解析进程是中是否发作过错,一同会校验@Configuration注解的类中的@Bean办法能否被复写(被final润饰或许拜访权限为private都不能被复写),假如不能被复写会抛出反常,由于cglib署理要经过复写父类的办法来完结署理,后文会做具体介绍
parser.validate();
// 现已解析过的装备类
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
// 移除现已解析过的装备类,避免重复加载了装备类中的bd
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());
}
// 将经过解析@Bean,@Import等注解得到相关信息解析成bd被注入到容器中
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
// 假如大于,阐明容器中新增了一些bd,那么需求从头判别新增的bd是否是装备类,假如是装备类,需求再次解析
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
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());
// 注册ImportRegistry到容器中
// 当经过@Import注解导入一个全装备类A(被@Configuration注解润饰的类),A能够完结ImportAware接口
// 经过这个Aware能够感知到是哪个类导入的A
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}

}
2、解析源码剖析
在上面的源码剖析中,咱们现已能够确认了Spring是经过ConfigurationClassParser的parse办法来完结对装备类的解析的。Spring对类的取名能够说是很考究了,ConfigurationClassParser直译过来便是装备类解析器。接着咱们就来看看它的源码

2.1、parse办法
public void parse(Set configCandidates) {

this.deferredImportSelectors = new LinkedList<>();
// 遍历一切的装备类,一个个完结解析
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
// 三个判别终究都会进入到同一个办法---->processConfigurationClass办法
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);
}
}
// 对ImportSelector进行推迟处理
processDeferredImportSelectors();

}

2.2、processConfigurationClass办法

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
// 解析@Conditional注解,判别是否需求解析
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
// 判别解析器是否现已解析过这个装备类了
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
// 不为null,阐明现已解析过了
if (existingClass != null) {
// 假如这个要被解析的装备类是被@Import注解导入的
if (configClass.isImported()) {
// 而且解析过的装备类也是被导入的
if (existingClass.isImported()) {
// 那么这个装备类的导入类调集中新增当时的装备类的导入类,(A经过@Import导入了B,那么A便是B的导入类,B被A导入)
existingClass.mergeImportedBy(configClass);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
// 假如现已解析过的装备类不是被导入的,那么直接疏忽新增的这个被导入的装备类。也便是说假如一个装备类一同被@Import导入以及正常的
// 增加到容器中,那么正常增加到容器中的装备类会掩盖被导入的类
return;
}
else {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
// 便是说新要被解析的这个装备类不是被导入的,所以这种状况下,直接移除调原有的解析的装备类
// 为什么不是remove(existingClass)呢?能够看看hashCode跟equals办法
// remove(existingClass)跟remove(configClass)是等价的
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// Recursively process the configuration class and its superclass hierarchy.
// 下面这段代码首要是递归的处理装备类及其父类
//  将装备类封装成一个SourceClass便利进行一致的处理
SourceClass sourceClass = asSourceClass(configClass);
do {
// doxxx办法,真实干活的办法,对装备类进行处理,回来值是当时这个类的父类
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}

2.3、doProcessConfigurationClass办法
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)

throws IOException {
// Recursively process any member (nested) classes first
// 递归处理内部类
processMemberClasses(configClass, sourceClass);
// Process any @PropertySource annotations
// 处理@PropertySources跟@PropertySource注解,将对应的特点资源增加容器中(实践上增加到environment中)
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、
// 处理@ComponentScan,@ComponentScans注解,真实进行扫描的当地便是这儿
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
// 查看扫描出来的bd是否是装备类,假如是装备类递归进行解析
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
// 一般状况下getOriginatingBeanDefinition获取到的都是null
// 什么时分不为null呢?,参阅:ScopedProxyUtils.createScopedProxy办法
// 在创立一个署理的bd时不会为null
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
// 判别扫描出来的bd是否是一个装备类,假如是的话持续递归处理
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// Process any @Import annotations
// 处理@Import注解
processImports(configClass, sourceClass, getImports(sourceClass), true);
// Process any @ImportResource annotations
// 处理@ImportResource注解
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
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
// 处理@Bean注解
// 获取到被@Bean标示的办法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
// 增加到configClass中
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process default methods on interfaces
// 处理接口中的default办法
processInterfaces(configClass, sourceClass);
// Process superclass, if any
// 回来父类,进行递归处理
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !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;

}
能够看到,在doProcessConfigurationClass真实完结了对装备类的解析,总共做了下面几件事

解析装备类中的内部类,看内部类中是否有装备类,假如有进行递归处理
处理装备类上的@PropertySources跟@PropertySource注解
处理@ComponentScan,@ComponentScans注解
处理@Import注解
处理@ImportResource注解
处理@Bean注解
处理接口中的default办法
回来父类,让外部的循环持续处理当时装备类的父类
咱们逐个进行剖析

2.4、处理装备类中的内部类
这段代码十分简略,限于篇幅原因我这儿就不再专门剖析了,便是获取到当时装备类中的一切内部类,然后遍历一切的内部类,判别是否是一个装备类,假如是装备类的话就递归进行解析

2.5、处理@PropertySource注解
代码也十分简略,依据注解中的信息加载对应的特点文件然后增加到容器中

2.6、处理@ComponentScan注解
这个段咱们就需求看一看了,Spring在这儿完结的扫描,咱们直接查看其间心办法,org.springframework.context.annotation.ComponentScanAnnotationParser#parse

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
// 榜首步就创立了一个ClassPathBeanDefinitionScanner方针
// 在这儿咱们就知道了,Spring在进行扫描时没有运用在最开端的时分创立的那个方针进行扫描
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
// 解析成bd时选用的beanName的生成规矩
Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
BeanUtils.instantiateClass(generatorClass));
// 装备这个扫描规矩下的ScopedProxyMode的默许值
ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
scanner.setScopedProxyMode(scopedProxyMode);
}
else {
Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
}
// 装备扫描器的匹配规矩
scanner.setResourcePattern(componentScan.getString("resourcePattern"));
// 装备扫描器需求扫描的组件
for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addIncludeFilter(typeFilter);
}
}
// 装备扫描器不需求扫描的组件
for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addExcludeFilter(typeFilter);
}
}
// 装备默许是否进行懒加载
boolean lazyInit = componentScan.getBoolean("lazyInit");
if (lazyInit) {
scanner.getBeanDefinitionDefaults().setLazyInit(true);
}
// 装备扫描器扫描的包名
Set<String> basePackages = new LinkedHashSet<>();
String[] basePackagesArray = componentScan.getStringArray("basePackages");
for (String pkg : basePackagesArray) {
String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
Collections.addAll(basePackages, tokenized);
}
for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
// 扫除本身
scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
@Override
protected boolean matchClassName(String className) {
return declaringClass.equals(className);
}
});
// 在完结对扫描器的装备后,直接调用其doScan办法进行扫描
return scanner.doScan(StringUtils.toStringArray(basePackages));
}

看到了吧,榜首步就创立了一个ClassPathBeanDefinitionScanner,紧接着经过解析注解,对这个扫描器进行了各种装备,然后调用doScan办法完结了扫描。

2.7、处理@Import注解

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
// 没有要导入的类,直接回来
if (importCandidates.isEmpty()) {
return;
}
// checkForCircularImports:Spring中写死的为true,需求查看循环导入
// isChainedImportOnStack办法:查看导入栈中是否存在了这个configClass,假如存在了阐明
// 呈现了A import B,B import A的状况,直接抛出反常
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
// 反射创立这个ImportSelector
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
// 履行xxxAware办法
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
// 假如是一个DeferredImportSelector,增加到deferredImportSelectors调集中去
// 在一切的装备类完结解析后再去处理deferredImportSelectors调集中的ImportSelector
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
this.deferredImportSelectors.add(
new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}
else {
// 不是一个DeferredImportSelector,那么经过这个ImportSelector获取到要导入的类名
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
// 将其转化成SourceClass
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
// 递归处理要导入的类,一般状况下这个时分进入的便是别的两个判别了
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
// 假如是一个ImportBeanDefinitionRegistrar
// 先经过反射创立这个ImportBeanDefinitionRegistrar
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
// 再履行xxxAware办法
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
// 最终将其增加到configClass的importBeanDefinitionRegistrars调集中
// 之后会一致调用其ImportBeanDefinitionRegistrar的registerBeanDefinitions办法,将对应的bd注册到容器中
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
// 既不是一个ImportSelector也不是一个ImportBeanDefinitionRegistrar,直接导入一个一般类
// 并将这个类作为装备类进行递归处理
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();
}
}
}

2.8、处理@ImportResource注解
代码也很简略,在指定的方位加载资源,然后增加到configClass中。一般状况下,咱们经过@ImportResource注解导入的便是一个XML装备文件。将这个Resource增加到configClass后,Spring会在后文中解析这个XML装备文件然后将其间的bd注册到容器中,能够参阅org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions办法

2.9、处理@Bean注解
将装备类中一切的被@Bean标示的办法增加到configClass的BeanMethod调集中

2.10、处理接口中的default办法
代码也很简略,Java8中接口能界说default办法,这儿便是处理接口中的default办法,看其是否有@Bean标示的办法

到此停止,咱们剖析完了整个解析的进程。能够发现Spring将一切解析到的装备信息都存储在了ConfigurationClass类中,可是到现在停止这些存储的信息都没有进行运用。那么Spring是在哪里运用的这些信息呢?回到咱们的第三段代码,其间有一行代码如图所示:

也便是在这儿Spring完结了对解析好的装备类的信息处理。

2.11、加载解析完结的装备信息
// configurationModel:被解析完结了装备类调集,其间保存了@Bean注解解析信息,@Import注解解析信息等等
public void loadBeanDefinitions(Set configurationModel) {

TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
// 调用这个办法完结的加载
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}

}
private void loadBeanDefinitionsForConfigurationClass(

ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
// 判别是否需求越过,例如A导入了B,A不满意加载的条件需求被越过,那么B也应该被越过
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;
}
// 判别装备类是否是被导入进来的,实践的代码便是判别解析出来的configclass中的importedBy调集是否为空
// 那么这个importedBy调集是做什么的呢?
// 例如A经过@Import导入了B,那么解析B得到得configclass中得importedBy调集就包含了A
// 简而言之,importedBy调集便是导入了这个类的其它类(或许一同被多个类导入)
// 在前文中咱们也剖析过了,被多个类一同导入时会调用mergeImportedBy办法在调集中增加一个元素
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
// 解析@Bean标示的Method得到对应的BeanDefinition并注册到容器中
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
// 解析导入的装备文件,并将从中得到的bd注册到容器中
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 履行configClass中的一切ImportBeanDefinitionRegistrar的registerBeanDefinitions办法
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());

}
这段代码阅览起来仍是十分简略的,这儿我就跟咱们一同看下BeanMethod的相关代码,首要是为了让咱们对BeanDefinition的了解能够愈加深化,其源码如下:

private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
ConfigurationClass configClass = beanMethod.getConfigurationClass();
MethodMetadata metadata = beanMethod.getMetadata();
String methodName = metadata.getMethodName();
// 依据@Conditional注解判别是否需求越过
if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
configClass.skippedBeanMethods.add(methodName);
return;
}
if (configClass.skippedBeanMethods.contains(methodName)) {
return;
}
// 获取@Bean注解中的特点
AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
Assert.state(bean != null, "No @Bean annotation attributes");
// 从这儿能够看出,假如没有装备beanName,默许会取办法称号作为beanName
List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));
String beanName = (!names.isEmpty() ? names.remove(0) : methodName);
// 注册别号
for (String alias : names) {
this.registry.registerAlias(beanName, alias);
}
// isOverriddenByExistingDefinition这个办法判别的是当时注册的bd是否被原有的存在的bd所掩盖了
// 什么是掩盖呢?后文中咱们具体剖析
if (isOverriddenByExistingDefinition(beanMethod, beanName)) {
// 满意下面这个if的话意味着@Bean创立的bean跟@Bean标示的办法所地点的装备类的称号相同了,这种状况下直接抛出反常
if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) {
throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),
beanName, "Bean name derived from @Bean method '" + beanMethod.getMetadata().getMethodName() +
"' clashes with bean name for containing configuration class; please make those names unique!");
}
return;
}
// 创立一个ConfigurationClassBeanDefinition,从这儿能够看出经过@Bean创立的Bean所对应的bd满是ConfigurationClassBeanDefinition
ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
beanDef.setResource(configClass.getResource());
beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
// @Bean是静态的,那么只需求知道静态办法地点类名以及办法名就能履行这个办法了
if (metadata.isStatic()) {
// static @Bean method
beanDef.setBeanClassName(configClass.getMetadata().getClassName());
beanDef.setFactoryMethodName(methodName);
}
else {
//
// instance @Bean method
beanDef.setFactoryBeanName(configClass.getBeanName());
beanDef.setUniqueFactoryMethodName(methodName);
}
// 接下来的代码便是设置一些bd的特点,然后将bd注册到容器中,相关的源码在之前的文章中现已剖析过了
// 这儿我就不在剖析了,参阅本文引荐阅览文章的《读源码,咱们能够从榜首行读起》
//.....
}

上面这个办法的首要意图便是将@Bean标示的办法解析成BeandDefinition然后注册到容器中。关于这个办法咱们能够比照下之前剖析过的org.springframework.context.annotation.AnnotatedBeanDefinitionReader#doRegisterBean办法。比照咱们能够发现,这两个办法最大的不同在于一个是依据Class方针的,而另一个则是依据Method方针的。

正由于如此,一切它们有一个很大的不同点在于BeanDefinition中BeanClasss特点的设置。能够看到,关于@Bean办法创立的Bean其BeanDefinition中是没有设置BeanClasss特点的,可是额定设置了其它的特点

静态办法下,设置了BeanClassName以及FactoryMethodName特点,其间的BeanClassName是静态办法地点类的类名,FactoryMethodName是静态办法的办法名
实例办法下,设置了FactoryBeanName以及FactoryMethodName特点,其间FactoryBeanName是实例对应的Bean的称号,而FactoryMethodName是实例中对应的办法名
之所以不必设置BeanClasss特点是由于,经过指定的静态办法或许指定的实例中的办法也能仅有确认一个Bean。

除此之外,注册@Bean办法得到的BeanDefinition时,还进行了一个isOverriddenByExistingDefinition(beanMethod, beanName)办法的判别,这个办法的首要效果是判别当时要注册的bean是否被之前现已存在的Bean掩盖了。可是在直接经过AnnotatedBeanDefinitionReader#doRegisterBean办法注册Bean时是没有进行这个判别的,假如存在就直接掩盖了,而不会用之前的bd来掩盖现在要注册的bd。这是为什么呢?据笔者自己的了解,是由于Spring将Bean也是分成了三六九等的,经过@Bean办法得到的bd能够掩盖扫描出来的一般bd(ScannedGenericBeanDefinition),可是不能掩盖装备类,所以当现已存在的bd是一个ScannedGenericBeanDefinition时,那么直接进行掩盖,可是当现已存在的bd是一个装备类时,就不能进行掩盖了,要运用现已存在的bd来掩盖本非必须注册的bd。

到此停止,咱们就完结了Spring中的整个装备类解析、注册的相关源码剖析,不过还没完,咱们还得处理一个问题,便是为什么要在装备类上增加@Configuration注解,在之前的源码剖析中咱们知道,增加@Configuration注解的效果便是讲装备类标志成了一个full configurationClass,这个的意图是什么呢?本来是计划一篇文章写完的,不过真实是太长了,挨近6w字,所以仍是拆成了两篇,预知后事怎样,请看下文:
装备类为什么要增加@Configuration注解呢?

总结
咱们结合上篇文章完全读懂Spring(一)读源码,咱们能够从榜首行读起收拾下现在Spring的履行流程

原图地址: 原图

明晰的知道了履行的流程,咱们再来回想下postProcessBeanDefinitionRegistry做了什么。

码字不易,对你有协助的话记住点个赞,重视一波哦,万分感谢!

作者:程序员DMZ

原文地址https://www.cnblogs.com/daimzh/p/12855338.html