Spring Bean的作用域scope你知道多少?如何自定義作用域?
環(huán)境:spring5.3.3
1 Scope作用
通過(guò)@Scope注解可以指定Bean的作用域,默認(rèn)情況都是單例的(ConfigurableBeanFactory.SCOPE_SINGLETON=singleton)
在創(chuàng)建bean實(shí)例時(shí)就是根據(jù)當(dāng)前定義BeanDefinition中的Scope來(lái)做不同的創(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 code
- try {
- 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)建實(shí)例
- // 判斷如果是單例
- if (mbd.isSingleton()) {
- // 如果是單例Bean會(huì)將Bean保存到緩存中singletonObjects
- sharedInstance = 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)集合中也不存在時(shí)拋出異常
- 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 code
- return (T) bean;
- }
從上面源碼看到分別判斷是了 是否是 Singleton及Proptotype,如果都不是則會(huì)從Map<String, Scope> scopes中獲取。如果當(dāng)前你配置的@Scope不是singleton及prototype那么從scopes集合中?。ㄟ@個(gè)集合是通過(guò)AbstractBeanFactory#registerScope方法進(jìn)行注冊(cè)的,一般我們可以通過(guò)
BeanDefinitionRegistryPostProcessor進(jìn)行注冊(cè)),如果集合中也不存在那么就會(huì)拋出異常。如果存在就會(huì)執(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 ;
- @Override
- public Object get(String name, ObjectFactory<?> objectFactory) {
- return target != null ? target : objectFactory.getObject() ;
- }
- // 如果調(diào)用了這個(gè)方法,那么下次在注入有@Scope("custom")的bean時(shí) 將會(huì)重寫(xiě)調(diào)用objectFactory.getObject()方法。
- @Override
- public Object remove(String name) {
- target = null ;
- return "success" ;
- }
- @Override
- public void registerDestructionCallback(String name, Runnable callback) {
- }
- @Override
- public Object resolveContextualObject(String key) {
- return null;
- }
- @Override
- public String getConversationId() {
- return null;
- }
- }
注冊(cè)Scope
- @Component
- public class CustomScopeRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
- @Override
- public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
- beanFactory.registerScope("custom", new CustomScope()) ;
- }
- @Override
- public 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{
- @Resource
- private ApplyScopeBean scopeBean ;
- @Resource
- private CustomScope customScope ;
- @GetMapping("/custom")
- public String custom() {
- return scopeBean.getCustom() ;
- }
- @GetMapping("/remove")
- public Object remove() {
- return customScope.remove("applyScopeBean") ;
- }
- }
這里將Controller設(shè)置為多例,以便查看效果。交替執(zhí)行上面的接口,只要?jiǎng)h除了就會(huì)創(chuàng)建新的實(shí)例。
3 多例注入
如果一個(gè)Bean 設(shè)置了@Scope(value =
ConfigurableBeanFactory.SCOPE_PROTOTYPE) 當(dāng)這個(gè)Bean需要在一個(gè)單例Bean中被注入時(shí),需要如下配置才可
- @Component
- @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
- public class ApplyScopeBean {
- }
這樣才能正確地注入Bean,否則因?yàn)楸旧硎褂谜呤菃卫?,屬性只?huì)被初始化一次。也可以在每次使用前調(diào)用BeanFactory#getBean()。