Spring之配置类解析

Spring的配置类解析过程

什么是配置类?配置类有什么作用?

从使用上来看,配置了可以用来配置Bean,将配置好的bean注册到容器中来进行使用。

Spring是怎么判断是配置类的?

在最开始的阶段org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors

进入之后,来到

但是当前Spring容器中只有一个ConfigurationClassPostProcessor这个后置处理器才会发挥作用,所以直接看下ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法。

问题一:Spring会将什么类当成是配置类?

首先spring会将容器中的BeanDefinition全部拿出来,然后挨个进行遍历。

对于第一个if判断来说,因为没有对BeanDefinition做过设置,所以不成立,那么直接来看第二个。

判断哪些是配置类

首先获取得到类上添加了@Configuratoin注解的类信息

  • 第一个判断的是Configuration注解中的proxyBeanMethods是否为true,如果为true,那么就会给BeanDefinitoin属性来设置一个标识

    表示当前的配置类是一个full类型的配置类

  • 第二个判断是如果没有@Configuration注解或者只要是存在@Component、@ComponentScan、@Import、@ImportResource四个中的一个,就是lite配置类

看一下candidateIndicators中的值:

private static final Set<String> candidateIndicators = new HashSet<>(8);

static {
    candidateIndicators.add(Component.class.getName());
    candidateIndicators.add(ComponentScan.class.getName());
    candidateIndicators.add(Import.class.getName());
    candidateIndicators.add(ImportResource.class.getName());
}

那么看hasBeanMethods的表示:

小结

总结到了这里,不禁感慨,原来之前用的@Component也是一个lite类型的配置类;甚至没有添加任何注释,只是在方法上添加了注释的@Bean的类也是一个lite类型的配置类(但是这里的前提是一定要存在某个metaData元数据描述器能够来描述这个类),如下所示:

如果仅仅只是这样的一个配置类,添加扫描器来进行扫描的时候,因为没有描述器来描述,这样的类不会成为一个lite配置类

public class MyConfig {

	@Bean
	public AnimalService animalService(){
		return new AnimalService();
	}
}

比如说,这里来添加一个元数据描述器

@Component
public class MyConfig {

	@Bean
	public AnimalService animalService(){
		return new AnimalService();
	}
}

就可以将其注入进来。

找到了配置类,开始解析配置类

首先来构建一个配置类的解析器,然后利用do....while来循环进行解析,这里就是利用了do..while的好处,解析完所有的配置类

因为对于配置类来说,可能解析完一个配置类之后,还会产生另外一个配置类,类似递归的这种思想。

如何解析配置类

根据每种不同的BeanDefinition的类型来进行解析,从第一个进去,因为大部分都是第一种类型。

protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
    // 把类的元信息和beanName封装为ConfigurationClass
    processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}

将元信息和beanName封装成一个配置类,然后来解析配置类:

这里又看见了do...while循环,那么这里是用来干嘛的?因为这里考虑到了父类,如果父类中也有是一个配置类标志,那么这里也会来进行添加。

然后下面分别来解析@Component、@PropertySources、@ComponentScans和@ComponentScan注解、@ImportResource和@Bean

解析配置类是加了@Component注解

这里的解析是十分有意思的

举例如下所示:

@Component
public class MyConfig implements AppInterface {
	
	class MyTest{
		
		@Bean
		public AnimalService animalService(){
			return new AnimalService();
		}
	}
}

解析配置类是加了@PropertySources注解

可以加载外部的文件,如properties文件等,这里是先找到位置,添加到propertySourceNames中,但是不是先来进行解析。

在后面统一来做处理。

解析配置类是加了@ComponentScans和ComponentScan注解

spring正常来进行扫描的逻辑,如mybatis的框架在扫描mapper的时候,也会在这一步来做处理。

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
	// spring重新创建了一个扫描器,然后获取得到在ApplicationContext中的componentScan中的默认
    // 扫描器中的默认规则获取得到
    ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
                                                                                componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);

    Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
    boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
    // 默认为AnnotationBeanNameGenerator
    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));
    }

    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);
        }
    });
    // 开始来记性扫描逻辑。扫描逻辑是在这里来执行的
    return scanner.doScan(StringUtils.toStringArray(basePackages));
}

