Spring(10)——bean作用范围(二)—自定义scope

简介: 10.7 自定义Scope 如果用户觉得Spring内置的几种Scope不能满足需求,则可以定制自己的Scope,即实现自己的org.springframework.beans.factory.config.Scope。

10.7 自定义Scope

如果用户觉得Spring内置的几种Scope不能满足需求,则可以定制自己的Scope,即实现自己的org.springframework.beans.factory.config.Scope。Scope接口定义了如下几个方法,详情请参看Spring的API文档。

public interface Scope {

	Object get(String name, ObjectFactory<?> objectFactory);

	Object remove(String name);

	void registerDestructionCallback(String name, Runnable callback);

	Object resolveContextualObject(String key);

	String getConversationId();

}

下面来看一下Spring内部Scope为application的定义,即ServletContextScope的定义。

public class ServletContextScope implements Scope, DisposableBean {

	private final ServletContext servletContext;

	private final Map<String, Runnable> destructionCallbacks = new LinkedHashMap<String, Runnable>();


	/**
	 * Create a new Scope wrapper for the given ServletContext.
	 * @param servletContext the ServletContext to wrap
	 */
	public ServletContextScope(ServletContext servletContext) {
		Assert.notNull(servletContext, "ServletContext must not be null");
		this.servletContext = servletContext;
	}


	@Override
	public Object get(String name, ObjectFactory<?> objectFactory) {
		Object scopedObject = this.servletContext.getAttribute(name);
		if (scopedObject == null) {
			scopedObject = objectFactory.getObject();
			this.servletContext.setAttribute(name, scopedObject);
		}
		return scopedObject;
	}

	@Override
	public Object remove(String name) {
		Object scopedObject = this.servletContext.getAttribute(name);
		if (scopedObject != null) {
			this.servletContext.removeAttribute(name);
			this.destructionCallbacks.remove(name);
			return scopedObject;
		}
		else {
			return null;
		}
	}

	@Override
	public void registerDestructionCallback(String name, Runnable callback) {
		this.destructionCallbacks.put(name, callback);
	}

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

	@Override
	public String getConversationId() {
		return null;
	}


	/**
	 * Invoke all registered destruction callbacks.
	 * To be called on ServletContext shutdown.
	 * @see org.springframework.web.context.ContextCleanupListener
	 */
	@Override
	public void destroy() {
		for (Runnable runnable : this.destructionCallbacks.values()) {
			runnable.run();
		}
		this.destructionCallbacks.clear();
	}

}

10.7.1 注册

自定义了Scope之后我们得在Spring中进行注册,好让Spring能够对其进行识别,这样我们才能在进行对应bean定义的时候使用自定义的Scope。自定义Scope的注册有两种方式,一种是程序化的,一种是通过XML进行配置的。
我们先来实现一个自定义的Scope供注册自定义Scope使用。

public class MyScope implements Scope {
	
	private Map<String, Object> beanMap = new ConcurrentHashMap<String, Object>();

	/**
	 * 获取指定beanName的bean实例
	 * @param name 对应bean的beanName
	 * @param objectFactory 可以产生对应bean实例的ObjectFactory
	 * @return 获取到的实例
	 */
	public Object get(String name, ObjectFactory<?> objectFactory) {
		System.out.println("------------get-----------" + name);
		synchronized (this) {
			if (!beanMap.containsKey(name)) {
				System.out.println("-----------not--exists-------" + name);
				beanMap.put(name, objectFactory.getObject());
			}
		}
		return beanMap.get(name);
	}

	/**
	 * 底层移除name对应的对象。实现者需要同时移除注册的销毁化回调方法
	 * @param name
	 * @return 移除的对象
	 */
	public Object remove(String name) {
		return beanMap.remove(name);
	}

	/**
	 * 注册一个销毁时的回调方法
	 * @param name
	 * @param callback
	 */
	public void registerDestructionCallback(String name, Runnable callback) {

	}

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

