写这个东西的起因是@黄勇 写的封装Servlet API的文章,让我点评想想还是发一下自己写的代码好了。
做这个WebContext类最初的起因是想代码里面随意的使用Servlet API而不必把request传来传去的。后来主要关注的是如何能简单的在配置上切换session作用域的实现而不需要改动应用程序的代码,比如用多值Cookie或者外部的缓存系统来实现,为这个而引入了SessionScope接口。
整个程序是基于Spring @MVC的,当然也可以适当修改,把拦截器换成Servlet Filter或者JFinal的拦截器,让他能脱离Spring @MVC,这个是后话。下面是代码,注释写的蛮多的,就不多说了。
--------------------------------------------------------------
首先的是scope包的接口定义,他主要是定义了对属性的操作
package org.fox.utils.web.scope;
import java.io.Serializable;
/**
* 本接口表示一个作用域实例
*
* @author fox
*/
public interface Scope {
/**
* 获取作用域中的属性
*
* @param key
* 属性键
* @return 属性值
*/
public abstract Object getAttribute(String key);
/**
* 设置作用域中的属性
*
* @param key
* 属性键
* @param value
* 属性值
*/
public abstract void setAttribute(String key, Serializable value);
/**
* 删除作用域中的属性
*
* @param key
* 属性键
*/
public abstract void removeAttribute(String key);
}
ApplicationScope和SessionScope都继承了Socpe接口
package org.fox.utils.web.scope;
import java.io.Serializable;
/**
* Session作用域接口
*
* @author fox
*/
public interface SessionScope extends Scope {
/**
* 返回会话ID
*
* @return
*/
public abstract Serializable getId();
}
package org.fox.utils.web.scope;
/**
* Application作用域接口
*
* @author fox
*/
public interface ApplicationScope extends Scope {
}
然后,为了便于配置,定义了这两个作用域的实例工厂接口
package org.fox.utils.web.scope;
import javax.servlet.http.HttpServletRequest;
/**
* 负责创建SessionScope实例的工厂接口
*
* @author fox
*/
public interface SessionScopeFactory {
/**
* 创建SessionScope实例
*
* @param request
* 请求对象
* @return SessionScope实例
*/
public SessionScope createSessionScope(HttpServletRequest request);
}
package org.fox.utils.web.scope;
import javax.servlet.ServletContext;
/**
* 负责创建ApplicationScope实例的工厂接口
*
* @author fox
*/
public interface ApplicationScopeFactory {
/**
* 创建ApplicationScope实例
*
* @param application
* Web应用的ServletContext
* @return ApplicationScope实例
*/
public ApplicationScope createApplicationScope(ServletContext application);
}
--------------------------------------------------------------
在上面几个的接口的基础上,实现一个基于Servlet API的实现包
package org.fox.utils.web.scope.servlet;
import java.io.Serializable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.fox.utils.web.scope.SessionScope;
/**
* 基于Servlet API的SessionScope实现
*
* @author fox
*/
class ServletSessionScope implements SessionScope {
// Log
private static Log log = LogFactory.getLog(ServletSessionScope.class);
// Servlet API request
private HttpServletRequest request = null;
/**
* 创建ServletSessionScope,本方法只供本包内的ServletSessionScopeFactory调用<br>
* <i><strong> 注意ServletSessionScope并不在构造时就产生,只有在setAttribute()和getId()
* 时才会产生HttpSession</strong></i>
*
* @param request
*/
ServletSessionScope(HttpServletRequest request) {
this.request = request;
}
/*
* (non-Javadoc)
*
* @see org.fox.utils.web.scope.SessionScope#getId()
*/
@Override
public Serializable getId() {
return request.getSession().getId();
}
/*
* (non-Javadoc)
*
* @see org.fox.utils.web.scope.SessionScope#getAttribute(java.lang.String)
*/
@Override
public Object getAttribute(String key) {
log.debug("get application attribute [" + key + "]");
// 获取当前的HttpSession,如果没有会话就直接返回null,不必启动一个session
HttpSession session = request.getSession(false);
if (session == null) {
return null;
}
return session.getAttribute(key);
}
/*
* (non-Javadoc)
*
* @see org.fox.utils.web.scope.SessionScope#setAttribute(java.lang.String,
* java.io.Serializable)
*/
@Override
public void setAttribute(String key, Serializable value) {
log.debug("set attribute [" + key + "] = " + value);
request.getSession().setAttribute(key, value);
}
/*
* (non-Javadoc)
*
* @see
* org.fox.utils.web.scope.SessionScope#removeAttribute(java.lang.String)
*/
@Override
public void removeAttribute(String key) {
log.debug("remove attribute [" + key + "]");
// 获取当前的HttpSession,如果没有会话就直接返回,不必启动一个session
HttpSession session = request.getSession(false);
if (session != null) {
session.removeAttribute(key);
}
}
}
package org.fox.utils.web.scope.servlet;
import java.io.Serializable;
import javax.servlet.ServletContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.fox.utils.web.scope.ApplicationScope;
/**
* 基于Servlet API的ApplicationScope实现
*
* @author fox
*/
class ServletApplicationScope implements ApplicationScope {
// Log
private static Log log = LogFactory.getLog(ServletApplicationScope.class);
// Servlet API application
private ServletContext application = null;
/**
* 创建ServletApplicationScope,本方法只供本包内的ServletApplicationScopeFactory调用
*
* @param application
*/
ServletApplicationScope(ServletContext application) {
this.application = application;
}
/*
* (non-Javadoc)
*
* @see org.fox.utils.web.scope.Scope#getAttribute(java.lang.String)
*/
@Override
public Object getAttribute(String key) {
log.debug("get attribute [" + key + "]");
return application.getAttribute(key);
}
/*
* (non-Javadoc)
*
* @see org.fox.utils.web.scope.Scope#setAttribute(java.lang.String,
* java.io.Serializable)
*/
@Override
public void setAttribute(String key, Serializable value) {
log.debug("set attribute [" + key + "] = " + value);
application.setAttribute(key, value);
}
/*
* (non-Javadoc)
*
* @see org.fox.utils.web.scope.Scope#removeAttribute(java.lang.String)
*/
@Override
public void removeAttribute(String key) {
log.debug("remove attribute [" + key + "]");
application.removeAttribute(key);
}
}
package org.fox.utils.web.scope.servlet;
import javax.servlet.http.HttpServletRequest;
import org.fox.utils.web.scope.SessionScope;
import org.fox.utils.web.scope.SessionScopeFactory;
/**
* 基于Servlet API的SessionScope工厂
*
* @author fox
*/
public class ServletSessionScopeFactory implements SessionScopeFactory {
/*
* (non-Javadoc)
*
* @see
* org.fox.utils.web.scope.SessionScopeFactory#createSessionScope(javax.
* servlet.http.HttpServletRequest)
*/
@Override
public SessionScope createSessionScope(HttpServletRequest request) {
return new ServletSessionScope(request);
}
}
package org.fox.utils.web.scope.servlet;
import javax.servlet.ServletContext;
import org.fox.utils.web.scope.ApplicationScope;
import org.fox.utils.web.scope.ApplicationScopeFactory;
/**
* 基于Servlet API的ApplicationScope工厂
*
* @author fox
*/
public class ServletApplicationScopeFactory implements ApplicationScopeFactory {
/*
* (non-Javadoc)
*
* @see
* org.fox.utils.web.scope.ApplicationScopeFactory#createApplicationScope
* (javax.servlet.ServletContext)
*/
@Override
public ApplicationScope createApplicationScope(ServletContext application) {
return new ServletApplicationScope(application);
}
}
--------------------------------------------------------------
现在有了Scope的Servlet实现之后,再来看WebContext
package org.fox.utils.web.context;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.fox.utils.tools.AssertUtils;
import org.fox.utils.web.scope.ApplicationScope;
import org.fox.utils.web.scope.SessionScope;
import org.springframework.web.context.WebApplicationContext;
/**
* Web上下文,一个按线程处理的的Web上下文对象
*
* @author fox
*/
public class WebConext {
/* ----- ----- ----- ----- 实例管理方法 ----- ----- ----- ----- */
/**
* 根据线程保存的WebConext实例池
*/
private static ThreadLocal<WebConext> instancePool = new ThreadLocal<WebConext>();
/**
* 不可直接调用,实例使用getInstance()或initInstance()进行获取
*/
private WebConext() {
}
/**
* 获取当前线程的WebContext对象
*
* @return 当前线程的WebContext对象
*/
public static WebConext getInstance() {
WebConext webConext = instancePool.get();
AssertUtils.statusAssert(webConext != null, "WebContext 为空,请检查拦截器WebConextInterceptor的设置");
return webConext;
}
/**
* 为当前线程初始化一个WebContext对象,该方法仅供本包内WebConextInterceptor调用
*
* @return 当前线程的WebContext对象
*/
static WebConext initInstance() {
WebConext webConext = instancePool.get();
AssertUtils.statusAssert(webConext == null, "已存在WebContext,请检查拦截器WebConextInterceptor的设置");
webConext = new WebConext();
instancePool.set(webConext);
return webConext;
}
/**
* 清除当前线程的WebContext对象
*/
public static void clearInstance() {
instancePool.remove();
}
/* ----- ----- ----- ----- 全局成员的管理和获取方法 ----- ----- ----- ----- */
/**
* Spring的应用程序上下文
*/
private static WebApplicationContext applicationContext = null;
/**
* 设置Spring的应用程序上下文,该方法仅供本包内WebConextInterceptor调用
*
* @param applicationContext
* Spring的应用程序上下文
*/
static void setApplicationContext(WebApplicationContext applicationContext) {
WebConext.applicationContext = applicationContext;
}
/**
* 返回Spring的应用程序上下文
*
* @return
*/
public static WebApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* ApplicationScope,类似ServletContext的对象
*/
private static ApplicationScope application = null;
/**
* 设置ApplicationScope,该方法仅供本包内WebConextInterceptor调用
*
* @param application
* ApplicationScope
*/
static void setApplication(ApplicationScope application) {
WebConext.application = application;
}
/**
* 返回ApplicationScope
*
* @return ApplicationScope
*/
public static ApplicationScope getApplication() {
return WebConext.application;
}
/**
* 获取当前工程的ContextPath
*
* @return 当前工程的ContextPath
*/
public static String getContextPath() {
return applicationContext.getServletContext().getContextPath();
}
/**
* 获取指定路径的真实路径
*
* @param path
* 指定路径
* @return 指定路径的真实路径
*/
public static String getRealPath(String path) {
return applicationContext.getServletContext().getRealPath(path);
}
/* ----- ----- ----- ----- 实例成员的管理和获取方法 ----- ----- ----- ----- */
/**
* SessionScope,类似HttpSession的对象
*/
private SessionScope session = null;
/**
* 设置SessionScope,该方法仅供本包内WebConextInterceptor调用
*
* @param session
* SessionScope
*/
void setSession(SessionScope session) {
this.session = session;
}
/**
* 返回SessionScope
*
* @return SessionScope
*/
public SessionScope getSession() {
return session;
}
/**
* HttpServletRequest
*/
private HttpServletRequest request = null;
/**
* 设置HttpServletRequest,该方法仅供本包内WebConextInterceptor调用
*
* @param request
*/
void setRequest(HttpServletRequest request) {
this.request = request;
}
/**
* 返回HttpServletRequest
*
* @return HttpServletRequest
*/
public HttpServletRequest getRequest() {
return request;
}
/**
* HttpServletResponse
*/
private HttpServletResponse response = null;
/**
* 设置HttpServletResponse,该方法仅供本包内WebConextInterceptor调用
*
* @param response
* HttpServletResponse
*/
void setResponse(HttpServletResponse response) {
this.response = response;
}
/**
* 返回HttpServletResponse
*
* @return HttpServletResponse
*/
public HttpServletResponse getResponse() {
return response;
}
}
为了在应用总可以直接的通过WebContext.getInstance()拿到WebContext对象,需要一个拦截器,这个就要看具体使用的Web框架了,它可以是一个JFinal,Struts的拦截器,或者Servlet的Filter,这里就提供一个基于Spring @MVC的拦截器来实现这个功能
package org.fox.utils.web.context;
import javax.annotation.PostConstruct;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.fox.utils.tools.AssertUtils;
import org.fox.utils.web.scope.ApplicationScopeFactory;
import org.fox.utils.web.scope.SessionScopeFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
/**
* 维护WebContext的拦截器
*
* @author fox
*/
public class WebConextInterceptor extends HandlerInterceptorAdapter implements ApplicationContextAware,
ServletContextAware {
// Log
private Log log = LogFactory.getLog(WebConextInterceptor.class);
/* ----- ----- ----- ----- Spring注入上下文管理方法 ----- ----- ----- ----- */
// Spring应用程序上下文
private WebApplicationContext applicationContext = null;
// Servlet Application上下文
private ServletContext application = null;
/**
* 设置Spring应用程序上下文
*
* @param applicationContext
* Spring应用程序上下文,由Spring容器提供
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
AssertUtils.statusAssert(applicationContext instanceof WebApplicationContext,
"当前Spring应用程序上下文不是WebApplicationContext");
this.applicationContext = (WebApplicationContext) applicationContext;
}
/**
* Servlet Application上下文
*
* @param application
* Servlet Application上下文,由Spring容器提供
*/
@Override
public void setServletContext(ServletContext application) {
this.application = application;
}
/* ----- ----- ----- ----- 作用域工厂注入 ----- ----- ----- ----- */
// ApplicationScope工厂
private ApplicationScopeFactory applicationScopeFactory = null;
// SessionScope工厂
private SessionScopeFactory sessionScopeFactory = null;
/**
* 注入ApplicationScope工厂
*
* @param applicationScopeFactory
* ApplicationScope工厂
*/
@Required
public void setApplicationScopeFactory(ApplicationScopeFactory applicationScopeFactory) {
this.applicationScopeFactory = applicationScopeFactory;
}
/**
* 注入SessionScope工厂
*
* @param sessionScopeFactory
* SessionScope工厂
*/
@Required
public void setSessionScopeFactory(SessionScopeFactory sessionScopeFactory) {
this.sessionScopeFactory = sessionScopeFactory;
}
/* ----- ----- ----- ----- 拦截器启动的时候初始化WebContext的类成员 ----- ----- ----- ----- */
/**
* 拦截器启动的时候初始化WebContext的全局成员
*/
@PostConstruct
public void initApplication() {
log.debug("Init WebContext Global Property!");
// 初始化 Spring应用程序上下文 和 ApplicationScope
WebConext.setApplicationContext(applicationContext);
WebConext.setApplicationContext(applicationContext);
WebConext.setApplication(applicationScopeFactory.createApplicationScope(application));
}
/* ----- ----- ----- 请求处理前初始化WebContext实例及其实例成员 ----- ----- ----- */
/**
* 在页面处理之前初始化WebContext实例并设置实例成员
*
* @param request
* 当前请求的请求对象
* @param response
* 当前请求的响应对象
* @param handler
* 当前请求匹配的handler实例
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
log.debug("Init WebContext!");
WebConext webConext = WebConext.initInstance();
webConext.setRequest(request);
webConext.setResponse(response);
webConext.setSession(sessionScopeFactory.createSessionScope(request));
return true;
}
/**
* 在处理结束后去除环境上下文
*
* @param request
* 当前请求的请求对象
* @param response
* 当前请求的响应对象
* @param handler
* 当前请求匹配的handler实例
* @param exception
* 执行过程中抛出的异常,null表示正常结束
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception exception) {
log.debug("Clear WebContext!");
WebConext.clearInstance();
}
}