解析配置类是加了@Import注解

但是这里又分成了三种:实现了ImportSelector接口、实现了ImportBeanDefinitionRegistrar接口和没有实现ImportSelector接口以及ImportBeanDefinitionRegistrar接口的类。

如果是实现了ImportSelector接口的,则会将ImportSelector.selectImports返回的String[]中的类名取出来,然后重新执行processImports逻辑,直到找到所有的lite类型的配置类。

如果是实现了ImportBeanDefinitionRegistrar接口的,这里只会将类实例化出来,然后添加到importBeanDefinitionRegistrars结合中,在后面做统一处理。

最终是既没有实现ImportSelector接口,也没有实现ImportBeanDefinitionRegistrar的类,重新执行processConfigurationClass方法,获取得到所有的配置类。

解析配置类是加了@ImportResource注解

显示找到外部的配置文件来进行扫描,找到位置之后,这里先是来进行添加,不做处理。类似@PropertySources中的逻辑,在后面统一做处理。这里只是放入到importedResources中,做个缓存处理。

解析配置类中方法是加了@Bean注解

可以看到这里又是将其添加到beanMethods集合中来进行保存,而不是先来进行执行。

解析配置类实现接口中的默认方法

public interface AppInterface {

	@Bean
	default DogService dogService(){
		return new DogService();
	}
}

@Component
public class MyConfig implements AppInterface {

}

这种方式,也是可以将DogService注入到容器中来的。

小结

在整合解析配置类的过程中,只有@ComponentScan和@ComponentScans会来扫描并最终成为BeanDefinition,然后添加到容器中去。

而对于其他的来说,仅仅只是保存而已,还没有来得及解析。

而且在扫描配置类的时候会形成很多新的配置类,那么就需要递归来进行调用。

最终将需要进行解析的配置类添加到configurationClasses中来,但是这里始终还是没有来进行解析的。在解析的最后一步中:

// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass, filter);
do {
    sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);

// ConfigurationClass重写了equals方法,只要两个ConfigurationClass对应的className相等就可以
this.configurationClasses.put(configClass, configClass);

扫描配置类完成,开始处理

添加解析完成之后的配置类之后,接着就是来对配置类来做处理

接下来就是利用读取器来对每种类型的配置类来做处理了。

那么看一下读取器开始来进行去取,读取的正是之前存储的内容。

比如:加了@Bean、读取xml的以及外部配置文件以及ImportBeanDefinitionRegistrar对象需要执行方法,开始来着手生成BeanDefinitoin的。

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
    TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
    for (ConfigurationClass configClass : configurationModel) {
        loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
    }
}

可能有多个,那么循环遍历,看看具体的执行逻辑。

看方法名称即可知道在这里来注册BeanDefinition。

但是从这里可以看到

this.reader.loadBeanDefinitions(configClasses);

这行代码执行之后,还会有BeanDefinitoin的出现,证明了还可能会有新的配置类产生,所以依然需要来做处理。

// 如果发现BeanDefinition增加了,则有可能增加了配置类
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);

            // 检查多出来的BeanDefinition是不是配置类,需不需要解析
            if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
                !alreadyParsedClasses.contains(bd.getBeanClassName())) {
                candidates.add(new BeanDefinitionHolder(bd, candidateName));
            }
        }
    }
    candidateNames = newCandidateNames;
}

所以这里就是外部的do..while来做循环的目的。

BeanDefinition的beanName重名

第一种是针对@Component重名,如果两个@Component直接报错;

@Component("a")
public class A{
    
}
@Component("a")
public class A{
    
}

第二种是@Bean类型,命名重命名,如果是重载

@Bean
public A a(){
    return new A();
}

@Bean
public A a(B b){
    return new A();
}

对应的源码在loadBeanDefinitionsForBeanMethod方法中:

if (isOverriddenByExistingDefinition(beanMethod, beanName)) {

    // 如果beanName等于"appConfig",就会抛异常
    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;
}

如果是这样子,那么只会有一个a作为beanName而存在,并不存在覆盖问题。

第三种:@Component和@Bean共存

先扫描,然后再来解析@Bean共存的问题。这个是这里的顺序。

这里才是真正的覆盖。

