Spring中Scope源码分析

目录

概述

Spring中内置的Scope

Scope实现原理分析

Scope接口

Scope注解 

 ConfigurableBeanFactory接口

BeanDefinition接口

 AbstractBeanFactory类

自定义Scope

定义一个线程Scope

定义一个线程作用域的注解

注册Scope

测试


概述

Scope翻译过来就是作用域的意思,是描述bean实例的bean定义中重要组成部分之一。我们平时接触最多的就是singleton(单例)和prototype(多例)。Spring给我们提供了几个内置的Scope,当内置Scope无法满足需求时,Spring框架允许我们自定义Scope。

Spring中内置的Scope

scopedescription
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的上下文中才有效

Scope实现原理分析

Scope接口

public interface Scope {

	/**
	 * 根据beanName获取作用域下的实例,如果没有,则从工厂中获取
	 * @param name beanName
	 * @param objectFactory 获取bean实例的工厂中获取实例并在作用域下创建
	 */
	Object get(String name, ObjectFactory<?> objectFactory);

    ...

}

Scope注解 

public @interface Scope {

	/**
	 * 指定作用域的名称scopeName.
	 * @see ConfigurableBeanFactory#SCOPE_SINGLETON
	 * @see ConfigurableBeanFactory#SCOPE_PROTOTYPE
	 * @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST
	 * @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION
	 */
	String value() default ConfigurableBeanFactory.SCOPE_SINGLETON;

    ...

}

 ConfigurableBeanFactory接口

public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry {

    ...

	/**
	 * 注册Scope
	 * @param scopeName 作用域名称
	 * @param 实现了Scope接口的类
	 */
	void registerScope(String scopeName, Scope scope);

    ...

}

BeanDefinition接口

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

    ...

	/**
     * 获取作用域名称
	 * Return the name of the current target scope for this bean,
	 * or {@code null} if not known yet.
	 */
	String getScope();

    ...

}

当@Scope注解作用在类或方法时,Spring容器启动后会为每个bean生成bean定义,其中getSope()方法就会返回注解中value的值。

 AbstractBeanFactory类

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {

    ...

	private final Map<String, Scope> scopes = new LinkedHashMap<>(8);
    
    ... 

    protected <T> T doGetBean(
                ...

                //单例逻辑

        		if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
						public Object getObject() throws BeansException {
							try {
								return createBean(beanName, mbd, args);
							}
							catch (BeansException ex) {
								// Explicitly remove instance from singleton cache: It might have been put there
								// eagerly by the creation process, to allow for circular reference resolution.
								// Also remove any beans that received a temporary reference to the bean.
								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 {
                    // 从beanDefination中获取作用域名称
					String scopeName = mbd.getScope();
                    // 根据作用域名称找到注册的Scope
					final Scope scope = this.scopes.get(scopeName);
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
					}
					try {
                        // 从作用域中获取实例
						Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
							public Object getObject() throws BeansException {
								beforePrototypeCreation(beanName);
								try {
                                    // 创建bean,执行bean的生命周期,因此每次执行返回实例不一样
									return createBean(beanName, mbd, args);
								}
								finally {
									afterPrototypeCreation(beanName);
								}
							}
						});
						bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}

              ...
	}

}

当通过ApplicationContext.getBean()时会执行AbstractBeanFactory类中doGetBean()方法,如果bean的作用域不是单例和多例,就从作用域里取出bean实例。

自定义Scope

通过分析Scope源码后,我们可以自定义线程作用域ThreadScope,即同一个线程内多次getBean是同一个对象,不同线程之间getBean对象不一样。

定义一个线程Scope

public class ThreadScope implements Scope {

    private final ThreadLocal<Map<String, Object>> local = ThreadLocal.withInitial(HashMap::new);

    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        Map<String, Object> map = local.get();
        Object value = map.get(name);
        if(value == null) {
            value = objectFactory.getObject();
            map.put(name, value);
        }
        return value;
    }

    @Override
    public Object remove(String name) {
        Map<String, Object> map = local.get();
        return map.remove(name);
    }

    @Override
    public void registerDestructionCallback(String name, Runnable callback) {
        // nothing to do
    }

    @Override
    public Object resolveContextualObject(String key) {
        return null;
    }

    @Override
    public String getConversationId() {
        return Thread.currentThread().getName();
    }
}

主要看get方法逻辑,在threadlocal的map中取出beanName对应的实例,如果不存在,则从对象工厂中获取creatBean后的实例并设置到map里。

定义一个线程作用域的注解

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Scope("thread")
public @interface ThreadScope {
}

自定义线程作用域注解,作用域名称为thread,可以标记在类或方法上。

注册Scope

@Configuration
public class ScopeConfig {

    @Bean
    public CustomScopeConfigurer registerThreadScope() {
        CustomScopeConfigurer configurer = new CustomScopeConfigurer();
        configurer.addScope("thread", new ThreadScope());
        return configurer;
    }

    @Bean
    @ThreadScope
    public User user() {
        return new User();
    }
}

注册一个User对象的Bean并指定作用域,同时注册线程作用域,指定scopeName和Scope。CustomScopeConfigurer是Spring提供给我们注册作用域的类,它实现了BeanFactoryPostProcessor接口,当Spring容器启动时,会回调postProcessBeanFactory方法,通过调用beanFactory.registerScope方法完成自定义Scope的注册。当然我们也可以自定义一个类实现BeanFactoryPostProcessor类并注册成一个Bean,实现思路是一样的。

public class CustomScopeConfigurer implements BeanFactoryPostProcessor, BeanClassLoaderAware, Ordered {

	@Nullable
	private Map<String, Object> scopes;

	private int order = Ordered.LOWEST_PRECEDENCE;

	@Nullable
	private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();


	/**
     * 设置自定义Scope
	 * Specify the custom scopes that are to be registered.
	 * <p>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<String, Object> 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");
				}
			});
		}
	}

}

测试

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();
    }
}

 从结果中可知,同一个线程内多次getBean返回的是同一个对象,不同线程之间getBean返回的是不同对象。Beautiful~~

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值