zoukankan      html  css  js  c++  java
  • 《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源码。

    This moment will nap, you will have a dream; But this moment study,you will interpret a dream.
  • 相关阅读:
    Jmeter简单使用
    Linux命令补充
    数据加密
    问题 Can't load AMD 64-bit .dll on a IA 32-bit platform
    需要知道的东西很多还要知道的牢固
    Sqlyog问题
    精神苦难和快乐
    了解一个名词——GTD
    超强记忆力提升九大心法-10连锁记忆法
    Array数组结构底层实现复习
  • 原文地址:https://www.cnblogs.com/jssj/p/12630751.html
Copyright © 2011-2022 走看看