Spring IOC容器中,默认的bean作用域有两种:
Spring框架提供了扩展机制让使用者自定义作用域,框架提供了两个关键的类实现自定义作用域
org.springframework.beans.factory.config.Scope接口,需要实现以下几个关键方法:
org.springframework.beans.factory.config.CustomScopeConfigurer,这是一个BFPP,它的职责是把作用域注册到容器中
下面通过一个例子来演示怎么自定义作用域并且分析框架中的代码自定义作用域是怎么实现,这个自定义scope的功能是把bean缓存到一个LRU缓存中,当bean被踢出缓存时触发析构回调
实现Scope接口,LRU缓存中最多只能放两个bean,被踢掉的bean会触犯析构回调,在removeEldestEntry方法中,析构回调保存在destructionCallback哈希表中:
public class LRUCacheScope implements Scope {
private class BeanCache extends LinkedHashMap<String, Object> {
private static final long serialVersionUID = -887300667768355251L;
@Override
protected boolean removeEldestEntry(Entry<String, Object> eldest) {
boolean flag = size() > maxBeanNumber;
if (flag) {
executeDesCallback(eldest.getKey());
}
return flag;
}
}
private static final int DEFAULT_MAX_BEAN_NUMBER = 2;
private Map<String, Object> beanCache = Collections
.synchronizedMap(new BeanCache());
private int maxBeanNumber;
private Map<String, Runnable> destructionCallback = new HashMap<String, Runnable>();
public LRUCacheScope() {
this(DEFAULT_MAX_BEAN_NUMBER);
}
public LRUCacheScope(int maxBeanNumber) {
super();
this.maxBeanNumber = maxBeanNumber;
}
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
Object bean = beanCache.get(name);
if (bean == null) {
bean = objectFactory.getObject();
beanCache.put(name, bean);
}
return bean;
}
@Override
public Object remove(String name) {
destructionCallback.remove(name);
return beanCache.remove(name);
}
@Override
public void registerDestructionCallback(String name, Runnable callback) {
destructionCallback.put(name, callback);
}
@Override
public Object resolveContextualObject(String key) {
return null;
}
@Override
public String getConversationId() {
return null;
}
private void executeDesCallback(String beanName) {
Runnable callBack = destructionCallback.get(beanName);
if (callBack != null) {
callBack.run();
}
destructionCallback.remove(beanName);
}
}定义CustomScopeConfigurer注册Scope,并且定义其它的测试bean
<bean id="scopedBean1" class="spring.beans.scope.ScopedBean" scope="lruCache"></bean> <bean id="scopedBean2" class="spring.beans.scope.ScopedBean" scope="lruCache"></bean> <bean id="scopedBean3" class="spring.beans.scope.ScopedBean" scope="lruCache"></bean> <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"> <property name="scopes"> <map> <entry key="lruCache"> <bean class="spring.beans.scope.LRUCacheScope"></bean> </entry> </map> </property> </bean>JUnit测试代码
@Test
public void test() {
BeanFactory context = new ClassPathXmlApplicationContext(
"spring/beans/scope/scope.xml");
ScopedBean bean1 = (ScopedBean) context.getBean("scopedBean1");
ScopedBean bean11 = (ScopedBean) context.getBean("scopedBean1");
assertEquals(bean1, bean11);
ScopedBean bean2 = (ScopedBean) context.getBean("scopedBean2");
ScopedBean bean3 = (ScopedBean) context.getBean("scopedBean3");
bean11 = (ScopedBean) context.getBean("scopedBean1");
assertNotEquals(bean1, bean11);
}22:28:48,738 DEBUG DefaultListableBeanFactory:432 - Creating instance of bean 'scopedBean1' 22:28:48,738 DEBUG DefaultListableBeanFactory:460 - Finished creating instance of bean 'scopedBean1' 22:28:48,738 DEBUG DefaultListableBeanFactory:432 - Creating instance of bean 'scopedBean2' 22:28:48,738 DEBUG DefaultListableBeanFactory:460 - Finished creating instance of bean 'scopedBean2' 22:28:48,738 DEBUG DefaultListableBeanFactory:432 - Creating instance of bean 'scopedBean3' 22:28:48,738 DEBUG DefaultListableBeanFactory:460 - Finished creating instance of bean 'scopedBean3' 22:28:48,738 DEBUG DisposableBeanAdapter:227 - Invoking destroy() on bean with name 'scopedBean1' destroy:spring.beans.scope.ScopedBean@18235ed 22:28:48,738 DEBUG DefaultListableBeanFactory:432 - Creating instance of bean 'scopedBean1' 22:28:48,738 DEBUG DefaultListableBeanFactory:460 - Finished creating instance of bean 'scopedBean1' 22:28:48,738 DEBUG DisposableBeanAdapter:227 - Invoking destroy() on bean with name 'scopedBean2' destroy:spring.beans.scope.ScopedBean@1a28362从日志可以看出在创建完scopedBean3并且添加到缓存中之后scopedBean1被踢掉了并且触发了析构回调,我们ScopedBean实现了DisposableBean接口,它的destroy方法被调用了:
public class ScopedBean implements DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("destroy:" + this);
}
}
现在大致分析一下自定义作用域时如何实现的
首先看下CustomScopeConfigurer类,看下它的postProcessBeanFactory方法:
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (this.scopes != null) {
for (Map.Entry<String, Object> entry : this.scopes.entrySet()) {
String scopeKey = entry.getKey();
Object value = entry.getValue();
if (value instanceof Scope) {
beanFactory.registerScope(scopeKey, (Scope) value);
}
else if (value instanceof Class) {
Class scopeClass = (Class) value;
Assert.isAssignable(Scope.class, scopeClass);
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);
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");
}
}
}
}在这个方法中把所有scope都注册到beanFactory中,来看看bean工厂的registerScope方法,在AbstractBeanFactory类中,在这个方法中把所有的作用域对象都存储到了scopes哈希表属性中,作用域名称作为哈希表的key:
public void registerScope(String scopeName, Scope scope) {
Assert.notNull(scopeName, "Scope identifier must not be null");
Assert.notNull(scope, "Scope must not be null");
if (SCOPE_SINGLETON.equals(scopeName) || SCOPE_PROTOTYPE.equals(scopeName)) {
throw new IllegalArgumentException("Cannot replace existing scopes 'singleton' and 'prototype'");
}
this.scopes.put(scopeName, scope);
}接下来看看bean的获取方法,在AbstractBeanFactory的doGetBean方法中,看doGetBean方法的代码片段:if (mbd.isSingleton()) {
...
}
else if (mbd.isPrototype()) {
...
}
else {
String scopeName = mbd.getScope();
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 {
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);
}
}可以看到获取自定义scope的bean调用了Scope的get方法,如果作用域没有缓存要找bean,那么会调用createBean来创建一个实例,这块创建bean实例的逻辑和prototype bean的是一样的。
下面来看看注册析构回调的代码,在AbstractBeanFactory类的registerDisposableBeanIfNecessary方法中,在bean创建(AbstractAutowireCapableBeanFactory的doCreateBean方法)完成之后:
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
if (mbd.isSingleton()) {
// Register a DisposableBean implementation that performs all destruction
// work for the given bean: DestructionAwareBeanPostProcessors,
// DisposableBean interface, custom destroy method.
registerDisposableBean(beanName,
new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
}
else {
// A bean with a custom scope...
Scope scope = this.scopes.get(mbd.getScope());
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope '" + mbd.getScope() + "'");
}
scope.registerDestructionCallback(beanName,
new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
}
}
}代码中可以看到自定义scope的bean创建完成之后会注册一个DisposableBeanAdapter析构回调到到Scope,看看DisposableBeanAdapter这个类的代码,在scope中执行回调时调用run方法,而run方法会直接调用destroy方法,主要代码在destroy方法中,从代码中可以看出在destroy方法中执行了所有的bean的析构回调包括DestructionAwareBeanPostProcessor析构处理器、DisposableBean的destroy、bean定义中的destroy-method。
public void destroy() {
if (this.beanPostProcessors != null && !this.beanPostProcessors.isEmpty()) {
for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
processor.postProcessBeforeDestruction(this.bean, this.beanName);
}
}
if (this.invokeDisposableBean) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking destroy() on bean with name '" + this.beanName + "'");
}
try {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
public Object run() throws Exception {
((DisposableBean) bean).destroy();
return null;
}
}, acc);
}
else {
((DisposableBean) bean).destroy();
}
}
catch (Throwable ex) {
String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
if (logger.isDebugEnabled()) {
logger.warn(msg, ex);
}
else {
logger.warn(msg + ": " + ex);
}
}
}
if (this.destroyMethod != null) {
invokeCustomDestroyMethod(this.destroyMethod);
}
else if (this.destroyMethodName != null) {
Method methodToCall = determineDestroyMethod();
if (methodToCall != null) {
invokeCustomDestroyMethod(methodToCall);
}
}
}在Spring框架中也定义了一些自定义作用域:
原文地址:http://blog.csdn.net/pentiumchen/article/details/44007121