	public String getConversationId() {
		return null;
	}

}

程序化注册自定义Scope是通过ConfigurableBeanFactory的registerScope()方法进行的,其对应定义如下,scopeName表示我们需要注册的scope的名称,第二个参数Scope表示我们需要注册的Scope的一个实例。

	/**
	 * Register the given scope, backed by the given Scope implementation.
	 * @param scopeName the scope identifier
	 * @param scope the backing Scope implementation
	 */
	void registerScope(String scopeName, Scope scope);

我们可以通过常用的ApplicationContext,如ClassPathXmlApplicationContext等的getBeanFactory()方法就能获取到对应的ConfigurableBeanFactory对象,然后进行注册。如:

	ClassPathXmlApplicationContext context = ...;
	context.getBeanFactory().registerScope("myScope", new MyScope());

通过XML配置进行注册是指通过在Spring的配置文件中定义一个CustomScopeConfigurer类型的bean,并通过其setScopes()方法注入自定义Scope。如下所示,我们通过XML配置注册了一个名叫myScope的Scope定义。

	<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
		<property name="scopes">
			<map>
				<entry key="myScope">
					<bean class="com.app.MyScope"/>
				</entry>
			</map>
		</property>
	</bean>

之后就可以在定义bean的时候使用我们自己定义的myScope来作为bean定义的Scope了。

	<bean id="hello" class="com.app.Hello" scope="myScope"/>

在上述配置中我们指定了id为hello的bean定义的scope为自定义的myScope。之后运行如下测试代码,我们可以看到控制台的输出过程。我们每从bean容器中获取一次hello的实例,对应MyScope的get()方法就会被调用一次。

	@org.junit.Test
	public void test() {
		System.out.println(context.getBean("hello"));
		System.out.println(context.getBean("hello"));
	}

(注:本文是基于Spring4.1.0所写)

 

目录
相关文章
|
17天前
|
缓存 Java Spring
Spring 框架中 Bean 的生命周期
Spring 框架中 Bean 的生命周期
29 1
|
1月前
|
XML Java 开发者
Spring Boot中的bean注入方式和原理
Spring Boot中的bean注入方式和原理
39 0
|
1月前
|
Java Spring 容器
【Java】Spring如何扫描自定义的注解?
【Java】Spring如何扫描自定义的注解?
34 0
|
1月前
|
XML 缓存 Java
Spring源码之 Bean 的循环依赖
循环依赖是 Spring 中经典问题之一,那么到底什么是循环依赖?简单说就是对象之间相互引用, 如下图所示: 代码层面上很好理解,在 bean 创建过程中 class A 和 class B 又经历了怎样的过程呢? 可以看出形成了一个闭环,如果想解决这个问题,那么在属性填充时要保证不二次创建 A对象 的步骤,也就是必须保证从容器中能够直接获取到 B。 一、复现循环依赖问题 Spring 中默认允许循环依赖的存在,但在 Spring Boot 2.6.x 版本开始默认禁用了循环依赖 1. 基于xml复现循环依赖 定义实体 Bean java复制代码public class A {
|
1月前
|
存储 NoSQL Java
Spring Boot统计一个Bean中方法的调用次数
Spring Boot统计一个Bean中方法的调用次数
32 1
|
2月前
|
Java 索引 Spring
spring创建bean的过程
spring创建bean的过程
|
1天前
|
Java 数据库连接 开发者
浅谈Spring的Bean生命周期
浅谈Spring的Bean生命周期
8 1
|
6天前
|
XML Java 数据格式
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
14 0
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
|
16天前
|
XML Java 程序员
作为Java程序员还不知道Spring中Bean创建过程和作用?
作为Java程序员还不知道Spring中Bean创建过程和作用?
12 0
|
21天前
|
XML 缓存 Java
天天用 Spring,bean 实例化原理你懂吗
天天用 Spring,bean 实例化原理你懂吗
15 0