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

环境:spring5.3.3

1 Scope作用

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

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

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.       // 根据BeanDefinition中定义的Scope创建实例       // 判断如果是单例       if (mbd.isSingleton()) {          // 如果是单例Bean会将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()) {          // Its 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);         // 当集合中也不存在时抛出异常           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,如果都不是少何则会从Mapscopes中获取。如果当前你配置的作道多@Scope不是singleton及prototype那么从scopes集合中取(这个集合是通过AbstractBeanFactory#registerScope方法进行注册的,一般我们可以通过

BeanDefinitionRegistryPostProcessor进行注册),如果集合中也不存在那么就会抛出异常。知自定如果存在就会执行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() ;   }   // 如果调用了这个方法,那么下次在注入有@Scope("custom")的作道多bean时 将会重写调用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;   } } 

注册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设置为多例,香港云服务器以便查看效果。知自定交替执行上面的少何接口,只要删除了就会创建新的作道多实例。

3 多例注入

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

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

@Component @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE,知自定 proxyMode = ScopedProxyMode.TARGET_CLASS) public class ApplyScopeBean {  } 

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

系统运维
上一篇:戴尔VxRail超融合解决方案 助力医药企业实现数字化转型
下一篇:充分利用数据中心现代化机会