[Spring 6.0源码解析] @Configuration注解源码解析

Posted 阿宅奋斗史

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Spring 6.0源码解析] @Configuration注解源码解析相关的知识,希望对你有一定的参考价值。

Spring 6.0源码解析之@Configuration

首先写一个启动代码:

public class ConfigurationAnnotationTest 

    private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationAnnotationTest.class);

    public static void main(String[] args) 
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigurationAnnotationConfig.class);
        ConfigurationAnnotationConfig config = context.getBean(ConfigurationAnnotationConfig.class);
        Person person1 = config.person();
        Person person2 = config.person();
        LOGGER.info("person1 是否等于 person2 ===>> ", (person1 == person2));
    

用 @Configuration 注解的配置类如下:

@Configuration
public class ConfigurationAnnotationConfig 
    @Bean
    public Person person()
        return new Person();
    

从上述代码中可以看到,我们通过AnnotationConfigApplicationContext类来为配置类加载Spring应用上下文,创建IOC容器。

所以就从AnnotationConfigApplicationContext类从手,来分析 @Configuration 注解加载的有关源码。

为了方便分析,先放一张 AnnotationConfigApplicationContext 类的 UML 图在这里。

一、注册 ConfigurationClassPostProcessor 源码分析

ConfigurationClassPostProcessorSpring中最重要的后置处理器。@Configuration注解解析涉及到了 ConfigurationClassPostProcessor 类的注册流程。

从启动代码中可以看到,使用了 AnnotationConfigApplicationContext 类的构造方法传入了配置类 ConfigurationAnnotationConfig,来创建了 IOC 容器,那么我们就以此为入口进行分析。

使用的 AnnotationConfigApplicationContext 类构造方法如下:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigurationAnnotationConfig.class);
  1. 进入该构造方法:
public AnnotationConfigApplicationContext(Class<?>... componentClasses) 
    this();
    register(componentClasses);
    refresh();

可以看到,在上述构造方法中,先调用了 AnnotationConfigApplicationContext 类的无参构造方法 this()

  1. 分析这个 this(),源码如下:
public AnnotationConfigApplicationContext() 
    StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
    this.reader = new AnnotatedBeanDefinitionReader(this);
    createAnnotatedBeanDefReader.end();
    this.scanner = new ClassPathBeanDefinitionScanner(this);

AnnotationConfigApplicationContext 的无参构造函数中,主要做了这些事情:

  • 实例化 AnnotatedBeanDefinitionReader 类的成员变量 readerreader主要是为了解析带有注解的 Bean 的 BeanDefinition,并将其注册到 Bean 工厂中。
  • 实例化 ClassPathBeanDefinitionScanner 类的成员变量 scannerscanner会扫描指定包下的类,并将符合过滤条件的类注册到IOC容器内。

注:StartupStep 接口主要作用是为了跟踪应用程序的启动顺序。

分析实例化 reader 这个成员变量的代码,也就是如下代码片段:

this.reader = new AnnotatedBeanDefinitionReader(this);

  1. 调用了 AnnotatedBeanDefinitionReader 的构造方法:
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) 
    this(registry, getOrCreateEnvironment(registry));

  1. 其中又调用了 AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) 构造方法,源码如下:
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) 
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    Assert.notNull(environment, "Environment must not be null");
    this.registry = registry;
    this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
    AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);

在该构造方法中,最重要的就是最后一句代码 AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);,将 BeanDefinitionRegistry 类型的registry传入使用。通过前面的代码跟踪可知,this.registry对象的本质是 AnnotationConfigApplicationContext 类对象的实例。

注:根据上文 AnnotationConfigApplicationContext 类的 UML 图可得知,AnnotationConfigApplicationContext 类继承了 GenericApplicationContextGenericApplicationContext 类 又实现了BeanDefinitionRegistry 接口。

  1. 分析 AnnotationConfigUtils 调用的 registerAnnotationConfigProcessors(BeanDefinitionRegistry registry)方法,源码如下:
public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) 
    registerAnnotationConfigProcessors(registry, null);

  1. 继续分析调用的 registerAnnotationConfigProcessors 方法,这个方法的源码很长,省略部分源码,如下:
public static final String CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME =
        "org.springframework.context.annotation.internalConfigurationAnnotationProcessor";

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
        BeanDefinitionRegistry registry, @Nullable Object source) 

    // ....省略其他代码....
    
    Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);

    if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) 
        RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
    
    
    // ....省略其他代码....
    
    return beanDefs;

