1 Scope作用
通過@Scope注解可以指定Bean的作用域,默認情況都是單例的(
ConfigurableBeanFactory.SCOPE_SINGLETON=singleton)
在創(chuàng)建bean實例時就是根據(jù)當(dāng)前定義BeanDefinition中的Scope來做不同的創(chuàng)建,源碼如下:
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)throws BeansException {String beanName = transformedBeanName(name);Object bean;// Eagerly check singleton cache for manually registered singletons.Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) {// other code} else {// other codetry {RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);checkMergedBeanDefinition(mbd, beanName, args);// Guarantee initialization of beans that the current bean depends on.// other code// Create bean instance.// 根據(jù)BeanDefinition中定義的Scope創(chuàng)建實例// 判斷如果是單例if (mbd.isSingleton()) {// 如果是單例Bean會將Bean保存到緩存中singletonObjectssharedInstance = getSingleton(beanName, () -> {try {return createBean(beanName, mbd, args);} catch (BeansException ex) {destroySingleton(beanName);throw ex;}});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}// 判斷如果是原型(多例)else if (mbd.isPrototype()) {// It's a prototype -> create a new instance.Object prototypeInstance = null;try {beforePrototypeCreation(beanName);prototypeInstance = createBean(beanName, mbd, args);} finally {afterPrototypeCreation(beanName);}bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);}else {String scopeName = mbd.getScope();if (!StringUtils.hasLength(scopeName)) {throw new IllegalStateException("No scope name defined for bean 麓" + beanName + "'");}Scope scope = this.scopes.get(scopeName);// 當(dāng)集合中也不存在時拋出異常if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");}try {Object scopedInstance = scope.get(beanName, () -> {beforePrototypeCreation(beanName);try {return createBean(beanName, mbd, args);} finally {afterPrototypeCreation(beanName);}});bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);} catch (IllegalStateException ex) {throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider " + "defining a scoped proxy for this bean if you intend to refer to it from a singleton", ex);}}} catch (BeansException ex) {cleanupAfterBeanCreationFAIlure(beanName);throw ex;}}// other codereturn (T) bean;}
從上面源碼看到分別判斷是了 是否是 Singleton及Proptotype,如果都不是則會從Map<String, Scope> scopes中獲取。如果當(dāng)前你配置的@Scope不是singleton及prototype那么從scopes集合中取(這個集合是通過AbstractBeanFactory#registerScope方法進行注冊的,一般我們可以通過
BeanDefinitionRegistryPostProcessor進行注冊),如果集合中也不存在那么就會拋出異常。如果存在就會執(zhí)行Scope#get方法
Scope scope = this.scopes.get(scopeName);Object scopedInstance = scope.get(beanName, () -> {beforePrototypeCreation(beanName);try {return createBean(beanName, mbd, args);} finally {afterPrototypeCreation(beanName);}});
2 自定義Scope
自定義Scope
public class CustomScope implements Scope {private Object target ;@Overridepublic Object get(String name, ObjectFactory<?> objectFactory) {return target != null ? target : objectFactory.getObject() ;}// 如果調(diào)用了這個方法,那么下次在注入有@Scope("custom")的bean時 將會重寫調(diào)用objectFactory.getObject()方法。@Overridepublic Object remove(String name) {target = null ;return "success" ;}@Overridepublic void registerDestructionCallback(String name, Runnable callback) {}@Overridepublic Object resolveContextualObject(String key) {return null;}@Overridepublic String getConversationId() {return null;}}
注冊Scope
@Componentpublic class CustomScopeRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {beanFactory.registerScope("custom", new CustomScope()) ;}@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {}}
使用Scope
@Component@Scope("custom")public class ApplyScopeBean {}
示例
@RestController@RequestMapping("/refresh")@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)public class RefreshController implements ApplicationContextAware{@Resourceprivate ApplyScopeBean scopeBean ;@Resourceprivate CustomScope customScope ;@GetMapping("/custom")public String custom() {return scopeBean.getCustom() ;}@GetMapping("/remove")public Object remove() {return customScope.remove("applyScopeBean") ;}}
這里將Controller設(shè)置為多例,以便查看效果。交替執(zhí)行上面的接口,只要刪除了就會創(chuàng)建新的實例。
3 多例注入
如果一個Bean 設(shè)置了@Scope(value =
ConfigurableBeanFactory.SCOPE_PROTOTYPE) 當(dāng)這個Bean需要在一個單例Bean中被注入時,需要如下配置才可@Component@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)public class ApplyScopeBean {}
這樣才能正確地注入Bean,否則因為本身使用者是單例的,屬性只會被初始化一次。也可以在每次使用前調(diào)用BeanFactory#getBean()。
完畢!!!






