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

95 篇文章 3 订阅
60 篇文章 2 订阅

环境: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()) {
        // 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

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

完毕!!!

给个关注+转发呗谢谢

公众:Springboot实战案例锦集

 

Spring通过源码分析了解这3种Bean处理器的执行过程

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值