在一个WEB程序中,你想不想在一个JAVABEAN中获取到httpservletrequest对象?
只需要使用一个filter就可以搞定了!!
这是通过
ThreadLocal实现的,下面引用一段定义:
ThreadLocal
是什么
ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是thread local variable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。线程局部变量并不是Java的新发明,在其它的一些语言编译器实现(如IBM XL FORTRAN)中,它在语言的层次提供了直接的支持。因为Java中没有提供在语言层次的直接支持,而是提供了一个ThreadLocal的类来提供支持,所以,在Java中编写线程局部变量的代码相对比较笨拙,这也许是线程局部变量没有在Java中得到很好的普及的一个原因吧。
ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是thread local variable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。线程局部变量并不是Java的新发明,在其它的一些语言编译器实现(如IBM XL FORTRAN)中,它在语言的层次提供了直接的支持。因为Java中没有提供在语言层次的直接支持,而是提供了一个ThreadLocal的类来提供支持,所以,在Java中编写线程局部变量的代码相对比较笨拙,这也许是线程局部变量没有在Java中得到很好的普及的一个原因吧。
其实hibernate中获取session的代码正式利用了这种原理:
package com.dcsoft.sbgl.hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.cfg.Configuration;
/**
* Configures and provides access to Hibernate sessions, tied to the
* current thread of execution. Follows the Thread Local Session
* pattern, see {@link http://hibernate.org/42.html }.
*/
public class HibernateSessionFactory {
/**
* Location of hibernate.cfg.xml file.
* Location should be on the classpath as Hibernate uses
* #resourceAsStream style lookup for its configuration file.
* The default classpath location of the hibernate config file is
* in the default package. Use #setConfigFile() to update
* the location of the configuration file for the current session.
*/
private static String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml";
private static final ThreadLocal threadLocal = new ThreadLocal();
private static Configuration configuration = new Configuration();
private static org.hibernate.SessionFactory sessionFactory;
private static String configFile = CONFIG_FILE_LOCATION;
private HibernateSessionFactory() {
}
/**
* Returns the ThreadLocal Session instance. Lazy initialize
* the <code>SessionFactory</code> if needed.
*
* @return Session
* @throws HibernateException
*/
public static Session getSession() throws HibernateException {
Session session = (Session) threadLocal.get();
if (session == null || !session.isOpen()) {
if (sessionFactory == null) {
rebuildSessionFactory();
}
session = (sessionFactory != null) ? sessionFactory.openSession()
: null;
threadLocal.set(session);
}
return session;
}
/**
* Rebuild hibernate session factory
*
*/
public static void rebuildSessionFactory() {
try {
configuration.configure(configFile);
sessionFactory = configuration.buildSessionFactory();
} catch (Exception e) {
System.err
.println("%%%% Error Creating SessionFactory %%%%");
e.printStackTrace();
}
}
/**
* Close the single hibernate session instance.
*
* @throws HibernateException
*/
public static void closeSession() throws HibernateException {
Session session = (Session) threadLocal.get();
threadLocal.set(null);
if (session != null) {
session.close();
}
}
/**
* return session factory
*
*/
public static org.hibernate.SessionFactory getSessionFactory() {
return sessionFactory;
}
/**
* return session factory
*
* session factory will be rebuilded in the next call
*/
public static void setConfigFile(String configFile) {
HibernateSessionFactory.configFile = configFile;
sessionFactory = null;
}
/**
* return hibernate configuration
*
*/
public static Configuration getConfiguration() {
return configuration;
}
}
从而保证了在一个线程中的session是同一个,既然这种方式能够被hibernate使用,那么,我们自己为什么不能利用一下呢??
下面我们想在任何地方都可以获取到request对象,因为对现在大部分服务器来说,每一次访问均在一个线程中。这就为我们的构思提供了可能性;
原理:
在开始一次请求的开始增加一个filter,将当前的request存入当前线程的一个ThreadLocal变量中。这样,我们就可以在任何地方都能够获取到request了。
下面是部分代码:
Filter:
package com.hf.servlet.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
/**
*
将此过滤器配置在 web.xml中,就可以在任何地方获取到当前的request了。
* @author Administrator
*
*/
public class WebContextHolderFilter implements Filter {
private static Logger logger = Logger.getLogger(WebContextHolderFilter.class);
/**
*
在这里可以进行字符集的设置
*/
public void init(FilterConfig config) throws ServletException {
logger.info("
初始化WebContextHolderFilter…………开始");
logger.info("
初始化WebContextHolderFilter………… 结束");
}
public void doFilter(ServletRequest srequest, ServletResponse response, FilterChain chain) throws IOException, ServletException {
logger.info("
进入一个WebContextHolderFilter的dofilter 方法");
if(WebContextHolder.get()==null){
logger.info("WebContextHolder
还没有相应的request,放入开始");
WebContextHolder.set((HttpServletRequest)srequest);
logger.info("WebContextHolder
还没有相应的request,放入结束");
}else{
logger.info("WebContextHolder
已经有相应的request,不进行重复放入");
}
logger.info("
后面的filter链 开始");
chain.doFilter(srequest, response);
logger.info("
后面的filter链 结束");
}
public void destroy() {
// TODO Auto-generated method stub
}
}
ThreadLocal
持有类:
package com.hf.servlet.filter;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
public class WebContextHolder {
private static Logger logger = Logger.getLogger(WebContextHolder.class);
private static ThreadLocal requestHolder = new ThreadLocal();
public static void set(HttpServletRequest request){
logger.info("
在filter中将request加入当前的ThreadLocal");
requestHolder.set(request);
}
public static HttpServletRequest get(){
logger.info("
从当前的ThreadLocal中获取当前的request");
return (HttpServletRequest) requestHolder.get();
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
使用方式:
可以写在任一地方
HttpServletRequest myRequest=WebContextHolder.get();
myRequest.setAttribute("test",myRequest.getRemoteAddr());
myRequest.getRequestDispatcher("/testResult.jsp").forward(myRequest,response);
要源代码的可以发邮件给我。
演示代码使用:
点击连接的时候会在servlet中调用HttpServletRequest
HttpServletRequest myRequest=WebContextHolder.get();
myRequest=WebContextHolder.get();
myRequest.setAttribute("test",myRequest.getRemoteAddr());
设置一个test 参数,请注意myRequest是从WebContextHolder中取到的^_^
然后转向到testResult.jsp,在页面上打印出存入的数据,这时是取的暗含对象request
Ps
:一直测试着没什么问题,但刚刚出现了null异常,说myRequest为null;并不是经常报出,不知道为什么,
将doFilter中的代码改为:
logger.info("
进入一个WebContextHolderFilter的dofilter 方法");
if(WebContextHolder.get()==null){
logger.info("WebContextHolder
还没有相应的request,放入开始");
WebContextHolder.set((HttpServletRequest)srequest);
logger.info("WebContextHolder
还没有相应的request,放入结束");
}else{
logger.info("WebContextHolder
已经有相应的request,不进行重复放入");
}
logger.info("
后面的filter链 开始");
chain.doFilter(srequest, response);
logger.info("
后面的filter链 结束");
logger.info("
清空filter中的request 开始");
WebContextHolder.set(null);
logger.info("
清空filter中的request 结束");
到目前为止还没出现问题,不知道为什么,明白的指点一下。
现在只能说慎用了暂时……