【Spring源碼】@Configuration和@Component的區(qū)別

@Configuration

image

該注解派生自@Component,和@Component注解有相同的功能

相同點(diǎn):
  1. 可以標(biāo)識(shí)該類實(shí)例被Spring-ioc容器管理
  2. 類中含有@Bean的方法,可以創(chuàng)建bean
不同點(diǎn):
  1. 如果是由@Configuration注解修飾的類,自身會(huì)生成一個(gè)cglib代理對(duì)象,在通過(guò)@Bean方式創(chuàng)建單例對(duì)象時(shí),經(jīng)過(guò)增強(qiáng),會(huì)嘗試從BeanFactory里返回對(duì)象,如果是第一次創(chuàng)建@Bean要生成的對(duì)象,才會(huì)反射調(diào)用被@Bean修飾的方法。不管是spring初次創(chuàng)建@Bean的對(duì)象,還是業(yè)務(wù)代碼手動(dòng)調(diào)用被@Bean方法修飾的方法,返回的永遠(yuǎn)是同一個(gè)被spring容器管理的對(duì)象。

實(shí)例代碼

@Configuration修飾的類
@Configuration
public class ConfigurationBean {

    @Bean
    public Person person(){
        return new Person();
    }

    public void printPerson(){
        System.out.println(person());
    }
}

測(cè)試
@Test
public void testImportAnno() {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.lb.springboot_simple_starter.bean");
    // 容器中獲取person
    System.out.println(applicationContext.getBean(Person.class));
    // 手動(dòng)調(diào)用ConfigurationBean.person()
    ConfigurationBean configurationBean = applicationContext.getBean(ConfigurationBean.class);
    configurationBean.printPerson();
}

可以看到輸出結(jié)果是同一個(gè)對(duì)象

image

如果是@Component中的類調(diào)用@Bean修飾的方法,每次都會(huì)創(chuàng)建并返回不同的對(duì)象

源碼解析

ConfigurationClassPostProcessor.postProcessBeanFactory(beanFactory)

1.對(duì)容器中已有的BeanDefinition 解析@Component / @ComponentScan / @Import / @ImportResource /@Bean

image

這里對(duì)上述注解的解析在其他筆記里有,其中和處理@Configuration有關(guān)的地方

先判斷類上是否有@Component / @ComponentScan / @Import / @ImportResource,獲取方法上有@Bean注解,判斷通過(guò)才會(huì)走下面的解析流程

image

其中如果有@Configuration注解,在設(shè)置BD的這個(gè)屬性為full

image

其他注解則會(huì)設(shè)置該屬性為lite.

2. 處理@Configuration修飾的類

image

首先取出容器中所有的BeanDefinition,并遍歷每一個(gè),獲取上一步中設(shè)置的那個(gè)屬性,如果是@Configuration,那么這個(gè)屬性值則為full

可以看到測(cè)試代碼中的ConfigurationBean的beanDefinition的該屬性值為full

image

那么會(huì)把這個(gè)bean裝到一個(gè)放類上有@Configuration修飾的beanDefinition的map中

image

循環(huán)該map容器,為每一個(gè)類上有@Configuration修飾的beanDefinition對(duì)應(yīng)的類型創(chuàng)建代理類,并設(shè)置到beanDefinition的BeanClass屬性中,那么在spring實(shí)例化該bean的時(shí)候,就會(huì)創(chuàng)建出一個(gè)代理對(duì)象。

image

返回單例對(duì)象的邏輯則是通過(guò)代理的方式實(shí)現(xiàn)的.看看代理的邏輯

@Configuration代理類

Cglib api示例
public class CallbackFilterDemo {

    public static void main(String[] args) {
        // 攔截器數(shù)組
        Callback[] callbacks = new Callback[] {
            new MethodInterceptorImpl(), NoOp.INSTANCE
        };
         
        // 創(chuàng)建增強(qiáng)器
        Enhancer enhancer = new Enhancer();
        // 設(shè)置被代理類,cglib是通過(guò)繼承被代理類來(lái)實(shí)現(xiàn)代理的
        enhancer.setSuperclass(MyClass.class);
        // 設(shè)置攔截類的數(shù)組
        enhancer.setCallbacks(callbacks);
        // 設(shè)置攔截器的Filter,該類的accept方法定義了選擇哪個(gè)攔截器進(jìn)行代理的邏輯
        enhancer.setCallbackFilter(new CallbackFilterImpl());
        // 創(chuàng)建代理對(duì)象
        MyClass myClass = (MyClass) enhancer.create();
         
        myClass.method();
        myClass.method1();
    }
     
