这两天写Jersey遇到瓶颈,主要是没有理解HK2的RequestScoped 这个到底是怎么用的。自然就联想到Spring MVC 的request / session scope 是怎么用的。
Scope
Spring 提供了两种Bean 的scope (singleton 和 prototype) 以及 三种用在web 应用的scope (request, session 和 globalSession)。
singleton 和 prototype的scope 不能再被继承扩展, 但是spring 提供了可以扩展的scope 机制。
下文以HttRequest为例,讲一下spring 怎么处理的request, session 和 globalSession 对象的注入,之后再介绍一下如何定制自己的scope。
1. Request 的注入
Spring MVC环境中的父上下文时:XmlWebApplicationContext。下面我们先看一下XmlWebApplicationContext的UML类图:
当context refresh 时 会调用
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
//注册Bean的生命周期的相关的类(实现了BeanPostProcessor接口)
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
//需要忽略依赖检查的接口
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
//这个是这次要重点分析的类
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
//注册一些环境变量相关的类
WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
}
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext):
public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, ServletContext sc) {
beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope(false));
beanFactory.registerScope(WebApplicationContext.SCOPE_GLOBAL_SESSION, new SessionScope(true));
if (sc != null) {
ServletContextScope appScope = new ServletContextScope(sc);
beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope);
// Register as ServletContext attribute, for ContextCleanupListener to detect it.
sc.setAttribute(ServletContextScope.class.getName(), appScope);
}
beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory());
beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory());
beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory());
beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory());
if (jsfPresent) {
FacesDependencyRegistrar.registerFacesDependencies(beanFactory);
}
}
在这个类中注册了Web开发相关的三个Scope:RequestScope、SessionScope和全局的SessionScope以及一个ServletContextScope。接下来了注册了几个实现了ObjectFactory的Bean。RequestObjectFactory、RequestObjectFactory、SessionObjectFactory、WebRequestObjectFactory。这几个类是在Controller中注入Request和Response的关键。我们先记住 RequestObjectFactory这个类。这几个类被放到了resolvableDependencies中。resolvableDependencies这个Map中存放的是在Autowire时所要使用到的bean的类型和对应的Object。
下面我们去看一下在Controller中注入Request的一个过程。首先看一下相应的一些调用链
DefaultListableBeanFactory.findAutowireCandidates这个方法中:
protected Map<String, Object> findAutowireCandidates(
@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this, requiredType, true, descriptor.isEager());
Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);
for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
Class<?> autowiringType = classObjectEntry.getKey();
if (autowiringType.isAssignableFrom(requiredType)) {
Object autowiringValue = classObjectEntry.getValue();
autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
if (requiredType.isInstance(autowiringValue)) {
result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
break;
}
}
}
for (String candidate : candidateNames) {
if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
if (result.isEmpty()) {
boolean multiple = indicatesMultipleBeans(requiredType);
// Consider fallback matches if the first pass failed to find anything...
DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
for (String candidate : candidateNames) {
if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) &&
(!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
if (result.isEmpty() && !multiple) {
// Consider self references as a final pass...
// but in the case of a dependency collection, not the very same bean itself.
for (String candidate : candidateNames) {
if (isSelfReference(beanName, candidate) &&
(!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
isAutowireCandidate(candidate, fallbackDescriptor)) {
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
}
}
return result;
}
在上面的代码中我们看到了resolvableDependencies这个属性。当注入HttpServletRequest的时候,requiredType值是HttpServletRequest.class,我们还记得在resolvableDependencies中放入了ServletRequest.class这个key。所以 if (autowiringType.isAssignableFrom(requiredType)) 这个判断会返回true,接着会取得RequestObjectFactory这个对象。接着会调用AutowireUtils.resolveAutowiringValue这个方法进一步的解析Autowire的值。我们进到这个方法中进行看一下
public static Object resolveAutowiringValue(Object autowiringValue, Class<?> requiredType) {
if (autowiringValue instanceof ObjectFactory && !requiredType.isInstance(autowiringValue)) {
ObjectFactory<?> factory = (ObjectFactory<?>) autowiringValue;
if (autowiringValue instanceof Serializable && requiredType.isInterface()) {
autowiringValue = Proxy.newProxyInstance(requiredType.getClassLoader(),
new Class<?>[] {requiredType}, new ObjectFactoryDelegatingInvocationHandler(factory));
}
else {
return factory.getObject();
}
}
return autowiringValue;
}
在这个方法中会先判断上一步取得的autowireValue是不是可以实例化为ObjectFactory 对象。上一步取得的autowireValue是RequestObjectFactory。我们看一下RequestObjectFactory这个类的内容:
private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {
@Override
public ServletRequest getObject() {
return currentRequestAttributes().getRequest();
}
@Override
public String toString() {
return "Current HttpServletRequest";
}
}
实现了ObjectFactory这个接口。由于RequestObjectFactory 是不能实例化为HttpServletRequest对象的。所以会进入到循环体中。接着进一步的判断上一步取得的autowireValue的值能不能被序列化,以及requiredType是不是可以接口,从上面的分析可以看出这个条件也是成立的。所以最终生成的autowireValue的值是一个 JDK动态代理生成的对象。InvocationHandler的实现类为:ObjectFactoryDelegatingInvocationHandler。所以我们在Controller层中所注入的HttpServletRequest其实是一个JDK动态代理生成的对象。 当我们在Controller中调用的时候:
@RequestMapping("testAutowiredRequest")
public String testAutowiredRequest() {
request.getParameter("userNmae");
return "success";
}
会进入到ObjectFactoryDelegatingInvocationHandler的invoke方法中
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if (methodName.equals("equals")) {
// Only consider equal when proxies are identical.
return (proxy == args[0]);
}
else if (methodName.equals("hashCode")) {
// Use hashCode of proxy.
return System.identityHashCode(proxy);
}
else if (methodName.equals("toString")) {
return this.objectFactory.toString();
}
try {
return method.invoke(this.objectFactory.getObject(), args);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
这里对equals方法、hashcode方法、toString方法进行了特殊处理。其他的方法则是直接执行method.invoke的方法。第一个参数为所调用的对象。是通过调用this.objectFactory.getObject()来获取的。objectFactory,通过我们上面的分析我们知道它是RequestObjectFactory这个对象。所以我们去RequestObjectFactory这个类中看一下objectFactory这个方法:
private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {
@Override
public ServletRequest getObject() {
return currentRequestAttributes().getRequest();
}
@Override
public String toString() {
return "Current HttpServletRequest";
}
}
在上面的代码中调用了currentRequestAttributes().getRequest()。接着去currentRequestAttributes()这个方法中看一下
private static ServletRequestAttributes currentRequestAttributes() {
RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
if (!(requestAttr instanceof ServletRequestAttributes)) {
throw new IllegalStateException("Current request is not a servlet request");
}
return (ServletRequestAttributes) requestAttr;
}
((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();看一下RequestContextHolder.currentRequestAttributes()这个方法
public static RequestAttributes currentRequestAttributes() throws IllegalStateException {
RequestAttributes attributes = getRequestAttributes();
if (attributes == null) {
if (jsfPresent) {
attributes = FacesRequestAttributesFactory.getFacesRequestAttributes();
}
if (attributes == null) {
throw new IllegalStateException("No thread-bound request found: " +
"Are you referring to request attributes outside of an actual web request, " +
"or processing a request outside of the originally receiving thread? " +
"If you are actually operating within a web request and still receive this message, " +
"your code is probably running outside of DispatcherServlet: " +
"In this case, use RequestContextListener or RequestContextFilter to expose the current request.");
}
}
return attributes;
}
通过getRequestAttributes()这个方法来获取RequestAttributes对象。在这个方法中继续看一下
public static RequestAttributes getRequestAttributes() {
RequestAttributes attributes = requestAttributesHolder.get();
if (attributes == null) {
attributes = inheritableRequestAttributesHolder.get();
}
return attributes;
}
发现是通过requestAttributesHolder或者inheritableRequestAttributesHolder的get()方法,来获取的RequestAttributes对象。那么requestAttributesHolder和inheritableRequestAttributesHolder是什么呢?
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<>("Request attributes");
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
new NamedInheritableThreadLocal<>("Request context");
这是两个 ThreadLocal的对象。每次都是从ThreadLocal对象中取的值,因此是线程安全的。什么时候往这两个ThreadLocal中放入值的呢?请接着往下看:org.springframework.web.servlet.FrameworkServlet#processRequest这个方法中有这样的几行代码:
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);//创建ServletRequestAttributes
initContextHolders(request, localeContext, requestAttributes);//往ThreadLocal中放入值
initContextHolders这个方法的代码如下:
private void initContextHolders(
HttpServletRequest request, LocaleContext localeContext, RequestAttributes requestAttributes) {
if (localeContext != null) {
LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
}
//把RequestAttributes放入到ThreadLocal中
if (requestAttributes != null) {
RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
}
}
RequestContextHolder.setRequestAttributes这个方法的内容如下:
public static void setRequestAttributes(RequestAttributes attributes, boolean inheritable) {
if (attributes == null) {
resetRequestAttributes();
}
else {
if (inheritable) {
inheritableRequestAttributesHolder.set(attributes);
requestAttributesHolder.remove();
}
else {
requestAttributesHolder.set(attributes);
inheritableRequestAttributesHolder.remove();
}
}
}
以上就是create 一个request scope bean 的全过程。不过上文只是提到了HttpRequest, HttpResponse显示在程序里这注册了factory 怎么构造的,对于RequestScope 或者 任意一个Annotation 怎么构造的,其实并没有说明。下面先介绍如何订制一个Scope, 并且如何使用。 再介绍程序是如何运行解析的。
2. Creating a Custom Scope Class
请按照下列顺序实现
2.1. Managing the Scoped Objects and Callbacks
首先写一个scope class:
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
public class TenantScope implements Scope {
private Map<String, Object> scopedObjects = Collections.synchronizedMap(new HashMap<String, Object>());
private Map<String, Runnable> destructionCallbacks = Collections.synchronizedMap(new HashMap<String, Runnable>());
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
if (!scopedObjects.containsKey(name)) {
scopedObjects.put(name, objectFactory.getObject());
}
return scopedObjects.get(name);
}
@Override
public Object remove(String name) {
destructionCallbacks.remove(name);
return scopedObjects.remove(name);
}
@Override
public void registerDestructionCallback(String name, Runnable callback) {
destructionCallbacks.put(name, callback);
}
@Override
public Object resolveContextualObject(String key) {
return null;
}
@Override
public String getConversationId() {
return "tenant";
}
}
}
2.2. Retrieving an Object from Scope
在get 方法里, 如果已经存在,从map 里返回,如果不存在,则用objectFactory 生成。
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
if(!scopedObjects.containsKey(name)) {
scopedObjects.put(name, objectFactory.getObject());
}
return scopedObjects.get(name);
}
在Scope 五个方法里,只有 get 必须实现,其他是可选,也可以直接抛出UnSupportedException。
2.3. Registering a Destruction Callback
要实现destroy callback 必须要实现 registerDestructionCallback 方法。
@Override
public void registerDestructionCallback(String name, Runnable callback) {
destructionCallbacks.put(name, callback);
}
2.4. Removing an Object from Scope
remove方法把object 从 scope在destroy 方法中移除。
@Override
public Object remove(String name) {
destructionCallbacks.remove(name);
return scopedObjects.remove(name);
}
2.5. Getting the Conversation ID
现在实现getConversationId 方法. 如果你的scope 有conversation ID的概念, 就在这里创造一个id,否则就返回null
@Override
public String getConversationId() {
return "tenant";
}
2.6. Resolving Contextual Objects
最后实现resolveContextualObject 方法。如果支持多context object, 需要把每个object 关联一个key,根据key 值返回object。 如果key 不存在返回null。
@Override
public Object resolveContextualObject(String key) {
return null;
}
2.7. Registering the Custom Scope
需要调用 ConfigurableBeanFactory instance**的registerScope,来让框架知道次Scope。
void registerScope(String scopeName, Scope scope);
第一个参数是Scope 名称,第二个参数是Scope 的实例。需要生成一个自己的BeanFactoryPostProcessor ,同时注册Scope。
public class TenantBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException {
factory.registerScope("tenant", new TenantScope());
}
}
现在注册写个config,注册BeanFactoryPostProcessor Bean。
@Configuration
public class TenantScopeConfig {
@Bean
public static BeanFactoryPostProcessor beanFactoryPostProcessor() {
return new TenantBeanFactoryPostProcessor();
}
}
2.8. Using the Custom Scope
先创建 TenantBean class :
public class TenantBean {
private final String name;
public TenantBean(String name) {
this.name = name;
}
public void sayHello() {
System.out.println(
String.format("Hello from %s of type %s",
this.name,
this.getClass().getName()));
}
}
写一个配置来定义TenantBean 并且其scope 为tenant。
@Configuration
public class TenantBeansConfig {
@Scope(scopeName = "tenant")
@Bean
public TenantBean foo() {
return new TenantBean("foo");
}
@Scope(scopeName = "tenant")
@Bean
public TenantBean bar() {
return new TenantBean("bar");
}
}
2.9. Testing the Custom Scope
现在可以load “tenant" scope 的TenantBean。
@Test
public final void whenRegisterScopeAndBeans_thenContextContainsFooAndBar() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
try{
ctx.register(TenantScopeConfig.class);
ctx.register(TenantBeansConfig.class);
ctx.refresh();
TenantBean foo = (TenantBean) ctx.getBean("foo", TenantBean.class);
foo.sayHello();
TenantBean bar = (TenantBean) ctx.getBean("bar", TenantBean.class);
bar.sayHello();
Map<String, TenantBean> foos = ctx.getBeansOfType(TenantBean.class);
assertThat(foo, not(equalTo(bar)));
assertThat(foos.size(), equalTo(2));
assertTrue(foos.containsValue(foo));
assertTrue(foos.containsValue(bar));
BeanDefinition fooDefinition = ctx.getBeanDefinition("foo");
BeanDefinition barDefinition = ctx.getBeanDefinition("bar");
assertThat(fooDefinition.getScope(), equalTo("tenant"));
assertThat(barDefinition.getScope(), equalTo("tenant"));
}
finally {
ctx.close();
}
}
2.10 Calling Sequence
从下图可以看出,定制的调用顺序,
可以看出AbstraactBeanFactory 里的逻辑
String scopeName = mbd.getScope();
if (!StringUtils.hasLength(scopeName)) {
throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
}
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, () -> {
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);
}
实际传入的BeanFactory是
() -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
同时我们再看AbstractBeanDefinition 缺省的scope为singleton.
/**
* Set the name of the target scope for the bean.
* <p>The default is singleton status, although this is only applied once
* a bean definition becomes active in the containing factory. A bean
* definition may eventually inherit its scope from a parent bean definition.
* For this reason, the default scope name is an empty string (i.e., {@code ""}),
* with singleton status being assumed until a resolved scope is set.
* @see #SCOPE_SINGLETON
* @see #SCOPE_PROTOTYPE
*/
@Override
public void setScope(@Nullable String scope) {
this.scope = scope;
}
那么同样,当@RequestScope 的发生时,也采取的是和定制化一样的逻辑。 先看RequestScope
public class RequestScope extends AbstractRequestAttributesScope {
@Override
protected int getScope() {
return RequestAttributes.SCOPE_REQUEST;
}
/**
* There is no conversation id concept for a request, so this method
* returns {@code null}.
*/
@Override
@Nullable
public String getConversationId() {
return null;
}
}
public abstract class AbstractRequestAttributesScope implements Scope {
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
Object scopedObject = attributes.getAttribute(name, getScope());
if (scopedObject == null) {
scopedObject = objectFactory.getObject();
attributes.setAttribute(name, scopedObject, getScope());
// Retrieve object again, registering it for implicit session attribute updates.
// As a bonus, we also allow for potential decoration at the getAttribute level.
Object retrievedObject = attributes.getAttribute(name, getScope());
if (retrievedObject != null) {
// Only proceed with retrieved object if still present (the expected case).
// If it disappeared concurrently, we return our locally created instance.
scopedObject = retrievedObject;
}
}
return scopedObject;
}
....
}
主要看它的Object, 从request attribute里拿对象,如果不存在就创建bean 然后放入request attributes 中。
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
Object scopedObject = attributes.getAttribute(name, getScope());