对应的源码也还是在上面的方法中,先来检查是否已经存在,存在的类型是ConfigurationClassBeanDefinition的话,表示的是利用@Bean方式产生的BeanDefiniton,而扫描的BeanDefinition不是ConfigurationClassBeanDefinition类型,而是ScannedGenericBeanDefinition类型的

BeanDefinition existingBeanDef = this.registry.getBeanDefinition(beanName);

// Is the existing bean definition one that was created from a configuration class?
// -> allow the current bean method to override, since both are at second-pass level.
// However, if the bean method is an overloaded case on the same configuration class,
// preserve the existing bean definition.
if (existingBeanDef instanceof ConfigurationClassBeanDefinition) {
    ConfigurationClassBeanDefinition ccbd = (ConfigurationClassBeanDefinition) existingBeanDef;
    if (ccbd.getMetadata().getClassName().equals(
        beanMethod.getConfigurationClass().getMetadata().getClassName())) {

        // 如果@Bean对应的beanName已经存在BeanDefinition,那么则把此BeanDefinition的isFactoryMethodUnique设置为false
        // 等到后续根据此BeanDefinition去创建Bean时,就知道不止一个对应方法了,要推断了
        if (ccbd.getFactoryMethodMetadata().getMethodName().equals(ccbd.getFactoryMethodName())) {
            ccbd.setNonUniqueFactoryMethodName(ccbd.getFactoryMethodMetadata().getMethodName());
        }
        return true;
    }
    else {
        return false;
    }
}

if (existingBeanDef instanceof ScannedGenericBeanDefinition) {
    return false;
}

最终执行到下面这里,生成新的ConfigurationClassBeanDefinition

ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata, beanName);
beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
......
this.registry.registerBeanDefinition(beanName, beanDefToRegister);

然后判断是否能够覆盖?

BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
    // 默认是允许BeanDefinition覆盖的
    if (!isAllowBeanDefinitionOverriding()) {
        throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
    }
    
    .....
    this.beanDefinitionMap.put(beanName, beanDefinition);

然后执行到这里,因为默认的isAllowBeanDefinitionOverriding,表示允许覆盖,那么就来进行覆盖。

就会来进行覆盖掉了,@Bean配置的代替了@Component的了。

所以最终@Bean和@Component生成的BeanDefinition以@Bean生成的Definition生成的为主。

测试一下这种情况:

@Component
public class UserService {}

@ComponentScan("com.gunag.ioc.demo3")
public class AppConfig3 {


@Bean
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public UserService userService(){
   return new UserService();
}

}

测试多次获取得到bean:

com.gunag.ioc.demo3.service.UserService@5577140b
com.gunag.ioc.demo3.service.UserService@1c6b6478
com.gunag.ioc.demo3.service.UserService@67f89fa3

@Bean的一些细节

为什么会有@Bean这个注解的出现呢?在spring3.0就开始出现了这个。

那么看一下xml中的使用方式。

工厂非静态方法

工厂非静态方法实例化:

public class NoStaticMethodFactory{
    public UserDao createUserDao(){
        return new UserDaoImpl();
    }
}

配置文件:

<!-- 先配置工厂 -->
<bean id="noStaticMethodFactory" class="com.guang.factory.NoStaticMethodFactory"></bean>

<!-- 再配置UserDao -->
<!-- factory-bean是工厂bean的名字,使用的是工厂bean中的非静态方法createUserDao来创建出来的userDao组件 -->    
<bean id="userDao" factory-bean="instanceFactory" factory-method="createUserDao"></bean>

对于这种bean来说,factory-bean的名字是instanceFactory,factory-method的名字是createUserDao。

也就是说是利用了factory工厂来实现的方式。

工厂静态方法

public class StaticMethodFactory{
    public static UserDao createUserDao(){
        return new UserDaoImpl();
    }
}

配置文件:

<bean id="userDao" class="com.guang.bean.StaticMethodFactory" factory-method="createUserDao"></bean>

配置文件中指明了使用StaticMethodFactory类中的createUserDao方法来进行实例化bean;

对于这种使用方式来说,factory-bean的名字是没有的,factory-method的名字是createUserDao,但是beanclass是StaticMethodFactory,将这个类记录下来了。

