13.postProcessBeanDefinitionRegistry源码(下)
第一部分主要是讲遍历候选的BeanDefinition,然后根据BeanDefinition的属性configClass判断是否是配置类,如果是配置类加入候选配置类集合,第二部分对候选配置类集合进行解析。
本文重点解析对候选配置类集合进行解析。
步骤5:创建ConfigurationClassParser,解析配置类
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory,
this.problemReporter,
this.environment,
this.resourceLoader,
this.componentScanBeanNameGenerator,
registry);
Spring的工具类ConfigurationClassParser用于分析@Configuration注解的配置类,产生一组ConfigurationClass对象。下文会用到。
//候选
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
//已解析
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
实例化两个set,,candidates用于将之前加入的configCandidates去重, alreadyParsed用于判断是否处理过了。 代码再往下就是开始循环do while 循环解析处理candidates配置类
//解析配置类 candidates就是configCandidates
//其实就是1个
parser.parse(candidates);
public void parse(Set<BeanDefinitionHolder> configCandidates) {
//遍历分析配置类
for (BeanDefinitionHolder holder : configCandidates) {
//得到配置类对应的BeanDefinition
BeanDefinition bd = holder.getBeanDefinition();
try {
// 这里根据Bean定义的不同类型走不同的分支,但是最终都会调用到方法
// processConfigurationClass(ConfigurationClass configClass)
// 判断是否是AnnotatedBeanDefinition类型
// 之前分析过自定义的Config通过AnnotatedBeanDefinitionReader解析为
// AnnotatedGenericBeanDefinition。
// AnnotatedBeanDefinition是AnnotatedGenericBeanDefinition的父类
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");
}
}
// 执行找到的 DeferredImportSelector
// DeferredImportSelector 是 ImportSelector 的一个变种。
// ImportSelector 被设计成其实和@Import注解的类同样的导入效果
// 但是实现 ImportSelector的类可以条件性地决定导入哪些配置。饥渴加载
// DeferredImportSelector 的设计目的是在所有其他的配置类被处理后才处理。
// 这也正是 该语句被放到本函数最后一行的原因。
this.deferredImportSelectorHandler.process();
}
先关注里面的parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());方法。
参数有两个,一个是BeanDefinition的注解元数据,一个bean的名称,继续跟进:
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName));
}
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
//获取配置类的@Condition注解 判断是否需要跳过
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);
}
return;
}
else {
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);
}
步骤5.1:判断配置类是否要跳过:@Conditional
首先将注解元数据和bean名称封装成ConfigurationClass,这个类翻译成中文就是“配置类”。
继续跟进:如果存在@Conditional注解并且条件不成立,那么返回true说明需要跳过,当前配置类不会被解析。
如果不存在@Conditional注解 返回false,代表需要解析。
//获取配置类的@Condition注解 判断是否需要跳过
//返回true直接跳过 即不需要解析
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(),
ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
//如果配置类上的注解为空或者配置类上没有@Conditional注解
//直接返回false也就是不跳过
if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
return false;
}
//phase是null才会进入判断
//此处的phase是ConfigurationPhase.PARSE_CONFIGURATION不会进入判断直接跳过
if (phase == null) {
if (metadata instanceof AnnotationMetadata &&
ConfigurationClassUtils
.isConfigurationCandidate((AnnotationMetadata) metadata)) {
return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
}
return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
}
//获取配置类上所有的@Conditional注解 放入集合
List<Condition> conditions = new ArrayList<>();
for (String[] conditionClasses : getConditionClasses(metadata)) {
for (String conditionClass : conditionClasses) {
Condition condition = getCondition(conditionClass,
this.context.getClassLoader());
conditions.add(condition);
}
}
AnnotationAwareOrderComparator.sort(conditions);
//遍历配置类上所有的@Conditional注解
for (Condition condition : conditions) {
ConfigurationPhase requiredPhase = null;
if (condition instanceof ConfigurationCondition) {
requiredPhase = ((ConfigurationCondition)
condition).getConfigurationPhase();
}
//如果@Condition的match方法返回的是true,代表不会跳过,需要解析
//如果@Condition的match方法返回的是false,代表要跳过,不需要解析
if ((requiredPhase == null || requiredPhase == phase) &&
!condition.matches(this.context, metadata)) {
return true;
}
}
return false;
}
processConfigurationClass中的第一行就是检查当前解析的配置bean是否包含Conditional注解,如果不包含则不需要跳过,当前配置类需要被解析。
如果包含了Conditional注解则执行match方法得到匹配结果,match方法返回false则跳过不解析,返回true不跳过需要解析。
我们打个比方: 先生成一个业务类
public class Person {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" + "name='" + name + ''' + ", age=" + age + '}';
}
}
@Configuration
public class BeanConfig {
@Bean(name = "bill")
public Person person1(){
return new Person("Bill Gates",62);
}
@Bean("linus")
public Person person2(){
return new Person("Linus",48);
}
}
public class TestCondition {
public static void main(String[] args) {
AnnotationConfigApplicationContext context
= new AnnotationConfigApplicationContext();
//注册配置类
context.register(BeanConfig.class);
context.refresh();
Map<String, Person> map = context.getBeansOfType(Person.class);
System.out.println(map);
}
}
//打印结果:
//{bill=Person{name='Bill Gates', age=62}, linus=Person{name='Linus', age=48}}
问题来了,如果我想根据当前操作系统来注入Person实例,windows下注入bill,linux下注入linus,怎么实现呢?
这就需要我们用到@Conditional注解了。
首先,创建一个WindowsCondition类:
public class WindowsCondition implements Condition {
/**
* @param conditionContext:判断条件能使用的上下文环境
* @param annotatedTypeMetadata:注解所在位置的注释信息
* */
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//获取ioc使用的beanFactory
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
//获取类加载器
ClassLoader classLoader = conditionContext.getClassLoader();
//获取当前环境信息
Environment environment = conditionContext.getEnvironment();
//获取bean定义的注册类
BeanDefinitionRegistry registry = conditionContext.getRegistry();
//获得当前系统名
String property = environment.getProperty("os.name");
//包含Windows则说明是windows系统,返回true
if (property.contains("Windows")){
return true;
}
return false;
}
}
创建LinuxCondition类:
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment environment = conditionContext.getEnvironment();
String property = environment.getProperty("os.name");
if (property.contains("Linux")){
return true;
}
return false;
}
}
接着就是使用这两个类了,因为此注解可以标注在方法上和类上,所以分开测试:
@Configuration
public class BeanConfig {
//只有一个类时,大括号可以省略
//如果WindowsCondition的实现方法返回true,则注入这个bean
@Conditional({WindowsCondition.class})
@Bean(name = "bill")
public Person person1(){
return new Person("Bill Gates",62);
}
//如果LinuxCondition的实现方法返回true,则注入这个bean
@Conditional({LinuxCondition.class})
@Bean("linus")
public Person person2(){
return new Person("Linus",48);
}
}
由于我们的操作系统是windows,所以打印结果:{bill=Person{name='Bill Gates', age=62}}。
注意一个方法只能注入一个bean实例,所以@Conditional标注在方法上只能控制一个bean实例是否注入。 标注在类上:一个类中可以注入很多实例,@Conditional标注在类上就决定了一批bean是否注入。
我们试一下,将BeanConfig改写,这时如果WindowsCondition返回true,则两个Person实例将被注入:
//如果WindowsCondition的实现方法返回true,则注入BeanConfig下的所有@Bean的方法对应的bean
@Conditional({WindowsCondition.class})
@Configuration
public class BeanConfig {
@Bean(name = "bill")
public Person person1(){
return new Person("Bill Gates",62);
}
@Bean("linus")
public Person person2(){
return new Person("Linus",48);
}
}
@Conditional注解传入的是一个Class数组,存在多种条件类的情况。这种情况貌似判断难度加深了,测试一波,新增新的条件类,实现的matches返回false(这种写死返回false的方法纯属测试用,没有实际意义O(∩_∩)O)
public class ObstinateCondition implements Condition{
@Override
public boolean matches(ConditionContext conditionContext,
AnnotatedTypeMetadata annotatedTypeMetadata) {
return false;
}
}
@Conditional({WindowsCondition.class,ObstinateCondition.class})
@Configuration
public class BeanConfig {
@Bean(name = "bill")
public Person person1(){
return new Person("Bill Gates",62);
}
@Bean("linus")
public Person person2(){
return new Person("Linus",48);
}
}
请读者自行测试,这个例子就是说明processConfigurationClass中的第一行代码的作用,如果不符合条件注册bean到IOC,否则不继续执行。
测试的结果是返回{},也就是@Conditional注解上有多个类时,使用每个类的matches方法的返回值做
&&操作。其实是只有1个@Conditional注解返回false就直接返回true就会跳过 不再解析这个配置类。
读到这里大家都知道,我们的配置类只有Config.java,我们在这个配置类上加一个Condition条件:
@Order(1)
@ComponentScan("com")
//返回的是false 代表不会
@Conditional({LinuxCondition.class})
public class Config {
}
@Conditional({LinuxCondition.class})返回false,上面的shouldSkip方法的返回true,代码直接return。
也就是说你这个配置类无效,spring不会解析这个配置类。
为什么?
spring启动过程中,我们是这样注册配置类的context.register(Config.class);
这个register源码之前讲过,一直跟到doRegisterBean中,有这么两行代码:
//@Conditional装配条件判断是否需要跳过注册
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
也就是说,我们在注册Config配置类的时候spring已经对Condition条件进行判断了,不会生成Config对应的BeanDefinition了,那怎么办?不让spring做判断,手动注册BeanDefinition。
//context.register(Config.class);
AnnotatedGenericBeanDefinition bd = new
AnnotatedGenericBeanDefinition(Config.class);
context.registerBeanDefinition("config",bd);
打断点调试:
好了,shouldSkip这行代码已经讲清楚了。
发现确实会return,Config对应的bd确实被注册上去了。但是还是有问题,不会作为配置类生效!!!!!
步骤5.2:真正解析:doProcessConfigurationClass
// 第一次进入的时候, configurationClasses size = 0,existingClass 肯定为 null
//在这里处理Configuration重复import
//如果同一个配置类被处理两次,两次都属于被import的则合并导入类,返回。
//如果配置类不是被导入的,则移除旧使用新的配置类
ConfigurationClass existingClass =
this.configurationClasses.get(configClass);
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
//如果要处理的配置类configClass在已经分析处理的配置类记录中已存在,
//合并二者的importedBy属性
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);
}
}
//递归地处理配置类及其超类层次结构。sourceClass包含了calss文件,后面获取父类用
SourceClass sourceClass = asSourceClass(configClass);
do {
// 真正的做解析
// 首先解析的配置类
// 返回的是配置类的父类
// 在doWhile中继续解析配置类的父类
// 如果配置类的父类还有父类 继续解析配置类的父类的父类 无限递归...
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
第一次调用existingClass 肯定为空,难道还有第二次调用?是的,@Import注解就是引入,先不管这个,
代码会跳过判断,执行SourceClass sourceClass = asSourceClass(configClass);,从当前配置类configClass开始向上沿着类继承结构逐层执行doProcessConfigurationClass,直到遇到的父类是由Java提供的类结束循环。
然后就是真正的解析sourceClass = doProcessConfigurationClass(configClass, sourceClass);,循环处理配置类configClass直到sourceClass变为null,doProcessConfigurationClass的返回值是其参数configClass的父类,如果该父类是由Java提供的类或者已经处理过,返回null。doProcessConfigurationClass()会对一个配置类执行真正的处理:
1. 一个配置类的成员类(配置类内定义的内部类)也可能是适配类,先遍历这些成员内部类,调用processConfigurationClass处理它们;
2. 处理配置类上的注解@PropertySources,@PropertySource
3. 处理配置类上的注解@ComponentScans,@ComponentScan
4. 处理配置类上的注解@Import
5. 处理配置类上的注解@ImportResource
6. 处理配置类中每个带有@Bean注解的方法
7. 处理配置类所实现接口的缺省方法
8. 检查父类是否需要处理,如果父类需要处理返回父类,否则返回null
返回父类表示当前配置类处理尚未完成,调用者processConfigurationClass会继续处理其父类;返回null才表示该配置类的处理完成。从这里可以推断一旦一个配置类被processConfigurationClass处理完成,表示其自身,内部嵌套类,各个实现接口以及各级父类都被处理完成。
步骤5.3:doProcessConfigurationClass
@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
//如果配置类带有@Component
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// 递归处理内部类
// 会在processMemberClasses调用processConfigurationClass递归处理
processMemberClasses(configClass, sourceClass);
}
//处理@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 {
//打印日志
}
}
//获取 @ComponentScan @ComponentScans注解对应的属性值
Set<AnnotationAttributes> componentScans =
AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(),
ComponentScans.class,
ComponentScan.class);
//判断扫描出来 @ComponentScan @ComponentScans的注解对应的属性值不为空
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip
(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
//循环遍历所有的 @ComponentScan
for (AnnotationAttributes componentScan : componentScans) {
//调用componentScanParser扫描bd
//底层调用的是ClassPathBeanDefinitionScanner
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse
(componentScan,sourceClass.getMetadata().getClassName());
//遍历解析的scanner扫描出来的bd
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand =
holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils
.checkConfigurationClassCandidate
(bdCand, this.metadataReaderFactory)) {
//解析Scanner扫描出来的BeanDefinition
//这里是一个递归处理 又进入了processConfigurationClass方法
//也就是说如果我有
//带有@ComponentScan的配置类Config
//带有@Component的User类
//先解析Config
//先通过Config类上面的@ComponentScan构造1个scanner去扫描到User
//然后开始解析User
//流程和上面1一样先解析内部类
//假设这里User已经被解析完毕继续往下走
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// getImports(sourceClass)方法获取所有带@Import注解的类
// ImportSelector
processImports(configClass, sourceClass, getImports(sourceClass), true);
// 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);
}
}
// 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);
// 注意这里返回的是配置类的父类
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// 找到父类, 返回继续递归。
return sourceClass.getSuperClass();
}
}
// No superclass -> processing is complete
return null;
}
步骤5.3.1:递归解析配置类的内部类
上面的步骤我们挨个分析,进入doProcessConfigurationClass源码,首先处理的就是内部类:
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
// 递归处理内部类
processMemberClasses(configClass, sourceClass);
}
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
//获取配置类的内部类
Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
if (!memberClasses.isEmpty()) {
List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
for (SourceClass memberClass : memberClasses) {
//判断内部类是不是配置类,上文讲过了如何判断是不是配置类
//如果是@Configuration 就是full
//如果是下面这些注解或者是带有@Bean的方法 就是Lite
/*
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
*/
if (ConfigurationClassUtils.
isConfigurationCandidate(memberClass.getMetadata()) &&
!memberClass.getMetadata().getClassName().
equals(configClass.getMetadata().getClassName())) {
candidates.add(memberClass);
}
}
//对配置类进行排序,上文也讲过了
OrderComparator.sort(candidates);
//1)出现配置类循环导入,直接报错
for (SourceClass candidate : candidates) {
if (this.importStack.contains(configClass)) {
this.problemReporter
.error(new CircularImportProblem
(configClass, this.importStack));
}else {
//将配置类入栈
this.importStack.push(configClass);
try {
// 递归调用processConfigurationClass方法,也许内部类还有内部类
// 其实,工作中我们不会内部类嵌套内部类再嵌套内部类再嵌套。。。
processConfigurationClass(candidate.asConfigClass(configClass));
}
finally {
this.importStack.pop();
}
}
}
}
}
注释解释的很清楚,就是判断内部类是否是配置类,上文已经讲过如何判断是否是内部类了,如果是配置类还是要调用processConfigurationClass方法来处理,这是一个递归的过程,一直到没有内部配置类为止。
步骤5.3.2:解析@PropertySources,@PropertySource
配置类内部类处理完,紧接着就是处理配置类上的注解@PropertySources,@PropertySource,如果配置类上有@PropertySource注解,则解析加载properties文件,并将属性添加到Spring上下文中。
源码:
//获取的是配置类的 @PropertySources @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.info("Ignoring @PropertySource annotation on Reason:"
+"Environment must implement ConfigurableEnvironment");
}
}
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 |
SocketException ex) {
// Placeholders not resolvable or resource not found when trying to open it
if (ignoreResourceNotFound) {
}
else {
throw ex;
}
}
}
}
这段源码,小编没有分析过,我就不误人子弟了,但是这段代码的执行结果就是将我们自定义的资源文件添加到spring上下文环境中,我们通过一个例子来说明: 首先在resources中添加一个资源文件demo.properties,内容如下:
demo.name=this is my properties
修改下我们的配置类:
@Order(1)
@Configuration
@ComponentScan("com")
@PropertySource({"classpath:demo.properties"})
public class Config {
@Value("${demo.name}")
private String name;
public String getName() {
return name;
}
}
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext();
//注册配置类
context.register(Config.class);
context.refresh();
System.out.println(context.getBean(Config.class).getName());
}
}
打印结果:
//demo.name=this is my properties
spring已经将配置文件加入到系统环境中了,在任何地方都可以通过@Value("${demo.name}")方式进行调用!
步骤5.3.3:解析@ComponentScans,@ComponentScan
@ComponentScan 和 @ComponentScans的用法
扫描指定类文件
@ComponentScan(basePackageClasses = Person.class)
2. 扫描指定包,使用默认扫描规则,即被@Component, @Repository, @Service, @Controller或者已经声明过@Component自定义注解标记的组件;
@ComponentScan(value = "com.yibai")
3. 扫描指定包,加载被@Component注解标记的组件和默认规则的扫描(因为useDefaultFilters默认为true)
@ComponentScan(value = "com.yibai", includeFilters = { @Filter(type = FilterType.ANNOTATION, value = Component.class) })
4. 扫描指定包,只加载Person类型的组件
@ComponentScan(value = "com.yibai", includeFilters = { @Filter(type = FilterType.ASSIGNABLE_TYPE, value = Person.class) }, useDefaultFilters = false)
5. 扫描指定包,过滤掉被@Component标记的组件
@ComponentScan(value = "com.yibai", excludeFilters = { @Filter(type = FilterType.ANNOTATION, value = Component.class) })
6. 扫描指定包,自定义过滤规则
@ComponentScan(value = "com.yibai", includeFilters = { @Filter(type = FilterType.CUSTOM, value = ColorBeanLoadFilter.class) }, useDefaultFilters = true)
@ComponentScans(value = {
@ComponentScan(value = "com.yibai.spring.annotation"),
@ComponentScan(value = "com.yibai.spring.annotation",
includeFilters = {@Filter(type = FilterType.CUSTOM,
value = ColorBeanLoadFilter.class)
})
})
源码如下
//OK,下一步处理配置类上的注解@ComponentScans,@ComponentScan。
// 第一步先获取@ComponentScans和@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) {
// 带有@ComponentScan注解的配置类会首先被立刻解析
// 备注:这个方法虽然有返回值
// 但是其实内部都已经把Bean定义信息加入到工厂里面去了
// 进入componentScanParser的parse方法
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(
componentScan,
sourceClass.getMetadata().getClassName());
/*
parse扫描注册完成后,将生成的BeanDefinition返回,还需要把每个Bean检测一遍。因为Scan出来的Bean,还有可能是@Configuration的,因此需要再次交给parse一遍,防止疏漏,换句话说,扫描后的业务类中还有配置类,我们还需要处理这些配置类。getOriginatingBeanDefinition返回原始的 BeanDefinition,没有的话返回 null,链式调用该方法,最终可获取到由用户定义的 BeanDefinition。
*/
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());
}
}
}
}
步骤5.3.3.1:componentScanParser#parse
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
//为什么还要new一个扫描器?
//因为每个@ConponentScan的包含过滤器和排除过滤器不一样,是独一无二的
//需要把每个@ConponentScan的包含过滤器和排除过滤器
//复制到ClassPathBeanDefinitionScanner扫描器中
ClassPathBeanDefinitionScanner scanner
= new ClassPathBeanDefinitionScanner
(this.registry,
componentScan.getBoolean("useDefaultFilters"),
this.environment, this.resourceLoader);
//创建名称生成器
//BeanName的生成器,我们可以单独制定。若不指定(大部分情况下都不指定)
//那就是默认的AnnotationBeanNameGenerator
//它的处理方式是:类名首字母小写。这个上面也讲过啦。
Class<? extends BeanNameGenerator> generatorClass =
componentScan.getClass("nameGenerator");
boolean useInheritedGenerator =
(BeanNameGenerator.class == generatorClass);
scanner.setBeanNameGenerator(useInheritedGenerator ?
this.beanNameGenerator :
BeanUtils.instantiateClass(generatorClass));
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));
}
//static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
//控制去扫描哪些.class文件的模版。一般不设置 默认值为:**/*.class 全扫嘛
scanner.setResourcePattern(componentScan.getString("resourcePattern"));
//之前讲过,就是扫描过滤器,如果你注解上写了过滤器
//就复制到ClassPathBeanDefinitionScanner扫描器中
//获取注解上的includeFilters添加到扫描器
for (AnnotationAttributes filter :
componentScan.getAnnotationArray("includeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addIncludeFilter(typeFilter);
}
}
//获取注解上的excludeFilters添加到扫描器
for (AnnotationAttributes filter :
componentScan.getAnnotationArray("excludeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addExcludeFilter(typeFilter);
}
}
//判断是否懒加载
//扫描的Bean,是否需要懒加载啦,默认为false。
boolean lazyInit = componentScan.getBoolean("lazyInit");
if (lazyInit) {
scanner.getBeanDefinitionDefaults().setLazyInit(true);
}
//获取basePackages属性
Set<String> basePackages = new LinkedHashSet<>();
String[] basePackagesArray = componentScan.getStringArray("basePackages");
//遍历解析basePackages
//因为basePackage中可能含有换行符 , ; 等特殊符号
for (String pkg : basePackagesArray) {
// Spring在此处有强大的容错处理。虽然他是支持数组的,但是它这里也容错处理。
//支持 , ; 换行等的符号分隔处理。
//并且,并且更强大的地方在于:它支持${...}这种占位符的形式,非常的强大。
//我们可以动态的进行扫包了~~~~~厉害了我的哥。
//String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
String[] tokenized = StringUtils.tokenizeToStringArray
(this.environment.resolvePlaceholders(pkg),
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
//将解析后的basePackage放入basePackages
Collections.addAll(basePackages, tokenized);
}
//获取basePackageClasses属性
//basePackageClasses有时候也挺好使的。它可以指定class.
//然后这个class所在的包(含子包)都会被扫描
//即 将basePackageClasses指定的类所在的包以及子包加入到 basePackages
for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
//如果 basePackages 和 basePackageClasses 属性为空 取当前配置类所在的包以及子包
//是不是很熟悉
//SpringBoot就是这么干的。
/*
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {}
*/
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
//这行代码的意思增加一个排他过滤器,配置类本身不需要添加到容器中,他只是用作解析。
scanner.addExcludeFilter(
new AbstractTypeHierarchyTraversingFilter(false, false) {
@Override
protected boolean matchClassName(String className) {
return declaringClass.equals(className);
}
});
return scanner.doScan(StringUtils.toStringArray(basePackages));
}
步骤5.3.3.2:ClassPathBeanDefinitionScanner#doScan
构造方法
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,Environment environment, @Nullable ResourceLoader resourceLoader) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
this.registry = registry;
if (useDefaultFilters) {
//添加默认的包含过滤器
registerDefaultFilters();
}
setEnvironment(environment);
setResourceLoader(resourceLoader);
}
主要是看registerDefaultFilters
protected void registerDefaultFilters() {
/*
*注册了@Component过滤器到includeFiters
*相当于同时注册了所有被@Component注释的注解,
*包括@Service ,@Repository,@Controller
*同时也支持java EE6 的javax.annotation.ManagedBean
*和JSR-330的@Named 注解。
*这就是为什么@Service @Controller @Repostory @Component 能够起作用的原因。
*/
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
//类加载器,这个没啥好解释的
ClassLoader cl =
ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
// 添加ManagedBean 注解过滤器
this.includeFilters.add(
new AnnotationTypeFilter(((Class<? extends Annotation>)
ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
}
catch (ClassNotFoundException ex) {
}
try {
// 添加Named 注解过滤器
this.includeFilters.add(
new AnnotationTypeFilter(((Class<? extends Annotation>)
ClassUtils.forName("javax.inject.Named", cl)), false));
}
catch (ClassNotFoundException ex) {
}
}
首先这里的includeFilters大家熟悉吗,还有个excludeFilters,先看一下属性
//包含的Filters
private final List<TypeFilter> includeFilters = new LinkedList<>();
//排除的Filters
private final List<TypeFilter> excludeFilters = new LinkedList<>();
这里提前往includeFilters里面添加需要扫描的特定注解
0.添加元注解@Component,需要注意的是@Repository、@Service、@Controller里面都标注了@Component。很好理解,扫描的时候用includeFilters 去过滤时,会找到并处理这4个注解的类。
1.上面源码中的两个注解@ManagedBean、@Named需要有对应的jar包,否则(也就是说把这个方法走完),includeFilters里面只会有一个元素。其实按照spring的加载流程,ClassPathBeanDefinitionScanner到这里的作用就结束,里面的很多重要方法是在流程加载后面用到的,但是既然都是一个类里面的方法,就在这里先讲一下吧。
scan扫描
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new
AnnotationConfigApplicationContext();
//注册配置类
context.register(Config.class);
context.scan("com");
context.refresh();
}
}
scan方法是开始扫描的入口,context.scan("com")->this.scanner.scan(basePackages);这个scanner就是默认构造函数初始化的ClassPathBeanDefinitionScanner,只有手动调用context.scan("com");这个初始化的scanner才有发挥的作用,这个类起始就是为程序员手动扫描用的,spring内部自动扫描会再生成一个ClassPathBeanDefinitionScanner对象完成扫描,从代码角度来讲完成的功能一模一样。
/**
* 扫描给定的包路径,生成BeanDefinition并注册到注册器
*/
public int scan(String... basePackages) {
////获取注册器中已注册的bean数量
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
//通过doScan给定包路径并生成BeanDefinition注册到registry中
doScan(basePackages);
// Register annotation config processors, if necessary.
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.
registerAnnotationConfigProcessors(this.registry);
}
//返回本次扫描并注册到IOC容器中的类
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
看源码可知,真正完成扫描的是doScan方法:
/**
* 扫描给定的包路径,生成BeanDefinition并注册到注册器
*/
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages,
"At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
/**
* 扫描basePackage路径下的java文件
* 先全部转为Resource,然后再判断拿出符合条件的bd
*/
for (String basePackage : basePackages) {
// 调用findCandidateComponents扫描包组装BeanDefinition集合
//findCandidateComponents方法是从父类继承的方法
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
//遍历BeanDefinition,根据条件将BeanDefinition注册到注册中心
for (BeanDefinition candidate : candidates) {
//解析scope属性
ScopeMetadata scopeMetadata = this.scopeMetadataResolver
.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
//获取beanName,先判断注解上有没有显示设置beanName
//没有的话,就以类名小写为beanName
String beanName = this.beanNameGenerator.
generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
/**
* 如果这个类是AbstractBeanDefinition类型
* 则为他设置默认值,比如lazy/init/destroy
* 通过扫描出来的bd是ScannedGenericBeanDefinition
* 实现了AbstractBeanDefinition
*/
postProcessBeanDefinition
((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
/**
* 处理加了注解的类
* 把常用注解设置到AnnotationBeanDefinition中
*/
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);
}
}
}
//返回扫描到的所有的beanDefinitions
return beanDefinitions;
}
先关注findCandidateComponents(basePackage);方法,进入到父类ClassPathScanningCandidateComponentProvider。
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex
(this.componentsIndex, basePackage);
}
else {
//完成真正的扫描
return scanCandidateComponents(basePackage);
}
}
根据方法名顾名思义,找到候选组件,在指定的包中找到候选组件。
进入scanCandidateComponents(basePackage);中
/**
* 扫描包,生成BeanDefinition集合
*/
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
// //根据包名组装包扫描路径
// 如,classpath*:com/**/*.class
String packageSearchPath =
ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
/*
resourcePatternResolver(资源加载器)根据匹配规则获取Resource[]
Resource数组中每一个对象都是对应一个Class文件
Spring用Resource定位资源,封装了资源的IO操作。
这里的Resource实际类型是FileSystemResource。资源加载器其实就是容器本身
*/
Resource[] resources =
getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
//循环处理每一个resource,相当于循环处理每一个class文件
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
/*
*读取类的注解信息和类信息,信息储存到MetadataReader
*meteDataFactory根据Resouce获取到MetadataReader对象
*MetadataReader提供了获取一个Class文件的
*ClassMetadata和AnnotationMetadata的操作。
*/
MetadataReader metadataReader =
getMetadataReaderFactory().getMetadataReader(resource);
/*
*判断元数据是否需要组装成BeanDefinition
*此处判断当前class是否需要注册到spring的IOC容器中通过IOC容器管理。
*spring默认对Component注解的类进行动态注册到IOC容器
*通过includeFilters与excludeFilters来判定匹配。
*/
if (isCandidateComponent(metadataReader)) {
// //把符合条件的 类转换成 BeanDefinition
ScannedGenericBeanDefinition sbd = new
ScannedGenericBeanDefinition(metadataReader);
sbd.setSource(resource);
// 再次判断 如果是实体类 返回true
//如果是抽象类,但是抽象方法 被 @Lookup 注解注释返回true
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug
("Identified candidate component class");
}
//返回BeanDefinition 注册到 BeanFactory
candidates.add(sbd);
}
else {
if (debugEnabled) {
logger.debug
("Ignored,not a concrete top-level-class");
}
}
}
else {
if (traceEnabled) {
logger.trace
("Ignored because not matching anyfilter");
}
}
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException
("Failed to read candidate component class: ");
}
}
else {
if (traceEnabled) {
logger.trace("Ignored because not readable: " + resource);
}
}
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException
("I/O failure during classpath scanning", ex);
}
return candidates;
}
上面有一个组件判断if (isCandidateComponent(metadataReader)),就是判断当前class文件符不符合扫描过滤器includeFilters与excludeFilters中的定义,最后返回一个符合条件的Set<BeanDefinition>。
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
//看下是否在排除的filter内 如果是返回false
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return false;
}
}
//看下是否在包含的filter内 如果是返回true
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return isConditionMatch(metadataReader);
}
}
return false;
}
再回到之前的 doScan(String... basePackages)中findCandidateComponents下面的方法for (BeanDefinition candidate : candidates),开始循环,处理注解,设置beanDefinition属性最后执行 registerBeanDefinition(definitionHolder, this.registry);注册beanDefinition
跟进此方法:registerBeanDefinition(definitionHolder, this.registry);
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
/**
* 这里的registerBeanDefinition是由父类GenericApplicationContext实现的
* 跟踪源码可知,是在父类中调用
* this.beanFactory.registerBeanDefinition(beanName, beanDefinition)
* 而这个beanFactory是AnnotationConfigApplicationContext
* 在执行自己的构造方法this()时
* 先去执行了父类GenericApplicationContext的构造方法
* 完成了this.beanFactory = new DefaultListableBeanFactory()
* 所以,最终将beanDefinition注册到了DefaultListableBeanFactory中
* */
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
//注册beanDefinition
registry.registerBeanDefinition(beanName,
definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
parse扫描注册完成后,将生成的BeanDefinition返回,还需要把每个Bean检测一遍。因为Scan出来的Bean,还有可能是@Configuration的,因此需要再次交给parse一遍,防止疏漏,换句话说,扫描后的业务类中还有配置类,我们还需要处理这些配置类。getOriginatingBeanDefinition返回原始的 BeanDefinition,没有的话返回 null,链式调用该方法,最终可获取到由用户定义的 BeanDefinition。
步骤5.4:递归调用处理@Import、@ImportSeletor、@DeferredSeletor、@ImportBeanDefinitionRegistrar
注解@ComponentScans,@ComponentScan讲完了,下一步看注解@Import的处理。
//getImports(sourceClass) 获取的是 @Import("User.class") 中的 User.class
//即使User上也有注解@Import(ImportedByUser.class)
//getImports(sourceClass) 返回的也是 User
//User会被当做普通的配置类处理解析
//解析的时候自然会解析User注解@Import(ImportedByUser.class)导入的ImportedByUser
processImports(configClass, sourceClass, getImports(sourceClass), true);
//importCandidates
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
// importCandidates为空直接返回
if (importCandidates.isEmpty()) {
return;
}
//检查循环依赖
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error
(new CircularImportProblem(configClass, this.importStack));
}else {
//入栈
this.importStack.push(configClass);
try {
//遍历循环配置类上的@Import注解上的value属性的值
for (SourceClass candidate : importCandidates) {
//第一种情况:candidate是ImportSelector接口类型
//比如@Import("User.class")
//User实现了ImportSelector接口或者DeferredImportSelector接口
if (candidate.isAssignable(ImportSelector.class)) {
// 获取ImportSelector对应的class
Class<?> candidateClass = candidate.loadClass();
// 反射调用ImportSelector的构造方法实例化
ImportSelector selector
= BeanUtils.instantiateClass
(candidateClass, ImportSelector.class);
//是否有实现相关Aware接口,如果有,调用相关方法
ParserStrategyUtils.invokeAwareMethods(
selector,
this.environment,
this.resourceLoader,
this.registry);
//1.如果ImportSelector是DeferredImportSelector
//即延迟加载的selector
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle
(configClass, (DeferredImportSelector) selector);
}else {
//2.ImportSelector 立即加载的selector
//普通的ImportSelector接口实现类 ,执行其selectImports方法
//获取需要导入的类的全限定类名数组
String[] importClassNames =
selector.selectImports
(currentSourceClass.getMetadata());
//获取需要导入类的class
Collection<SourceClass> importSourceClasses =
asSourceClasses(importClassNames);
// 使用导入的类作为配置类 递归调用 processImports方法
processImports(configClass,
currentSourceClass,
importSourceClasses, false);
}
}else if (candidate.isAssignable
(ImportBeanDefinitionRegistrar.class)) {
//第二种情况:
//candidate是ImportBeanDefinitionRegistrar接口的实现类
//比如@Import("User.class")
//User实现了ImportBeanDefinitionRegistrar接口
//ImportBeanDefinitionRegistrar是注册一个beanDefinition
//获取 ImportBeanDefinitionRegistrar实现类对应的class
Class<?> candidateClass = candidate.loadClass();
//反射实例化ImportBeanDefinitionRegistrar的实现类
ImportBeanDefinitionRegistrar registrar
= BeanUtils.instantiateClass
(candidateClass, ImportBeanDefinitionRegistrar.class);
//是否有实现相关Aware接口,如果有,调用相关方法
ParserStrategyUtils.invokeAwareMethods(
registrar,
this.environment,
this.resourceLoader,
this.registry);
//将实例化好的ImportBeanDefinitionRegistrar放入configClass
//注册BeanDefinition
configClass.addImportBeanDefinitionRegistrar
(registrar, currentSourceClass.getMetadata());
}
//第三种情况
//既没有实现ImportSelector接口
//也没有实现ImportBeanDefinitionRegistrar接口
//candidate是就是1个普通类 上面没有任何注解
//比如@Import("User.class")
//就算是一个普通的class也会被当做带有@Configuration的class去解析
else {
// 普通 @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(),
candidate.getMetadata().getClassName());
// 继续解析导入的@Configuration class
// 因为解析的已经是一个class 所以将class转换为ConfigurationClass继续解析就可以了
// 所以递归调用重新进入processConfigurationClass
//解析配置类调用链
//ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry //ConfigurationClassPostProcessor#processConfigBeanDefinitions
//进入ConfigurationClassParser#parse(Set<BeanDefinitionHolder> configCandidates)
//ConfigurationClassParser#parse(AnnotationMetadata,String)
//ConfigurationClassParser#processConfigurationClass(ConfigurationClass)
processConfigurationClass
(candidate.asConfigClass(configClass));
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates " +
"for configuration class" + ex);
}
finally {
this.importStack.pop();
}
}
}
@Import注解场景
那下面我们来罗列有哪些添加组件的方式?
1、通过@CompentScan +@Controller @Service @Respository @compent
适合场景:我们自己写的组件可以通过这种方式来进行加载到容器中。
2、通过@Bean的方式来导入组件
适合场景: 适用于导入第三方组件的类
3、通过@Import 来导入组件
适合场景:导入组件的id为全路径,用处最多的是其他框架整个Spring时,使用@Import注解导入整合类。
processImports方法负责对@Import注解进行解析。 configClass是配置类,sourceClass又是通过configClass创建的。getImports(sourceClass)从sourceClass获取@Import注解导入的所有的类,然后调用ConfigurationClassParser#processImports。 在应用中,有时没有把某个类注入到IOC容器中,但在运用的时候需要获取该类对应的bean,比如第三方的组件?此时就需要用到@Import注解。
spring首先把import进来的类当作配置类处理,然后递归处理该配置类引入的其他类。
继续打比方: 首先创建一个业务类,且不要放到扫描包下,是不会扫描到IOC容器中的:
public class User {
}
然后import到配置类上
@Order(1)
@Configuration
@PropertySource({"classpath:demo.properties"})
@ComponentScan(value = "com,net",basePackageClasses = Person.class)
@Import(User.class)
public class Config {
@Value("${demo.name}")
private String name;
public String getName() {
return name;
}
}
我们看下processImports的源码怎么处理注解@Import的
if (importCandidates.isEmpty()) {
return;
}
这行代码的意思是如果没有@Import注解,就返回不做任何处理。
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}else {
//三种情况
}
if这行判断是解决循环引用的,暂不关心,以后会详细将循环引用问题。
第一次的话执行else语句,然后拿出@Import注解中的所有类进行循环处理,这里只有User。
if语句中有三种处理情况:
1.实现ImportSelector接口
2.实现ImportBeanDefinitionRegistrar接口
3.既没有实现ImportSelector接口也没有实现ImportBeanDefinitionRegistrar接口
1.实现了ImportSelector接口
ImportSelector接口:ImportSelector接口只定义了一个selectImports(),用于指定需要注册为bean的Class名称。当在@Configuration标注的Class上使用@Import引入了一个ImportSelector实现类后,会把实现类中返回的Class名称(是1个字符串数组) 都定义为bean。
来看一个简单的示例,假设现在有一个接口HelloService,需要把所有它的实现类都定义为bean,而且它的实现类上是没有加Spring默认会扫描的注解的,比如@Component、@Service等。 首先让上文的User类实现ImportSelector接口,然后在方法中返回User2全限定名,这样spring就会将User2添加到容器中。
public class User2 {
}
//因为@Import(User.class)会加载Uuser
//加载User时发现User 实现了 ImportSelector
//会将selectImports 返回的bean都加入到工厂中
public class User implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[] {User2.class.getName()};
}
}
递归调用processImports方法解析ImportSelector接口导入的类
为什么要递归调用,因为你导入的类可能还有Import注解啊,那也要处理的!
递归调用processImports,看看ImportSelector接口导入的Class是不是也导入了新的类。
//如果实现了ImportSelector接口
if (candidate.isAssignable(ImportSelector.class)) {
Class<?> candidateClass = candidate.loadClass();
// 反射创建这个类的实例对象
ImportSelector selector =
BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
//是否有实现相关Aware接口,如果有,调用相关方法
ParserStrategyUtils
.invokeAwareMethods
(selector, this.environment, this.resourceLoader, this.registry);
// 延迟加载的ImportSelector
if (selector instanceof DeferredImportSelector) {
// 延迟加载的ImportSelector先放到List中,延迟加载
this.deferredImportSelectorHandler
.handle(configClass, (DeferredImportSelector) selector);
}else {
// 普通的ImportSelector ,执行其selectImports方法,获取需要导入的类的全限定类名数组
String[] importClassNames = selector.selectImports
(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses =
asSourceClasses(importClassNames);
// 递归调用processImports 看看User导入的Class是不是有导入了新的类
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
代码很简单,相信大家能看懂,但是有这么行代码
ParserStrategyUtils.invokeAwareMethods(
selector,
this.environment,
this.resourceLoader,
this.registry);
判断是否有实现相关Aware接口,如果有,这调用相关方法。Aware,是感应和感知的意思。当bean实现了对应的Aware接口时,BeanFactory会在生产bean时根据它所实现的Aware接口,给bean注入对应的属性,从而让bean获取外界的信息。Aware是顶级接口,它有很多子接口:
如果我们被Import的User类实现了Aware接口,就会在上面invokeAwareMethods源码内调用实现该接口的方法,但是在此处不是所有有Aware接口都可以,invokeAwareMethods源码:
public static void invokeAwareMethods(Object parserStrategyBean, Environment
environment,ResourceLoader resourceLoader, BeanDefinitionRegistry registry) {
if (parserStrategyBean instanceof Aware) {
if (parserStrategyBean instanceof BeanClassLoaderAware) {
ClassLoader classLoader =
(registry instanceof ConfigurableBeanFactory ?
((ConfigurableBeanFactory) registry).getBeanClassLoader() :
resourceLoader.getClassLoader());
if (classLoader != null) {
((BeanClassLoaderAware) parserStrategyBean)
.setBeanClassLoader(classLoader);
}
}
if (parserStrategyBean instanceof BeanFactoryAware &&
registry instanceof BeanFactory) {
((BeanFactoryAware) parserStrategyBean)
.setBeanFactory((BeanFactory) registry);
}
if (parserStrategyBean instanceof EnvironmentAware) {
((EnvironmentAware) parserStrategyBean)
.setEnvironment(environment);
}
if (parserStrategyBean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) parserStrategyBean)
.setResourceLoader(resourceLoader);
}
}
}
只有BeanClassLoaderAware、BeanFactoryAware 、EnvironmentAware、ResourceLoaderAware接口才能被调用,其他Aware接口啥时候调用以后再说。举个例子:
public class User implements ImportSelector, BeanFactoryAware {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[] {User2.class.getName()};
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("获取到了bean工厂");
}
}
也就是说我们能在User实现接口的方法内操作以下方法:
Aware接口处理完后,就是判断User是否是延迟加载:
if (selector instanceof DeferredImportSelector)
显然,我们的User实现的ImportSelector接口,没有实现DeferredImportSelector接口,所以就不是延迟加载。
如果不是延迟加载的话,if条件不执行,执行else中的语句:
// 普通的ImportSelector ,执行其selectImports方法,获取需要导入的类的全限定类名数组
String[] importClassNames =
selector.selectImports(currentSourceClass.getMetadata());
//selectImports返回的类的全限定类名数组对应的Class
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
// 递归调用
processImports(configClass, currentSourceClass, importSourceClasses, false);
为什么要递归调用,因为你导入的类可能还有Import注解啊,那也要处理的!
2.实现了DeferredImportSelector
DeferredImportSelector是ImportSelector的子接口。如果我们要实现延迟加载功能,让User实现DeferredImportSelector接口即可:
public class User implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
System.out.println("延迟加载User2");
return new String[] {User2.class.getName()};
}
}
// 反射创建实现DeferredImportSelector这个类的实例对象
ImportSelector selector =
BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
//是否有实现相关Aware接口,如果有,调用相关方法
ParserStrategyUtils
.invokeAwareMethods
(selector, this.environment, this.resourceLoader, this.registry);
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle
(configClass, (DeferredImportSelector) selector);
}
启动spring,就会进入上面的if判断里的
this.deferredImportSelectorHandler
.handle(configClass, (DeferredImportSelector) selector);
handle方法内会将selector再封装成DeferredImportSelectorHolder类型的对象,然后保存在数组中等以后调用。
调用时机是在parse(Set<BeanDefinitionHolder> configCandidates)方法的最后一行
/****
@Nullable
private List<DeferredImportSelectorHolder> deferredImportSelectors
= new ArrayList<>();
***/
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
//封装成DeferredImportSelectorHolder类型的对象
DeferredImportSelectorHolder holder
= new DeferredImportSelectorHolder(configClass, importSelector);
if (this.deferredImportSelectors == null) {
DeferredImportSelectorGroupingHandler handler
= new DeferredImportSelectorGroupingHandler();
handler.register(holder);
handler.processGroupImports();
}
else {
//加入deferredImportSelectors
this.deferredImportSelectors.add(holder);
}
}
3.实现了ImportBeanDefinitionRegistrar接口:加入集合
ImportBeanDefinitionRegistrar接口是也是spring的扩展点之一,它可以支持我们自己写的代码封装成BeanDefinition对象;实现此接口的类会回调postProcessBeanDefinitionRegistry方法,注册到spring容器中。把bean注入到spring容器不止有 @Service @Component等注解方式;还可以实现此接口。
比如:
public class User implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
BeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClassName(User2.class.getName());
MutablePropertyValues values = beanDefinition.getPropertyValues();
values.addPropertyValue("id", 1);
values.addPropertyValue("name", "源码之路");
//这里注册bean
registry.registerBeanDefinition("testBean", beanDefinition );
}
}
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 =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
//调用Aware接口
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
//添加到集合importBeanDefinitionRegistrars
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
4.@Import导入的普通类
最后,既没有实现ImportSelector接口也没有实现ImportBeanDefinitionRegistrar接口:
会被作为普通的带有@Configuration的类解析,但是并不会加入BeanDefinitionMap
会被作为普通的带有@Configuration的类解析,但是并不会加入BeanDefinitionMap
会被作为普通的带有@Configuration的类解析,但是并不会加入BeanDefinitionMap
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
// 解析@Import导入的类
// 会被作为普通的带有@Configuration的类解析
// 注意processImports和processConfigurationClass的区别!
// processImports最后会被作为bd注册
// processConfigurationClass则不会
processConfigurationClass(candidate.asConfigClass(configClass));
// 继续解析@Import导入的类
// 因为解析的已经是一个class 所以将class转换为ConfigurationClass继续解析就可以了
// 所以递归调用重新进入processConfigurationClass
//解析配置类调用链
//ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry //ConfigurationClassPostProcessor#processConfigBeanDefinitions
//进入ConfigurationClassParser#parse(Set<BeanDefinitionHolder> configCandidates)
//ConfigurationClassParser#parse(AnnotationMetadata,String)
//ConfigurationClassParser#processConfigurationClass(ConfigurationClass)
按普通配置类处理~
@Import注解处理暂时先告一段落,下面继续看@ImportResource注解的处理。
步骤5.5@ImportResource
@ImportResource用来导入Spring 的配置文件,如核心配置文件 "beans.xml",从而让配置文件里面的内容生效。
讲道理,大家平时都不用xml配置文件来做开发了吧,注解方式差不多完全替代XML方式了。这个也很简单,不讲了,这篇文章还能坚持看到这里的读者,想必有能力分析这块的源码了。
注意,这块源码的处理并没有解析配置文件,而是先保存在map中,后续解析。先记着,整个方法分析完了你就知道了。
步骤5.5:递归调用处理@Bean注解的方法
//找到含有@Bean注解的方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
//先添加到配置类的beanMethods集合中
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
//下一步,处理配置类所实现接口的缺省方法:
// java8中,新增加了一个特性,接口中可以有默认实现方法:
// Process default methods on interfaces
//找到配置类的所有接口,遍历接口
//找到所有含有@Bean注解的默认方法
//过滤抽象方法
//最后把符合条件的也加入到配置类的beanMethods集合中
processInterfaces(configClass,sourceClass);
步骤5.5.1: 找到含有@Bean注解的方法放入集合
private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {
AnnotationMetadata original = sourceClass.getMetadata();
//获取带有@Bean注解的方法
//将@Bean方法转化为BeanMethod对象,保存在集合中,后续再用。
Set<MethodMetadata> beanMethods =
original.getAnnotatedMethods(Bean.class.getName());
//遍历beanMethods
if (beanMethods.size() > 1
&& original instanceof StandardAnnotationMetadata) {
try {
AnnotationMetadata asm =
this.metadataReaderFactory.getMetadataReader
(original.getClassName()).getAnnotationMetadata();
Set<MethodMetadata> asmMethods =
asm.getAnnotatedMethods(Bean.class.getName());
if (asmMethods.size() >= beanMethods.size()) {
Set<MethodMetadata> selectedMethods
= new LinkedHashSet<>(asmMethods.size());
for (MethodMetadata asmMethod : asmMethods) {
for (MethodMetadata beanMethod : beanMethods) {
if (beanMethod.getMethodName().
equals(asmMethod.getMethodName())) {
//先添加到集合中
selectedMethods.add(beanMethod);
break;
}
}
}
if (selectedMethods.size() == beanMethods.size()) {
beanMethods = selectedMethods;
}
}
}
catch (IOException ex) {
logger.debug("Failed to read class file via ASM "+
"for determining@Bean method order", ex);
}
}
return beanMethods;
}
将@Bean方法转化为BeanMethod对象,保存在集合中,后续再用。
步骤5.5.2: 递归获取配置类及父类接口中默认方法加入集合
下一步,处理配置类所实现接口的缺省方法:java8中,新增加了一个特性,接口中可以有默认实现方法:
public interface UserInterface {
/*
* 默认方法,有方法体
* 任何一个实现了UserInterface接口的类都会向动继承isEmpty的实现
*/
@Bean
default User2 getUser2() {
return new User2();
}
}
默认方法,有方法体,任何一个实现了Sized接口的类都会向动继承isEmpty的实现,这个默认方法加一个@Bean注解的话,且返回一个对象,spring会将这个对象注册到spring容器中。
源码:
processInterfaces(configClass, sourceClass);就是完成这个功能的。这个方法的源码也比较简单:
private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
//找到配置类的所有接口,遍历接口
for (SourceClass ifc : sourceClass.getInterfaces()) {
//找到含有@Bean注解的默认方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(ifc);
for (MethodMetadata methodMetadata : beanMethods) {
//过滤抽象方法
if (!methodMetadata.isAbstract()) {
//添加到集合
configClass.addBeanMethod
(new BeanMethod(methodMetadata, configClass));
}
}
//递归,因为接口可能有父接口
processInterfaces(configClass, ifc);
}
}
最后,最后,最后一步,如果有父类,则解析父类。坚持下!
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();
}
}
如果有父类则返回父类Class对象,继续调用该方法。直到返回null,外层循环结束。怎么讲?doProcessConfigurationClass此时结束了,我们看调用此方法的上一层方法processConfigurationClass。
以上省略
do {
// 真正的做解析
//返回的 sourceClass 是刚才解析的configClass的父类
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}while (sourceClass != null);
就是说,如果你有父类,此时返回来的sourceClass 是父类,继续递归调用doProcessConfigurationClass方法,直到返回的是null后while循环才结束。
如果没有父类,则执行doProcessConfigurationClass方法的最后一行代码return null;,此时的while循环也结束了,继续执行最后一行代码:this.configurationClasses.put(configClass, configClass);
这行代码的意思是,需要被处理的配置类configClass已经被分析处理,将它记录到已处理配置类记录。
至此,我们的processConfigurationClass->doProcessConfigurationClass方法分析完了,往回返,一直到入口处,即ConfigurationClassPostProcessor.processConfigBeanDefinitions方法中parser.parse(candidates);代码处,对吧,上面讲了那么多,就是讲了parse方法的处理。
步骤5.5.3处理DeferredImportSelector
deferredImportSelectorHandler.process()
入口:this.deferredImportSelectorHandler.process();
public void process() {
//在处理@Import注解时
//DeferredImportSelector的实现类已经实例化
//并封装为DeferredImportSelectorHolder
//放入deferredImportSelectors集合
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<>();
}
}
}
processGroupImports
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping :
this.groupings.values()) {
//调用getImports
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass =
this.configurationClasses.get(entry.getMetadata());
try {
//调用processImports方法
//也就是5.4步的processImports方法
//将这些被DeferredSelectImporter接口导入的类
//作为普通的@Configuration的Class被解析
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 ", ex);
}
});
}
}
getImports
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport :
this.deferredImports) {
//调用process方法
this.group.process
(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
//返回的是调用selector的selectImports
return this.group.selectImports();
}
@Override
public void process(AnnotationMetadata metadata,
DeferredImportSelector selector) {
//调用selector的selectImports
for (String importClassName : selector.selectImports(metadata)) {
this.imports.add(new Entry(metadata, importClassName));
}
}
步骤5.6:校验配置类
我们继续分析parse的后续源码,坚持住,马上看到曙光了。
public void validate() {
//遍历所有的 configurationClasses
for (ConfigurationClass configClass : this.configurationClasses.keySet()) {
configClass.validate(this.problemReporter);
}
}
校验所有的configurationClasses
public void validate(ProblemReporter problemReporter) {
//配置类不能为final (CGLIB limitation)
if (getMetadata().isAnnotated(Configuration.class.getName())) {
if (getMetadata().isFinal()) {
problemReporter.error(new FinalConfigurationProblem());
}
}
//获取configurationClass的所有的@Bean修饰的方法
for (BeanMethod beanMethod : this.beanMethods) {
beanMethod.validate(problemReporter);
}
}
@Bean修饰的方法主要包括两方面的校验:
public void validate(ProblemReporter problemReporter) {
if (getMetadata().isStatic()) {
// @Bean修饰的方法是静态的可以直接返回
return;
}
//如果配置类被加了@Configuration注解
if (this.configurationClass.getMetadata()
.isAnnotated(Configuration.class.getName())) {
//如果不可以被重写,那么不能被CGLIB
//就报告错误
if (!getMetadata().isOverridable()) {
// instance @Bean methods within @Configuration classes
// must be overridable to accommodate CGLIB
problemReporter.error(new NonOverridableMethodError());
}
}
}
@Override
public boolean isOverridable() {
//非静态
//非final
//非私有
return (!isStatic()
&& !isFinal()
&& ((this.access & Opcodes.ACC_PRIVATE) == 0));
}
0.配置类不能是final(CGLIB限制)
1.@Configuration配置类中的 @Bean方法必须可重写(非final&& 非静态 && 非私有) 以容纳CGLIB。
步骤5.7:loadBeanDefinitions
入口:this.reader.loadBeanDefinitions(configClasses);
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator
= new TrackedConditionEvaluator();
//遍历所有的配置类
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass
(configClass, trackedConditionEvaluator);
}
}
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
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;
}
//判断配置类是否被@Import注解导入的类
if (configClass.isImported()) {
//如果是 注入@Import注解导入的类对应的bd
//默认是用的类的全限定名
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
//遍历配置类中带有@Bean的方法
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
//注入@Bean的方法对应的bd
loadBeanDefinitionsForBeanMethod(beanMethod);
}
//从ImportedResource加载bd
loadBeanDefinitionsFromImportedResources
(configClass.getImportedResources());
//实现了ImportBeanDefinitionRegistrar接口的类
//会被加入集合importBeanDefinitionRegistrars
//configClass.getImportBeanDefinitionRegistrars()就是这个集合
//遍历importBeanDefinitionRegistrars集合
//从ImportBeanDefinitionRegistrar加载bd
loadBeanDefinitionsFromRegistrars
(configClass.getImportBeanDefinitionRegistrars());
}
在这里统一处理没有注册的进行注册,将上一步parser解析出的ConfigurationClass类加载成BeanDefinition,实际上经过上一步的parse()后,解析出来的bean已经放入到BeanDefinition中了,但是由于这些bean可能会引入新的bean。
例如实现了ImportBeanDefinitionRegistrar或者实现Import接口的bean,或者bean中存在被@Bean注解的方法,因此需要执行一次loadBeanDefinition()。
这样就会执行ImportBeanDefinitionRegistrar或者ImportSelector接口的方法或者@Bean注释的方法。 再有20多行代码就分析完了,加油!
在这里统一处理没有注册的进行注册,将上一步parser解析出的ConfigurationClass类加载成BeanDefinition,实际上经过上一步的parse()后,解析出来的bean已经放入到BeanDefinition中了,但是由于这些bean可能会引入新的bean,例如实现了ImportBeanDefinitionRegistrar或者ImportSelector接口的bean,或者bean中存在被@Bean注解的方法,因此需要执行一次loadBeanDefinition(),这样就会执行ImportBeanDefinitionRegistrar或者ImportSelector接口的方法或者@Bean注释的方法。是不是蒙蔽了这里,其实之前的源码都经历过了,建议大家收藏这篇博文,对照着源码反复看。 再有20多行代码就分析完了,加油!
do {
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses =
new LinkedHashSet<>(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<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses =
new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses
.add(configurationClass.getMetadata().getClassName());
}
// 如果有未解析的类,则将其添加到candidates中
//这样candidates不为空,就会进入到下一次的while的循环中
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 && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME,
parser.getImportRegistry());
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
// Clear cache in externally provided MetadataReaderFactory; this is a no-op
// for a shared cache since it'll be cleared by the ApplicationContext.
((CachingMetadataReaderFactory)
this.metadataReaderFactory).clearCache();
}
}
这里判断registry.getBeanDefinitionCount() > candidateNames.length的目的是为了知道reader.loadBeanDefinitions(configClasses)这一步有没有向BeanDefinitionMap中添加新的BeanDefinition,实际上就是看配置类, 如果有,registry.getBeanDefinitionCount()就会大于candidateNames.length ,这样就需要再次遍历新加入的BeanDefinition,并判断这些bean是否已经被解析过了,如果未解析,需要重新进行解析,这里的Config类向容器中添加的bean,实际上在parser.parse()这一步已经全部被解析了,所以为什么还需要做这个判断,目前没看懂,似乎没有任何意义。 好了,到此为止,ConfigurationClassPostProcessor 中最牛逼的postProcessBeanDefinitionRegistry的方法就分析完了,spring启动过程中,会自动调用改处理器的postProcessBeanDefinitionRegistry方法完成扫描与注册。
用框图总结一下!
示例代码:processImports
package parse;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import({User.class,Person.class,People.class,Normal.class})
public class Config {
public Config() {
System.out.println("------------------------Config实例化了");
}
}
package parse;
public class ImportedByPerson {
}
package parse;
public class ImportedByUser {
public ImportedByUser() {
System.out.println("ImportedByUser");
}
}
package parse;
public class Normal {
}
package parse;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.context.annotation.Primary;
import org.springframework.core.type.AnnotationMetadata;
@Primary
public class People implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
System.out.println(importingClassMetadata);
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(People.class);
registry.registerBeanDefinition("people", genericBeanDefinition);
}
}
package parse;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
public class Person implements ImportSelector {
private int age =31;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
String name = ImportedByPerson.class.getName();
return new String[]{name};
}
}
package parse;
import org.springframework.context.annotation.DeferredImportSelector;
import org.springframework.context.annotation.Description;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.stereotype.Component;
//为了方便测试可以先注释掉
//@Component
@Description("业务类")
public class User implements DeferredImportSelector {
private int age =31;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
String name = ImportedByUser.class.getName();
return new String[]{name};
}
}
package parse;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
public class ParseTest {
public static void main(String[] args) throws InterruptedException {
start1();
}
public static void start1() {
//其中Config.class可以指定为
//包扫描启动:@ComponentScan("tyrant")
//使用xml文件启动:@ImportResource("applicationContext.xml")
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
//注册配置类
context.register(Config.class);
context.refresh();
Object user = context.getBean("user");
System.out.println(user);
}
public static void start2() {
//其中Config.class可以指定为
//包扫描启动:@ComponentScan("tyrant")
//使用xml文件启动:@ImportResource("applicationContext.xml")
AnnotationConfigApplicationContext context = new
AnnotationConfigApplicationContext(Config.class);
//加载或者刷新当前的配置信息
context.refresh();
}
}
processImports逻辑梳理
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
//检测Import循环依赖
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error
(new CircularImportProblem(configClass, this.importStack));
}else {
this.importStack.push(configClass);
try {
//遍历的importCandidates 是@Import导入的类集合
// result = {LinkedHashSet@1112} size = 3
// 0 = {ConfigurationClassParser$SourceClass@1192} "parse.User"
// 1 = {ConfigurationClassParser$SourceClass@1193} "parse.Person"
// 2 = {ConfigurationClassParser$SourceClass@1194} "parse.People"
for (SourceClass candidate : importCandidates) {
//如果导入的类是实现了ImportSelector接口
if (candidate.isAssignable(ImportSelector.class)) {
// 处理 ImportSelector
Class<?> candidateClass = candidate.loadClass();
//实例化ImportSelector
ImportSelector selector
= BeanUtils.instantiateClass
(candidateClass, ImportSelector.class);
//处理Aware接口
ParserStrategyUtils.invokeAwareMethods(
selector,
this.environment,
this.resourceLoader,
this.registry);
//判断是否是DeferredImportSelector 延迟加载的selector
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler
.handle(configClass,
(DeferredImportSelector) selector);
}
//ImportSelector 立即加载的selector
else {
//普通的ImportSelector
//执行其selectImports方法,获取需要导入的类的全限定类名数组
String[] importClassNames
= selector.selectImports
(currentSourceClass.getMetadata());
//遍历selectImports方法导入的类名
Collection<SourceClass> importSourceClasses
= asSourceClasses(importClassNames);
// 递归调用
processImports(configClass,
currentSourceClass,
importSourceClasses,
false);
}
}
//处理实现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导入的类既没有实现ImportSelector接口
//也没有实现ImportBeanDefinitionRegistrar接口
//也就是最后@Import导入的类一定会被当做普通的配置类解析
// 普通 @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(),
candidate.getMetadata().getClassName());
// 递归解析导入的@Configuration class
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();
}
}
}
OK 最后总结一下
步骤1:解析Config获取Config的@Import导入的几个类
public class User implements DeferredImportSelector
public class Person implements ImportSelector
public class People implements ImportBeanDefinitionRegistrar
public class Normal
步骤2:先处理User,User实现了DeferredImportSelector接口。
将User对应的bd封装为DeferredImportSelectorHolder并加入集合
this.deferredImportSelectors.add(holder);
注意User对应的bd没有被注册!
步骤3:处理Person,Person实现了ImportSelector接口,调用Person的selectorImports方法,并递归调用processImports方法解析Person的selectorImports方法返回的类ImportedByPerson。
ImportedByPerson没有实现任何接口会被当做普通的配置类处理!加入配置类集合。
this.configurationClasses.put(configClass, configClass);
注意Person对应的bd没有被注册!
步骤4:处理People,People实现了ImportBeanDefinitionRegistrar接口。会把实例化Peole并加入Config的集合importBeanDefinitionRegistrars。
步骤5:处理Normal,Normal什么都没有。作为普通类处理!加入配置类集合。
this.configurationClasses.put(configClass, configClass);
步骤6:this.deferredImportSelectorHandler.process();
调用User的selectorImports方法,并递归调用processImports方法解析Person的selectorImports方法返回的类ImportedByUser。
ImportedByUser没有实现任何接口会被当做普通的配置类处理!加入配置类集合。
this.configurationClasses.put(configClass, configClass);
注意User对应的bd没有被注册!
步骤7:this.reader.loadBeanDefinitions(configClasses);configClasses有以下几个
ImportedByPerson
Normal
Config
ImportedByUser
主要对上面的配置类作如下操作
//判断配置类是否被@Import注解导入的,如果是那么注入对应的bd
//遍历配置类中带有@Bean的方法注入bd
//从ImportedResource加载bd
//从ImportBeanDefinitionRegistrar加载bd
步骤8:
ImportedByPerson被 ImportSelector注解导入,注册ImportedByPerson对应的bd
步骤:9
Normal被@Import注解导入,注册Normal对应的bd
步骤10:Config作为配置类被解析,People实现了ImportBeanDefinitionRegistrar接口。会把Peole加入Config的集合importBeanDefinitionRegistrars。
在这里会从importBeanDefinitionRegistrars取出实现了ImportBeanDefinitionRegistrar接口的实例。调用People的
registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry)
importingClassMetadata是导入People的类上面的注解信息,也就是Config上的注解信息!
步骤11:ImportedByPerson被DeferredImportSelector导入,注册ImportedByPerson对应的bd!
转载自:https://juejin.cn/post/7237043816888156218