`
deejay
  • 浏览: 144958 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

深入OpenSessionInViewFilter内幕

    博客分类:
  • ssh2
阅读更多

最近不知道干些什么好,无论做什么都觉得没劲,为了强迫自己学习,准备研究些开源项目的源码,从中找点乐趣,先来个大家熟悉的OpenSessionInViewFilter,下面我将逐步分析OpenSessionInViewFilter 的源码,了解OpenSessionInViewFilter 的原理,欣赏spring优雅的代码,本文只分析源码不做功能介绍,如果有朋友不熟悉OpenSessionInViewFilter,但是又有兴致阅读此文可以先百度或google一下.
Spring中对OpenSessionInViewFilter的描述如下:
     它是一个Servlet2.3过滤器,用来把一个Hibernate Session和一次完整的请求过程对应的线程相绑定。目的是为了实现"Open Session in View"的模式。例如: 它允许在事务提交之后延迟加载显示所需要的对象。
下面从处理请求的入口读起,下面所指的session均为hibernate session不再特别说明,本文观点纯属个人观点如有错误请批评指正不胜感激.
转载请注明:来自http://blog.csdn.net/sunyujia/ 作者:孙钰佳

//这个方法是OpenSessionInViewFilter 的父类OncePerRequestFilter(抽象类)的方法,是过滤器的入口,是处理请求的第一个方法.
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {
    //首选判断进行过滤的是否是http请求
    if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
        throw new ServletException("OncePerRequestFilter just supports HTTP requests");
    }
    //如果是http请求的话进行强转
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    HttpServletResponse httpResponse = (HttpServletResponse) response;
    //alreadyFilteredAttributeName 是一个标识,用于判断是否需要进行OpenSessionInViewFilter
    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);
        //下面这个方法是abstract方法由OpenSessionInViewFilter 实现,是OpenSessionInViewFilter 的核心方法
        doFilterInternal(httpRequest, httpResponse, filterChain);
    }
}
好下面让我们进入doFilterInternal一探究竟

protected void doFilterInternal(HttpServletRequest request,
        HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {
    /**
     * 从spring的上下文中取得SessionFactory对象
     * WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
     * return (SessionFactory) wac.getBean(getSessionFactoryBeanName(),SessionFactory.class);
     * getSessionFactoryBeanName()方法默认返回"sessionFactory"字符串,在spring配置文件中可要注意了,别写错了.
     */
    SessionFactory sessionFactory = lookupSessionFactory(request);
    boolean participate = false;// 标识过滤器结束时是否进行关闭session等后续处理
    if (isSingleSession()) {//单session模式
        //判断能否在当前线程中取得sessionFactory对象对应的session
        if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
            //当能够找到session的时候证明会有相关类处理session的收尾工作,这个过滤器不能进行关闭session操作,否则会出现重复关闭的情况.
            participate = true;//但我并没有想出正常使用的情况下什么时候会出现这种情况.
        } else {
            Session session = getSession(sessionFactory);//当前线程取不到session的时候通过sessionFactory创建,下面还会详细介绍此方法
            //将session绑定到当前的线程中,SessionHolder是session的一层封装,里面有个存放session的map,而且是线程同步的Collections.synchronizedMap(new HashMap(1));
            //但是单session模式下一个SessionHolder对应一个session,核心方法是getValidatedSession 取得一个open状态下的session,并且取出后从map中移出.
            TransactionSynchronizationManager.bindResource(sessionFactory,
                    new SessionHolder(session));
        }
    } else {//这段是非单session模式的处理情况,没有研究.但粗略看上去,大概思想是一样的
        if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) {
            participate = true;
        } else {
            SessionFactoryUtils.initDeferredClose(sessionFactory);
        }
    }
    try {
        //将session绑定到了当前线程后,就该处理请求了
        filterChain.doFilter(request, response);
    }finally {
        if (!participate) {
            if (isSingleSession()) {
                //当请求结束时,对于单session模式,这时候要取消session的绑定,因为web容器(Tomcat等)的线程是采用线程池机制的,线程使用过后并不会销毁.
                SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager
                        .unbindResource(sessionFactory);
                //取消绑定只是取消session对象和线程对象之间的引用,还没有关闭session,不关闭session相对于不关闭数据库连接,所以这里要关闭session
                closeSession(sessionHolder.getSession(), sessionFactory);
            } else {
                //非单session模式,没有研究.
                SessionFactoryUtils.processDeferredClose(sessionFactory);
            }
        }
    }
}
下面详细介绍TransactionSynchronizationManager的几个关键的方法

public abstract class TransactionSynchronizationManager {
//线程局部变量,为每一个使用该变量的线程都提供一个变量值的副本,每一个线程都可以独立地改变自己的副本,而不会与其它线程的副本冲突,用于存放session
private static final ThreadLocal resources = new ThreadLocal();
public static boolean hasResource(Object key) {//判断当前线程是否已经绑定了session,key是sessionFactory对象,一个sessionFactory可以绑定一个session
    Assert.notNull(key, "Key must not be null");//spring的Assert类不错,大家可以看看很简单
    Map map = (Map) resources.get();
    return (map != null && map.containsKey(key));
}
public static void bindResource(Object key, Object value) throws IllegalStateException {//绑定session到当前线程
    Assert.notNull(key, "Key must not be null");
    Assert.notNull(value, "Value must not be null");
    Map map = (Map) resources.get();//ThreadLocal对象只可以存放一个对象,所以使用map来扩展
    if (map == null) {
        map = new HashMap();
        resources.set(map);
    }
    if (map.containsKey(key)) {
        throw new IllegalStateException("Already value [" + map.get(key) + "] for key [" + key +
                "] bound to thread [" + Thread.currentThread().getName() + "]");
    }
    map.put(key, value);
}
public static Object unbindResource(Object key) throws IllegalStateException {//取消当前线程对session的绑定
    Assert.notNull(key, "Key must not be null");
    Map map = (Map) resources.get();
    if (map == null || !map.containsKey(key)) {
        throw new IllegalStateException(
                "No value for key [" + key + "] bound to thread [" + Thread.currentThread().getName() + "]");
    }
    Object value = map.remove(key);
    if (map.isEmpty()) {
        resources.set(null);
    }
    return value;
}
另外一个非常关键的方法是OpenSessionInViewFilter的getSession方法,我们看这个方法的关键并不是如何取得session,而且注意这里设置了FlushMode
protected Session getSession(SessionFactory sessionFactory)
        throws DataAccessResourceFailureException {
    Session session = SessionFactoryUtils.getSession(sessionFactory, true);
    FlushMode flushMode = getFlushMode();//默认情况下是FlushMode.NEVER
    if (flushMode != null) {
        session.setFlushMode(flushMode);
    }
    return session;
}
读到这个地方,大家对ThreadLocal 感兴趣的话,可以看下我以前写的一篇文章http://blog.csdn.net/sunyujia/archive/2008/06/15/2549564.aspx
FlushMode.NEVER:
调用Session的查询方法时,不清理缓存
调用Session.commit()时,不清理缓存
调用Session.flush()时,清理缓存
不过FlushMode.NEVER已经不再建议使用了
官方描述如下
Deprecated. use MANUAL instead. 使用FlushMode.MANUAL来代替
The Session is never flushed unless Session.flush() is explicitly called by the application. This mode is very efficient for read only transactions.
直到调用Session.flush()才会将变化反应到数据库,在只读的情况下是效率非常高的
详见http://www.hibernate.org/hib_docs/v3/api/org/hibernate/FlushMode.html
我们来细看SessionFactoryUtils.getSession(sessionFactory, true);

public static Session getSession(SessionFactory sessionFactory, boolean allowCreate) throws DataAccessResourceFailureException, IllegalStateException {
    try {
        return doGetSession(sessionFactory, null, null, allowCreate);
    }
    catch (HibernateException ex) {
        throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
    }
}
从上面可以看出doGetSession才是真正的核心方法,这里非常重要的是HibernateTemplate也是调用此方法

protected Session getSession() {
    if (isAlwaysUseNewSession()) {
        return SessionFactoryUtils.getNewSession(getSessionFactory(), getEntityInterceptor());
    }else if (!isAllowCreate()) {
        return SessionFactoryUtils.getSession(getSessionFactory(), false);
    }else {
        return SessionFactoryUtils.getSession(
                getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator());
    }
}
无论如何调用最后都是落实到SessionFactoryUtils.doGetSession这个方法上面,无论这个方法最后是否返回了null实际上在方法中都取得了session,
这是整个流程中最复杂的一个方法,部分代码我理解的也很不好,大家一起研究研究.

private static Session doGetSession(
        SessionFactory sessionFactory, Interceptor entityInterceptor,
        SQLExceptionTranslator jdbcExceptionTranslator, boolean allowCreate)
        throws HibernateException, IllegalStateException {
    Assert.notNull(sessionFactory, "No SessionFactory specified");
    //取当前线程绑定的session
    SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
    //sessionHolder就可以看成是当前线程绑定的session了
    if (sessionHolder != null && !sessionHolder.isEmpty()) {
        Session session = null;
        if (TransactionSynchronizationManager.isSynchronizationActive() &
                sessionHolder.doesNotHoldNonDefaultSession()) {//判断spring的事务管理是否是激活的,同时SessionHolder对象中有且仅有一个session
            // Spring transaction management is active ->
            // register pre-bound Session with it for transactional flushing.
            session = sessionHolder.getValidatedSession();
            //在开事务的时候HibernateTransactionManager类中的doBegin方法会将isSynchronizedWithTransaction设置为true,暂时不知道什么情况下会进入如下代码块
            if (session != null && !sessionHolder.isSynchronizedWithTransaction()) {
                TransactionSynchronizationManager.registerSynchronization(
                        new SpringSessionSynchronization(sessionHolder, sessionFactory, jdbcExceptionTranslator, false));
                sessionHolder.setSynchronizedWithTransaction(true);
                // Switch to FlushMode.AUTO, as we have to assume a thread-bound Session
                // with FlushMode.NEVER, which needs to allow flushing within the transaction.
                FlushMode flushMode = session.getFlushMode();
                if (flushMode.lessThan(FlushMode.COMMIT) &
                        !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
                    session.setFlushMode(FlushMode.AUTO);
                    sessionHolder.setPreviousFlushMode(flushMode);
                }
            }
        }
        else {
            // No Spring transaction management active -> try JTA transaction synchronization.
            //在没用事务的情况下下面的方法中只调用了sessionHolder.getValidatedSession();
            session = getJtaSynchronizedSession(sessionHolder, sessionFactory, jdbcExceptionTranslator);
        }
        if (session != null) {
            return session;//在使用了OpenSessionInViewFilter的情况下,HibernateTemplate执行此方法会在这里return
        }
    }
    //如果当前线程没有绑定session那么无论如何都是要创建session的,但是否会return还要取决于allowCreate等条件,在后面会看到
    Session session = (entityInterceptor != null ?
            sessionFactory.openSession(entityInterceptor) : sessionFactory.openSession());

    // Use same Session for further Hibernate actions within the transaction.
    // Thread object will get removed by synchronization at transaction completion.
    //判断spring的事务管理是否是激活的,目前我分析结果是在单session情况下,只有OpenSessionInViewFilter调用此方法,才会执行到这里,这种情况下是不会有事务的.
    //可见下面if块里面的代码是针对非单session,并且有事务的情况处理的.
    if (TransactionSynchronizationManager.isSynchronizationActive()) {
        // We're within a Spring-managed transaction, possibly from JtaTransactionManager.
        logger.debug("Registering Spring transaction synchronization for new Hibernate Session");
        SessionHolder holderToUse = sessionHolder;
        if (holderToUse == null) {
            holderToUse = new SessionHolder(session);
        }
        else {
            holderToUse.addSession(session);
        }
        if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
            session.setFlushMode(FlushMode.NEVER);//只读情况下,可以提高效率同时也防止了脏数据等
        }
        TransactionSynchronizationManager.registerSynchronization(
                new SpringSessionSynchronization(holderToUse, sessionFactory, jdbcExceptionTranslator, true));
        holderToUse.setSynchronizedWithTransaction(true);//这个变量真是很费解不知道什么情况下会为false呢?
        if (holderToUse != sessionHolder) {//从这里可以看出非单session情况下,有事务也是要绑定session的只是颗粒度不同而已,我猜非单session事务结束后session就被关闭了.
            TransactionSynchronizationManager.bindResource(sessionFactory, holderToUse);
        }
    }
    else {
        // No Spring transaction management active -> try JTA transaction synchronization.
        registerJtaSynchronization(session, sessionFactory, jdbcExceptionTranslator, sessionHolder);
    }

    // Check whether we are allowed to return the Session.
    //校验能否创建session的动作居然是放在创建session之后处理的,有点不解.
    if (!allowCreate && !isSessionTransactional(session, sessionFactory)) {
        closeSession(session);
        throw new IllegalStateException("No Hibernate Session bound to thread, " +
            "and configuration does not allow creation of non-transactional one here");
    }
    return session;
}
发表于 @ 2008年08月08日 16:28:00 | 评论( 5 ) | 编辑| 举报| 收藏

旧一篇:javascript 实现表格排序(二) | 新一篇:Java程序员所需的批处理和VBS脚本silence1214 发表于08年10月7日 13:06:42  IP:举报删除
看来 open session in view 和是否使用事务还是没多大关系的啊
那段时间我用了 open session in view 采用getHibernateTemplate().load()还是报出 session has close的异常 看来particate 有关啊 以及是否使用单例获取session有关啊 jijianbo 发表于08年10月16日 11:25:27  IP:举报删除
这个过滤器配置的位置是怎样的? 是struts之后还是之前纳
sunyujia 发表于08年10月16日 13:05:42  IP:举报删除
Re jijianbo: 之前yihaijian1 发表于08年12月18日 12:26:56  IP:举报删除
在Struts2的核心过滤器之前才是可以的emmanuelkong 发表于09年1月19日 19:47:19  IP:举报删除
你好, 我用了 open session in view 在 struts2, hibernate, postgres DB 的项目里, 在运行时没有问题但有事connection 一直没有关,连接的数量达到连接限额是就会报出 ”too many connection“ 的错误。 请你帮帮忙。wd_01 发表于09年11月19日 11:06:45  IP:举报删除
我用这中方式配了之后报No WebApplicationContext found: no ContextLoaderListener registered? 错误~请高手帮我解决下呀~正郁闷中

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/sunyujia/archive/2008/08/08/2788192.aspx

分享到:
评论

相关推荐

    关于OpenSessionInViewFilter的学习

    通过阅读《OpenSessionInViewFilter说明.doc》文档,你可以更深入地了解其内部实现细节、配置方法以及如何在实际项目中合理使用。这个文档应该包含了OpenSessionInViewFilter的源码分析、配置示例以及常见问题的解答...

    OpenSessionInViewFilter

    OpenSessionInViewFilter个人学习总结

    SSH项目整合示例【OpenSessionInView】所用到的jar包

    SSH是Java Web开发中的一个流行框架组合,由Struts、Hibernate和Spring三个组件构成。这个框架集成提供了模型-视图-控制器(MVC)架构,数据持久化,以及依赖注入和事务管理等功能,大大简化了企业级应用的开发工作...

    jar包(struts2.0.8+spring2.0+hibernate3.2)

    Struts2、Spring和Hibernate是Java开发中三个非常重要的框架,它们共同构成了经典的MVC(Model-View-Controller)架构的实现。这个压缩包“struts2.0.8+spring2.0+hibernate3.2”包含了这三个框架的特定版本,这将...

    懒加载异常解决.docx

    - 在高并发场景下,需要注意OpenSessionInViewFilter可能会导致的性能问题,因为它使得Session的生命周期变长,增加了Session占用资源的时间。 - 如果项目中同时使用了其他Session管理机制,需要确保它们之间不会...

    过滤器对于session周期的控制

    在探讨如何通过`OpenSessionInViewFilter`来有效管理Hibernate中的Session生命周期之前,我们需要先理解Session及其生命周期的基本概念。 #### Session与生命周期 在Hibernate框架中,`Session`是执行数据库操作的...

    Spring提供的CharacterEncoding和OpenSessionInView功能

    首先,让我们深入了解一下`CharacterEncodingFilter`。在Web应用中,字符编码问题常常引发乱码问题,尤其是在用户输入的数据与服务器处理数据之间存在编码不一致时。`CharacterEncodingFilter`的作用就是确保请求和...

    Spring hibernate opensessioninview

    该模式的核心在于通过Spring提供的`OpenSessionInViewFilter`过滤器,在视图渲染过程中保持Hibernate Session的打开状态,从而简化了事务管理,并避免了一些常见的懒加载异常。 #### 一、OpenSessionInViewFilter...

    Sping 事务管理.doc

    OpenSessionInViewFilter解决Web应用程序的问题

    S2SH集成 案例

    **S2SH集成详解** ...总的来说,这个S2SH集成案例涵盖了Web应用开发的关键技术,从用户交互到数据持久化,再到事务管理,为初学者提供了一个完整的实践平台,有助于深入理解和掌握Java Web开发的核心概念。

    hibernate 中 fetch=FetchType.LAZY 懒加载失败处理方法

    为了解决这个问题,我们可以使用 Spring 提供的一个支持类OpenSessionInViewFilter,这个类可以在发起一个页面请求时打开 Hibernate 的 Session,并保持这个 Session 的生命周期,直到这个请求结束。这样可以确保 ...

    spring监听器

    ### Spring监听器与过滤器详解...- **Spring Web环境下的监听器和过滤器**:在Spring MVC环境中,除了上述提到的OpenSessionInViewFilter和CharacterEncodingFilter,还有多种其他类型的过滤器和监听器可以使用,例如`...

    struts2.3+spring3.1.2+hibernate4.1.6 配置说明

    Struts2、Spring和Hibernate是Java开发中的三大框架,它们的集成...开发者需要对这三个框架有深入理解,才能顺利完成这样的集成工作。同时,对于新版本的Hibernate,开发者需要了解其变化,以便进行适应性的代码调整。

    S2SH的配置

    本次讨论将围绕给定的`web.xml`和`struts.xml`配置文件进行深入解析,并结合这些配置文件中的具体内容来阐述相关的知识点。 ## 二、`web.xml`配置分析 ### 2.1 Spring配置 在`web.xml`文件中,首先定义了Spring的...

    ssh配置文件

    因此,以下将围绕这部分内容进行深入解析,详细介绍如何在Web项目中整合这三大框架。 ### Struts2、Spring、Hibernate集成 #### 1. 创建Web Project项目 首先,在开发环境中创建一个新的Web项目。这通常在IDE(如...

    使用Spring引起的错误

    2. **OpenSessionInViewFilter**:通过Web容器的过滤器(filter)来实现。 #### 异常原因 当使用Open Session In View模式时,如果Session的Flush Mode被设置为NEVER,并且尝试执行写操作(如更新或删除),就会触发...

    ssh基本配置过程,是struts2的

    在`web.xml`中配置Spring监听器以初始化Spring容器,并添加`OpenSessionInViewFilter`以解决懒加载问题: ```xml <listener-class>org.springframework.web.context.ContextLoaderListener <filter-name>...

    SSH整合的配置文件详解

    本文将深入解析SSH框架整合的配置文件,旨在为开发者提供全面的理解与指导。 ### SSH框架整合的核心:配置文件 #### 1. **web.xml** —— Servlet容器的配置文件 `web.xml`是Web应用程序的核心配置文件,用于配置...

    spring学习笔记

    ### Spring学习笔记知识点详解 #### 一、面向接口(抽象)编程的概念与好处 面向接口编程是一种编程方式,强调在设计系统时...通过深入理解这些知识点,可以帮助开发者更好地掌握 Spring 框架的核心原理和技术实践。

    SSH2.0配置日记

    在 OpenSessionInViewFilter 中,需要配置 Hibernate 的 Session 管理,以便实现 Hibernate 的 Session 的打开和关闭。 9. web.xml 配置 在 web.xml 文件中,需要配置 Servlet 监听器、上下文变量、Filter 等,...

Global site tag (gtag.js) - Google Analytics