对于@Bean来说,这里是一对一的关系。

首先,Spring会把@Bean修饰的方法解析成BeanDefinition:

  1. 如果方法不是static的,也就是静态方法,那么解析出来的BeanDefinition中:
    1. factoryBeanName为AppConfig所对应的beanName,比如"appConfig"
    2. factoryMethodName为对应的方法名,比如"aService"
    3. factoryClass为AppConfig.class
  2. 如果方法是static的,那么解析出来的BeanDefinition中:
    1. factoryBeanName为null
    2. factoryMethodName为对应的方法名,比如"aService"
    3. factoryClass也为AppConfig.class

在由@Bean生成的BeanDefinition中,有一个重要的属性isFactoryMethodUnique,表示factoryMethod是不是唯一的,在普通情况下@Bean生成的BeanDefinition的isFactoryMethodUnique为true,但是如果出现了方法重载,那么就是特殊的情况,比如:

 @Bean
 public static AService aService(){
  return new AService();
 }

 @Bean
 public AService aService(BService bService){
  return new AService();
 }

虽然有两个@Bean,但是肯定只会生成一个aService的Bean,那么Spring在处理@Bean时,也只会生成一个aService的BeanDefinition,比如Spring先解析到第一个@Bean,会生成一个BeanDefinition,此时isFactoryMethodUnique为true,但是解析到第二个@Bean时,会判断出来beanDefinitionMap中已经存在一个aService的BeanDefinition了,那么会把之前的这个BeanDefinition的isFactoryMethodUnique修改为false,并且不会生成新的BeanDefinition了。

并且后续在根据BeanDefinition创建Bean时,会根据isFactoryMethodUnique来操作,如果为true,那就表示当前BeanDefinition只对应了一个方法,那也就是只能用这个方法来创建Bean了,但是如果isFactoryMethodUnique为false,那就表示当前BeanDefition对应了多个方法,需要和推断构造方法的逻辑一样,去选择用哪个方法来创建Bean。

@Bean生成BeanDefinition的过程

直接来到对应的逻辑中来:

org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass

// @Bean生成BeanDefinition并注册
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
    loadBeanDefinitionsForBeanMethod(beanMethod);
}

因为之前已经缓存了BeanMethod,现在获取得到所有的BeanMethod,开始循环遍历

直接看源码:

