Spring源码系列(六)你真的理解Spring的Configuration的注解吗?

1.写在前面

上一篇博客大概讲了一下Spring的扫描机制,但是调用ConfigurationClassPostProcessor类中的postProcessBeanDefinitionRegistry方法,由于这个方法比较复杂,笔者只讲了下什么类会被解析成配置类,还有讲了一下Spring是如何调用的doScan的方法的。解析成配置类最终的表现的形式就是给BeanDefinition中的Attribute的属性中的org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass的值设置成full或者是lite,那这两个有什么区别呢?就是我们这篇博客要解决的问题。

2.Lite与Full

上一篇博客中我们写到,只有是加了@Configuration的注解,同时@Configuration注解中proxyBeanMethods的值为true,这个时候属性的值为full,如果是proxyBeanMethods的值为false,那么这个时候属性的值就为lite。那笔者可以写出如下的代码,具体的代码如下:

package com.ys.configurationTest;

public class A {
}
  • 1
  • 2
  • 3
  • 4
package com.ys.configurationTest;

public class B {
}
  • 1
  • 2
  • 3
  • 4
package com.ys.configurationTest;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration()
public class AppConfig {

    @Bean
    public A a() {
        System.out.println("a init");
        b();
        return new A();
    }

    @Bean
    public B b() {
        System.out.println("b init");
        return new B();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
package com.ys.configurationTest;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

运行的结果如下:

在这里插入图片描述

上面的情况是full的情况,可以发现我们b init 这个就打印了一次,但是如果我们将AppConfig这个类改成如下的代码:

package com.ys.configurationTest;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class AppConfig {

    @Bean
    public A a() {
        System.out.println("a init");
        b();
        return new A();
    }

    @Bean
    public B b() {
        System.out.println("b init");
        return new B();
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

这个时候运行的结果如下:

在这里插入图片描述

你会发现b init打印了两次,证明这个对象不是单例,同时也是lite的情况。那么为什么有这种区别呢?我们可以看下两种打印的AppConfig的类型。下面是full的运行的结果。具体如下:

在这里插入图片描述

可以发现我们的AppConfig类成了一个Cglib的代理类。我们再看下lite的运行的结果,结果如下:

在这里插入图片描述

是原始的类型,所以可以确定的就是如果是full类型的,AppConfig就是Cglib的代理的代理类。同时我们也在官网找到了对应的解释,具体的如下:

在这里插入图片描述

大概的区别:

  • lite @Bean方法不能声明Bean之间的依赖关系,相反,它们对包含它们的组件的内部状态进行操作,并可选地对它们可能声明的参数进行操作。因此,这样的@Bean方法不应该调用其他的@Bean方法。
  • full 确保跨方法引用因此被重定向到容器的生命周期管理。也就是保证是单例的。

那么Spring是如何实现的呢?要搞懂这个问题,我们需要了解一下Cglib的知识。

3.Cglib的简单的使用

3.1Cglib是什么?

Cglib是一个强大的、高效的代码生成包,它广泛被许多AOP框架使用,为他们提供方法的拦截。下图是对应的Cglib与一些框架和语言的关系:

在这里插入图片描述

对此图总结一下:

  • 最底层的是字节码ByteCode,字节码是Java为了保证“一次编译、到处运行”而产生的一种虚拟指令格式,例如iload_0、iconst_1、if_icmpne、dup等
  • 位于字节码之上的是ASM,这是一种直接操作字节码的框架,应用ASM需要对Java字节码、Class结构比较熟悉
  • 位于ASM之上的是CGLIBGroovyBeanShell,后两种并不是Java体系中的内容而是脚本语言,它们通过ASM框架生成字节码变相执行Java代码,这说明在JVM中执行程序并不一定非要写Java代码----只要你能生成Java字节码,JVM并不关心字节码的来源,当然通过Java代码生成的JVM字节码是通过编译器直接生成的,算是最“正统”的JVM字节码
  • 位于CGLIB、Groovy、BeanShell之上的就是Hibernate、Spring AOP这些框架了,这一层大家都比较熟悉
  • 最上层的是Applications,即具体应用,一般都是一个Web项目或者本地跑一个程序

3.2使用Cglib代码对类做代理

下面演示一下Cglib代码示例----对类做代理。首先定义一个Dao类,里面有一个select()方法和一个update()方法:

package com.ys.configurationTest.cglib;

public class Dao {

    public void select() {
        System.out.println("Dao.select");
    }

    public void update() {
        System.out.println("Dao.update");
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

创建一个Dao代理类,实现MethodInterceptor接口,目标是在update()方法与select()方法调用前后输出两句话:

package com.ys.configurationTest.cglib;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class DaoProxy implements MethodInterceptor {

    @Override
    public Object intercept(Object object, Method method, Object[] objects, MethodProxy proxy) throws Throwable {
        System.out.println("Before Method Invoke");
        proxy.invokeSuper(object, objects);
        System.out.println("After Method Invoke");
        return object;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

intercept方法的参数名并不是原生的参数名,我做了自己的调整,几个参数的含义为:

  • Object表示要进行增强的对象
  • Method表示拦截的方法
  • Object[]数组表示参数列表,基本数据类型需要传入其包装类型,如int–>Integer、long-Long、double–>Double
  • MethodProxy表示对方法的代理,invokeSuper方法表示对被代理对象方法的调用

写一个测试类

package com.ys.configurationTest.cglib;

import net.sf.cglib.proxy.Enhancer;
import org.junit.jupiter.api.Test;

public class CglibTest {
    @Test
    public void testCglib() {
        DaoProxy daoProxy = new DaoProxy();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Dao.class);
        enhancer.setCallback(daoProxy);
        Dao dao = (Dao)enhancer.create();
        dao.update();
        dao.select();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

运行的结果如下:

在这里插入图片描述

3.3使用Cglib定义不同的拦截策略

再扩展一点点,比方说在AOP中我们经常碰到的一种复杂场景是:我们想对类A的B方法使用一种拦截策略、类A的C方法使用另外一种拦截策略

在本例中,即我们想对select()方法与update()方法使用不同的拦截策略,那么我们先定义一个新的Proxy:

package com.ys.configurationTest.cglib;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class DaoAnotherProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object object, Method method, Object[] objects, MethodProxy proxy) throws Throwable {
        System.out.println("StartTime=[" + System.currentTimeMillis() + "]");
        proxy.invokeSuper(object, objects);
        System.out.println("EndTime=[" + System.currentTimeMillis() + "]");
        return object;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

方法调用前后输出一下开始时间与结束时间。为了实现我们的需求,实现一下CallbackFilter

package com.ys.configurationTest.cglib;

import net.sf.cglib.proxy.CallbackFilter;

import java.lang.reflect.Method;

public class DaoFilter implements CallbackFilter {
    @Override
    public int accept(Method method) {
        if ("select".equals(method.getName())) {
            return 0;
        }
        return 1;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

返回的数值表示顺序,结合下面的代码解释,测试代码要修改一下:

package com.ys.configurationTest.cglib;

import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.NoOp;
import org.junit.jupiter.api.Test;

public class CglibTest {

    @Test
    public void testCglib() {
        DaoProxy daoProxy = new DaoProxy();
        DaoAnotherProxy daoAnotherProxy = new DaoAnotherProxy();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Dao.class);
        enhancer.setCallbacks(new Callback[]{daoProxy, daoAnotherProxy, NoOp.INSTANCE});
        enhancer.setCallbackFilter(new DaoFilter());
        Dao dao = (Dao)enhancer.create();
        dao.update();
        dao.select();
    }
    
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

意思是CallbackFilteraccept方法返回的数值表示的是顺序,顺序和setCallbacks里面Proxy的顺序是一致的。再解释清楚一点,Callback数组中有三个callback,那么:

  • 方法名为"select"的方法返回的顺序为0,即使用Callback数组中的0位callback,即DaoProxy
  • 方法名不为"select"的方法返回的顺序为1,即使用Callback数组中的1位callback,即DaoAnotherProxy

因此,方法的执行结果为:

在这里插入图片描述

符合我们的预期,因为update()方法不是方法名为"select"的方法,因此返回1,返回1使用DaoAnotherProxy,即打印时间;select()方法是方法名为"select"的方法,因此返回0,返回0使用DaoProxy,即方法调用前后输出两句话。

这里要额外提一下,Callback数组中我特意定义了一个NoOp.INSTANCE,这表示一个空Callback,即如果不想对某个方法进行拦截,可以在DaoFilter中返回2,具体效果可以自己尝试一下。

3.4构造函数不拦截方法

如果update()方法与select()方法在构造函数中被调用,那么也是会对这两个方法进行相应的拦截的,现在我想要的是构造函数中调用的方法不会被拦截,那么应该如何做?先改一下Dao代码,加一个构造方法Dao(),调用一下update()方法:

package com.ys.configurationTest.cglib;

public class Dao {

    public Dao() {
        update();
    }

    public void select() {
        System.out.println("Dao.select");
    }

    public void update() {
        System.out.println("Dao.update");
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

如果想要在构造函数中调用update()方法时,不拦截的话,Enhancer中有一个setInterceptDuringConstruction(boolean interceptDuringConstruction)方法设置为false即可,默认为true,即构造函数中调用方法也是会拦截的。那么测试方法这么写:

package com.ys.configurationTest.cglib;

import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.NoOp;
import org.junit.jupiter.api.Test;

public class CglibTest {

    @Test
    public void testCglib() {
        DaoProxy daoProxy = new DaoProxy();
        DaoAnotherProxy daoAnotherProxy = new DaoAnotherProxy();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Dao.class);
        enhancer.setCallbacks(new Callback[]{daoProxy, daoAnotherProxy, NoOp.INSTANCE});
        enhancer.setCallbackFilter(new DaoFilter());
        enhancer.setInterceptDuringConstruction(false);
        Dao dao = (Dao) enhancer.create();
        dao.update();
        dao.select();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

运行结果为:

在这里插入图片描述

看到第一次update()方法的调用,即Dao类构造方法中的调用没有拦截,符合预期。

4.Spring中怎么对full的配置类进行代理的

上次我们的博客主要讲了ConfigurationClassPostProcessor类中的postProcessBeanDefinitionRegistry类的方法,这个方法主要负责扫描然后处理这个类上面的一些注解,但是这个方法比较复杂,我们只介绍了一部分。而进行代理的方法就是调用postProcessBeanFactory方法,具体代码如下:

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    //获取对应的hashcode,主要用来看看有没有执行过
    int factoryId = System.identityHashCode(beanFactory);
    if (this.factoriesPostProcessed.contains(factoryId)) {
        throw new IllegalStateException(
            "postProcessBeanFactory already called on this post-processor against " + beanFactory);
    }
    //如果没有执行过,就添加到factoriesPostProcessed
    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));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

上面的方法enhanceConfigurationClasses(beanFactory);是进行生成对应的代理类。具体的代码如下:

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
    //创建一个Map用来存配置的BeanDefinition
    Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
    //遍历所有的BeanDefinition的名字
    for (String beanName : beanFactory.getBeanDefinitionNames()) {
        //根据BeanDefinition的名字获取对应的BeanDefinition
        BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
        //获取之前设置的属性,是full还lite,还是是null
        Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
        MethodMetadata methodMetadata = null;
        //只有是配置类才会进这个判断
        if (beanDef instanceof AnnotatedBeanDefinition) {
            methodMetadata = ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();//获取工厂方法的元数据
        }
        //获取设置的属性不为空,工厂方法的元数据不为空
        if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
            // Configuration class (full or lite) or a configuration-derived @Bean method
            // -> resolve bean class at this point...
            AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
            //判断是否有对应的beanClass
            if (!abd.hasBeanClass()) {
                try {
                    abd.resolveBeanClass(this.beanClassLoader);
                }
                catch (Throwable ex) {
                    throw new IllegalStateException(
                        "Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
                }
            }
        }
        //判断这个设置是不是full
        if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
            if (!(beanDef instanceof AbstractBeanDefinition)) {
                throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
                                                       beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
            }
            else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
                logger.info("Cannot enhance @Configuration bean definition '" + beanName +
                            "' since its singleton instance has been created too early. The typical cause " +
                            "is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
                            "return type: Consider declaring such methods as 'static'.");
            }
            //将对应的配置类放到刚才创建的map中去
            configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
        }
    }
    //如果没有指定配置类,和配置类是lite的属性,就直接返回,不进行下面的代理了
    if (configBeanDefs.isEmpty()) {
        // nothing to enhance -> return immediately
        return;
    }

    ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
    //遍历刚才的Map
    for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
        //获取对应BeanDefinition
        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()));
            }
            //将原来的BeanDefinition的BeanClass的属性改成代理类的
            beanDef.setBeanClass(enhancedClass);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76

上面的代码就是进行一系列的判断,判断解析的类的属性是不是Full,如果是的话,就进行对应的代理,如果不是,就直接return,不进行对应的代理。然后代理类生成的方法主要是enhancer.enhance(configClass, this.beanClassLoader);方法,具体的代码如下:

public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
    //有两个Class类型的类象,一个是调用isAssignableFrom方法的类对象(后称对象a),
    //以及方法中作为参数的这个类对象(称之为对象b),这两个对象如果满足以下条件则返回true,否则返回false:
    //a对象所对应类信息是b对象所对应的类信息的父类或者是父接口,简单理解即a是b的父类或接口
    //a对象所对应类信息与b对象所对应的类信息相同,简单理解即a和b为同一个类或同一个接口
    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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

上面代码走来先判断这个类是否解析过了,如果没有再生成代理,主要生成代理的方法就是 createClass(newEnhancer(configClass, classLoader));,首先我们先看下newEnhancer(configClass, classLoader)代码,具体的代码如下:

private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
    Enhancer enhancer = new Enhancer();
    //设置父类
    enhancer.setSuperclass(configSuperClass);
    //设置接口
    enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
    enhancer.setUseFactory(false);
    //设置名字生成策略 加上BySpringCGLIB
    enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
    enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
    //设置拦截的策略
    enhancer.setCallbackFilter(CALLBACK_FILTER);
    //设置拦截的类型
    enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
    return enhancer;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

上面设置拦截的策略,我们需要看它指定的数组,同时还要看accept的方法。具体的代码如下:

private static final Callback[] CALLBACKS = new Callback[] {
    new BeanMethodInterceptor(),
    new BeanFactoryAwareMethodInterceptor(),
    NoOp.INSTANCE
};
  • 1
  • 2
  • 3
  • 4
  • 5
@Override
public int accept(Method method) {
    for (int i = 0; i < this.callbacks.length; i++) {
        Callback callback = this.callbacks[i];
        if (!(callback instanceof ConditionalCallback) || ((ConditionalCallback) callback).isMatch(method)) {
            return i;
        }
    }
    throw new IllegalStateException("No callback available for method " + method.getName());
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

我们分别看下BeanMethodInterceptor的指定的isMatch(method)的代码,具体如下:

@Override
public boolean isMatch(Method candidateMethod) {
    //判断不是Object类,同时不是实现BeanFactoryAware接口的是setBeanFactory方法,同时也是加了@Bean的注解的
    return (candidateMethod.getDeclaringClass() != Object.class &&
            !BeanFactoryAwareMethodInterceptor.isSetBeanFactory(candidateMethod) &&
            BeanAnnotationHelper.isBeanAnnotated(candidateMethod));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

然后我们再看下BeanFactoryAwareMethodInterceptor的指定的isMatch(method)的代码,具体的如下:

@Override
public boolean isMatch(Method candidateMethod) {
    //判断这个方法是不是实现BeanFactoryAware接口的setBeanFactory方法
    return isSetBeanFactory(candidateMethod);
}

public static boolean isSetBeanFactory(Method candidateMethod) {
    return (candidateMethod.getName().equals("setBeanFactory") &&
            candidateMethod.getParameterCount() == 1 &&
            BeanFactory.class == candidateMethod.getParameterTypes()[0] &&
            BeanFactoryAware.class.isAssignableFrom(candidateMethod.getDeclaringClass()));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

所以看了上面的拦截的规则,我们大概知道了拦截的规则,就是如果是加了@Bean注解的方法,同时不是实现BeanFactoryAware接口的setBeanFactory方法,会调用BeanMethodInterceptor中的intercept的方法。如果是实现BeanFactoryAware接口的setBeanFactory方法,就会调用BeanFactoryAwareMethodInterceptor中的intercept的方法。

4.1BeanMethodInterceptor

我们先看BeanMethodInterceptor中的intercept的方法,具体的代码如下:

public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
                        MethodProxy cglibMethodProxy) throws Throwable {

    //获取对应的BeanFactory
    ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
    //获取对应的BeanName
    String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);

    // Determine whether this bean is a scoped-proxy
    //判断这个bean是不是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.
    
    //首先,检查所请求的bean是否为FactoryBean。
    //如果是这样,则创建一个子类代理,以拦截对getObject()的调用并返回所有缓存的Bean实例。 
    //这样可确保从@Bean方法中调用FactoryBean的语义
    //与在XML中引用FactoryBean的语义相同
    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);
        }
    }

    //如果不是就是正常的方法,并不是处理FactoryBean的 判断是不是调用当前的工厂的方法
    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()));
        }
        //是的话就直接调用父类的方法,也就是原始的创建的方法
        return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
    }
	
    //不是的话,就会调用如下的方法
    return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

在别的方法中调用这个创建FactoryBean的方法。首先,检查所请求的bean是否为FactoryBean。如果是这样,则创建一个子类代理,以拦截对getObject()的调用并返回所有缓存的Bean实例。判断是不是 ScopedProxyFactoryBean,如果是,就不进行下一步的处理,如果不是调用enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);方法,具体的代码如下:

private Object enhanceFactoryBean(final Object factoryBean, Class<?> exposedType,
                                  final ConfigurableBeanFactory beanFactory, final String beanName) {

    try {
        //获取对应的class
        Class<?> clazz = factoryBean.getClass();
        //判断是不是final的类
        boolean finalClass = Modifier.isFinal(clazz.getModifiers());
        //判断getObject是不是final的方法
        boolean finalMethod = Modifier.isFinal(clazz.getMethod("getObject").getModifiers());
        //两个都是final的
        if (finalClass || finalMethod) {
            //返回值的是接口
            if (exposedType.isInterface()) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Creating interface proxy for FactoryBean '" + beanName + "' of type [" +
                                 clazz.getName() + "] for use within another @Bean method because its " +
                                 (finalClass ? "implementation class" : "getObject() method") +
                                 " is final: Otherwise a getObject() call would not be routed to the factory.");
                }
                return createInterfaceProxyForFactoryBean(factoryBean, exposedType, beanFactory, beanName);
            }
            else {
                //返回的值不是接口
                if (logger.isDebugEnabled()) {
                    logger.debug("Unable to proxy FactoryBean '" + beanName + "' of type [" +
                                 clazz.getName() + "] for use within another @Bean method because its " +
                                 (finalClass ? "implementation class" : "getObject() method") +
                                 " is final: A getObject() call will NOT be routed to the factory. " +
                                 "Consider declaring the return type as a FactoryBean interface.");
                }
                return factoryBean;
            }
        }
    }
    catch (NoSuchMethodException ex) {
        // No getObject() method -> shouldn't happen, but as long as nobody is trying to call it...
    }
    //其中有一个不是final的
    return createCglibProxyForFactoryBean(factoryBean, beanFactory, beanName);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