这里只展示了这个方法中涉及到的 ConfigurationClassPostProcessor 类注册到IOC的相关代码。

可以看到,调用了 registerPostProcessor 方法,从方法名就可以看出,注册后置处理器。进入这个方法。

  1. registerPostProcessor方法的源码如下:
private static BeanDefinitionHolder registerPostProcessor(
        BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) 

    definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    registry.registerBeanDefinition(beanName, definition);
    return new BeanDefinitionHolder(definition, beanName);

可以看到,调用了 registry 参数的 registerBeanDefinition 方法来注册了 ConfigurationClassPostProcessor

代码跟踪到这里,先来整理一下思路。从上面的整条链路上来看,registry 参数实际上就是 AnnotationConfigApplicationContext 类的实例,definition 参数实际上是带着 ConfigurationClassPostProcessor类的 RootBeanDefinition 实例。

  1. 进入注册方法 registerBeanDefinition,源码如下:
public interface BeanDefinitionRegistry extends AliasRegistry  
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException;
    // .....

是调用了BeanDefinitionRegistry接口的registerBeanDefinition方法。

在之前的 UML 图中可以知道,AnnotationConfigApplicationContext 类继承了 GenericApplicationContextGenericApplicationContext 类 又实现了BeanDefinitionRegistry 接口,所以跳转到GenericApplicationContext类的实现。

  1. 源码如下:
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
        throws BeanDefinitionStoreException 

    this.beanFactory.registerBeanDefinition(beanName, beanDefinition);

可以看到,实际上是调用了 DefaultListableBeanFactory 类的 registerBeanDefinition 方法。this.beanFactoryDefaultListableBeanFactory 类的实例。

  1. 分析 DefaultListableBeanFactory 类的 registerBeanDefinition 方法,主要源码如下:
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException 
        // ... 省略其他代码 ...

        BeanDefinition existingDefinition = (BeanDefinition)this.beanDefinitionMap.get(beanName);
        if (existingDefinition != null) 
            // ... 省略其他代码 ...

            this.beanDefinitionMap.put(beanName, beanDefinition);
         else 
            // ... 省略其他代码 ...
            this.beanDefinitionMap.put(beanName, beanDefinition);
            // ... 省略其他代码 ...
        

        // ... 省略其他代码 ...

    

通过这段代码可以看到,其实向 SpringIOC容器中注册Bean,就是向beanDefinitionMap对象中增加元素。beanDefinitionMap对象的本质是一个 ConcurrentHashMap 对象:

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap(256);

key是beanName的名字,value就是定义的Bean。在这里注册的就是 ConfigurationClassPostProcessor 类。

通过代码跟踪,我们可以知道在 AnnotationConfigApplicationContext 类的无参构造函数中,完成了 ConfigurationClassPostProcessor 类的注册。

那么到这里,涉及到的 ConfigurationClassPostProcessor 后置处理器的注册过程源码分析结束。

二、注册 @Configuration 配置类源码分析

从启动代码中可以看到,使用了 AnnotationConfigApplicationContext 类的构造方法传入了配置类 ConfigurationAnnotationConfig,来创建了 IOC 容器。

调用的构造方法如下:

public AnnotationConfigApplicationContext(Class<?>... componentClasses) 
    this();
    register(componentClasses);
    refresh();

在之前已经分析过了 this() 构造函数方法,主要是完成了 后置处理器 ConfigurationClassPostProcessor 的注册,接下来分析 register 对配置类 ConfigurationAnnotationConfig 的注册。

  1. 进入 register(componentClasses) 方法:
@Override
public void register(Class<?>... componentClasses) 
    Assert.notEmpty(componentClasses, "At least one component class must be specified");
    StartupStep registerComponentClass = this.getApplicationStartup().start("spring.context.component-classes.register").tag("classes", () -> Arrays.toString(componentClasses));
    this.reader.register(componentClasses);
    registerComponentClass.end();

主要代码是调动了 this.reader.register(componentClasses) 方法,传入了 componentClasses 进行注册。

  1. 分析 reader 对象的 register 方法。

注:reader 对象在这里就是 AnnotatedBeanDefinitionReader 的实例化对象

public void register(Class<?>... componentClasses) 
    for (Class<?> componentClass : componentClasses) 
        registerBean(componentClass);
    