private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
    // 获取得到配置类
    ConfigurationClass configClass = beanMethod.getConfigurationClass();
    // 获取得到@Bean注解的元信息
    MethodMetadata metadata = beanMethod.getMetadata();
    // 获取得到@Bean的方法名称
    String methodName = metadata.getMethodName();

    // Do we need to mark the bean as skipped by its condition?
    if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
        configClass.skippedBeanMethods.add(methodName);
        return;
    }
    if (configClass.skippedBeanMethods.contains(methodName)) {
        return;
    }

    AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
    Assert.state(bean != null, "No @Bean annotation attributes");

    // Consider name and any aliases
    List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));
    String beanName = (!names.isEmpty() ? names.remove(0) : methodName);

    // Register aliases even when overridden
    for (String alias : names) {
        this.registry.registerAlias(beanName, alias);
    }

    // Has this effectively been overridden before (e.g. via XML)?
    // 如果出现了两个@Bean修改的方法名字一样(比如方法重载了),则直接return,并且会把已经存在的BeanDefinition的isFactoryMethodUnique为false
    if (isOverriddenByExistingDefinition(beanMethod, beanName)) {

        // 如果beanName等于"appConfig",就会抛异常
        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;
    }
	
    // 开始生成@Bean对应的ConfigurationClassBeanDefinition
    ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata, beanName);
    beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
	// 如果@Bean方法是static关键字修饰的
    if (metadata.isStatic()) {
        // static @Bean method
        if (configClass.getMetadata() instanceof StandardAnnotationMetadata) {
            beanDef.setBeanClass(((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass());
        }
        else {
            // 设置beanClassName为当前的配置类的名字,也就是appconfig
            beanDef.setBeanClassName(configClass.getMetadata().getClassName());
        }
        beanDef.setUniqueFactoryMethodName(methodName);
    }
    else {
        // 如果不是static关键字,那么这里会将BeanDefinition中的一个变量isFactoryMethodUnique设置为true
        // 设置FactoryBeanName为appconfig
        // instance @Bean method
        beanDef.setFactoryBeanName(configClass.getBeanName());
        beanDef.setUniqueFactoryMethodName(methodName);
    }
	
    // 设置FactoryMethod为当前方法
    if (metadata instanceof StandardMethodMetadata) {
        beanDef.setResolvedFactoryMethod(((StandardMethodMetadata) metadata).getIntrospectedMethod());
    }
	
    // 设置为根据构造方法来进行注入
    beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
    // 设置属性,跳过检查
    beanDef.setAttribute(org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor.
                         SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);

    AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);
	
    // 在这里来判断注入类型
    Autowire autowire = bean.getEnum("autowire");
    if (autowire.isAutowire()) {
        beanDef.setAutowireMode(autowire.value());
    }
	
    // 是否自动注入!这里也来做了判断
    boolean autowireCandidate = bean.getBoolean("autowireCandidate");
    if (!autowireCandidate) {
        beanDef.setAutowireCandidate(false);
    }

    String initMethodName = bean.getString("initMethod");
    if (StringUtils.hasText(initMethodName)) {
        beanDef.setInitMethodName(initMethodName);
    }

    String destroyMethodName = bean.getString("destroyMethod");
    beanDef.setDestroyMethodName(destroyMethodName);

    // Consider scoping
    ScopedProxyMode proxyMode = ScopedProxyMode.NO;
    AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, Scope.class);
    if (attributes != null) {
        beanDef.setScope(attributes.getString("value"));
        proxyMode = attributes.getEnum("proxyMode");
        if (proxyMode == ScopedProxyMode.DEFAULT) {
            proxyMode = ScopedProxyMode.NO;
        }
    }

    // Replace the original bean definition with the target one, if necessary
    BeanDefinition beanDefToRegister = beanDef;
    if (proxyMode != ScopedProxyMode.NO) {
        BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(
            new BeanDefinitionHolder(beanDef, beanName), this.registry,
            proxyMode == ScopedProxyMode.TARGET_CLASS);
        beanDefToRegister = new ConfigurationClassBeanDefinition(
            (RootBeanDefinition) proxyDef.getBeanDefinition(), configClass, metadata, beanName);
    }

    if (logger.isTraceEnabled()) {
        logger.trace(String.format("Registering bean definition for @Bean method %s.%s()",
                                   configClass.getMetadata().getClassName(), beanName));
    }
    this.registry.registerBeanDefinition(beanName, beanDefToRegister);
}

然后将其注入到beandefinitionmap中去保存,等到后续实例化操作。

什么时候来实例化@Bean标注的BeanDefinition的

在推断构造方法中有两行代码表示:

// @Bean对应的BeanDefinition
if (mbd.getFactoryMethodName() != null) {
    return instantiateUsingFactoryMethod(beanName, mbd, args);
}

在这里走的逻辑类似推断构造方法,来找到最匹配的来选择执行哪个方法。

上面几个过程中都没有提到@Configuration必须出现的场景

下面来考虑一个问题,如下所示:

@ComponentScan("com.gunag.ioc.demo3")
public class AppConfig3 {


    @Bean
    public A a() {
        A a = new A();
        System.out.println("容器中的a是:"+a);
        return a;
    }

    @Bean
    public B b(A a) {
        A a1 = a();
        System.out.println("容器中的b持有的a是:"+a);
        System.out.println("容器中的b持有的a1是:"+a1);
        return new B();
    }

}

如果是正常的话,我们想让b中可以使用容器中的a这个bean,但是如果不加@Configuration注解,那么b中获取得到的a就不是容器中的。

测试一下:

容器中的a是:com.gunag.ioc.demo3.service.A@3b192d32
容器中的a是:com.gunag.ioc.demo3.service.A@14899482
容器中的b持有的a是:com.gunag.ioc.demo3.service.A@3b192d32
容器中的b持有的a1是:com.gunag.ioc.demo3.service.A@14899482

但是如果想保持唯一,那么只需要在配置类上添加@Configuration注解

