<返回更多

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

2023-09-05    Springboot实战案例锦集
加入收藏

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()) {        // 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);        // 当集合中也不存在时抛出异常          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,如果都不是则会从Map<String, Scope> scopes中获取。如果当前你配置的@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

@Componentpublic 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()。

完毕!!!

关键词:Spring      点击(20)
声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多Spring相关>>>