register 方法中循环遍历传入的可变参数,每次循环都会调用 registerBean 方法。

  1. 分析 registerBean 方法
public void registerBean(Class<?> beanClass) 
    doRegisterBean(beanClass, null, null, null, null);

registerBean 中又调用了 doRegisterBean 方法。

  1. 分析 doRegisterBean 方法
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
        @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
        @Nullable BeanDefinitionCustomizer[] customizers) 

    AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
    // ... 省略其他代码 ...
    String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

    // ... 省略其他代码 ...

    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);


可以看到,主要是调用了 AnnotationConfigUtils.applyScopedProxyMode() 方法和 BeanDefinitionReaderUtils.registerBeanDefinition() 方法。

AnnotationConfigUtilsapplyScopedProxyMode方法主要是为了解析 Bean 中的@Scope注解,应用 @Scope注解中的 ProxyMode属性。这里不做重点讲解。

  1. 主要解析 BeanDefinitionReaderUtils.registerBeanDefinition() 方法:
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException 
    String beanName = definitionHolder.getBeanName();
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    String[] aliases = definitionHolder.getAliases();
    if (aliases != null) 
        String[] var4 = aliases;
        int var5 = aliases.length;

        for(int var6 = 0; var6 < var5; ++var6) 
            String alias = var4[var6];
            registry.registerAlias(beanName, alias);
        
    


可以看到,这边也使用了 registryregisterBeanDefinition 方法向 IOC 容器中注册了 Bean。从上面的跟踪代码可知,调用 registry.registerBeanDefinition() 方法注册的 Bean 其实就是ConfigurationAnnotationConfig配置类。

而这部分的注册源码逻辑与之前注册 ConfigurationClassPostProcessor 类的代码相同,同样是调用了 DefaultListableBeanFactory 类的 registerBeanDefinition 方法,不再重复分析。

至此我们可以看到,这部分就是对 ConfigurationAnnotationConfig 配置类的 Bean 进行了注册。

三、实例化流程源码分析

上面分析了注册 ConfigurationClassPostProcessor 后置处理器的源码与注册 ConfigurationAnnotationConfig 配置类的源码,接下来继续分析,Spring IOC 容器在刷星时,会实例化 @Configuration 注解标注的类。

  1. 继续从这个构造函数开始:
public AnnotationConfigApplicationContext(Class<?>... componentClasses) 
    this();
    register(componentClasses);
    refresh();

可以看到,是调用了 refresh() 这个方法来刷新 Spring IOC 容器。

  1. 进入 refresh() 方法:
@Override
public void refresh() throws BeansException, IllegalStateException 
    synchronized (this.startupShutdownMonitor) 
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
        // 预处理操作,包括初始化环境变量、处理系统属性、设置属性访问器等等
        prepareRefresh();
        // 调用 obtainFreshBeanFactory 方法获取一个新的 BeanFactory 对象,该对象用于保存所有 Bean 的定义信息和实例化对象。 
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // BeanFactory的准备工作
        prepareBeanFactory(beanFactory);

        try 
            // 是在对 bean 工厂进行准备以及初始化之后的一个补充处理,用于允许子类对 bean 工厂做一些自定义的处理,以满足特定的应用场景
            // 当我们集成 AbstractApplicationContext  类时,可以通过重写  postProcessBeanFactory()  方法来进行自定义处理。
            // 如果在这个方法中将某个 bean 的属性值修改了,那么在后面读取该 bean 的属性值时,就会读到被修改过的属性值。 
            // 总的来说, postProcessBeanFactory()  方法是一个非常灵活的扩展点,允许我们通过自定义的方式对 bean 工厂进行处理,从而实现一些特定的应用场景。
            postProcessBeanFactory(beanFactory);
            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
            // 用于调用所有实现了 BeanFactoryPostProcessor 接口的类的 postProcessBeanFactory 方法
            // 是执行处理 BeanFactoryPostProcessor 的核心方法
            invokeBeanFactoryPostProcessors(beanFactory);
            // 注册BeanPostProcessor(Bean的后置处理器),用于拦截bean创建过程
            registerBeanPostProcessors(beanFactory);
            beanPostProcess.end();
            // 初始化MessageSource组件(做国际化功能;消息绑定,消息解析)
            initMessageSource();
            // 初始化事件派发器
            initApplicationEventMulticaster();
            // 空方法,可以用于子类实现在容器刷新时自定义逻辑
            onRefresh();
            //  注册时间监听器,将所有项目里面的ApplicationListener注册到容器中来
            registerListeners();
            // 初始化所有剩下的单实例bean,单例bean在初始化容器时创建,原型bean在获取时(getbean)时创建
            finishBeanFactoryInitialization(beanFactory);
            //  完成BeanFactory的初始化创建工作,IOC容器就创建完成
            finishRefresh();
        
        // ... 省略部分代码 ...
    