容器中的a是:com.gunag.ioc.demo3.service.A@34ce8af7
容器中的b持有的a是:com.gunag.ioc.demo3.service.A@34ce8af7
容器中的b持有的a1是:com.gunag.ioc.demo3.service.A@34ce8af7

这个@Configuratoin注解是必须要存在的。上面只是执行完了BeanDefinitionRegistryPostProcessor中的postProcessBeanDefinitionRegistry方法,而没有执行postProcessBeanFactory方法,所以来看下这个方法中执行的逻辑。

这个方法执行到这里的时候,说明容器中的BeanDefinition已经全部添加完成了,但是对应的Bean还没有生成。

终于来到了熟悉的方法,那么只只需要看下这里的代理逻辑:

设置AppConfig中的beanclass设置成了AppConfig的代理类的类型

在这里来将AppConfig对应的BeanDefinition中的beanclass属性设置成了AppConfig的代理类的类型,所以最终来执行的时候,执行的是代理类中的逻辑。

加入说:

@Configuration
public class AppConfig{
    @Bean
    public A a(){
        return new A();
    }
}

在使用AppConfig来调用a方法的时候,肯定执行的是代理类中的逻辑。

在ConfigurationClassEnhancer类中已经指定了需要回调的方法:

	private static final Callback[] CALLBACKS = new Callback[] {
			new BeanMethodInterceptor(),
			new BeanFactoryAwareMethodInterceptor(),
			NoOp.INSTANCE
	};

对于加了@Configuration且其中proxyBeanMethods=true的情况下

那么看一下创建方法

private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(configSuperClass);
    enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
    enhancer.setUseFactory(false);
    enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
    enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
    enhancer.setCallbackFilter(CALLBACK_FILTER);
    enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
    return enhancer;
}

创建代理类对象,那么在@Configuration标注的类上,且@Configuration属性proxyBeanMethods=true的情况下,在执行方法的时候,一定会执行到拦截方法中来:BeanMethodInterceptor中的intercept方法中来。

public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
                        MethodProxy cglibMethodProxy) throws Throwable {
    // enhancedConfigInstance是代理对象
    ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
    String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);

    // Determine whether this bean is a scoped-proxy
    if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
        String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
        if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
            beanName = scopedBeanName;
        }
    }

    // To handle the case of an inter-bean method reference, we must explicitly check the
    // container for already cached instances.

    // First, check to see if the requested bean is a FactoryBean. If so, create a subclass
    // proxy that intercepts calls to getObject() and returns any cached bean instance.
    // This ensures that the semantics of calling a FactoryBean from within @Bean methods
    // is the same as that of referring to a FactoryBean within XML. See SPR-6602.
    if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
        factoryContainsBean(beanFactory, beanName)) {
        Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
        if (factoryBean instanceof ScopedProxyFactoryBean) {
            // Scoped proxy factory beans are a special case and should not be further proxied
        }
        else {
            // It is a candidate FactoryBean - go ahead with enhancement
            return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
        }
    }

    // 如果代理对象正在执行的方法就是正在创建Bean的工厂方法,那就直接执行对应的方法得到对象作为Bean
    if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
        // The factory is calling the bean method in order to instantiate and register the bean
        // (i.e. via a getBean() call) -> invoke the super implementation of the method to actually
        // create the bean instance.
        if (logger.isInfoEnabled() &&
            BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
            logger.info(String.format("@Bean method %s.%s is non-static and returns an object " +
                                      "assignable to Spring's BeanFactoryPostProcessor interface. This will " +
                                      "result in a failure to process annotations such as @Autowired, " +
                                      "@Resource and @PostConstruct within the method's declaring " +
                                      "@Configuration class. Add the 'static' modifier to this method to avoid " +
                                      "these container lifecycle issues; see @Bean javadoc for complete details.",
                                      beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName()));
        }
        // 注意这里传入的是代理对象,相当于在执行父类的方法,注意和Spring事务做区分
        return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
    }

    // 如果代理对象正在执行的方法不是正在创建Bean的方法,那就直接根据方法的名字去Spring容器中获取
    return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}

这里有一行关键性代码:

isCurrentlyInvokedFactoryMethod(beanMethod)

判断是否是当前正在调用的FactoryMethod方法。为什么要这样子来做判断呢?

如下代码:

