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,如果都不是少何则会从Map
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()。