《Java Spring框架》Spring ConfigurationClassPostProcessor源码分析

Posted 加速丨世界

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《Java Spring框架》Spring ConfigurationClassPostProcessor源码分析相关的知识,希望对你有一定的参考价值。

先看一个案例:

package accelerate1.bean;

public class Test1 {
}
package accelerate1.bean;

public class Test2 {
}
package accelerate1.app;

import accelerate1.bean.Test1;
import accelerate1.bean.Test2;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@ComponentScan("accelerate1")
public class AppConfig {

    @Bean
    public Test1 test1(){
        System.out.println("create test1");
        return new Test1();
    }

    @Bean
    public Test2 test2(){
        test1();
        System.out.println("create test2");
        return new Test2();
    }
}
package accelerate1.test;

import accelerate1.app.AppConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Demo4 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext =
                new AnnotationConfigApplicationContext(AppConfig.class);

        System.out.println(annotationConfigApplicationContext.getBean(AppConfig.class));
    }
}

运行结果:

我们看到打印了两次“create test1” ,就表示该Test1对象被实例化两次,不符合Spring 单例原则。

为了让Spring只能初始化一次,我们需要增加一个注解在AppConfig类上。

package accelerate1.app;

import accelerate1.bean.Test1;
import accelerate1.bean.Test2;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@ComponentScan("accelerate1")
@Configuration
public class AppConfig {

    @Bean
    public Test1 test1(){
        System.out.println("create test1");
        return new Test1();
    }

    @Bean
    public Test2 test2(){
        test1();
        System.out.println("create test2");
        return new Test2();
    }
}

就加一个注解@Configuration 其他代码不变。

运行结果:

发现的变化为:create test1  打印了一次,保证了Spring单例原则。

并且发现我们AppConfig对象也编程代理对象了。由此可以却选Spring修改我们的AppConfig类内容将他转变成代理类来控制实现单例只实例化一次。

下面我们看看源码:Spring是如何实现的。

在 ConfigurationClassEnhancer 对象的 newEnhancer 对象中创建一个代理类(代理类有CGLib技术实现:https://www.cnblogs.com/jssj/p/12635206.html

    private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
        Enhancer enhancer = new Enhancer();
        //增强父类,地球人都知道cglib是基于继承来的
        enhancer.setSuperclass(configSuperClass);
        //增强接口,为什么要增强接口?
        //便于判断,表示一个类以及被增强了
        enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
        //不继承Factory接口
        enhancer.setUseFactory(false);
        enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
        // BeanFactoryAwareGeneratorStrategy是一个生成策略
        // 主要为生成的CGLIB类中添加成员变量$$beanFactory
        // 同时基于接口EnhancedConfiguration的父接口BeanFactoryAware中的setBeanFactory方法,
        // 设置此变量的值为当前Context中的beanFactory,这样一来我们这个cglib代理的对象就有了beanFactory
        //有了factory就能获得对象,而不用去通过方法获得对象了,因为通过方法获得对象不能控制器过程
        //该BeanFactory的作用是在this调用时拦截该调用,并直接在beanFactory中获得目标bean
        enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
        //过滤方法,不能每次都去new
        enhancer.setCallbackFilter(CALLBACK_FILTER);
        enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
        return enhancer;
    }    

将AppConfig 变成代理类之后。

后面Spring获取Bean的时候就执行如下核心方法。如何防止重复创建对象的原理已经写在注释当中。

    /**
     * @author hubt 用于拦截@Bean方法的调用,并直接从BeanFactory中获取目标bean,而不是通过执行方法。
     * Intercepts the invocation of any {@link Bean}-annotated methods in order to ensure proper
     * handling of bean semantics such as scoping and AOP proxying.
     * @see Bean
     * @see ConfigurationClassEnhancer
     */
    private static class BeanMethodInterceptor implements MethodInterceptor, ConditionalCallback {

        /**
         * Enhance a {@link Bean @Bean} method to check the supplied BeanFactory for the
         * existence of this bean object.
         * @throws Throwable as a catch-all for any exception that may be thrown when invoking the
         * super implementation of the proxied method i.e., the actual {@code @Bean} method
         */
        @Override
        @Nullable
        public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
                    MethodProxy cglibMethodProxy) throws Throwable {

            //enhancedConfigInstance 代理
            // 通过enhancedConfigInstance中cglib生成的成员变量$$beanFactory获得beanFactory。
            ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);

            String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);

            // Determine whether this bean is a scoped-proxy
            Scope scope = AnnotatedElementUtils.findMergedAnnotation(beanMethod, Scope.class);
            if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
                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);
                }
            }

            /**
             * @author hubt 判断当前执行方法是否是本次被调用方法。
             * 相同则调用父类(我们的全注解类)方法,将对象实例化出来。
             * 否则就不调用该方法,不再实例化对象,保证了Spring 单例只是实例化一次。
             * A 方法 ( 调用B方法 , return new A  )
             * B 方法 ( return new )
             * Spring 先执行A方法(调用和执行都是A方法故执行父类方法),父类方法执行到B方法时,变成执行B方法了:
             * 由于这个判断(当前执行B方法不等于,调用方法,因为调用方法还是A方法),就不会执行B方法了。
             * A方法继续往下执行,创建A。
             */
            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.isWarnEnabled() &&
                        BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
                    logger.warn(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()));
                }
                return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
            }

            return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
        }

总结:Spring 源码各个环节之间都有紧密相连,单单看一个逻辑并不好理解,建议还是从整体看Spring源码。

以上是关于《Java Spring框架》Spring ConfigurationClassPostProcessor源码分析的主要内容,如果未能解决你的问题,请参考以下文章

Java框架之spring框架的优点,为什么要学习spring框架

Java学习笔记:Spring框架

10分钟学Spring:初识Spring框架

Java面试-框架篇(SSH-Spring)

带你总结Spring事务

如何改变spring框架jdk版本