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

环境:spring5.3.3

1 Scope作用

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

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

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

从上面源码看到分别判断是了 是否是 Singleton及Proptotype,如果都不是则会从Map 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.   @Override
  5.   public Object get(String name, ObjectFactory objectFactory) {
  6.     return target != null ? target : objectFactory.getObject() ;
  7.   }
  8.   // 如果调用了这个方法,那么下次在注入有@Scope("custom")的bean时 将会重写调用objectFactory.getObject()方法。
  9.   @Override
  10.   public Object remove(String name) {
  11.     target = null ;
  12.     return "success" ;
  13.   }
  14.   @Override
  15.   public void registerDestructionCallback(String name, Runnable callback) {
  16.   }
  17.   @Override
  18.   public Object resolveContextualObject(String key) {
  19.     return null;
  20.   }
  21.   @Override
  22.   public String getConversationId() {
  23.     return null;
  24.   }
  25. }

注册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.   }
  10. }

使用Scope

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

示例

 
 
 
  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.   }  
  17. }

这里将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 {
  4. }

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

新闻标题:SpringBean的作用域scope你知道多少?如何自定义作用域?
文章分享:http://www.csdahua.cn/qtweb/news13/372363.html

网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网