Spring的@Scope注解詳細解析
@Scope是什么,有什么用?
@Scope注解主要作用是調(diào)節(jié)Ioc容器中的作用域,在Spring IoC容器中主要有以下五種作用域:
基本作用域:singleton(單例)、prototype(多例);Web 作用域(reqeust、session、globalsession),自定義作用域。
@Scope注解源碼解析
使用@Scope注解對象注入到IOC容器過程
(1) springboot 程序啟動時會對classpath路徑下的包中的類進行掃描,將類解析成BeanDefinition,在掃描完BeanDefinition后會對其進行注冊,便于后面創(chuàng)建Bean時使用
// org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan /** * Perform a scan within the specified base packages, * returning the registered bean definitions. * <p>This method does <i>not</i> register an annotation config processor * but rather leaves this up to the caller. * @param basePackages the packages to check for annotated classes * @return set of beans registered if any for tooling registration purposes (never {@code null}) */ protected Set<BeanDefinitionHolder> doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>(); for (String basePackage : basePackages) { Set<BeanDefinition> candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }
(2)接著會對掃描出來的BeanDefinition處理,其中下面代碼對掃描完的Bean的作用域的代理模式進行處理:
// org.springframework.context.annotation.AnnotationConfigUtils#applyScopedProxyMode static BeanDefinitionHolder applyScopedProxyMode( ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) { ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode(); if (scopedProxyMode.equals(ScopedProxyMode.NO)) { return definition; } boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS); return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass); }
上面代碼會判斷作用域的代碼模式,如果是ScopedProxyMode.NO,不進行代理處理直接返回BeanDefinition,如果不是NO,再判斷是否是ScopedProxyMode.TARGET_CLASS,在后續(xù)邏輯中會根據(jù)此來決定是否創(chuàng)建目標類的代理類。
// org.springframework.aop.scope.ScopedProxyUtils#createScopedProxy public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition, BeanDefinitionRegistry registry, boolean proxyTargetClass) { String originalBeanName = definition.getBeanName(); BeanDefinition targetDefinition = definition.getBeanDefinition(); String targetBeanName = getTargetBeanName(originalBeanName); // Create a scoped proxy definition for the original bean name, // "hiding" the target bean in an internal target definition. RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class); proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName)); proxyDefinition.setOriginatingBeanDefinition(targetDefinition); proxyDefinition.setSource(definition.getSource()); proxyDefinition.setRole(targetDefinition.getRole()); proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName); if (proxyTargetClass) { targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE); // ScopedProxyFactoryBean's "proxyTargetClass" default is TRUE, so we don't need to set it explicitly here. } else { proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE); } // Copy autowire settings from original bean definition. proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate()); proxyDefinition.setPrimary(targetDefinition.isPrimary()); if (targetDefinition instanceof AbstractBeanDefinition) { proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition); } // The target bean should be ignored in favor of the scoped proxy. targetDefinition.setAutowireCandidate(false); targetDefinition.setPrimary(false); // Register the target bean as separate bean in the factory. registry.registerBeanDefinition(targetBeanName, targetDefinition); // Return the scoped proxy definition as primary bean definition // (potentially an inner bean). return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases()); }
上面過程向BeanDefinitionRegistry中注入了兩個BeanDefinition分別是目標類、目標類的代理類(beanClass為ScopedProxyFactoryBean.class)。
最終返回BeanDefinitionHolder的beanName為目標類名,beanClass為ScopedProxyFactoryBean.class的BeanDenifition。
return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
其中ScopedProxyFactoryBean類實現(xiàn)了BeanFactoryAware,Aware接口由Spring在AbstractAutowireCapableBeanFactory.initializeBean(beanName, bean,mbd)方法中通過調(diào)用invokeAwareMethods(beanName, bean)方法和applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)觸發(fā)Aware方法的調(diào)用setBeanFactory方法,該方法中會創(chuàng)建一個目前類的代理類暫存在其proxy變量中。
// org.springframework.aop.scope.ScopedProxyFactoryBean#setBeanFactory @Override public void setBeanFactory(BeanFactory beanFactory) { if (!(beanFactory instanceof ConfigurableBeanFactory)) { throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory); } ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory; this.scopedTargetSource.setBeanFactory(beanFactory); ProxyFactory pf = new ProxyFactory(); pf.copyFrom(this); pf.setTargetSource(this.scopedTargetSource); Assert.notNull(this.targetBeanName, "Property 'targetBeanName' is required"); Class<?> beanType = beanFactory.getType(this.targetBeanName); if (beanType == null) { throw new IllegalStateException("Cannot create scoped proxy for bean '" + this.targetBeanName + "': Target type could not be determined at the time of proxy creation."); } if (!isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) { pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader())); } // Add an introduction that implements only the methods on ScopedObject. ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName()); pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject)); // Add the AopInfrastructureBean marker to indicate that the scoped proxy // itself is not subject to auto-proxying! Only its target bean is. pf.addInterface(AopInfrastructureBean.class); this.proxy = pf.getProxy(cbf.getBeanClassLoader()); }
在注入目標對象時,將會時用beanName從BeanDefinitionRegistry中取出beanclass為ScopedProxyFactoryBean.class的BeanDenifition,使用該BeanDenifition創(chuàng)建一個實例,創(chuàng)建完實例后會調(diào)用getObjectForBeanInstance方法來獲取beanInstance,如果時FactoryBean則會調(diào)用其getObject()方法。
// org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean // Create bean instance. if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. destroySingleton(beanName); throw ex; } }); beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }
// org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) { // Don't let calling code try to dereference the factory if the bean isn't a factory. if (BeanFactoryUtils.isFactoryDereference(name)) { if (beanInstance instanceof NullBean) { return beanInstance; } if (!(beanInstance instanceof FactoryBean)) { throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass()); } if (mbd != null) { mbd.isFactoryBean = true; } return beanInstance; } // Now we have the bean instance, which may be a normal bean or a FactoryBean. // If it's a FactoryBean, we use it to create a bean instance, unless the // caller actually wants a reference to the factory. if (!(beanInstance instanceof FactoryBean)) { return beanInstance; } Object object = null; if (mbd != null) { mbd.isFactoryBean = true; } else { object = getCachedObjectForFactoryBean(beanName); } if (object == null) { // Return bean instance from factory. FactoryBean<?> factory = (FactoryBean<?>) beanInstance; // Caches object obtained from FactoryBean if it is a singleton. if (mbd == null && containsBeanDefinition(beanName)) { mbd = getMergedLocalBeanDefinition(beanName); } boolean synthetic = (mbd != null && mbd.isSynthetic()); object = getObjectFromFactoryBean(factory, beanName, !synthetic); } return object; }
目標類會被解析成ScopedProxyFactoryBean,而ScopedProxyFactoryBean是FactoryBean,所以會調(diào)用ScopedProxyFactoryBean的getObject方法,而ScopedProxyFactoryBean的getObject方法返回的是其屬性proxy即創(chuàng)建的目標類的代理類,
@Override public Object getObject() { if (this.proxy == null) { throw new FactoryBeanNotInitializedException(); } return this.proxy; }
到此這篇關(guān)于Spring的@Scope注解詳細解析的文章就介紹到這了,更多相關(guān)@Scope注解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring @async方法如何添加注解實現(xiàn)異步調(diào)用
這篇文章主要介紹了Spring @async方法如何添加注解實現(xiàn)異步調(diào)用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-01-01java通過模擬post方式提交表單實現(xiàn)圖片上傳功能實例
這篇文章主要介紹了java通過模擬post方式提交表單實現(xiàn)圖片上傳功能實例,涉及Java針對表單的提交操作響應及文件傳輸?shù)南嚓P(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-11-11Java并發(fā)編程必備之Synchronized關(guān)鍵字深入解析
本文我們深入探索了Java中的Synchronized關(guān)鍵字,包括其互斥性和可重入性的特性,文章詳細介紹了Synchronized的三種使用方式:修飾代碼塊、修飾普通方法和修飾靜態(tài)方法,感興趣的朋友一起看看吧2025-04-04Java多線程之 FutureTask:帶有返回值的函數(shù)定義和調(diào)用方式
這篇文章主要介紹了Java多線程之 FutureTask:帶有返回值的函數(shù)定義和調(diào)用方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07Spring Boot結(jié)合IDEA自帶Maven插件如何快速切換profile
IDEA是目前 Java 開發(fā)者中使用最多的開發(fā)工具,它有著簡約的設計風格,強大的集成工具,便利的快捷鍵,這篇文章主要介紹了Spring Boot結(jié)合IDEA自帶Maven插件快速切換profile,需要的朋友可以參考下2023-03-03