spring中自定义scope的代码案例和源码解读

使用spring的小伙伴都知道,一个bean实例是有其作用于的,比如:单例(singleton),多例(prototype),这些都是spring容器管理实例的一种方式,那么在spring中有没有办法按照我们自己的意愿去管理我们的实例呢?答案是肯定的,那就是spring中自定义scope

那么如何自定义呢?

先看一个简单的bean标签:

其中我定义了一个scope=“JackScope”的自定义bean作用域,那么如何让这个作用域生效呢?

 

1、定义一个类实现BeanFactoryPostProcessor

public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        beanFactory.registerScope("jackScope",new CustomScope());
    }
}

2、定义一个类实现Scope

public class CustomScope implements Scope  {

    private ThreadLocal local = new ThreadLocal();

    /*
    * 这个方法就是自己管理bean
    * */
    @Override
    public Object get(String name, ObjectFactory objectFactory) {
        System.out.println("=============CustomScope========");
        if(local.get() != null) {
            return local.get();
        } else {
            //这个方法就是掉createbean方法获得一个实例
            Object object = objectFactory.getObject();
            local.set(object);
            return object;
        }
    }

    @Override
    public Object remove(String name) {
        return null;
    }

    @Override
    public void registerDestructionCallback(String name, Runnable callback) {

    }

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

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

在get方法中我们定义了获取bean的方法,我们定义了一个ThreadLocal,不同的用户获取到的bean的实例不一样,也就是说实例和用户绑定了,这样就不会有线程安全问题,当然这只是一个我们自己管理bean的例子,prototype也是这样的管理方式。

3、测试

public static void customScopeTest(final ApplicationContext context) {
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i1 = 0; i1 < 2; i1++) {
                        System.out.println(Thread.currentThread().getId() + "-->" + context.getBean("customScopeBean"));
                    }
                }
            }).start();
        }

    }

测试结果:

spring中自定义scope的代码案例和源码解读_第1张图片

从测试结果来看,相同线程ID打印出来的实例的hashCode是相同的,所以达到了我们的设计预期。OK,自定义scope就搞定了,那么为什么要这么做呢?来我们看源码。

 

 1、首先为什么我们要定义一个类实现Scope接口

我们把spring源码定位到bean实例化的地方:

	protected  T doGetBean(
			final String name, final Class requiredType, final Object[] args, boolean typeCheckOnly)
			throws BeansException {

		final String beanName = transformedBeanName(name);
		Object bean;

		// Eagerly check singleton cache for manually registered singletons.
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			if (logger.isDebugEnabled()) {
				if (isSingletonCurrentlyInCreation(beanName)) {
					logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
							"' that is not fully initialized yet - a consequence of a circular reference");
				}
				else {
					logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
				}
			}
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

		else {
			// Fail if we're already creating this bean instance:
			// We're assumably within a circular reference.
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

			// Check if bean definition exists in this factory.
			BeanFactory parentBeanFactory = getParentBeanFactory();
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				// Not found -> check parent.
				String nameToLookup = originalBeanName(name);
				if (args != null) {
					// Delegation to parent with explicit args.
					return (T) parentBeanFactory.getBean(nameToLookup, args);
				}
				else {
					// No args -> delegate to standard getBean method.
					return parentBeanFactory.getBean(nameToLookup, requiredType);
				}
			}

			if (!typeCheckOnly) {
				markBeanAsCreated(beanName);
			}

			try {
				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				checkMergedBeanDefinition(mbd, beanName, args);

				// Guarantee initialization of beans that the current bean depends on.
				String[] dependsOn = mbd.getDependsOn();
				if (dependsOn != null) {
					for (String dep : dependsOn) {
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
						registerDependentBean(dep, beanName);
						getBean(dep);
					}
				}

				// Create bean instance.
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, new ObjectFactory() {
						@Override
						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 {
					String scopeName = mbd.getScope();
					final 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, new ObjectFactory() {
							@Override
							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);
					}
				}
			}
			catch (BeansException ex) {
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
		}

		// Check if required type matches the type of the actual bean instance.
		if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
			try {
				return getTypeConverter().convertIfNecessary(bean, requiredType);
			}
			catch (TypeMismatchException ex) {
				if (logger.isDebugEnabled()) {
					logger.debug("Failed to convert bean '" + name + "' to required type '" +
							ClassUtils.getQualifiedName(requiredType) + "'", ex);
				}
				throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
			}
		}
		return (T) bean;
	}

读过spring源码的都知道,doGetBean方法就是spring对bean的实例化过程调用的方法,其中有单例实例、多例实例化

request、session、自定义scope作用的实例化。

自定义的scope,我们把目光放到最后的else里面

				else {
					String scopeName = mbd.getScope();
					final 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, new ObjectFactory() {
							@Override
							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);
					}
				}

那么什么情况下,代码会走到这个else呢?就如最前面我的那个例子哪里的bean标签定义:

如果作用域既不是单例也不是多例,那么bean标签解析封装成BeanDefinition对象时,mbd.isSingleton(),mbd.isPrototype()这两个属性都是为false的。所以就会走到这个else。

我们先看前两行代码:

String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);

第一行是获取bean标签中配置的scopename,既 “jackScope”.

第二行代码,从scopes容器中根据scopeName获取到一个Scope类型的实例,这里就是关键。那么scopes容器中有些什么样的东西呢?

 

在AbstractBeanFactory类中定义了scopes容器变量。

/** Map from scope identifier String to corresponding Scope */
private final Map scopes = new LinkedHashMap(8);

那么这个容器什么时候初始化的呢?

其实这个容器的初始化,需要我们自己动手调用方法的。

AbstractBeanFactory中的如下方法就是scopes容器初始化的方法,需要我们自己调用的。

	@Override
	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'");
		}
		Scope previous = this.scopes.put(scopeName, scope);
		if (previous != null && previous != scope) {
			if (logger.isInfoEnabled()) {
				logger.info("Replacing scope '" + scopeName + "' from [" + previous + "] to [" + scope + "]");
			}
		}
		else {
			if (logger.isDebugEnabled()) {
				logger.debug("Registering scope '" + scopeName + "' with implementation [" + scope + "]");
			}
		}
	}

Scope previous = this.scopes.put(scopeName, scope);

这行代码就是对scopes容器的初始化,那么这个方法什么时候掉的呢。

问题就很简单了,我们就只要获取到AbstractBeanFactory类的实例就 可以调到这个registerScope方法。

 

获取AbstractBeanFactory,即BeanFactory容器对象,我们就只要实现

BeanFactoryPostProcessor接口就可以了:
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        beanFactory.registerScope("jackScope",new CustomScope());
    }
}

实现BeanFactoryPostProcessor接口,然后spring就会调到这个postProcessBeanFactory方法,把BeanFactory对象传进来,这样我们就拿到了容器对象,就可以调用registerScope方法了。至于为什么实现BeanFactoryPostProcessor接口就会被spring调到

postProcessBeanFactory方法,请读源码:refresh()方法中的:invokeBeanFactoryPostProcessors(beanFactory);方法。

 

 

你可能感兴趣的:(spring源码,scope,自定义scope,scope源码,spring源码)