refresh() 方法是在 AbstractApplicationContext 容器中实现的,里面做了很多的事情。

由上面的代码解析可以看出,在 refresh 方法中,invokeBeanFactoryPostProcessors 方法是执行处理 BeanFactoryPostProcessor 后置处理器的核心方法,在前面的解析中可知注册了 ConfigurationClassPostProcessor 后置处理器,所以这里重点关注 invokeBeanFactoryPostProcessors 的执行过程。

  1. 进入 invokeBeanFactoryPostProcessors 方法。
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) 
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

    // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
    // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
    if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) 
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    

其中又调用了 PostProcessorRegistrationDelegate 类的 invokeBeanFactoryPostProcessors 静态方法。

  1. 进入 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors 方法
public static void invokeBeanFactoryPostProcessors(
        ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) 
	// 定义一个记录已经调用过的 BeanFactoryPostProcessor 的 Set,用于防止重复调用。
    Set<String> processedBeans = new HashSet<>();
    
    // ... 省略部分代码 ...
    // 调用普通的 BeanFactoryPostProcessor 的 postProcessBeanFactory 方法
    invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
    invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
    
    // ... 省略部分代码 ...

invokeBeanFactoryPostProcessors 方法主要用于调用 BeanFactoryPostProcessor 接口实现类的 postProcessBeanFactory 方法,处理 BeanFactory

如上代码可知,在该方法中又调用了 PostProcessorRegistrationDelegate 类的另一个 invokeBeanFactoryPostProcessors 方法。

  1. 进入 invokeBeanFactoryPostProcessors 方法:
private static void invokeBeanFactoryPostProcessors(
        Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) 

    for (BeanFactoryPostProcessor postProcessor : postProcessors) 
        StartupStep postProcessBeanFactory = beanFactory.getApplicationStartup().start("spring.context.bean-factory.post-process")
                .tag("postProcessor", postProcessor::toString);
        postProcessor.postProcessBeanFactory(beanFactory);
        postProcessBeanFactory.end();
    

可以看到,在该方法中会去遍历所有的 BeanFactoryPostProcessor 集合,并调用元素的 postProcessBeanFactory 方法,出传入 beanFactory 来实例化对象。

从上面的代码解析来看,这边调用的 BeanFactoryPostProcessor 接口实现类为 ConfigurationClassPostProcessor 类,所以调用实现的是 ConfigurationClassPostProcessor 类的 postProcessBeanFactory 方法。

  1. 解析 ConfigurationClassPostProcessor 类的 postProcessBeanFactory 方法。
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) 
    int factoryId = System.identityHashCode(beanFactory);
    if (this.factoriesPostProcessed.contains(factoryId)) 
        throw new IllegalStateException(
                "postProcessBeanFactory already called on this post-processor against " + beanFactory);
    
    this.factoriesPostProcessed.add(factoryId);
    if (!this.registriesPostProcessed.contains(factoryId)) 
        // BeanDefinitionRegistryPostProcessor hook apparently not supported...
        // Simply call processConfigurationClasses lazily at this point then.
        processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
    

    enhanceConfigurationClasses(beanFactory);
    beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));

