本文共 5052 字,大约阅读时间需要 16 分钟。
Session管理 |
作者:未知 来源:月光软件站 加入时间:2005-2-28 月光软件站 |
id="cproIframe1" align="center,center" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" width="300" height="250" allowtransparency="" src="http://cpro.baidu.com/cpro/ui/uijs.php?tn=text_default_300_250&n=williamlongcpr&rsi1=250&rsi0=300&rad=&ch=0&cad=0&aurl=&rss7=%23FFFFFF&cpa=1&fv=10&if=0&word=http%3A%2F%2Fwww.moon-soft.com%2Fdoc%2F24787.htm&refer=http%3A%2F%2Fwww.baidu.com%2Fs%3Fwd%3Dsession%2Bjava%2B%25B6%25E0%25CF%25DF%25B3%25CC%26oq%3Dsession%2Bja%26sug%3Dsession%2Bjava%26n%3D1%26rsp%3D0%26inputT%3D100879&lmt=1287826553&csp=1280,800&ccd=24&chi=1&cja=true&cpl=11&cmi=48&cce=true&csl=zh-CN&rt=4&dt=1309865811&ev=50331648&jn=3&cn=1&rss0=%23ffffff&rss1=%23ffffff&rss2=%23000000&rss3=%23444444&rss4=%23444444&rss5=&rss6=%23444444&rsi5=4&ts=1&at=7&did=1&c01=0&jk=8455fbd11780c775&tu=u158698"> 在各种Session 管理方案中, ThreadLocal 模式得到了大量使用。ThreadLocal 是 Java中一种较为特殊的线程绑定机制。通过ThreadLocal存取的数据,总是与当前线程相关,也就是说,JVM 为每个运行的线程,绑定了私有的本地实例存取空间,从而为多线程环境常出现的并发访问问题提供了一种隔离机制。首先,我们需要知道,SessionFactory负责创建Session,SessionFactory是线程安全的,多个并发线程可以同时访问一个SessionFactory 并从中获取Session 实例。而Session并非线程安全,也就是说,如果多个线程同时使用一个Session实例进行数据存取,则将会导致Session 数据存取逻辑混乱。下面是一个典型的Servlet,我们试图通过一个类变量session实现Session的重用,以避免每次操作都要重新创建:public class TestServlet extends HttpServlet { private Session session;public void doGet( HttpServletRequest request,HttpServletResponse response)throws ServletException, IOException { session = getSession();doSomething();session.flush();}public void doSomething(){ ......//基于session的存取操作}}代码看上去正确无误,甚至在我们单机测试的时候可能也不会发生什么问题,但这样的代Hibernate Developer’s Guide Version 1.0September 2, 2004 So many open source projects. Why not Open your Documents?码一旦编译部署到实际运行环境中,接踵而来的莫名其妙的错误很可能会使得我们摸不找头脑。问题出在哪里?首先,Servlet 运行是多线程的,而应用服务器并不会为每个线程都创建一个Servlet实例,也就是说,TestServlet在应用服务器中只有一个实例(在Tomcat中是这样,其他的应用服务器可能有不同的实现),而这个实例会被许多个线程并发调用,doGet 方法也将被不同的线程反复调用,可想而知,每次调用doGet 方法,这个唯一的TestServlet 实例的session 变量都会被重置,线程A 的运行过程中,其他的线程如果也被执行,那么session的引用将发生改变,之后线程A 再调用session,可能此时的session 与其之前所用的session就不再一致,显然,错误也就不期而至。ThreadLocal的出现,使得这个问题迎刃而解。我们对上面的例子进行一些小小的修改:public class TestServlet extends HttpServlet { private ThreadLocal localSession = new ThreadLocal();public void doGet( HttpServletRequest request,HttpServletResponse response)throws ServletException, IOException { localSession.set(getSession());doSomething();session.flush();}public void doSomething(){ Session session = (Session)localSession.get();......//基于session的存取操作}}可以看到,localSession 是一个ThreadLocal 类型的对象,在doGet 方法中,我们通过其set 方法将获取的session 实例保存,而在doSomething 方法中,通过get 方法取出session实例。这也就是ThreadLocal的独特之处,它会为每个线程维护一个私有的变量空间。实际上,其实现原理是在JVM 中维护一个Map,这个Map的key 就是当前的线程对象,而value则是线程通过ThreadLocal.set方法保存的对象实例。当线程调用ThreadLocal.get方法时,ThreadLocal会根据当前线程对象的引用,取出Map中对应的对象返回。这样,ThreadLocal通过以各个线程对象的引用作为区分,从而将不同线程的变量隔离开来。Hibernate官方开发手册的示例中,提供了一个通过ThreadLocal维护Session的好 榜样:public class HibernateUtil { private static SessionFactory sessionFactory;static { try { // Create the SessionFactorysessionFactory = newConfiguration().configure().buildSessionFactory();} catch (HibernateException ex) { throw new RuntimeException("Configuration problem: " + ex.getMessage(),ex);}}public static final ThreadLocal session = new ThreadLocal();public static Session currentSession() throws HibernateException{ Session s = (Session) session.get();// Open a new Session, if this Thread has none yetif (s == null) { s = sessionFactory.openSession();session.set(s);}return s;}public static void closeSession() throws HibernateException { Session s = (Session) session.get();session.set(null);if (s != null)s.close();}}在代码中,只要借助上面这个工具类获取Session 实例,我们就可以实现线程范围内的Session 共享,从而避免了在线程中频繁的创建和销毁Session 实例。不过注意在线程结束时关闭Session。同时值得一提的是,新版本的Hibernate在处理Session的时候已经内置了延迟加载机 制,只有在真正发生数据库操作的时候,才会从数据库连接池获取数据库连接,我们不必过于担心Session的共享会导致整个线程生命周期内数据库连接被持续占用。
对于Web程序 而言,我们可以借助Servlet2.3规范中新引入的Filter机制,轻松实现线程生命周期内的Session管理(关于Filter的具体描述,请参考Servlet2.3规范)。Filter的生命周期贯穿了其所覆盖的Servlet(JSP也可以看作是一种特殊的Servlet)及其底层对象。Filter在Servlet被调用之前执行,在Servlet调用结束之后结束。因此,在Filter 中管理Session 对于Web 程序而言就显得水到渠成。下面是一个通过Filter 进行Session管理的典型案例:public class PersistenceFilter implements Filter{ protected static ThreadLocal hibernateHolder = new ThreadLocal();public void doFilter(ServletRequest request, ServletResponseresponse, FilterChain chain)throws IOException, ServletException{ hibernateHolder.set(getSession());try{ ……chain.doFilter(request, response);……}finally{ Session sess = (Session)hibernateHolder.get();if (sess != null){ hibernateHolder.set(null);try{ sess.close();}catch (HibernateException ex) { throw new ServletException(ex);}}}}……Hibernate Developer’s Guide Version 1.0September 2, 2004 So many open source projects. Why not Open your Documents?}通过在doFilter中获取和关闭Session,并在周期内运行的所有对象(Filter链中其余的Filter,及其覆盖的Servlet 和其他对象)对此Session 实例进行重用,保证了一个Http Request处理过程中只占用一个Session,提高了整体性能表现。在实际设计中,Session的重用做到线程级别一般已经足够,企图通过HttpSession实现用户级的Session重用反而可能导致其他的问题。凡事不能过火,Session重用也一样。J |
转载地址:http://qspob.baihongyu.com/