Spring 的扩展点 是Spring易扩展的一个重要体现,熟悉这些扩展点的定义方式,以及其调用时机,不仅成为工作中利器,也能深度理解Spring框架的切入点。
Scope定义了Bean在Spring中的作用范围到底有多大,就像用户单次的访问权限一样,它设定了Bean在应用中可用范围。
scope | description |
singleton | (默认)将单个bean定义作用于每个Spring IoC容器的单个对象实例 |
prototype | 将单个bean定义作用于任意数量的对象实例 |
request | 将单个bean定义的范围扩大到单个HTTP请求的生命周期。也就是说,每个HTTP请求都有自己的bean实例,该实例是在单个bean定义的后面创建的。只有在感知web的Spring ApplicationContext的上下文中才有效 |
session | 将单个bean定义作用于HTTP会话的生命周期。只有在感知web的Spring ApplicationContext的上下文中才有效 |
application | 将一个bean定义作用域作用到ServletContext的生命周期。只有在感知web的Spring ApplicationContext的上下文中才有效 |
websocket | 将一个bean定义定义到WebSocket的生命周期。只有在感知web的Spring ApplicationContext的上下文中才有效 |
ThreadScope即同一个线程内多次getBean是同一个对象,不同线程之间getBean对象不一样
public class ThreadLocalScope implements Scope {
private final ThreadLocal
定义一个线程Scope对象
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Scope("thread")
public @interface ThreadLocalScopeAnn {
}
自定义注解
@Configuration
public class ScopeConfig {
@Bean
public CustomScopeConfigurer registerThreadLocalScope() {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
configurer.addScope("thread", new ThreadLocalScope());
return configurer;
}
@Bean
@ThreadLocalScopeAnn
public User user() {
return new User();
}
注册Scope
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ScopeConfig.class);
new Thread(() -> {
System.out.println(applicationContext.getBean(User.class));
System.out.println(applicationContext.getBean(User.class));
}).start();
new Thread(() -> {
System.out.println(applicationContext.getBean(User.class));
System.out.println(applicationContext.getBean(User.class));
}).start();
}
测试
测试结果
当@Scope注解作用在类或方法时,Spring容器启动后会为每个bean生成beandifinition,其中getSope()方法就会返回注解中value的值。其底层是在AbstractBeanFactory#doGetBean中进行判断的。
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
...
private final Map scopes = new LinkedHashMap<>(8);
...
protected T doGetBean(
...
//单例逻辑
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory
CustomScopeConfigurer是Spring提供给我们注册作用域的类,它实现了BeanFactoryPostProcessor接口,当Spring容器启动时,会回调postProcessBeanFactory方法,通过调用beanFactory.registerScope方法完成自定义Scope的注册。当然我们也可以自定义一个类实现BeanFactoryPostProcessor类并注册成一个Bean,实现思路是一样的。
public class CustomScopeConfigurer implements BeanFactoryPostProcessor, BeanClassLoaderAware, Ordered {
@Nullable
private Map scopes;
private int order = Ordered.LOWEST_PRECEDENCE;
@Nullable
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
/**
* 设置自定义Scope
* Specify the custom scopes that are to be registered.
* The keys indicate the scope names (of type String); each value
* is expected to be the corresponding custom {@link Scope} instance
* or class name.
*/
public void setScopes(Map scopes) {
this.scopes = scopes;
}
/**
* 添加自定义Scope
* Add the given scope to this configurer's map of scopes.
* @param scopeName the name of the scope
* @param scope the scope implementation
* @since 4.1.1
*/
public void addScope(String scopeName, Scope scope) {
if (this.scopes == null) {
this.scopes = new LinkedHashMap<>(1);
}
this.scopes.put(scopeName, scope);
}
public void setOrder(int order) {
this.order = order;
}
@Override
public int getOrder() {
return this.order;
}
@Override
public void setBeanClassLoader(@Nullable ClassLoader beanClassLoader) {
this.beanClassLoader = beanClassLoader;
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 通过beanFactory.registerScope方法注册自定义Scope
if (this.scopes != null) {
this.scopes.forEach((scopeKey, value) -> {
if (value instanceof Scope) {
beanFactory.registerScope(scopeKey, (Scope) value);
}
else if (value instanceof Class) {
Class> scopeClass = (Class>) value;
Assert.isAssignable(Scope.class, scopeClass, "Invalid scope class");
beanFactory.registerScope(scopeKey, (Scope) BeanUtils.instantiateClass(scopeClass));
}
else if (value instanceof String) {
Class> scopeClass = ClassUtils.resolveClassName((String) value, this.beanClassLoader);
Assert.isAssignable(Scope.class, scopeClass, "Invalid scope class");
beanFactory.registerScope(scopeKey, (Scope) BeanUtils.instantiateClass(scopeClass));
}
else {
throw new IllegalArgumentException("Mapped value [" + value + "] for scope key [" +
scopeKey + "] is not an instance of required type [" + Scope.class.getName() +
"] or a corresponding Class or String value indicating a Scope implementation");
}
});
}
}
}
关注公众号:齐疾行者Code
一头爱分享技术的程序猿 ,一个技术与职场的宝藏博主 | 专注Java生态与职场经验分享 ,在这里分享技术、感悟、生活 | 每天进步一点点,和我一起奇迹行者
近期文章精选:
架构のSpring扩展点(二):Bean定义操作-BeanDefinitionRegistryPostProcessor
架构のSpring扩展点(一):上下文创建前的动态处理-ApplicationContextInitializer
架构のSpring扩展点(三):Bean生命周期操作-InstantiationAwareBeanPostProcessor
架构のSpring扩展点(四):Bean初始化时对象自动注入-Aware全解析
认知结构的提升,需要合适的思考方法-架构提升