postProcessBeanFactory 方法中又调用了 enhanceConfigurationClasses 方法。

  1. 解析 enhanceConfigurationClasses 方法。
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) 
    // ... 省略部分代码 ...
    ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
    for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) 
        AbstractBeanDefinition beanDef = entry.getValue();
        // If a @Configuration class gets proxied, always proxy the target class
        beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
        // Set enhanced subclass of the user-specified bean class
        Class<?> configClass = beanDef.getBeanClass();
        Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
        if (configClass != enhancedClass) 
            if (logger.isTraceEnabled()) 
                logger.trace(String.format("Replacing bean definition \'%s\' existing class \'%s\' with " +
                        "enhanced class \'%s\'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
            
            beanDef.setBeanClass(enhancedClass);
        
    
    enhanceConfigClasses.tag("classCount", () -> String.valueOf(configBeanDefs.keySet().size())).end();

可以看出这段代码中,主要是用 ConfigurationClassEnhancer 对象调用 enhance 方法来生成代理类,也就是使用 CGLib 生成代理类。

  1. 解析 enhance 方法。
public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) 
    if (EnhancedConfiguration.class.isAssignableFrom(configClass)) 
        if (logger.isDebugEnabled()) 
            logger.debug(String.format("Ignoring request to enhance %s as it has " +
                    "already been enhanced. This usually indicates that more than one " +
                    "ConfigurationClassPostProcessor has been registered (e.g. via " +
                    "<context:annotation-config>). This is harmless, but you may " +
                    "want check your configuration and remove one CCPP if possible",
                    configClass.getName()));
        
        return configClass;
    
    Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
    if (logger.isTraceEnabled()) 
        logger.trace(String.format("Successfully enhanced %s; enhanced class name is: %s",
                configClass.getName(), enhancedClass.getName()));
    
    return enhancedClass;

可以看到调用了 createClass 方法,该方法用于创建代理类。在调用这个方法之前先调用了 newEnhancer 方法来实例化一个 Enhancer 对象。

  1. 先看 newEnhancer 方法。
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.setAttemptLoad(true);
    enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
    enhancer.setCallbackFilter(CALLBACK_FILTER);
    enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
    return enhancer;

newEnhancer 方法主要是用于生成 CGLib 动态代理 Enhancer 对象,后续会使用 Enhancer 对象生成代理类。

从源码中可以看出,newEnhancer 方法为要生成的代理类设置了父类和接口。

设置的接口是 EnhancedConfiguration 类,EnhancedConfiguration 类又继承了 BeanFactoryAware 接口,所以在之后,生成的代理类中可以调用 BeanFactoryAware 接口的 setBeanFactory 方法来获取到 beanFactory 对象。

  1. 接着来看 createClass 方法。
private Class<?> createClass(Enhancer enhancer) 
    // 创建代理类
    Class<?> subclass = enhancer.createClass();
    // Registering callbacks statically (as opposed to thread-local)
    // is critical for usage in an OSGi environment (SPR-5932)...
    Enhancer.registerStaticCallbacks(subclass, CALLBACKS);
    return subclass;

createClass 方法中,调用了 enhancer.createClass 方法创建了代理类。

enhancer.createClass 方法是CGLib的方法,创建的代理类目标类的子类,所以这里创建出来的代理类是目标类的子类。

  1. 关注下 CALLBACKS 这个参数。
static final Callback[] CALLBACKS = new Callback[] 
    new BeanMethodInterceptor(),
    new BeanFactoryAwareMethodInterceptor(),
    NoOp.INSTANCE
;

可以看到 CALLBACKS 这个参数其实是个 Callback 类型的数组,数组中的每个元素都是 Callback 类型。同时 BeanMethodInterceptorBeanFactoryAwareMethodInterceptor 类也是拦截器类型。

BeanMethodInterceptor 类实现了 MethodInterceptor 接口和 ConditionalCallback 接口,主要作用是对标注了 @Bean 的注解方法进行拦截,执行 intercept 方法来生成 Bean 的实例对象。

  1. 浅看一下 BeanMethodInterceptor 类的源码
private static class BeanMethodInterceptor implements MethodInterceptor, ConditionalCallback 
	@Override
    @Nullable
    public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
                MethodProxy cglibMethodProxy) throws Throwable 

        // ... 省略部分代码 ...
        // 如果已经创建了 Bean 的代理实例对象,则调用父类的方法
        if (isCurrentlyInvokedFactoryMethod(beanMethod)) 
            // ... 省略部分代码 ...
            return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
        

        return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
    
    // ... 省略部分代码 ...

上述代码可以保证在类上添加 @Configuration 注解后,只会为一个类生成一个代理对象,也就是能保证标注了 @Configuration 注解的类生成的代理类是单例模式的。

因为使用 CGLib 类创建出来的代理类是目标类的子类,所以第一次执行上述代码片段时,会调用 cglibMethodProxy.invokeSuper 方法执行父类,也就是执行目标类的方法。

第二次调用上述代码片段时,会调用 resolveBeanReference 方法。

13.最后再简单看下 resolveBeanReference 方法。只挑了重点需要看的地方。