上面的代码主要是分成三种情况:

  • 对应的类是finalgetObject的方法也是final,然后这个调用的方法的返回值接口的,这个时候就会调用createInterfaceProxyForFactoryBean(factoryBean, exposedType, beanFactory, beanName);你会发现使用的jdk的动态的代理,具体的代码如下:
private Object createInterfaceProxyForFactoryBean(final Object factoryBean, Class<?> interfaceType,
				final ConfigurableBeanFactory beanFactory, final String beanName) {

    return Proxy.newProxyInstance(
        factoryBean.getClass().getClassLoader(), new Class<?>[] {interfaceType},
        (proxy, method, args) -> {
            if (method.getName().equals("getObject") && args == null) {
                return beanFactory.getBean(beanName);
            }
            return ReflectionUtils.invokeMethod(method, factoryBean, args);
        });
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

上面的代码就是如果调用getObject的方法就是从IOC容器中直接取。如果不是调用getObject的方法,就直接调用。

  • 对应的类是finalgetObject的方法也是final,然后这个调用的方法的返回值不是接口,这个时候就直接返回factoryBean

  • 对应的类和getObject的方法有一个不是final的,就会调用createCglibProxyForFactoryBean(factoryBean, beanFactory, beanName);方法,具体的代码如下:

    private Object createCglibProxyForFactoryBean(final Object factoryBean,
                                                  final ConfigurableBeanFactory beanFactory, final String beanName) {
    
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(factoryBean.getClass());
        enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
        enhancer.setCallbackType(MethodInterceptor.class);
    
        // Ideally create enhanced FactoryBean proxy without constructor side effects,
        // analogous to AOP proxy creation in ObjenesisCglibAopProxy...
        Class<?> fbClass = enhancer.createClass();
        Object fbProxy = null;
    
        if (objenesis.isWorthTrying()) {
            try {
                fbProxy = objenesis.newInstance(fbClass, enhancer.getUseCache());
            }
            catch (ObjenesisException ex) {
                logger.debug("Unable to instantiate enhanced FactoryBean using Objenesis, " +
                             "falling back to regular construction", ex);
            }
        }
    
        if (fbProxy == null) {
            try {
                fbProxy = ReflectionUtils.accessibleConstructor(fbClass).newInstance();
            }
            catch (Throwable ex) {
                throw new IllegalStateException("Unable to instantiate enhanced FactoryBean using Objenesis, " +
                                                "and regular FactoryBean instantiation via default constructor fails as well", ex);
            }
        }
    
        //然后当调用的是getObject的方法的时候,返回的直接从容器中取得
        ((Factory) fbProxy).setCallback(0, (MethodInterceptor) (obj, method, args, proxy) -> {
            if (method.getName().equals("getObject") && args.length == 0) {
                return beanFactory.getBean(beanName);
            }
            return proxy.invoke(factoryBean, args);
        });
        return fbProxy;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    上面的大概的介绍了FactoryBean的处理过程,就是配置类中的方法调用对应创建的FactoryBean的方法。具体的代码如下:

    package com.ys.configurationTest;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration()
    public class AppConfig{
        @Bean
        public B b() {
            //调用创建的FactoryBean的方法
            myFactoryBean();
            System.out.println("b init");
            return new B();
        }
    
        @Bean
        public MyFactoryBean myFactoryBean() {
            return new MyFactoryBean();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    还有最后一种情况,具体的代码如下:

    package com.ys.configurationTest;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration()
    public class AppConfig{
        
        @Bean
        public A a(){
            System.out.println("a init");
            b();
            return new B();
        }
        
        @Bean
        public B b() {
            System.out.println("b init");
            return new B();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    这个时候我们继续看原来的代码,我只列出最重要的部分,具体的代码如下:

    public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
                            MethodProxy cglibMethodProxy) throws Throwable {
    
        //省略部分代码。。。
        //如果不是就是正常的方法,并不是处理FactoryBean的 判断是不是调用当前的工厂的方法
        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()));
            }
            //是的话就直接调用父类的方法,也就是原始的创建的方法
            return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
        }
    	
        //不是的话,就会调用如下的方法
        return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    走来判断当前执行的方法名是不是调用的方法名一样,很明显是的,于是就会调用父类的方法,这个时候会打印一句话a init,然后会执行 b();方法,于是和当前的调用的方法名的是不一样的,于是就会执行resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName),具体的代码如下:

    private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,
                                        ConfigurableBeanFactory beanFactory, String beanName) {
    
        // The user (i.e. not the factory) is requesting this bean through a call to
        // the bean method, direct or indirect. The bean may have already been marked
        // as 'in creation' in certain autowiring scenarios; if so, temporarily set
        // the in-creation status to false in order to avoid an exception.
        boolean alreadyInCreation = beanFactory.isCurrentlyInCreation(beanName);
        try {
            if (alreadyInCreation) {
                beanFactory.setCurrentlyInCreation(beanName, false);
            }
            boolean useArgs = !ObjectUtils.isEmpty(beanMethodArgs);
            if (useArgs && beanFactory.isSingleton(beanName)) {
                // Stubbed null arguments just for reference purposes,
                // expecting them to be autowired for regular singleton references?
                // A safe assumption since @Bean singleton arguments cannot be optional...
                for (Object arg : beanMethodArgs) {
                    if (arg == null) {
                        useArgs = false;
                        break;
                    }
                }
            }
            Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) :
                                   beanFactory.getBean(beanName));
            if (!ClassUtils.isAssignableValue(beanMethod.getReturnType(), beanInstance)) {
                // Detect package-protected NullBean instance through equals(null) check
                if (beanInstance.equals(null)) {
                    if (logger.isDebugEnabled()) {
                        logger.debug(String.format("@Bean method %s.%s called as bean reference " +
                                                   "for type [%s] returned null bean; resolving to null value.",
                                                   beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName(),
                                                   beanMethod.getReturnType().getName()));
                    }
                    beanInstance = null;
                }
                else {
                    String msg = String.format("@Bean method %s.%s called as bean reference " +
                                               "for type [%s] but overridden by non-compatible bean instance of type [%s].",
                                               beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName(),
                                               beanMethod.getReturnType().getName(), beanInstance.getClass().getName());
                    try {
                        BeanDefinition beanDefinition = beanFactory.getMergedBeanDefinition(beanName);
                        msg += " Overriding bean of same name declared in: " + beanDefinition.getResourceDescription();
                    }
                    catch (NoSuchBeanDefinitionException ex) {
                        // Ignore - simply no detailed message then.
                    }
                    throw new IllegalStateException(msg);
                }
            }
            Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();
            if (currentlyInvoked != null) {
                String outerBeanName = BeanAnnotationHelper.determineBeanNameFor(currentlyInvoked);
                beanFactory.registerDependentBean(beanName, outerBeanName);
            }
            return beanInstance;
        }
        finally {
            if (alreadyInCreation) {
                beanFactory.setCurrentlyInCreation(beanName, true);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    上面有一行很重要的代码就是getBean(),这儿还有一些细节,要等到讲Bean的生命周期的时候才会讲到。看完了BeanMethodInterceptor,我们再来看下BeanFactoryAwareMethodInterceptor

4.2BeanFactoryAwareMethodInterceptor

我们直接看对应的方法,具体的代码如下:

@Override
@Nullable
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
    //获取$$beanFactory属性
    Field field = ReflectionUtils.findField(obj.getClass(), BEAN_FACTORY_FIELD);
    Assert.state(field != null, "Unable to find generated BeanFactory field");
    //取出方法中的BeanFactory的属性,设置给$$beanFactory
    field.set(obj, args[0]);

    //实际的(非CGLIB)超类是否实现BeanFactoryAware? 
    //如果是这样,则调用其setBeanFactory()方法。如果没有,请退出。
    if (BeanFactoryAware.class.isAssignableFrom(ClassUtils.getUserClass(obj.getClass().getSuperclass()))) {
        return proxy.invokeSuper(obj, args);
    }
    return null;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

至此有关@Configuration注解涉及到的所有的东西都讲完了。

5.写在最后

本篇博客主要讲了Spring注解的@Configuration注解,以及一些处理的逻辑和流程,有一部分的东西,还要等到将Bean的生命周期才能讲到。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值