@Configuration
該注解派生自@Component,和@Component注解有相同的功能
相同點(diǎn):
- 可以標(biāo)識(shí)該類實(shí)例被Spring-ioc容器管理
- 類中含有@Bean的方法,可以創(chuàng)建bean
不同點(diǎn):
- 如果是由@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ì)象
如果是@Component中的類調(diào)用@Bean修飾的方法,每次都會(huì)創(chuàng)建并返回不同的對(duì)象
源碼解析
ConfigurationClassPostProcessor.postProcessBeanFactory(beanFactory)
1.對(duì)容器中已有的BeanDefinition 解析@Component / @ComponentScan / @Import / @ImportResource /@Bean
這里對(duì)上述注解的解析在其他筆記里有,其中和處理@Configuration有關(guān)的地方
先判斷類上是否有@Component / @ComponentScan / @Import / @ImportResource,獲取方法上有@Bean注解,判斷通過(guò)才會(huì)走下面的解析流程
其中如果有@Configuration注解,在設(shè)置BD的這個(gè)屬性為full
其他注解則會(huì)設(shè)置該屬性為lite.
2. 處理@Configuration修飾的類
首先取出容器中所有的BeanDefinition,并遍歷每一個(gè),獲取上一步中設(shè)置的那個(gè)屬性,如果是@Configuration,那么這個(gè)屬性值則為full
可以看到測(cè)試代碼中的ConfigurationBean的beanDefinition的該屬性值為full
那么會(huì)把這個(gè)bean裝到一個(gè)放類上有@Configuration修飾的beanDefinition的map中
循環(huán)該map容器,為每一個(gè)類上有@Configuration修飾的beanDefinition對(duì)應(yīng)的類型創(chuàng)建代理類,并設(shè)置到beanDefinition的BeanClass屬性中,那么在spring實(shí)例化該bean的時(shí)候,就會(huì)創(chuàng)建出一個(gè)代理對(duì)象。
返回單例對(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ì)象
- newEnhancer方法創(chuàng)建一個(gè)增強(qiáng)器,并設(shè)置攔截器數(shù)組,并且設(shè)置代理類實(shí)現(xiàn)接口EnhancedConfiguration,EnhancedConfiguration又繼承了BeanFactoryAware,用來(lái)在代理對(duì)象中持有BeanFactory對(duì)象
攔截器數(shù)組Filter會(huì)持有攔截器數(shù)組,
這里有兩個(gè)試用的攔截器
- BeanMethodInterceptor:用來(lái)增強(qiáng)@Bean的方法
-
BeanFactoryAwareMethodInterceptor用來(lái)設(shè)置BeanFactory
image
具體使用哪里攔截器進(jìn)行增強(qiáng),由攔截器數(shù)組的accept方法來(lái)決定。
具體的代理邏輯
1. 攔截器的選擇ConditionalCallbackFilter.accept方法
遍歷所有的攔截器,調(diào)用match方法,返回支持增強(qiáng)該方法的攔截器下標(biāo)
1.1. BeanFactoryAwareMethodInterceptor.match
match返回true的條件為 :
- 方法名為setBeanFactory
- 參數(shù)列表長(zhǎng)度為1
- 唯一的一個(gè)參數(shù)為BeanFactory類型
- 方法的類為BeanFactoryAware實(shí)現(xiàn)類
可以看出,該攔截器的就是為了增強(qiáng)實(shí)現(xiàn)自BeanFactoryAware的setBeanFactory(BeanFactory beanFactory) 方法
1.2. BeanMethodInterceptor.match方法
match返回true的條件為 :
- 不是Object類的方法
- 不是SetBeanFactory方法
- 并且方法上有@Bean注解
可以看出BeanMethodInterceptor就是為了增強(qiáng)@BeanMethod方法
那么接下來(lái)具體的增強(qiáng)邏輯肯定是在兩個(gè)攔截器的intercept方法中了
增強(qiáng)
1. BeanMethodInterceptor.match :對(duì)setBeanFactory方法的增強(qiáng)
- 用cglib為代理對(duì)象生成一個(gè)$$beanFactory屬性,并將setBeanFactory(BeanFactory beanFactory)的參數(shù)反射設(shè)置給這個(gè)屬性
- 如果被代理類已經(jīng)是BeanFactoryAware實(shí)現(xiàn)類,那么直接調(diào)用setBeanFactory(BeanFactory beanFactory)方法
2.BeanMethodInterceptor:對(duì)@Bean的方法增強(qiáng)
- 獲取BeanFactory屬性,方法的返回值
- 獲取@Bean方法定義的beanName,要么是注解中的name,要么是方法名首字母小寫
- 如果beanName指向的bean是FactoryBean類型,那么這里很有可能會(huì)再生成一次代理
判斷條件:如果能以&+beanName,beanName都能拿bean工廠拿到實(shí)例則說(shuō)明,說(shuō)明該@Bean方法返回的是一個(gè)實(shí)現(xiàn)了FactoryBean接口的類,是的話要返回代理對(duì)象,對(duì)getObject方法進(jìn)行代理,以保證返回的是同一個(gè)對(duì)象
image
- 首先判斷實(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
- 否則會(huì)使用cglib代理
imageimage其中的攔截類是一個(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.
- 判斷是否是spring容器使用@Bean方法創(chuàng)建bean,是的話,直接調(diào)被代理類(@Configuration類)的@Bean方法
image判斷是否是spring容器創(chuàng)建bean而調(diào)用@Bean方法
image
- 從ThreadLocal中拿當(dāng)前正在用@Bean(FactoryMethod)方式創(chuàng)建實(shí)例的Method,ioc創(chuàng)建bean,如果是使用FactoryMethod,會(huì)在調(diào)用FactoryMethod之前往ThreadLocal放FactoryMethod,反射調(diào)用完,remove
imageimage這里反射調(diào)用就有可能走進(jìn)@Configuration的bean的對(duì)象對(duì)@Bean方法的增強(qiáng),就是上面的BeanMethodInterceptor的intercept方法中,從而可以從ThreadLocal中拿到FactoryMethod,進(jìn)行判斷
image
- 判斷ThreadLocal是否存在FactoryMethod且方法名稱,參數(shù)列表是否一樣
- 如果手動(dòng)調(diào)用@Configuration的bean的@Bean方法,會(huì)走這里
image也是從BeanFactory中返回對(duì)象,以保證始終返回的是同一個(gè)對(duì)象
image