zl程序教程

您现在的位置是:首页 >  Javascript

当前栏目

Spring Bean的作用域scope你知道多少?如何自定义作用域?

2023-03-09 22:05:20 时间

环境:spring5.3.3

1 Scope作用

通过@Scope注解可以指定Bean的作用域,默认情况都是单例的(ConfigurableBeanFactory.SCOPE_SINGLETON=singleton)

在创建bean实例时就是根据当前定义BeanDefinition中的Scope来做不同的创建,源码如下:

  1. protected <T> T doGetBean( 
  2.             String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) 
  3.             throws BeansException { 
  4.   String beanName = transformedBeanName(name); 
  5.   Object bean; 
  6.   // Eagerly check singleton cache for manually registered singletons. 
  7.   Object sharedInstance = getSingleton(beanName); 
  8.   if (sharedInstance != null && args == null) { 
  9.     // other code 
  10.   } else { 
  11.     // other code 
  12.     try { 
  13.       RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); 
  14.       checkMergedBeanDefinition(mbd, beanName, args); 
  15.  
  16.       // Guarantee initialization of beans that the current bean depends on
  17.       // other code 
  18.       // Create bean instance. 
  19.       // 根据BeanDefinition中定义的Scope创建实例 
  20.       // 判断如果是单例 
  21.       if (mbd.isSingleton()) { 
  22.         // 如果是单例Bean会将Bean保存到缓存中singletonObjects   
  23.         sharedInstance = getSingleton(beanName, () -> { 
  24.           try { 
  25.             return createBean(beanName, mbd, args); 
  26.           } catch (BeansException ex) { 
  27.             destroySingleton(beanName); 
  28.             throw ex; 
  29.           } 
  30.         }); 
  31.         bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); 
  32.       } 
  33.       // 判断如果是原型(多例) 
  34.       else if (mbd.isPrototype()) { 
  35.         // It's a prototype -> create a new instance. 
  36.         Object prototypeInstance = null
  37.         try { 
  38.           beforePrototypeCreation(beanName); 
  39.           prototypeInstance = createBean(beanName, mbd, args); 
  40.         } finally { 
  41.           afterPrototypeCreation(beanName); 
  42.         } 
  43.         bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); 
  44.       }  
  45.       else { 
  46.         String scopeName = mbd.getScope(); 
  47.         if (!StringUtils.hasLength(scopeName)) { 
  48.           throw new IllegalStateException("No scope name defined for bean 麓" + beanName + "'"); 
  49.         } 
  50.         Scope scope = this.scopes.get(scopeName); 
  51.         // 当集合中也不存在时抛出异常   
  52.         if (scope == null) { 
  53.           throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'"); 
  54.         } 
  55.         try { 
  56.           Object scopedInstance = scope.get(beanName, () -> { 
  57.             beforePrototypeCreation(beanName); 
  58.             try { 
  59.               return createBean(beanName, mbd, args); 
  60.             } finally { 
  61.               afterPrototypeCreation(beanName); 
  62.             } 
  63.           }); 
  64.           bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); 
  65.         } catch (IllegalStateException ex) { 
  66.           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); 
  67.         } 
  68.       } 
  69.     } catch (BeansException ex) { 
  70.       cleanupAfterBeanCreationFailure(beanName); 
  71.       throw ex; 
  72.     } 
  73.   } 
  74.   // other code 
  75.   return (T) bean; 

从上面源码看到分别判断是了 是否是 Singleton及Proptotype,如果都不是则会从Map<String, Scope> scopes中获取。如果当前你配置的@Scope不是singleton及prototype那么从scopes集合中取(这个集合是通过AbstractBeanFactory#registerScope方法进行注册的,一般我们可以通过
BeanDefinitionRegistryPostProcessor进行注册),如果集合中也不存在那么就会抛出异常。如果存在就会执行Scope#get方法

  1. Scope scope = this.scopes.get(scopeName); 
  2. Object scopedInstance = scope.get(beanName, () -> { 
  3.   beforePrototypeCreation(beanName); 
  4.   try { 
  5.     return createBean(beanName, mbd, args); 
  6.   } finally { 
  7.     afterPrototypeCreation(beanName); 
  8.   } 
  9. }); 

2 自定义Scope

自定义Scope

  1. public class CustomScope implements Scope { 
  2.      
  3.   private Object target ; 
  4.  
  5.   @Override 
  6.   public Object get(String name, ObjectFactory<?> objectFactory) { 
  7.     return target != null ? target : objectFactory.getObject() ; 
  8.   } 
  9.   // 如果调用了这个方法,那么下次在注入有@Scope("custom")的bean时 将会重写调用objectFactory.getObject()方法。 
  10.   @Override 
  11.   public Object remove(String name) { 
  12.     target = null ; 
  13.     return "success" ; 
  14.   } 
  15.  
  16.   @Override 
  17.   public void registerDestructionCallback(String name, Runnable callback) { 
  18.   } 
  19.  
  20.   @Override 
  21.   public Object resolveContextualObject(String key) { 
  22.     return null
  23.   } 
  24.  
  25.   @Override 
  26.   public String getConversationId() { 
  27.     return null
  28.   } 
  29.  

注册Scope

  1. @Component 
  2. public class CustomScopeRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { 
  3.   @Override 
  4.   public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { 
  5.     beanFactory.registerScope("custom", new CustomScope()) ; 
  6.   } 
  7.   @Override 
  8.   public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { 
  9.   } 

使用Scope

  1. @Component 
  2. @Scope("custom"
  3. public class ApplyScopeBean { 

示例

  1. @RestController 
  2. @RequestMapping("/refresh"
  3. @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) 
  4. public class RefreshController implements ApplicationContextAware{ 
  5.   @Resource 
  6.   private ApplyScopeBean scopeBean ; 
  7.   @Resource 
  8.   private CustomScope customScope ; 
  9.   @GetMapping("/custom"
  10.   public String custom() { 
  11.     return scopeBean.getCustom() ; 
  12.   } 
  13.   @GetMapping("/remove")  
  14.   public Object remove() { 
  15.     return customScope.remove("applyScopeBean") ; 
  16.   }   

这里将Controller设置为多例,以便查看效果。交替执行上面的接口,只要删除了就会创建新的实例。

3 多例注入

如果一个Bean 设置了@Scope(value =

ConfigurableBeanFactory.SCOPE_PROTOTYPE) 当这个Bean需要在一个单例Bean中被注入时,需要如下配置才可

  1. @Component 
  2. @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS) 
  3. public class ApplyScopeBean { 

这样才能正确地注入Bean,否则因为本身使用者是单例的,属性只会被初始化一次。也可以在每次使用前调用BeanFactory#getBean()。