    private static class CallbackFilterImpl implements CallbackFilter {
 
        // 這里會(huì)先被調(diào)到,返回?cái)r截器數(shù)組的下標(biāo)
        @Override
        public int accept(Method method) {
            if (method.getName().equals("method"))
                return 1;
            else
                return 0;
        }
         
    }
     
    private static class MethodInterceptorImpl implements MethodInterceptor {
 
        //增強(qiáng)邏輯
        @Override
        public Object intercept(Object obj, Method method, Object[] args,
                MethodProxy proxy) throws Throwable {
            System.err.println("Before invoke " + method);
            // 執(zhí)行被代理方法
            Object result = proxy.invokeSuper(obj, args);
            System.err.println("After invoke" + method);
            return result;
        }
         
    }
}
代理類的創(chuàng)建

Enhancer.enhance會(huì)創(chuàng)建出代理類的Class對(duì)象

image
image
  1. newEnhancer方法創(chuàng)建一個(gè)增強(qiáng)器,并設(shè)置攔截器數(shù)組,并且設(shè)置代理類實(shí)現(xiàn)接口EnhancedConfiguration,EnhancedConfiguration又繼承了BeanFactoryAware,用來(lái)在代理對(duì)象中持有BeanFactory對(duì)象
image

攔截器數(shù)組Filter會(huì)持有攔截器數(shù)組,

image

這里有兩個(gè)試用的攔截器

  1. BeanMethodInterceptor:用來(lái)增強(qiáng)@Bean的方法
  2. BeanFactoryAwareMethodInterceptor用來(lái)設(shè)置BeanFactory


    image

具體使用哪里攔截器進(jìn)行增強(qiáng),由攔截器數(shù)組的accept方法來(lái)決定。

具體的代理邏輯

1. 攔截器的選擇ConditionalCallbackFilter.accept方法
image

遍歷所有的攔截器,調(diào)用match方法,返回支持增強(qiáng)該方法的攔截器下標(biāo)

1.1. BeanFactoryAwareMethodInterceptor.match

match返回true的條件為 :

  1. 方法名為setBeanFactory
  2. 參數(shù)列表長(zhǎng)度為1
  3. 唯一的一個(gè)參數(shù)為BeanFactory類型
  4. 方法的類為BeanFactoryAware實(shí)現(xiàn)類
image

可以看出,該攔截器的就是為了增強(qiáng)實(shí)現(xiàn)自BeanFactoryAware的setBeanFactory(BeanFactory beanFactory) 方法

1.2. BeanMethodInterceptor.match方法

match返回true的條件為 :

  1. 不是Object類的方法
  2. 不是SetBeanFactory方法
  3. 并且方法上有@Bean注解
image

可以看出BeanMethodInterceptor就是為了增強(qiáng)@BeanMethod方法

那么接下來(lái)具體的增強(qiáng)邏輯肯定是在兩個(gè)攔截器的intercept方法中了

增強(qiáng)
1. BeanMethodInterceptor.match :對(duì)setBeanFactory方法的增強(qiáng)
  1. 用cglib為代理對(duì)象生成一個(gè)$$beanFactory屬性,并將setBeanFactory(BeanFactory beanFactory)的參數(shù)反射設(shè)置給這個(gè)屬性
  2. 如果被代理類已經(jīng)是BeanFactoryAware實(shí)現(xiàn)類,那么直接調(diào)用setBeanFactory(BeanFactory beanFactory)方法
image
2.BeanMethodInterceptor:對(duì)@Bean的方法增強(qiáng)
  1. 獲取BeanFactory屬性,方法的返回值
  2. 獲取@Bean方法定義的beanName,要么是注解中的name,要么是方法名首字母小寫
image
  1. 如果beanName指向的bean是FactoryBean類型,那么這里很有可能會(huì)再生成一次代理

