Spring中@Configuration注解修改的類(lèi)生成代理原因解析
前言
在Spring中只要被@Configuration
注解修飾的類(lèi),Spring就會(huì)為其生成代理對(duì)象,至于這樣做的主要原因就是為了解決生成對(duì)象的單例問(wèn)題。
說(shuō)明
實(shí)際上作者在ConfigurationClassEnhancer
這個(gè)類(lèi)也有注解說(shuō)明
場(chǎng)景
如果Spring不做處理,下面輸出的一定的是false
,但是實(shí)際上輸出的結(jié)果是true
,那么只有可能是代理類(lèi)做了特殊處理。
@Configuration public class MyConfiguration { @Bean public TestA a(){ return new TestA(); } @Bean public TestB b(){ TestA a = a(); TestA b = a(); System.out.println(a == b); return new TestB(); } }
處理分析
生成的代理對(duì)象,最終會(huì)調(diào)用ConfigurationClassEnhancer內(nèi)部類(lèi)BeanMethodInterceptor的intercept方法
,如果不是當(dāng)前調(diào)用的Bean對(duì)象(也就是isCurrentlyInvokedFactoryMethod(beanMethod)
返回false),則會(huì)調(diào)用resolveBeanReference
方法。
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs, MethodProxy cglibMethodProxy) throws Throwable { ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance); String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod); // Determine whether this bean is a 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. 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); } } 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); }
此方法會(huì)通過(guò)getBean
來(lái)獲取對(duì)象,這樣就可以控制對(duì)象的生成了。
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; } } } // 通過(guò)getBean就可以控制對(duì)象的生成,對(duì)象如果生成過(guò),則可以直接從一級(jí)緩存中獲取 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); } } }
總結(jié)
到此這篇關(guān)于Spring中@Configuration注解修改的類(lèi)生成代理原因解析的文章就介紹到這了,更多相關(guān)Spring @Configuration注解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot如何解決跨域后session獲取不到sessionId不一致
這篇文章主要介紹了springboot如何解決跨域后session獲取不到sessionId不一致問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01Java實(shí)現(xiàn)將Word轉(zhuǎn)換成Html的示例代碼
在業(yè)務(wù)中,常常會(huì)需要在瀏覽器中預(yù)覽Word文檔,或者需要將Word文檔轉(zhuǎn)成HTML文件保存,本文主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)Word轉(zhuǎn)換成Html的相關(guān)方法,希望對(duì)大家有所幫助2024-02-02Java數(shù)組轉(zhuǎn)換為L(zhǎng)ist的四種方式
這篇文章主要介紹了Java開(kāi)發(fā)技巧數(shù)組轉(zhuǎn)List的四種方式總結(jié),每種方式結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09springboot跨域過(guò)濾器fetch react Response to p
這篇文章主要介紹了springboot跨域過(guò)濾器fetch react Response to preflight request doesn‘t pass access control check問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03Java分支結(jié)構(gòu)程序設(shè)計(jì)實(shí)例詳解
這篇文章主要介紹了Java分支結(jié)構(gòu)程序設(shè)計(jì)例題,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-01-01Java Spring MVC獲取請(qǐng)求數(shù)據(jù)詳解操作
Spring MVC 是 Spring 提供的一個(gè)基于 MVC 設(shè)計(jì)模式的輕量級(jí) Web 開(kāi)發(fā)框架,本質(zhì)上相當(dāng)于 Servlet,Spring MVC 角色劃分清晰,分工明細(xì)。由于 Spring MVC 本身就是 Spring 框架的一部分,可以說(shuō)和 Spring 框架是無(wú)縫集成2021-11-11