新聞中心
環(huán)境:Spring5.3.23

創(chuàng)新互聯(lián)公司長(zhǎng)期為上千家客戶(hù)提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開(kāi)放共贏平臺(tái),與合作伙伴共同營(yíng)造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為佳縣企業(yè)提供專(zhuān)業(yè)的網(wǎng)站制作、成都做網(wǎng)站,佳縣網(wǎng)站改版等技術(shù)服務(wù)。擁有十載豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開(kāi)發(fā)。
1. 簡(jiǎn)介
@Configuration 是一個(gè)類(lèi)級(jí)注解,表示一個(gè)對(duì)象是 Bean 定義的來(lái)源。@Configuration 類(lèi)通過(guò) @Bean 注解的方法聲明 Bean。對(duì) @Configuration 類(lèi)上 @Bean 方法的調(diào)用也可用于定義Bean之間的依賴(lài)關(guān)系。
使用@Configuration注解的主要作用是替代Spring的applicationContext.xml文件,使得配置更加靈活和方便。當(dāng)某個(gè)類(lèi)標(biāo)注了@Configuration注解時(shí),表示這個(gè)類(lèi)是Spring的一個(gè)配置類(lèi),能夠自動(dòng)注冊(cè)到IOC容器并進(jìn)行實(shí)例化。
2. 應(yīng)用示例
static class Person {}
@Configuration
static class AppConfig {
@Bean
public Person person() {
return new Person() ;
}
}AppConfig是一個(gè)配置類(lèi),在該類(lèi)中通過(guò)@Bean標(biāo)注方法注冊(cè)Bean對(duì)象。示例非常的簡(jiǎn)單,但是不是一定的用@Configuration呢?換成@Component試試
@Component
static class AppConfig {
@Bean
public Person person() {
return new Person() ;
}
}
// 測(cè)試是否能夠獲取Person bean對(duì)象
try (GenericApplicationContext context = new GenericApplicationContext()) {
context.registerBean(AppConfig.class) ;
// ...
System.out.println(context.getBean(Person.class)) ;
}上面的示例能夠正確的獲取Person bean對(duì)象,那這里的@Component與@Configuration有什么區(qū)別呢?接著看下面代碼示例:
@Configuration
static class AppConfig {
@Bean
public Person person() {
return new Person() ;
}
@Bean
public Date d1() {
System.out.println(person()) ;
return new Date() ;
}
@Bean
public Date d2() {
System.out.println(person()) ;
return new Date() ;
}
}在上面的示例中,隨意定義了2個(gè)Date類(lèi)型的Bean,這2個(gè)方法中都調(diào)用person()方法,執(zhí)行結(jié)果:
com.pack.m.b.CMain$Person@55183b20
com.pack.m.b.CMain$Person@55183b20控制臺(tái)輸出的一模一樣,是不是感覺(jué)好奇怪,調(diào)用2次應(yīng)該是不同的Person對(duì)象才對(duì)是吧?先繼續(xù)往下看,吧@Configuration換成@Component(就是換注解,其它都沒(méi)有變化,代碼就不貼了),執(zhí)行結(jié)果:
com.pack.m.b.CMain$Person@78aab498
com.pack.m.b.CMain$Person@5dd6264這次輸出是2個(gè)不同的Person對(duì)象了,此時(shí)你是不是覺(jué)得這次符合你的預(yù)期?如果你這么認(rèn)為那么就出大事了。
在 Spring 中,實(shí)例化的 Bean 默認(rèn)具有單例作用域,但是如上執(zhí)行情況,明顯成了多例,我們就應(yīng)該確保容器中任何時(shí)候使用的都是同一個(gè)實(shí)例。如果這里是DataSource那問(wèn)題就更加嚴(yán)重了。
所以,這里雖然可以使用@Component定義配置類(lèi),但是強(qiáng)烈不建議你這樣使用,既然是配置類(lèi)你就的按規(guī)矩來(lái)使用@Configuration注解。那@Configuration是如何保證在內(nèi)部方法調(diào)用返回的對(duì)象是同一個(gè)呢?
先給出答案:那是因?yàn)槭褂聾Configuration注解的類(lèi)被生成了代理類(lèi)(通過(guò)CGLIB)。接下來(lái)我們來(lái)看看它的原理。
3. 實(shí)現(xiàn)原理
Spring提供了一個(gè)ConfigurationClassPostProcessor處理器來(lái)注冊(cè)@Configuration注解。該處理器是一個(gè)BeanFactoryPostProcessor。我們就從這里看起
3.1 給@Configuration注解的類(lèi)打標(biāo)記
這里所說(shuō)的大標(biāo)記其實(shí)就確定你當(dāng)前這個(gè)配置類(lèi)要不要生成代理,而這個(gè)標(biāo)記模式是生成代理
// 這里的proxyBeanMethods值為true,意為會(huì)為當(dāng)前的配置類(lèi)生成代理
@Configuration(proxyBeanMethods = true)
static class AppConfig {}處理配置類(lèi)
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor {
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// ...
// 處理配置類(lèi)bean
processConfigBeanDefinitions(registry);
}
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
// 獲取所有的bean
String[] candidateNames = registry.getBeanDefinitionNames();
// 遍歷
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName) ;
// 判斷當(dāng)前的bean是否有ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE屬性
// 默認(rèn)都是沒(méi)有的,所以這里進(jìn)入到else if 中
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
// 打印日志
}
// 在checkConfigurationClassCandidate會(huì)處理配置類(lèi)的相應(yīng)屬性
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// ...
}
}ConfigurationClassUtils
abstract class ConfigurationClassUtils {
public static final String CONFIGURATION_CLASS_FULL = "full";
public static final String CONFIGURATION_CLASS_LITE = "lite";
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
// 省去無(wú)關(guān)代碼
// 獲取到當(dāng)前配置類(lèi)的所有注解信息
AnnotationMetadata metadata = AnnotationMetadata.introspect(beanClass) ;
// 獲取注解類(lèi)@Configuration信息
Map config = metadata.getAnnotationAttributes(Configuration.class.getName());
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
// 如果@Configuration中的proxyBeanMethods屬性為true,那么就將當(dāng)前配置類(lèi)對(duì)應(yīng)的BeanDefinition設(shè)置屬性
// 標(biāo)記為true,其實(shí)這里的目的就是要不要?jiǎng)?chuàng)建代理,如果為true創(chuàng)建代理
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
// 不創(chuàng)建代理
else if (config != null || isConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
return false;
}
}
} 上面對(duì)配置類(lèi)進(jìn)行了標(biāo)記要不要?jiǎng)?chuàng)建代理,下面就是創(chuàng)建代理了。
3.2 為配置類(lèi)生成代理
上面對(duì)配置類(lèi)要不要?jiǎng)?chuàng)建代理是通過(guò)BeanDefinition 設(shè)置屬性的方式來(lái)標(biāo)記,標(biāo)記完后會(huì)在postProcessBeanFactory中創(chuàng)建代理。
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// ...
// 增強(qiáng)配置類(lèi),創(chuàng)建代理
enhanceConfigurationClasses(beanFactory);
}
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
Map configBeanDefs = new LinkedHashMap<>();
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
// 獲取設(shè)置的標(biāo)記屬性,要么是full,要么是lite
Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
// 先保存到集合匯總
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
}
}
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
for (Map.Entry entry : configBeanDefs.entrySet()) {
AbstractBeanDefinition beanDef = entry.getValue();
// 確定配置類(lèi)的class
Class> configClass = beanDef.getBeanClass();
// 創(chuàng)建代理
Class> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
if (configClass != enhancedClass) {
beanDef.setBeanClass(enhancedClass);
}
}
}
} 接下來(lái)是核心通過(guò)ConfigurationClassEnhancer#enhance創(chuàng)建目標(biāo)配置類(lèi)的代理對(duì)象。
class ConfigurationClassEnhancer {
private static final Callback[] CALLBACKS = new Callback[] {
new BeanMethodInterceptor(),
new BeanFactoryAwareMethodInterceptor(),
NoOp.INSTANCE
}
public Class> enhance(Class> configClass, @Nullable ClassLoader classLoader) {
Class> enhancedClass = createClass(newEnhancer(configClass, classLoader));
return enhancedClass;
}
private Enhancer newEnhancer(Class> configSuperClass, @Nullable ClassLoader classLoader) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(configSuperClass);
// 設(shè)置生成的子類(lèi)要實(shí)現(xiàn)的接口,該接口實(shí)現(xiàn)了BeanFactoryAware,
// 所以容器在實(shí)例化初始化該代理對(duì)象的時(shí)候會(huì)自動(dòng)注入當(dāng)前容器的BeanFactory對(duì)象。
enhancer.setInterfaces(new Class>[] {EnhancedConfiguration.class});
enhancer.setUseFactory(false);
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
// 這里有個(gè)作用就是為當(dāng)前的代理bean添加BeanFactory類(lèi)型的字段。
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
enhancer.setCallbackFilter(CALLBACK_FILTER);
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
return enhancer;
}
private Class> createClass(Enhancer enhancer) {
Class> subclass = enhancer.createClass();
Enhancer.registerStaticCallbacks(subclass, CALLBACKS);
return subclass;
}
}上面的代碼就是通過(guò)CGLIB創(chuàng)建代理,這些不是我們關(guān)心的,我們主要關(guān)心的是它是如何攔截配置方法的。所以這里我們主要關(guān)注的上面createClass方法中設(shè)置的CALLBACKS。在這個(gè)數(shù)組中通過(guò)名稱(chēng)也就清楚核心攔截器是BeanMethodInterceptor。
private static class BeanMethodInterceptor implements MethodInterceptor {
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
MethodProxy cglibMethodProxy) throws Throwable {
ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
// 獲取當(dāng)前@Bean注解的方法名(也就是對(duì)應(yīng)的BeanName)
String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
// ...
// 從容器中查找對(duì)應(yīng)的bean(也就是你調(diào)用的那個(gè)方法創(chuàng)建的bean對(duì)象)
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}
private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,
ConfigurableBeanFactory beanFactory, String beanName) {
// 獲取bean實(shí)例
Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) :
beanFactory.getBean(beanName));
// ...
return beanInstance;
}
}以上就是@Configuration注解創(chuàng)建代理及方法調(diào)用時(shí)的執(zhí)行原理。
你學(xué)到了嗎?
新聞名稱(chēng):@Configuration注解天天用,你真的了解它嗎?
網(wǎng)站路徑:http://www.dlmjj.cn/article/djpijgo.html


咨詢(xún)
建站咨詢(xún)