判斷條件:如果能以&+beanName,beanName都能拿bean工廠拿到實(shí)例則說(shuō)明,說(shuō)明該@Bean方法返回的是一個(gè)實(shí)現(xiàn)了FactoryBean接口的類,是的話要返回代理對(duì)象,對(duì)getObject方法進(jìn)行代理,以保證返回的是同一個(gè)對(duì)象

image
  1. 首先判斷實(shí)現(xiàn)FactoryBean接口的bean類是否是final,或者getObject是否是final,如果是的話,會(huì)選擇jdk動(dòng)態(tài)代理(cglib無(wú)法代理final類,無(wú)法增強(qiáng)final修飾的方法,因?yàn)槭鞘褂美^承被代理類的方式),并且要求返回的是一個(gè)接口,這個(gè)接口可以認(rèn)為是FactoryBean接口,因?yàn)閖dk是采用實(shí)現(xiàn)被代理類的同一個(gè)接口的方式來(lái)對(duì)被代理類實(shí)現(xiàn)接口的方法進(jìn)行代理。這里被代理類實(shí)現(xiàn)的是FactoryBean接口。
image

示例代碼:

@Configuration
public class ConfigurationBean {
    @Bean
    public FactoryBean myFactoryBean(){
        // MyFactoryBean實(shí)現(xiàn)了FactoryBean接口
        return new MyFactoryBean();
    }

    public  final class MyFactoryBean implements FactoryBean {
        @Override
        public final Object getObject() {
            return new Man();
        }

        @Override
        public Class<?> getObjectType() {
            return Man.class;
        }
    }

    public void printMyFactoryBeanGetObject() throws Exception {
        System.out.println("ConfigurationBean手動(dòng)調(diào)用MyFactoryBean.getObject方法:" + myFactoryBean().getObject());
        System.out.println("ConfigurationBean手動(dòng)調(diào)用MyFactoryBean.getObject方法:" + myFactoryBean().getObject());
    }
}

這樣的寫法會(huì)使用jdk動(dòng)態(tài)代理,代理類會(huì)同樣會(huì)實(shí)現(xiàn)FactoryBean接口,增強(qiáng)getObject方法 : 從bean工廠返回單例對(duì)象,不會(huì)多次創(chuàng)建實(shí)例

image
  1. 否則會(huì)使用cglib代理
image
image

其中的攔截類是一個(gè)匿名對(duì)象,代理的邏輯同樣是bean工廠返回單例對(duì)象

image

總結(jié) : 如果該@Bean方法的beanName對(duì)應(yīng)的bean實(shí)現(xiàn)了Factory接口,會(huì)再次返回一個(gè)代理對(duì)象,根據(jù)類和getObjct方法是否final以及方法返回值是否是FactoryBean接口選擇代理方法,是選擇jdk,否則cglib,增強(qiáng)的邏輯都是在執(zhí)行g(shù)etObject方法的時(shí)候從bean工廠返回單例bean.

  1. 判斷是否是spring容器使用@Bean方法創(chuàng)建bean,是的話,直接調(diào)被代理類(@Configuration類)的@Bean方法
image

判斷是否是spring容器創(chuàng)建bean而調(diào)用@Bean方法

image
  1. 從ThreadLocal中拿當(dāng)前正在用@Bean(FactoryMethod)方式創(chuàng)建實(shí)例的Method,ioc創(chuàng)建bean,如果是使用FactoryMethod,會(huì)在調(diào)用FactoryMethod之前往ThreadLocal放FactoryMethod,反射調(diào)用完,remove
image
image

這里反射調(diào)用就有可能走進(jìn)@Configuration的bean的對(duì)象對(duì)@Bean方法的增強(qiáng),就是上面的BeanMethodInterceptor的intercept方法中,從而可以從ThreadLocal中拿到FactoryMethod,進(jìn)行判斷


image
  1. 判斷ThreadLocal是否存在FactoryMethod且方法名稱,參數(shù)列表是否一樣
  1. 如果手動(dòng)調(diào)用@Configuration的bean的@Bean方法,會(huì)走這里
image

也是從BeanFactory中返回對(duì)象,以保證始終返回的是同一個(gè)對(duì)象

image
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容