WebContext与session/application作用域的封装

写这个东西的起因是@黄勇 写的封装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();
	}
}

转载于:https://my.oschina.net/shishuifox/blog/164286

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值