在之前的博客中,分析bean扫描的时候,看到这个过程是在ConfigurationClassPostProcessor这个后置处理器中执行的,这里来仔细分析一下其中的过程。
首先进入ConfigurationClassPostProcessor这个类,找到processConfigBeanDefinitions方法,在这个方法中,会创建一个ConfigurationClassParser类,调用paser方法,传入javaconfig的配置类,这个配置类在spring容器最开始的代码register方法中生成BeanDefinition。
public void parse(Set<BeanDefinitionHolder> configCandidates) {
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);
}
}
this.deferredImportSelectorHandler.process();
}
从上面的代码中看出,会去判断BeanDefinition的类型,这里分析注解扫描,所以会是一个AnnotatedBeanDefinition ,接着会去调用processConfigurationClass方法
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName));
}
这里先判断了一下conditional,先跳过,接着处理了重复导入的情况,如果都是import导入的,那就合并,否则覆盖。
再往下创建一个SourceClass,接着执行doProcessConfigurationClass方法,最后将configClass放入configurationClasses这个map中,如果下次再进来这个方法,就能获取到,就能进入if条件中了
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
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);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
这里有个do ,while循环,这个是当有这个class的父类返回,会继续执行,先分析一下doProcessConfigurationClass方法做了什么。
首先判断一下当前类是否有@Component注解,有的话,调用processMemberClasses方法
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass);
}
这个方法在当前的注解扫描情况下,就是处理当前扫描的Class的内部类,如果有内部类,判断内部类是否有 @Component @ComponentScan @Import @ImportResource这样四个注解,或者有@Bean注解的方法,如果是的话,就会为这个类创建一个ConfigClass,最后还是会调用processConfigurationClass方法
/**
* Check the given metadata for a configuration class candidate
* (or nested component class declared within a configuration/component class).
* @param metadata the metadata of the annotated class
* @return {@code true} if the given class is to be registered for
* configuration class processing; {@code false} otherwise
*/
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
// Do not consider an interface or an annotation...
if (metadata.isInterface()) {
return false;
}
// Any of the typical annotations found?
// 判断是否有这四个注解 @Component @ComponentScan @Import @ImportResource
for (String indicator : candidateIndicators) {
if (metadata.isAnnotated(indicator)) {
return true;
}
}
// Finally, let's look for @Bean methods...
// 查看是否有@Bean的方法
try {
return metadata.hasAnnotatedMethods(Bean.class.getName());
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);
}
return false;
}
}
返回doProcessConfigurationClass方法中,接下来会获取当前class的PropertySources注解的信息Set,循环遍历,调用processPropertySource方法,处理@PropertySources注解
// 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.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
processPropertySource中的逻辑比较简单,就是根据@PropertySource中的路径加载,通过自定义或者默认的PropertySourceFactory 解析生成一个PropertySource,通过addPropertySource添加进this.environment的MutablePropertySources集合中。
/**
* Process the given <code>@PropertySource</code> annotation metadata.
* @param propertySource metadata for the <code>@PropertySource</code> annotation found
* @throws IOException if loading a property source failed
*/
private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
String name = propertySource.getString("name");
if (!StringUtils.hasLength(name)) {
name = null;
}
String encoding = propertySource.getString("encoding");
if (!StringUtils.hasLength(encoding)) {
encoding = null;
}
String[] locations = propertySource.getStringArray("value");
Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));
for (String location : locations) {
try {
String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
Resource resource = this.resourceLoader.getResource(resolvedLocation);
addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
}
catch (IllegalArgumentException | FileNotFoundException | UnknownHostException ex) {
// Placeholders not resolvable or resource not found when trying to open it
if (ignoreResourceNotFound) {
if (logger.isInfoEnabled()) {
logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());
}
}
else {
throw ex;
}
}
}
}
接着看@ComponentScans和@ComponentScan注解的处理
调用AnnotationConfigUtils.attributesForRepeatable找出当前class的@ComponentScans和@ComponentScan注解的AnnotationAttributes注解信息集合,循环调用
// 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());
}
}
}
}
第一个重点的方法是this.componentScanParser.parse方法,这里调用的是ComponentScanAnnotationParser的parser方法,先是根据AnnotationAttributes构建相应的ClassPathBeanDefinitionScanner。主要有这么几个属性:useDefaultFilters,nameGenerator,scopedProxy,scopeResolver,resourcePattern,includeFilters,excludeFilters,lazyInit,basePackages,basePackageClasses。而如果没有basePackages,那么就会将当前class所在的包路径添加进集合中。
而在ClassPathBeanDefinitionScanner的构造方法中,注册了一个默认的include过滤器AnnotationTypeFilter,用来过滤@Component的,还会尝试去注册ManagedBean和Named的过滤器,这两个比较少用。
protected void registerDefaultFilters() {
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
}
try {
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
接着调用doScan方法,依次循环扫描basePackages集合,首先通过findCandidateComponents方法查询出所有当前需要的类,先忽略使用索引加载的情况,接着将会调用scanCandidateComponents方法,方法中将当前包路径下的所有类资源加载成一个Resource数组集合,接着循环Resource数组,
先说明过滤器的过滤规则是不会被任何excludeFilters匹配,但必须匹配一个includeFilters的不被过滤。所以根据当前的过滤规则,找出所需要的类就是有着@component注解,或者@ManagedBean注解,或者@Named,主要看最熟悉的@component,@component注解的注解还有着@Configration,@Controller,@Service等这几个比较熟悉的注解,这些注解的类都满足条件,都会被生成一个ScannedGenericBeanDefinition,接着再判断一次,这次是判断有没有依赖,是不是抽象类,如果是抽象类有没有@lookup注解,符合条件的添加进candidates这个BeanDefinition的Set集合,最后返回
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
if (isCandidateComponent(metadataReader)) {
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);
}
else {
if (debugEnabled) {
logger.debug("Ignored because not a concrete top-level class: " + resource);
}
}
}
else {
if (traceEnabled) {
logger.trace("Ignored because not matching any filter: " + resource);
}
}
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to read candidate component class: " + resource, ex);
}
}
else {
if (traceEnabled) {
logger.trace("Ignored because not readable: " + resource);
}
}
}
接着再依次循环这个candidates,最后会生成一个BeanDefinitionHolder ,添加到beanDefinitions集合中,最后返回,并且注册到了registry中。
for (BeanDefinition candidate : candidates) {
//从类的各个注解上获取各种属性数据
//设置scope
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
//生成beanName
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
//设置默认的属性
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
//设置Lazy,Primary,DependsOn,Role,Description
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
返回到doProcessConfigurationClass,这里会循环之前得到的BeanDefinitionHolder集合,执行parse方法,最后看到每一个BeanDefinition又会调用processConfigurationClass方法,变成一个递归的调用。
protected final void parse(@Nullable String className, String beanName) throws IOException {
Assert.notNull(className, "No bean class name for configuration class bean definition");
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
processConfigurationClass(new ConfigurationClass(reader, beanName));
}
接着往下看,@Import注解的处理
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
先从当前类上获取所有的@Import注解的类,然后依次循环,先放入importStack中,这个是用来判断是否有两个类循环Import了,是的话会抛异常。
接着判断@Import注解的类是不是继承了ImportSelector接口,或是继承了ImportBeanDefinitionRegistrar接口,还是什么也没继承。
先看什么也没继承的情况,如果仅仅是一个普通的类,那就会把这个类当成一个Configuration的class去处理,这里可以看到又去调用了processConfigurationClass方法
// 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));
接着看继承了ImportSelector接口的处理,这里又判断了是不是继承了DeferredImportSelector接口,先看不是的情况,如果不是DeferredImportSelector接口,那么就会调用ImportSelector的selectImports方法,然后根据自定义的代码逻辑返回一个class的数组,接着继续调用当前的processImports方法,将之前得到的class数组当成import的class集合传入,最后的最后就又会进入到processConfigurationClass方法
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
如果是继承了DeferredImportSelector的情况,那么就会将当前类生成一个DeferredImportSelectorHolder并放入到deferredImportSelectors这个集合中,这里要注意的是,从代码上看还有一种情况,那就是deferredImportSelectors ==null的时候,不过deferredImportSelectors初始化了一个空集合,并不会等于null,这个null的情况就是在另一个process的这个真正处理的方法中一开始就设置成了null,这就表明null的情况就是process方法正在执行,所以当这种情况,就会去直接处理这个DeferredImportSelectorHolder
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
/**
* Handle the specified {@link DeferredImportSelector}. If deferred import
* selectors are being collected, this registers this instance to the list. If
* they are being processed, the {@link DeferredImportSelector} is also processed
* immediately according to its {@link DeferredImportSelector.Group}.
* @param configClass the source configuration class
* @param importSelector the selector to handle
*/
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(
configClass, importSelector);
if (this.deferredImportSelectors == null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
handler.register(holder);
handler.processGroupImports();
}
else {
this.deferredImportSelectors.add(holder);
}
}
接着看最后一种情况,继承了ImportBeanDefinitionRegistrar接口,会将这个类生成实例,接着注册到configClass,也就是注解的那个类的ConfigurationClass中。而这个注册其实就是往map里面添加对象,在这里ImportBeanDefinitionRegistrar的实现类并没有去实现具体的逻辑
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
再回到doProcessConfigurationClass,往下看@ImportResource注解的处理
这里逻辑比较明了,就是取出注解中的locations以及reader,read默认是BeanDefinitionReader,然后放入到configClass的一个map集合中。
// Process any @ImportResource annotations
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);
}
}
再往下,这个方法先跳过,只会处理StandardAnnotationMetadata的情况,当前注解扫描暂时没看到什么情况下会被使用到
// 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);
最后,找出父类,如果没处理过,就返回,外层继续调用doProcessConfigurationClass方法
// 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();
}
}
最后返回到parse方法中,这里处理了继承了DeferredImportSelector的@Import
this.deferredImportSelectorHandler.process();
public void process() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
if (deferredImports != null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
deferredImports.forEach(handler::register);
handler.processGroupImports();
}
}
finally {
this.deferredImportSelectors = new ArrayList<>();
}
}
这里首先创建了一个DeferredImportSelectorGroupingHandler ,接着排序
再接着,根据DeferredImportSelectorHolder集合循环,依次调用刚才创建的DeferredImportSelectorGroupingHandler的register方法,参数是DeferredImportSelectorHolder,在register方法中,根据当前的group的实际class,创建实例对象,作为DeferredImportSelectorGrouping构造方法的参数,传入,接着将DeferredImportSelectorHolder 也放入到这个DeferredImportSelectorGrouping中,最后放入到groupings集合中
public void register(DeferredImportSelectorHolder deferredImport) {
Class<? extends Group> group = deferredImport.getImportSelector()
.getImportGroup();
DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
(group != null ? group : deferredImport),
key -> new DeferredImportSelectorGrouping(createGroup(group)));
grouping.add(deferredImport);
this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getConfigurationClass());
}
接着调用processGroupImports方法,这里最重要的就是getImports方法,和普通的@import不同,继承了DeferredImportSelector接口的类,只会调用process,以及selectImports方法,最后根据selectImports返回的集合对象,循环调用processImports方法,processImports方法之前说明过
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(
entry.getMetadata());
try {
processImports(configurationClass, asSourceClass(configurationClass),
asSourceClasses(entry.getImportClassName()), false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
}
});
}
}
/**
* Return the imports defined by the group.
* @return each import with its associated configuration class
*/
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
最后返回
至此为止,所有spring体系中的类都被转化成了ConfigurationClass
往下,是一个验证方法,就是验证@Configuration注解的类的@Bean方法满不满足要求,这里主要是不能是fianl,必须能被重写,不满足则验证不通过
public void validate(ProblemReporter problemReporter) {
// A configuration class may not be final (CGLIB limitation) unless it declares proxyBeanMethods=false
Map<String, Object> attributes = this.metadata.getAnnotationAttributes(Configuration.class.getName());
if (attributes != null && (Boolean) attributes.get("proxyBeanMethods")) {
if (this.metadata.isFinal()) {
problemReporter.error(new FinalConfigurationProblem());
}
for (BeanMethod beanMethod : this.beanMethods) {
beanMethod.validate(problemReporter);
}
}
}
然后构建一个ConfigurationClassBeanDefinitionReader,这个类负责解析之前的@ImportResource,@Import的ImportBeanDefinitionRegistrar,@Bean,@Import导入的类
// 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);
ImportResource的解析,主要是解析@importResource导入的资源文件,默认一般是xml,或者是groovy的文件,也可以实现自定义的reader的类,这里细节比较多,跳过。
至于@Import的ImportBeanDefinitionRegistrar的处理非常简单,就是去调用@import的那个类的registerBeanDefinitions方法。
再看@Bean的处理,会把之前生成好的BeanMethods取出,循环,生成一个个ConfigurationClassBeanDefinition,注册到registry中。
接着之前一些通过@import导入的类,都会解析成AnnotatedGenericBeanDefinition,注册到registry中。
到此,扫描基本就结束了