@ComponentScan("com.gunag.ioc.demo3")
@Configuration
public class AppConfig3 {


    @Bean
    public A a() {
        A a = new A();
        System.out.println("容器中的a是:"+a);
        return a;
    }

    @Bean
    public B b(A a) {
        A a1 = a();
        System.out.println("容器中的b持有的a是:"+a);
        System.out.println("容器中的b持有的a1是:"+a1);
        return new B();
    }

}

如果当前正在创建的方法是a(),这里会来做一个记录(用集合保存当前@Bean方法创建中),表示当前创建A是由a()方法引起的;创建完成之后将这个方法移除;

当代用b方法来创建b的时候,这里来做一个记录b,表示是有b方法引起的;而在b在调用中,发现会调用类中的a方法,正在执行的方法和记录到集合中的方法是不一样的,那么spring将会走cglib的代理逻辑。

明确@Bean方法调用时机

是在实例化阶段才会来创建,那么继续走到

// @Bean对应的BeanDefinition
if (mbd.getFactoryMethodName() != null) {
    return instantiateUsingFactoryMethod(beanName, mbd, args);
}

走到这个方法中来,最终执行到

bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, factoryMethodToUse, argsToUse));

中来,然后下面里面的设置,最终执行到org.springframework.beans.factory.support.SimpleInstantiationStrategy#instantiate(org.springframework.beans.factory.support.RootBeanDefinition, java.lang.String, org.springframework.beans.factory.BeanFactory, java.lang.Object, java.lang.reflect.Method, java.lang.Object...)方法中来

private static final ThreadLocal<Method> currentlyInvokedFactoryMethod = new ThreadLocal<>();

Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();
try {
    currentlyInvokedFactoryMethod.set(factoryMethod);
    // factoryBean就是AppConfig的代理对象(如果加了@Configuration)
    // factoryMethod就是@Bean修饰的方法
    Object result = factoryMethod.invoke(factoryBean, args);
    if (result == null) {
        result = new NullBean();
    }
    return result;
}
finally {
    if (priorInvokedFactoryMethod != null) {
        currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);
    }
    else {
        currentlyInvokedFactoryMethod.remove();
    }
}

保存当前线程执行的是@Bean对应的方法。这里只是来进行设置。然后会来执行

Object result = factoryMethod.invoke(factoryBean, args);

因为factoryBean是一个代理的bean,所以会走到对应的intercepter方法中来,也就是一个回调方法。

那么接着执行:

// 如果代理对象正在执行的方法就是正在创建Bean的工厂方法,那就直接执行对应的方法得到对象作为Bean
if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
    .........
    // 注意这里传入的是代理对象,相当于在执行父类的方法,注意和Spring事务做区分
    return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
}

// 如果代理对象正在执行的方法不是正在创建Bean的方法,那就直接根据方法的名字去Spring容器中获取
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);

那么直接来看对应的处理逻辑:

private boolean isCurrentlyInvokedFactoryMethod(Method method) {
    Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();
    return (currentlyInvoked != null && method.getName().equals(currentlyInvoked.getName()) &&
            Arrays.equals(method.getParameterTypes(), currentlyInvoked.getParameterTypes()));
}
	public static Method getCurrentlyInvokedFactoryMethod() {
		return currentlyInvokedFactoryMethod.get();
	}

如果说当前factorybean中正在执行的方法和当前线程中保存的方法是一致的。那么直接去执行父类中的方法;

如果说factorybean中正在执行的方法和当前线程中保存的方法是不一致的,那么表示的是当前@Bean方法正在调用其他@Bean加的方法,那么就需要不需要来执行父类中的方法,直接去从容器中来进行获取。

为什么说@Bean标注的方法的参数值会从容器中来进行查找

在@Bean标注的方法中,会找到@Bean中的方法来进行查找。 最终会去容器中查找。

而因为对于@Bean方法和推断构造方法来说,找值都是去容器中找

 this.beanFactory.resolveDependency(
new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter);

所以在构造方法和@Bean对应的方法中,在查找bean的时候,可以使用@Qulifired来标注使用哪个名字的bean。

posted @ 2022-08-29 21:43  雩娄的木子  阅读(291)  评论(0编辑  收藏  举报