Spring对HibernateSession的管理之OpenSessionInViewFilter
由于Java EE的学习进入到了一个重要的阶段——开始学习SSH框架(Struts2+Spring+Hibernate)了,在初步认识了框架的整合后,我对Spring是如何管理Session(Hibernate)抱有一些疑问。在进行了一些研究后有一些心得,在此记录下来,以便自己日后查询和供后来者作为借鉴。
进入正题:
首先我们打开org.springframework.orm.hibernate3.support.OpenSessionInViewFilter,看不出什么什么门道,连过滤器的doFilter方法都没有。很明显,它会调用父类的doFilter方法,而我们正是需要研究Spring是怎样通过过滤器来实现Session有效期的延长的(注意:Spring默认将Session与Request进程绑定,Request生命周期即Session能够存在的时长,所以在处理器处理请求后向JSP页面跳转时Session随着Request的生命周期而失效,以致页面无法使用Session)。
那么我们马上进入OpenSessionInViewFilter的父类org.springframework.web.filter.OncePerRequestFilter看看(省略注释):
1 public abstract class OncePerRequestFilter extends GenericFilterBean { 2 3 public static final String ALREADY_FILTERED_SUFFIX = ".FILTERED"; 4 5 public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) 6 throws ServletException, IOException { 7 8 if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) { 9 throw new ServletException("OncePerRequestFilter just supports HTTP requests"); 10 } 11 HttpServletRequest httpRequest = (HttpServletRequest) request; 12 HttpServletResponse httpResponse = (HttpServletResponse) response; 13 14 String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName(); 15 if (request.getAttribute(alreadyFilteredAttributeName) != null || shouldNotFilter(httpRequest)) { 16 filterChain.doFilter(request, response); 17 } 18 else { 19 request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE); 20 try { 21 doFilterInternal(httpRequest, httpResponse, filterChain); 22 } 23 finally { 24 request.removeAttribute(alreadyFilteredAttributeName); 25 } 26 } 27 } 28 29 protected String getAlreadyFilteredAttributeName() { 30 String name = getFilterName(); 31 if (name == null) { 32 name = getClass().getName(); 33 } 34 return name + ALREADY_FILTERED_SUFFIX; 35 } 36 37 protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { 38 return false; 39 } 40 41 protected abstract void doFilterInternal( 42 HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 43 throws ServletException, IOException; 44 45 }
从上面代码的15行到21行左右大家可以看出:这个方法进行了一个判断,看是否需要延长Session存在时间到整个请求响应过程,而实现延长时间的方法就是OpenSessionInViewFilter重写了的方法(省略部分注释):
1 package org.springframework.orm.hibernate3.support; 2 3 import java.io.IOException; 4 import javax.servlet.FilterChain; 5 import javax.servlet.ServletException; 6 import javax.servlet.http.HttpServletRequest; 7 import javax.servlet.http.HttpServletResponse; 8 9 import org.hibernate.FlushMode; 10 import org.hibernate.Session; 11 import org.hibernate.SessionFactory; 12 13 import org.springframework.dao.DataAccessResourceFailureException; 14 import org.springframework.orm.hibernate3.SessionFactoryUtils; 15 import org.springframework.orm.hibernate3.SessionHolder; 16 import org.springframework.transaction.support.TransactionSynchronizationManager; 17 import org.springframework.web.context.WebApplicationContext; 18 import org.springframework.web.context.support.WebApplicationContextUtils; 19 import org.springframework.web.filter.OncePerRequestFilter; 20 21 public class OpenSessionInViewFilter extends OncePerRequestFilter { 22 23 public static final String DEFAULT_SESSION_FACTORY_BEAN_NAME = "sessionFactory"; 24 25 26 private String sessionFactoryBeanName = DEFAULT_SESSION_FACTORY_BEAN_NAME; 27 28 private boolean singleSession = true; 29 30 private FlushMode flushMode = FlushMode.MANUAL; 31 32 public void setSessionFactoryBeanName(String sessionFactoryBeanName) { 33 this.sessionFactoryBeanName = sessionFactoryBeanName; 34 } 35 36 protected String getSessionFactoryBeanName() { 37 return this.sessionFactoryBeanName; 38 } 39 40 public void setSingleSession(boolean singleSession) { 41 this.singleSession = singleSession; 42 } 43 44 protected boolean isSingleSession() { 45 return this.singleSession; 46 } 47 48 public void setFlushMode(FlushMode flushMode) { 49 this.flushMode = flushMode; 50 } 51 52 protected FlushMode getFlushMode() { 53 return this.flushMode; 54 } 55 56 57 @Override 58 protected void doFilterInternal( 59 HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 60 throws ServletException, IOException { 61 62 SessionFactory sessionFactory = lookupSessionFactory(request); 63 boolean participate = false; 64 65 if (isSingleSession()) { 66 // single session mode 67 if (TransactionSynchronizationManager.hasResource(sessionFactory)) { 68 // Do not modify the Session: just set the participate flag. 69 participate = true; 70 } 71 else { 72 logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter"); 73 Session session = getSession(sessionFactory); 74 TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session)); 75 } 76 } 77 else { 78 // deferred close mode 79 if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) { 80 // Do not modify deferred close: just set the participate flag. 81 participate = true; 82 } 83 else { 84 SessionFactoryUtils.initDeferredClose(sessionFactory); 85 } 86 } 87 88 try { 89 filterChain.doFilter(request, response); 90 } 91 92 finally { 93 if (!participate) { 94 if (isSingleSession()) { 95 // single session mode 96 SessionHolder sessionHolder = 97 (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory); 98 logger.debug("Closing single Hibernate Session in OpenSessionInViewFilter"); 99 closeSession(sessionHolder.getSession(), sessionFactory); 100 } 101 else { 102 // deferred close mode 103 SessionFactoryUtils.processDeferredClose(sessionFactory); 104 } 105 } 106 } 107 } 108 109 protected SessionFactory lookupSessionFactory(HttpServletRequest request) { 110 return lookupSessionFactory(); 111 } 112 113 protected SessionFactory lookupSessionFactory() { 114 if (logger.isDebugEnabled()) { 115 logger.debug("Using SessionFactory '" + getSessionFactoryBeanName() + "' for OpenSessionInViewFilter"); 116 } 117 WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); 118 return wac.getBean(getSessionFactoryBeanName(), SessionFactory.class); 119 } 120 121 protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException { 122 Session session = SessionFactoryUtils.getSession(sessionFactory, true); 123 FlushMode flushMode = getFlushMode(); 124 if (flushMode != null) { 125 session.setFlushMode(flushMode); 126 } 127 return session; 128 } 129 130 protected void closeSession(Session session, SessionFactory sessionFactory) { 131 SessionFactoryUtils.closeSession(session); 132 } 133 134 }
这里面org.springframework.orm.hibernate3.SessionFactoryUtils拥有很重要的地位,其重要一个字段如下:
ThreadLocal<Map<SessionFactory, Set<Session>>> deferredCloseHolder =
new NamedThreadLocal<Map<SessionFactory, Set<Session>>>("Hibernate Sessions registered for deferred close")
另外org.springframework.transaction.support.TransactionSynchronizationManager也是很为重要:
ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<Map<Object, Object>>("Transactional resources") 及其他属性和方法与上面SessionFactoryUtils中的属性和方法一起,在OpenSessionInViewFilter#doFilterInternal方法中将Session绑定到ThreadLocal,在整个请求响应过程完成后关闭Session。
也就是说:原本Spring是将Session绑定到Request所在线程里,而使用了OpenSessionInViewFilter过滤器后,会将Session绑定到当前进程中。
也是最近越来越烦躁,总是不能将整篇博文做到完整,本文章中的所有内容都可以在网络上搜索得到,扩展的知识却没来得及添加进来。
另外,本篇博文所用资源src.zip。
(最后编辑时间2012-10-09 22:59:36)