大数跨境
0
0

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

Spring Bean的作用域scope你知道多少?如何自定义作用域? Spring全家桶实战案例
2021-07-02
0
导读: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()) {
       // 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实战案例锦集


【声明】内容源于网络
0
0
Spring全家桶实战案例
Java全栈开发,前端Vue2/3全家桶;Spring, SpringBoot 2/3, Spring Cloud各种实战案例及源码解读
内容 832
粉丝 0
Spring全家桶实战案例 Java全栈开发,前端Vue2/3全家桶;Spring, SpringBoot 2/3, Spring Cloud各种实战案例及源码解读
总阅读368
粉丝0
内容832