现在的位置: 首页 > 综合 > 正文

OpenSessionInViewFilter 原理

2013年08月02日 ⁄ 综合 ⁄ 共 4180字 ⁄ 字号 评论关闭

 OpenSessionInView在每次Request请求的时候都打开一个Session放在ThreadLocal里面,该Session在Request期间一直可以使用,当View应用层的逻辑结束后,即filterChain.doFilter(request,
response);完成后才会通过OpenSessionInViewFilter的closeSession方法关闭session。OpenSessionInViewFilter调用流程: request->open session并开始transaction->controller->View->结束transaction并close session. 

public class OpenSessionInViewFilter extends OncePerRequestFilter {

}

OncePerRequestFilter过滤器doFilter:

	public final void doFilter(ServletRequest request,
			ServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		if (!(request instanceof HttpServletRequest)
				|| !(response instanceof HttpServletResponse)) {
			throw new ServletException(
					"OncePerRequestFilter just supports HTTP requests");
		}
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		HttpServletResponse httpResponse = (HttpServletResponse) response;

		String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
		if (request.getAttribute(alreadyFilteredAttributeName) != null
				|| shouldNotFilter(httpRequest)) {
			// Proceed without invoking this filter...
			filterChain.doFilter(request, response);
		} else {
			// Do invoke this filter...
			request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
			try {
				doFilterInternal(httpRequest, httpResponse, filterChain);
			} finally {
				// Remove the "already filtered" request attribute for this
				// request.
				request.removeAttribute(alreadyFilteredAttributeName);
			}
		}
	}

调用了子类的doFilterInternal(httpRequest, httpResponse, filterChain);,也就是OpenSessionInViewFilter的doFilterInternal(httpRequest,
httpResponse, filterChain);

	protected void doFilterInternal(HttpServletRequest request,
			HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		SessionFactory sessionFactory = lookupSessionFactory(request);// ①
		boolean participate = false;// ②

		if (isSingleSession()) { // 配置文件中配置的
									// <param-name>singleSession</param-name>
			// single session mode
			if (TransactionSynchronizationManager.hasResource(sessionFactory)) {// ③
				// Do not modify the Session: just set the participate flag.
				participate = true;
			} else {
				logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter");
				Session session = getSession(sessionFactory);
				TransactionSynchronizationManager.bindResource(sessionFactory,
						new SessionHolder(session));// ④
			}
		} else {
			// deferred close mode
			if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) {
				// Do not modify deferred close: just set the participate flag.
				participate = true;
			} else {
				SessionFactoryUtils.initDeferredClose(sessionFactory);
			}
		}

		try {
			filterChain.doFilter(request, response);// ⑤
		}

		finally {
			if (!participate) {
				if (isSingleSession()) { // ⑥
					// single session mode
					SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager
							.unbindResource(sessionFactory);
					logger.debug("Closing single Hibernate Session in OpenSessionInViewFilter");
					closeSession(sessionHolder.getSession(), sessionFactory);
				} else {
					// deferred close mode
					SessionFactoryUtils.processDeferredClose(sessionFactory);
				}
			}
		}
	}

该方法都主要作用就是处理请求在当前线程共享同一个session。

① 获得一个SessionFactory

② 该布尔值用于标识过滤器结束时是否进行关闭session

public static
boolean hasResource(Object key) {
          Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
          Object value = doGetResource(actualKey);
          return (value != null);
}

       unwrapResourceIfNecessary:解除包装

③ 判断能否在当前线程中取得sessionFactory对象对应的session,第一次访问必然为false,如果有participate=true,意味着不关闭当前Session(具体看finally部分),以便整个请求中都使用同一个Session(例如:Action之间的转发,虽然过滤了多次,但还是同一个请求)

④在③中判断了当前线程中有没有对应都session,第一次请求肯定是没有都。自然而然就要创建一个,并绑定到当前线程中,SessionHolder是session的一层封装,里面有个存放session的map,而且是线程同步的Collections.synchronizedMap(new
HashMap(1));

 
         private final Map sessionMap = Collections.synchronizedMap(new HashMap(1));
核心方法是getValidatedSession 取得一个open状态下的session,并且取出后从map中移出.

public
Session getValidatedSession() {
          return getValidatedSession(DEFAULT_KEY);
}

public
Session getValidatedSession(Object key) {
          Session session = (Session) this.sessionMap.get(key);
          // Check for dangling Session that's around but already closed.
          // Effectively an assertion: that should never happen in practice.
          // We'll seamlessly remove the Session here, to not let it cause
          // any side effects.
          if (session != null && !session.isOpen()) {
           
        
this.sessionMap.remove(key);
           
        
session = null;
          }
          return session;
}

⑤ 完整的请求处理

⑥ 当请求结束时,对于singleSession模式,要取消session的绑定,线程使用过后并不会销毁,因为web容器的线程是采用线程池机制的,所以最后一定要关闭session(closeSession);

抱歉!评论已关闭.