private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,
        ConfigurableBeanFactory beanFactory, String beanName) 
    
    boolean alreadyInCreation = beanFactory.isCurrentlyInCreation(beanName);
    try 
         // ... 省略部分代码 ...
        Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) :
                beanFactory.getBean(beanName));
         // ... 省略部分代码 ...
        return beanInstance;
    
     // ... 省略部分代码 ...

可以看到,在 resolveBeanReference 方法中,会通过 beanFactory 获取已经初始化好的 bean 对象,并将这个已经初始化好的 bean 对象返回,并不会再进行第二次初始化的操作。

四、总结

做一个简单的思维导图。

参考连接:【冰河的星球】https://articles.zsxq.com/id_88k0ww2tsr6n.html

Spring中的Properties

Properties 注入

  • 通过 xml 配置
  • 通过 @PropertySource 配置
  • PropertyPlaceholderConfigurer
  • PropertySourcesPlaceholderConfigurer

Properties 的使用

  • 在 xml 配置文件中使用
  • 通过 @Value 注入使用
  • 通过 Environment 获取
  • 通过 application.properties 获取

Spring Boot 相关

  • @ConfigurationProperties
  • 配置优先级

Properties 配置

通过 xml 配置

<context:property-placeholder location="classpath:sys.properties" />

通过 @PropertySource 配置

@PropertySource("classpath:sys.properties")
@Configuration
public class DemoConfig {

}

@PropertySource 在这里必须搭配 @Configuration 来使用。

PropertyPlaceholderConfigurer

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <list>
            <value>classpath:sys.properties</value>
        </list>
    </property>
    <property name="ignoreUnresolvablePlaceholders" value="true"/>
      <!-- 这里可以配置一些属性 -->
</bean>  

 java configuration 版本

@Bean
public PropertyPlaceholderConfigurer propertiess() {
    PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
    Resource[] resources = new ClassPathResource[]{new ClassPathResource("sys.properties")};
    ppc.setLocations(resources);
    ppc.setIgnoreUnresolvablePlaceholders(true);
    return ppc;
}

PropertySourcesPlaceholderConfigurer

<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
    <property name="locations">
        <list>
            <value>classpath:sys.properties</value>
        </list>
    </property>
    <property name="ignoreUnresolvablePlaceholders" value="true"/>
    <!-- 这里可以配置一些属性 -->
</bean>

 java configuration 版本

@Bean
public PropertySourcesPlaceholderConfigurer properties() {
    PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer();
    Resource[] resources = new ClassPathResource[]{new ClassPathResource("sys.properties")};
    pspc.setLocations(resources);
    pspc.setIgnoreUnresolvablePlaceholders(true);
    return pspc;
}

Properties 的使用

在 xml 配置文件中使用

<bean id="xxx" class="com.demo.Xxx">
      <property name="url" value="${mysql.jdbc.url}" />
</bean>

通过 @Value 注入使用

@Value("${demo.jdbc.url}")
private String url;

通过 Environment 获取

只有使用注解 @PropertySource 的时候可以用,否则会得到 null。

@Autowired
private Environment env;

public String getUrl() {
    return env.getProperty("demo.jdbc.url");
}  

通过 application.properties 获取 

demo.database.url=jdbc:mysql:  

Spring Boot 相关

@ConfigurationProperties

application.properties
demo.db.url=jdbc:mysql: demo.db.username=test demo.db.password=123456 @Configuration @ConfigurationProperties(prefix = "demo.db") @Data public class DataBase { String url; String username; String password; }

配置优先级

java -Dspring.profiles.active=env -jar app.jar

如果存在这两个文件,application.propertiesapplication-env.properties ,则两个文件中的配置都会注册进去,如果有重复的 key,application-env.properties 文件中的优先级较高。

总结:启动参数 > application-{env}.properties > application.properties

  


以上是关于[Spring 6.0源码解析] @Configuration注解源码解析的主要内容,如果未能解决你的问题,请参考以下文章

YOLOv5-6.0 源码解析 —— 卷积神经单元

Android 6.0+ 运行时权限——EasyPermissions源码解析

yolov5 6.0 源码解析---utils /augmentations.py

终于有人把Spring核心源码讲清楚了!

[Android FrameWork 6.0源码学习] LayoutInflater 类分析

Spring 学习总结(35)—— Spring 6.0 新特性总结