詳解配置類為什么要添加@Configuration注解
不加@Configuration導(dǎo)致的問題
我們先來(lái)看看如果不在配置類上添加@Configuration注解會(huì)有什么問題,代碼示例如下:
@ComponentScan("com.dmz.source.code") //@Configuration public class Config{ @Bean public A a(){ return new A(dmzService()); } @Bean public DmzService dmzService(){ return new DmzService(); } } public class A { public A(DmzService dmzService){ System.out.println("create A by dmzService"); } } @Component public class DmzService { public DmzService(){ System.out.println("create dmzService"); } }
不添加@Configuration注解運(yùn)行結(jié)果:
create dmzService
create A by dmzService
create dmzService
添加@Configuration注解運(yùn)行結(jié)果:
create dmzService
create A by dmzService
在上面的例子中,我們會(huì)發(fā)現(xiàn)沒有添加@Configuraion注解時(shí)dmzService
被創(chuàng)建了兩次, 這是因?yàn)榈谝淮蝿?chuàng)建是被Spring容器所創(chuàng)建的,Spring調(diào)用這個(gè)dmzService()創(chuàng)建了一個(gè)Bean被放入了單例池中(沒有添加其它配置默認(rèn)是單例的),第二次創(chuàng)建是Spring容器在創(chuàng)建a時(shí)調(diào)用了a(),而a()又調(diào)用了dmzService()方法。
這樣的話,就出現(xiàn)問題了。
第一,對(duì)于dmzService而言,它被創(chuàng)建了兩次,單例被打破了
第二,對(duì)于a而言,它所依賴的dmzService不是Spring所管理的,而是直接調(diào)用的一個(gè)普通的java method創(chuàng)建的普通對(duì)象。這個(gè)對(duì)象不被Spring所管理意味著,首先它的域(Scope)定義失效了,其次它沒有經(jīng)過一個(gè)完整的生命周期,那么我們所定義所有的Bean的后置處理器都沒有作用到它身上,其中就包括了完成AOP的后置處理器,所以AOP也失效了。
上面的分析不能說服你的話,我們可以看看官方在@Bean上給出的這一段注釋
首先,Spring就在注釋中指出了,通常來(lái)說,BeanMethod一般都申明在一個(gè)被@Configuration注解標(biāo)注的類中,在這種情況下,BeanMethod可能直接引用了在同一個(gè)類中申明的beanMethod,就像本文給出的例子那樣,a()直接引用了dmzService(),我們重點(diǎn)再看看劃紅線的部分,通過調(diào)用另外一個(gè)beanMethod進(jìn)入的Bean的引用會(huì)被保證是遵從域定義以及AOP語(yǔ)義的,就像getBean所做的那樣。這是怎么實(shí)現(xiàn)的呢?在最后被紅線標(biāo)注的地方也有說明,是通過在運(yùn)行時(shí)期為沒有被@Configuration注解標(biāo)注的配置類生成一個(gè)CGLIB的子類。
源碼分析
Spring是在什么時(shí)候創(chuàng)建的代理呢?到目前為止我們應(yīng)該沒有落掉Spring整個(gè)啟動(dòng)流程的任何關(guān)鍵代碼,那么我們不妨帶著這個(gè)問題繼續(xù)往下看。目前來(lái)說我們已經(jīng)閱讀到了Spring執(zhí)行流程圖中的3-5
步,也就是org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors
方法,在之前的分析中我們已經(jīng)知道了,這個(gè)方法的主要作用就是執(zhí)行BeanFactoryPostProcessor中的方法,首先執(zhí)行的是BeanDefinitionRegistryPostProcessor(繼承了BeanFactoryPostProcessor)的postProcessBeanDefinitionRegistry
方法,然后執(zhí)行postProcessBeanFactory
方法。而到目前為止我們并沒有向容器中注冊(cè)bean工廠的后置處理器(BeanFactoryPostProcessor),這就意味著當(dāng)前容器中只有一個(gè)ConfigurationClassPostProcessor會(huì)被執(zhí)行,在前文中我們已經(jīng)分析過了它的postProcessBeanDefinitionRegistry
方法,緊接著我們就來(lái)看看它的postProcessBeanFactory
方法做了什么。其源碼如下:
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { int factoryId = System.identityHashCode(beanFactory); // 防止重復(fù)處理 if (this.factoriesPostProcessed.contains(factoryId)) { throw new IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + beanFactory); } this.factoriesPostProcessed.add(factoryId); // 在執(zhí)行postProcessBeanDefinitionRegistry方法的時(shí)就已經(jīng)將這個(gè)id添加到registriesPostProcessed集合中了 if (!this.registriesPostProcessed.contains(factoryId)) { processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory); } // 看起來(lái)這個(gè)方法就是完成了代理 enhanceConfigurationClasses(beanFactory); // 添加了一個(gè)后置處理器 beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory)); }
enhanceConfigurationClasses源碼分析
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) { // map中放置的是所有需要被代理的類 Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>(); for (String beanName : beanFactory.getBeanDefinitionNames()) { BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName); if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) { // 省略異常跟日志代碼.... // 這個(gè)代碼的含義就是如果是一個(gè)被@Configuration注解標(biāo)注的類,那么將其放入到configBeanDefs這個(gè)集合中 configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef); } } if (configBeanDefs.isEmpty()) { // nothing to enhance -> return immediately return; } // 對(duì)配置類進(jìn)行代理的核心類 ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer(); for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) { AbstractBeanDefinition beanDef = entry.getValue(); // 對(duì)于配置類永遠(yuǎn)使用cglib代理 beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE); try { // cglib代理是基于類實(shí)現(xiàn)的,所以在這之前要明確代理的類是什么 Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader); if (configClass != null) { // 通過ConfigurationClassEnhancer獲取到一個(gè)經(jīng)過代理的class Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader); // 省略日志.... // 將原有的配置類的bd中的beanClass屬性替換成代理后的class beanDef.setBeanClass(enhancedClass); } } } catch (Throwable ex) { throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex); } } }
這段代碼非常簡(jiǎn)單,核心的代碼在ConfigurationClassEnhancer
中,所以我們要分析下ConfigurationClassEnhancer
的源碼,在分析它的源碼前,我們需要對(duì)cglib有一定的了解。
1、cglib原理分析
1.1、使用示例
public class Target{ public void f(){ System.out.println("Target f()"); } public void g(){ System.out.println("Target g()"); } } public class Interceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("I am intercept begin"); //Note: 此處一定要使用proxy的invokeSuper方法來(lái)調(diào)用目標(biāo)類的方法 proxy.invokeSuper(obj, args); System.out.println("I am intercept end"); return null; } } public class Test { public static void main(String[] args) { // 設(shè)置這個(gè)屬性,將代理類的字節(jié)碼文件生成到F盤的code目錄下 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:\\code"); //實(shí)例化一個(gè)增強(qiáng)器,也就是cglib中的一個(gè)class generator Enhancer eh = new Enhancer(); //設(shè)置目標(biāo)類 eh.setSuperclass(Target.class); // 設(shè)置攔截對(duì)象 eh.setCallback(new Interceptor()); // 生成代理類并返回一個(gè)實(shí)例 Target t = (Target) eh.create(); t.f(); t.g(); } }
運(yùn)行結(jié)果為:
I am intercept begin
Target f()
I am intercept end
I am intercept begin
Target g()
I am intercept end
1.2、原理分析
查看F盤的code目錄,會(huì)發(fā)現(xiàn)多了以下幾個(gè)文件
其中第二個(gè)文件就是我們的代理類字節(jié)碼,將其直接用IDEA打開
// 省略多余的方法,我們就關(guān)注g方法 public class Target$$EnhancerByCGLIB$$788444a0 extends Target implements Factory { final void CGLIB$g$0() { super.g(); } // 經(jīng)過代理過的g方法 public final void g() { // 查看是否有攔截器存在 MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0; if (tmp4_1 == null) { CGLIB$BIND_CALLBACKS(this); tmp4_1 = this.CGLIB$CALLBACK_0; } // 如果有攔截器的存在的話,直接調(diào)用攔截器的方法 if (this.CGLIB$CALLBACK_0 != null) { tmp4_1.intercept(this, CGLIB$g$0$Method, CGLIB$emptyArgs, CGLIB$g$0$Proxy); } // 如果沒有攔截器,說明不需要代理,直接調(diào)用父類方法,也就是目標(biāo)類的方法 else{ super.g(); } } }
可以看到,代理類繼承了目標(biāo)類(Target),代理類為每個(gè)目標(biāo)類的方法生成兩個(gè)方法,例如針對(duì)目標(biāo)類中的每個(gè)非private方法,代理類會(huì)生成兩個(gè)方法,以g方法為例:一個(gè)是@Override的g方法,一個(gè)是CGLIB$g$0(CGLIB$g$0相當(dāng)于目標(biāo)類的g方法)。我們?cè)谑纠a中調(diào)用目標(biāo)類的方法t.g()時(shí),實(shí)際上調(diào)用的是代理類中的g()方法。
從這里就能看出,跟JDK動(dòng)態(tài)代理不同的是,cglib代理采用的是繼承的方式生成的代理對(duì)象。
在上面的例子中,我們實(shí)現(xiàn)了對(duì)cglib中方法的攔截,但是就目前而言我們沒有辦法選擇性的攔截目標(biāo)類中的某一個(gè)方法,假設(shè)現(xiàn)在我們只想攔截Target中的g方法而不攔截f方法有什么方法呢?我們看下面這個(gè)例子
public class Main { public static void main(String[] args) { System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:\\code"); //實(shí)例化一個(gè)增強(qiáng)器,也就是cglib中的一個(gè)class generator Enhancer eh = new Enhancer(); //設(shè)置目標(biāo)類 eh.setSuperclass(Target.class); // 設(shè)置攔截對(duì)象 eh.setCallbacks(new Callback[]{new Interceptor(), NoOp.INSTANCE}); eh.setCallbackFilter(new CallbackFilter() { @Override public int accept(Method method) { if(method.getName().equals("g")) // 這里返回的是上面定義的callback數(shù)組的下標(biāo),0就是我們的Interceptor對(duì)象,1是內(nèi)置的NoOp對(duì)象,代表不做任何操作 return 0; else return 1; } }); // 生成代理類并返回一個(gè)實(shí)例 Target t = (Target) eh.create(); t.f(); t.g(); } }
運(yùn)行結(jié)果:
Target f()
I am intercept begin
Target g()
I am intercept end
此時(shí)f方法已經(jīng)不會(huì)被代理了
2、ConfigurationClassEnhancer源碼分析
2.1、創(chuàng)建代理過程分析
在對(duì)cglib的原理有了一定了解后,我們?cè)賮?lái)看ConfigurationClassEnhancer
的源碼就輕松多了
我們就關(guān)注其中核心的幾個(gè)方法,代碼如下:
public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) { // 如果已經(jīng)實(shí)現(xiàn)了EnhancedConfiguration接口,說明被代理過了,直接返回 if (EnhancedConfiguration.class.isAssignableFrom(configClass)) { return configClass; } // 否則調(diào)用newEnhancer方法先創(chuàng)建一個(gè)增強(qiáng)器,然后直接使用這個(gè)增強(qiáng)器生成代理類的字節(jié)碼對(duì)象 Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader)); if (logger.isDebugEnabled()) { logger.debug(String.format("Successfully enhanced %s; enhanced class name is: %s", configClass.getName(), enhancedClass.getName())); } return enhancedClass; } private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) { Enhancer enhancer = new Enhancer(); // 設(shè)置目標(biāo)類 enhancer.setSuperclass(configSuperClass); // 讓代理類實(shí)現(xiàn)EnhancedConfiguration接口,這個(gè)接口繼承了BeanFactoryAware接口 // 主要兩個(gè)作用:1.起到標(biāo)記作用,如果實(shí)現(xiàn)了,代表已經(jīng)被代理過了 // 2.代理類需要訪問BeanFactory,所有實(shí)現(xiàn)了BeanFactoryAware接口 enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class}); // 設(shè)置生成的代理類不實(shí)現(xiàn)factory接口 enhancer.setUseFactory(false); // 設(shè)置代理類名稱的生成策略 enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); // 代理類中引入一個(gè)BeanFactory字段 enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader)); // 設(shè)置過濾器,CALLBACK_FILTER中也同時(shí)設(shè)置了攔截器 enhancer.setCallbackFilter(CALLBACK_FILTER); enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes()); return enhancer; } // 使用增強(qiáng)器生成代理類的字節(jié)碼對(duì)象 private Class<?> createClass(Enhancer enhancer) { Class<?> subclass = enhancer.createClass(); Enhancer.registerStaticCallbacks(subclass, CALLBACKS); return subclass; }
并且我們會(huì)發(fā)現(xiàn),在最開始這個(gè)類就申明了三個(gè)攔截器
// 聲明的三個(gè)攔截器 private static final Callback[] CALLBACKS = new Callback[] { new BeanMethodInterceptor(), new BeanFactoryAwareMethodInterceptor(), NoOp.INSTANCE };
2.2、攔截器源碼分析
基于我們之前對(duì)cglib的學(xué)習(xí),肯定能知道,代理的核心邏輯就是依賴于攔截器實(shí)現(xiàn)的。其中NoOp.INSTANCE
代表什么都沒做,我們就關(guān)注前面兩個(gè)。
BeanFactoryAwareMethodInterceptor
之所以把這個(gè)攔截器放到前面分析是因?yàn)檫@個(gè)攔截器的執(zhí)行時(shí)機(jī)是在創(chuàng)建配置類的時(shí)候,其源碼如下:
private static class BeanFactoryAwareMethodInterceptor implements MethodInterceptor, ConditionalCallback { @Override @Nullable public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 在生成代理類的字節(jié)碼時(shí),使用了BeanFactoryAwareGeneratorStrategy策略 // 這個(gè)策略會(huì)在代理類中添加一個(gè)字段,BEAN_FACTORY_FIELD = "$$beanFactory" Field field = ReflectionUtils.findField(obj.getClass(), BEAN_FACTORY_FIELD); Assert.state(field != null, "Unable to find generated BeanFactory field"); // 此時(shí)調(diào)用的方法是setBeanFactory方法, // 直接通過反射將beanFactory賦值給BEAN_FACTORY_FIELD字段 field.set(obj, args[0]); // Does the actual (non-CGLIB) superclass implement BeanFactoryAware? // If so, call its setBeanFactory() method. If not, just exit. // 如果目標(biāo)配置類直接實(shí)現(xiàn)了BeanFactoryAware接口,那么直接調(diào)用目標(biāo)類的setBeanFactory方法 if (BeanFactoryAware.class.isAssignableFrom(ClassUtils.getUserClass(obj.getClass().getSuperclass()))) { return proxy.invokeSuper(obj, args); } return null; } @Override // 在調(diào)用setBeanFactory方法時(shí)才會(huì)攔截 // 從前文我們知道,代理類是實(shí)現(xiàn)了實(shí)現(xiàn)EnhancedConfiguration接口的, // 這就意味著它也實(shí)現(xiàn)了BeanFactoryAware接口,那么在創(chuàng)建配置類時(shí), // setBeanFactory方法就會(huì)被調(diào)用,之后會(huì)就進(jìn)入到這個(gè)攔截器的intercept方法邏輯中 public boolean isMatch(Method candidateMethod) { 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())); } }
BeanMethodInterceptor
相比于上面一個(gè)攔截器,這個(gè)攔截器的邏輯就要復(fù)雜多了,我們先來(lái)看看它的執(zhí)行時(shí)機(jī),也就是isMatch方法
public boolean isMatch(Method candidateMethod) { // 第一個(gè)條件,不能是Object,這個(gè)必定是滿足的 // 第二個(gè)條件,不能是setBeanFactory方法,顯而易見的嘛,我們要攔截的方法實(shí)際只應(yīng)該是添加了@Bean注解的方法 // 第三個(gè)條件,添加了@Bean注解 return (candidateMethod.getDeclaringClass() != Object.class && !BeanFactoryAwareMethodInterceptor.isSetBeanFactory(candidateMethod) && BeanAnnotationHelper.isBeanAnnotated(candidateMethod)); }
簡(jiǎn)而言之,就是攔截@Bean標(biāo)注的方法,知道了執(zhí)行時(shí)機(jī)后,我們?cè)賮?lái)看看它的攔截邏輯,代碼其實(shí)不是很長(zhǎng),但是理解起來(lái)確很不容易,牽涉到AOP以及Bean的創(chuàng)建了,不過放心,我會(huì)結(jié)合實(shí)例給你講明白這段代碼,下面我們先看源碼:
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs, MethodProxy cglibMethodProxy) throws Throwable { // 之前不是給BEAN_FACTORY_FIELD這個(gè)字段賦值了BeanFactory嗎,這里就是反射獲取之前賦的值 ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance); // 確定Bean的名稱 String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod); // Determine whether this bean is a scoped-proxy // 判斷這個(gè)Bean是否是一個(gè)域代理的類 Scope scope = AnnotatedElementUtils.findMergedAnnotation(beanMethod, Scope.class); // 存在@Scope注解,并且開啟了域代理模式 if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) { String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName); // 域代理對(duì)象的目標(biāo)對(duì)象正在被創(chuàng)建,什么時(shí)候會(huì)被創(chuàng)建?當(dāng)然是使用的時(shí)候嘛 if (beanFactory.isCurrentlyInCreation(scopedBeanName)) { // 使用的時(shí)候調(diào)用@Bean方法來(lái)創(chuàng)建這個(gè)域代理的目標(biāo)對(duì)象,所以@Bean方法代理的時(shí)候針對(duì)的是域代理的目標(biāo)對(duì)象,目標(biāo)對(duì)象需要通過getBean的方式創(chuàng)建 beanName = scopedBeanName; } } // 判斷這個(gè)bean是否是一個(gè)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) { // ScopedProxyFactoryBean還記得嗎?在進(jìn)行域代理時(shí)使用的就是這個(gè)對(duì)象 // 對(duì)于這個(gè)FactoryBean我們是不需要進(jìn)行代理的,因?yàn)檫@個(gè)factoryBean的getObject方法 // 只是為了得到一個(gè)類似于占位符的Bean,這個(gè)Bean只是為了讓依賴它的Bean在創(chuàng)建的過程中不會(huì)報(bào)錯(cuò) // 所以對(duì)于這個(gè)FactoryBean我們是不需要進(jìn)行代理的 // 我們只需要保證這個(gè)FactoryBean所生成的代理對(duì)象的目標(biāo)對(duì)象是通過getBean的方式創(chuàng)建的即可 } else { // 而對(duì)于普通的FactoryBean我們需要代理其getObject方法,確保getObject方法產(chǎn)生的Bean是通過getBean的方式創(chuàng)建的 // It is a candidate FactoryBean - go ahead with enhancement return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName); } } // 舉個(gè)例子,假設(shè)我們被@Bean標(biāo)注的是A方法,當(dāng)前創(chuàng)建的BeanName也是a,這樣就符合了這個(gè)條件 // 但是如果是這種請(qǐng)求,a(){b()},a方法中調(diào)用的b方法,那么此時(shí)調(diào)用b方法創(chuàng)建b對(duì)象時(shí)正在執(zhí)行的就是a方法 // 此時(shí)就不滿足這個(gè)條件,會(huì)調(diào)用這個(gè)resolveBeanReference方法來(lái)解決方法引用 if (isCurrentlyInvokedFactoryMethod(beanMethod)) { // 如果當(dāng)前執(zhí)行的方法就是這個(gè)被攔截的方法,(說明是在創(chuàng)建這個(gè)Bean的過程中) // 那么直接執(zhí)行目標(biāo)類中的方法,也就是我們?cè)谂渲妙愔杏聾Bean標(biāo)注的方法 return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs); } // 說明不是在創(chuàng)建中了,而是別的地方直接調(diào)用了這個(gè)方法,這時(shí)候就需要代理了,實(shí)際調(diào)用getBean方法 return 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. // 什么時(shí)候會(huì)是alreadyInCreation?就是正在創(chuàng)建中,當(dāng)Spring完成掃描后得到了所有的BeanDefinition // 那么之后就會(huì)遍歷所有的BeanDefinition,根據(jù)BeanDefinition一個(gè)個(gè)的創(chuàng)建Bean,在創(chuàng)建Bean前會(huì)將這個(gè)Bean // 標(biāo)記為正在創(chuàng)建的,如果是正在創(chuàng)建的Bean,先將其標(biāo)記為非正在創(chuàng)建,也就是這行代碼beanFactory.setCurrentlyInCreation(beanName, false) // 這是因?yàn)橹笥謺?huì)調(diào)用getBean方法,如果已經(jīng)被標(biāo)記為創(chuàng)建中了,那么在調(diào)用getBean時(shí)會(huì)報(bào)錯(cuò) boolean alreadyInCreation = beanFactory.isCurrentlyInCreation(beanName); try { // 如果是正在創(chuàng)建的Bean,先將其標(biāo)記為非正在創(chuàng)建,避免后續(xù)調(diào)用getBean時(shí)報(bào)錯(cuò) if (alreadyInCreation) { beanFactory.setCurrentlyInCreation(beanName, false); } // 在調(diào)用beanMthod的時(shí)候,也就是被@Bean注解標(biāo)注的方法的時(shí)候如果使用了參數(shù),只要有一個(gè)參數(shù)為null,就直接調(diào)用getBean(beanName),否則帶參數(shù)調(diào)用getBean(beanName,args),后面通過例子解釋這段代碼 boolean useArgs = !ObjectUtils.isEmpty(beanMethodArgs); if (useArgs && beanFactory.isSingleton(beanName)) { for (Object arg : beanMethodArgs) { if (arg == null) { useArgs = false; break; } } } Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) : beanFactory.getBean(beanName)); // 這里發(fā)現(xiàn)getBean返回的類型不是我們方法返回的類型,這意味著什么呢? // 在《你知道Spring是怎么解析配置類的嗎?》我有提到過BeanDefinition的覆蓋 // 這個(gè)地方說明beanMethod所定義的bd被覆蓋了 if (!ClassUtils.isAssignableValue(beanMethod.getReturnType(), beanInstance)) { if (beanInstance.equals(null)) { beanInstance = null; } else { // 省略日志 throw new IllegalStateException(msg); } } // 注冊(cè)Bean之間的依賴關(guān)系 // 這個(gè)method是當(dāng)前執(zhí)行的一個(gè)創(chuàng)建bean的方法 Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod(); // 不等于null意味著currentlyInvoked這個(gè)方法創(chuàng)建的bean依賴了beanName所代表的Bean // 在開頭的例子中,currentlyInvoked就是a(),beanName就是dmzService,outBeanName就是a if (currentlyInvoked != null) { String outerBeanName = BeanAnnotationHelper.determineBeanNafanhr(currentlyInvoked); // 注冊(cè)的就是a跟dmzService的依賴關(guān)系,注冊(cè)到容器中的dependentBeanMap中 // key為依賴,value為依賴所在的bean beanFactory.registerDependentBean(beanName, outerBeanName); } return beanInstance; } finally { if (alreadyInCreation) { // 實(shí)際還在創(chuàng)建中,要走完整個(gè)生命周期流程 beanFactory.setCurrentlyInCreation(beanName, true); } } }
3、結(jié)合例子講解難點(diǎn)代碼
這部分內(nèi)容非常細(xì)節(jié),不感興趣可以跳過,主要是BeanMethodInterceptor中的方法。
3.1、判斷這個(gè)Bean是否是一個(gè)域代理的類示例代碼
@Configuration @EnableAspectJAutoProxy public class Config { @Bean @Scope(value = WebApplicationContext.SCOPE_REQUEST,proxyMode = ScopedProxyMode.TARGET_CLASS) public DmzService dmzService() { return new DmzService(); } } @RestController @RequestMapping("/test") public class Controller { DmzService dmzService; @Autowired public void setDmzService(DmzService dmzService) { this.dmzService = dmzService; } @GetMapping("/get") public ResponseEntity<?> get() { System.out.println(dmzService); return ResponseEntity.ok().build(); } }
我們需要調(diào)試兩種情況
創(chuàng)建Controller時(shí),注入dmzService,因?yàn)閐mzService是一個(gè)request域的對(duì)象,正常情況下注入肯定是報(bào)錯(cuò)的,但是我們?cè)谂渲妙惿蠈?duì)域?qū)ο箝_啟了代理模式,所以在創(chuàng)建Controller時(shí)會(huì)注入一個(gè)代理對(duì)象。
端點(diǎn)調(diào)試,也確實(shí)如我們所料,這個(gè)地方注入的確實(shí)是一個(gè)代理對(duì)象,因?yàn)槲覀冊(cè)谂渲妙惿仙昝髁?code>proxyMode = ScopedProxyMode.TARGET_CLASS,所以這里是一個(gè)cglib的代理對(duì)象。
使用dmzService的時(shí)候,這個(gè)時(shí)候使用的應(yīng)該是實(shí)際的目標(biāo)對(duì)象。所以按照我們的分析應(yīng)該通過getBean(targetBeanName)的方式來(lái)獲取到這個(gè)Bean,執(zhí)行流程應(yīng)該是代理對(duì)象cglibDmzService調(diào)用了toString方法,然后調(diào)用getBean,getBean要根據(jù)BeanDefinition創(chuàng)建Bean,而根據(jù)BeanDefinition的定義,需要使用配置類中的BeanMethod來(lái)創(chuàng)建Bean,所以此時(shí)會(huì)進(jìn)入到BeanMethodInterceptor的intecept方法。
我們直接在intecept方法中進(jìn)行斷點(diǎn),會(huì)發(fā)現(xiàn)此時(shí)的調(diào)用棧如下
- 打印時(shí),調(diào)用了toString方法
- 實(shí)際將會(huì)去創(chuàng)建目標(biāo)Bean,所以此時(shí)getBean時(shí)對(duì)應(yīng)的BeanName為targetBeanName(“scopedTarget.”+beanName)
- 在getBean時(shí)根據(jù)BeanDefinition的定義會(huì)通過執(zhí)行配置類中的beanMethod方法來(lái)創(chuàng)建Bean
- 最終就進(jìn)入了攔截器中這個(gè)方法
這種情況下就會(huì)進(jìn)入到下面這段代碼的邏輯中
// 判斷這個(gè)Bean是否是一個(gè)域代理的類 Scope scope = AnnotatedElementUtils.findMergedAnnotation(beanMethod, Scope.class); // 存在@Scope注解,并且開啟了域代理模式 if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) { String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName); // 域代理對(duì)象的目標(biāo)對(duì)象正在被創(chuàng)建,什么時(shí)候會(huì)被創(chuàng)建?當(dāng)然是使用的時(shí)候嘛 if (beanFactory.isCurrentlyInCreation(scopedBeanName)) { // 使用的時(shí)候調(diào)用@Bean方法來(lái)創(chuàng)建這個(gè)域代理的目標(biāo)對(duì)象,所以@Bean方法代理的時(shí)候針對(duì)的是域代理的目標(biāo)對(duì)象 beanName = scopedBeanName; } }
3.3、方法引用的情況下,為什么會(huì)出現(xiàn)Bean正在創(chuàng)建中(isCurrentlyInCreation)?
也就是下面這段代碼什么時(shí)候會(huì)成立
if (alreadyInCreation) { beanFactory.setCurrentlyInCreation(beanName, false); }
示例代碼
@ComponentScan(value = "com.dmz.spring.first") @Configuration public class Config { @Bean public A a(){ return new A(); } @Bean public B b(){ a(); return new B(); } } class A{ B b; @Autowired public void setB(B b) { this.b = b; } } class B{ }
上面這種配置,在啟動(dòng)的時(shí)候就會(huì)進(jìn)入到if條件中,在創(chuàng)建a的時(shí)候發(fā)現(xiàn)需要注入b,那么Spring此時(shí)就會(huì)去創(chuàng)建b,b在創(chuàng)建的過程中又調(diào)用了a方法,此時(shí)a方法在執(zhí)行時(shí)又被攔截了,然后就會(huì)進(jìn)入到if判斷中去。對(duì)Spring有一定了解的同學(xué)應(yīng)該能感覺到,這個(gè)其實(shí)跟循環(huán)依賴的原理是一樣的。關(guān)于循環(huán)依賴,在后面我單獨(dú)寫一篇文章進(jìn)行說明。
3.4、if (arg == null) {useArgs = false;}是什么意思?
這個(gè)代碼我初看時(shí)也很不明白,為什么只要有一個(gè)參數(shù)為null就直接標(biāo)記成不使用參數(shù)呢?我說說自己的理解。
beanMethodArgs代表了調(diào)用beanMethod時(shí)傳入的參數(shù),正常Spring自身是不會(huì)傳入這個(gè)參數(shù)的,因?yàn)闆]有必要,創(chuàng)建Bean時(shí)其依賴早就通過BeanDefinition確定了,但是可能出現(xiàn)下面這種情況
示例代碼
@Configuration public class AnotherConfig { @Bean public DmzService dmzService(IndexService indexService) { return new DmzService(indexService); } @Bean public OrderService orderService() { DmzService dmzService = dmzService(null); return dmzService.createOrder(); } } @Component public class IndexService { } public class DmzService { public DmzService(IndexService indexService) { } public OrderService createOrder() { return new OrderService(); } } public class OrderService { }
這種情況下,我們?cè)趏rderService()為了得到當(dāng)前容器中的dmzService調(diào)用了對(duì)應(yīng)的BeanMethod,但是按照方法的定義我們不得不傳入一個(gè)參數(shù),但是實(shí)際上我們知道BeanMethod等價(jià)于getBean,所以上面這段代碼可以等價(jià)于
@Configuration public class AnotherConfig { @Autowired ApplicationContext applicationContext; @Bean public DmzService dmzService(IndexService indexService) { return new DmzService(indexService); } @Bean public OrderService orderService() { DmzService dmzService = (DmzService) applicationContext.getBean("dmzService"); return dmzService.createOrder(); } }
對(duì)于getBean而言,傳入?yún)?shù)跟不傳參數(shù)在創(chuàng)建Bean時(shí)是有區(qū)別的,但是創(chuàng)建后從容器中獲取Bean時(shí)跟傳入的參數(shù)沒有一毛錢關(guān)系(單例情況),因?yàn)檫@是從緩存中獲取嘛。也就是說單例下,傳入的參數(shù)只會(huì)影響第一次創(chuàng)建。正因?yàn)槿绱耍琯etBean在單純的做獲取的時(shí)候不需要參數(shù),那就意味著beanMthod在獲取Bean的時(shí)候也可以不傳入?yún)?shù)嘛,但是beanMthod作為一個(gè)方法又定義了形參,Spring就說,這種情況你就傳個(gè)null吧,反正我知道要去getBean,當(dāng)然,這只是筆者的個(gè)人理解。
4、結(jié)合Spring整體對(duì)ConfigurationClassEnhancer相關(guān)源碼分析總結(jié)
4.1、Bean工廠后置處理器修改bd,對(duì)應(yīng)enhance方法執(zhí)行流程
修改bd的整個(gè)過程都發(fā)生在Bean工廠后置處理器的執(zhí)行邏輯中
執(zhí)行邏輯
在上文中我們已經(jīng)知道了,在執(zhí)行bean工廠后置處理器前,Spring容器的狀態(tài)如下:
那么執(zhí)行完成Bean工廠后置處理器后(不考慮程序員自定義的后置處理器),容器的狀態(tài)應(yīng)該是這樣的
4.2、BeanFactoryAwareMethodInterceptor執(zhí)行流程
在容器中的bd就緒后,Spring會(huì)通過bd來(lái)創(chuàng)建Bean了,會(huì)先創(chuàng)建配置類,然后創(chuàng)建配置類中beanMethod定義的bean。在創(chuàng)建配置類的過程中在初始化Bean時(shí),如果實(shí)現(xiàn)了Aware接口,會(huì)調(diào)用對(duì)于的setXxx方法,具體代碼位于org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean
在調(diào)用setBeanFactory
方法時(shí),會(huì)被攔截,進(jìn)入到攔截器的邏輯中
執(zhí)行邏輯
4.3、BeanMethodInterceptor執(zhí)行流程
以下面這段代碼為例:
@Configuration public class AnotherConfig { @Bean public DmzService dmzService(){ return new DmzService(); } @Bean public OrderService orderService(){ return new OrderService(dmzService()); } }
Spring會(huì)根據(jù)beanMethod
在配置類中定義順序來(lái)創(chuàng)建Bean,所以上面這段配置會(huì)先創(chuàng)建dmzServcice
,之后在創(chuàng)建orderService
。
那么BeanMethodInterceptor
的攔截將會(huì)發(fā)生在兩個(gè)地方
- 直接創(chuàng)建
dmzService
的過程中,攔截的是dmzService()
方法 - 創(chuàng)建
orderService
過程中,第一次攔截的是orderService()
方法 orderService()
方法調(diào)用了dmzService()
方法,dmzService()
方法又被攔截
在直接創(chuàng)建dmzService
時(shí),由于isCurrentlyInvokedFactoryMethod(beanMethod)
這句代碼會(huì)成立,所以會(huì)直接調(diào)用目標(biāo)類的方法,也就是cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs)
,就是我們?cè)谂渲妙愔卸x的dmzService()
方法,通過這個(gè)方法返回一個(gè)dmzService
而創(chuàng)建orderService
時(shí),方法的調(diào)用就略顯復(fù)雜,首先它類似于上面的直接創(chuàng)建dmzService
的流程,orderService()
方法會(huì)被攔截,但是由于正在執(zhí)行的方法就是orderService()
方法,所以orderService()
也會(huì)被直接調(diào)用。但是orderService()
中又調(diào)用了dmzService()
方法,dmzService()
方法又被攔截了,此時(shí)orderService()
還沒被執(zhí)行完成,也就是說正在執(zhí)行的方法是orderService()
方法,所以isCurrentlyInvokedFactoryMethod(beanMethod)
這句代碼就不成立了,那么就會(huì)進(jìn)入org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#resolveBeanReference
這個(gè)方法的邏輯中,在這個(gè)方法中,最終又通過getBean
方法來(lái)獲取dmzService
,因?yàn)?code>dmzService之前已經(jīng)被創(chuàng)建過了,所以在單例模式下,就直接從單例池中返回了,而不會(huì)再次調(diào)用我們?cè)谂渲妙愔卸x的dmzService()
方法。
執(zhí)行邏輯
總結(jié)
這里就在上篇文章的基礎(chǔ)上對(duì)流程圖再做一次完善吧,因?yàn)閳D片太大了,就放個(gè)鏈接~
Spring創(chuàng)建bean前的執(zhí)行流程
到此這篇關(guān)于詳解配置類為什么要添加@Configuration注解 的文章就介紹到這了,更多相關(guān)配置類添加@Configuration注解 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
實(shí)現(xiàn)quartz定時(shí)器及quartz定時(shí)器原理介紹
Quartz是一個(gè)大名鼎鼎的Java版開源定時(shí)調(diào)度器,功能強(qiáng)悍,使用方便,下面我們看看如何使用它2013-12-12Java服務(wù)如何調(diào)用系統(tǒng)指令、Bat腳本記錄
這篇文章主要介紹了Java服務(wù)如何調(diào)用系統(tǒng)指令、Bat腳本記錄,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06解析Java的Jackson庫(kù)中Streaming API的使用
這篇文章主要介紹了解析Java的Jackson庫(kù)中Streaming API的使用,Jackson被用于Java對(duì)象和JSON的互相轉(zhuǎn)換,需要的朋友可以參考下2016-01-01Spring Boot實(shí)戰(zhàn)之發(fā)送郵件示例代碼
本篇文章主要介紹了Spring Boot實(shí)戰(zhàn)之發(fā)送郵件示例代碼,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-03-03