一般在SSH配置中,web.xml都是这样配置的:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- 配置上下文 -->
<!-- 作用:定义Spring配置文件位置,可以定义多个文件,也可以使用通配符 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/classes/applicationContext*.xml
</param-value>
</context-param>
<!-- 配置Spring过滤器 -->
<!-- 作用:启动web应用,就加载Spring,让Spring管理Bean -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<!-- 解决hibernate延迟加载出现的问题,要放在Struts2过滤器之前 -->
<!-- 作用:Spring管理hibernate的Session,在事务管理的类执行完后,不立刻关闭Session,
而将Session保存在一个线程变量中,在线程退出前关闭Session;这样在整个request过程中
始终使用一个session,也就可以在request的任何时期lazy loading数据。
主要是为了实现hibernate的延迟加载功能 -->
<filter>
<filter-name>lazyLoadingFilter</filter-name>
<filter-class>
org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
</filter-class>
<!-- singleSession默认设置为true,如何为false就等于没有设置OpenSessionInView -->
<init-param>
<param-name>singleSession</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<!-- Struts2过滤器 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.FilterDispatcher
</filter-class>
</filter>
<!-- 解决hibernate延迟加载出现的问题,仍需要放在Struts2过滤器的filter-mapping之前 -->
<filter-mapping>
<filter-name>lazyLoadingFilter</filter-name>
<url-pattern>*.act
</filter-mapping>
<!-- Struts2过滤器的filter-mapping -->
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
那其中的OpenSessionInViewFilter是干什么用的呢
spring中对OpenSessionInViewFilter的描述如下:
它是一个Servlet2.3过滤器,用来把一个Hibernate Session和一次完整的请求过程对应的线程相绑定。目的是为了实现"Open Session in View"的模式。
假设在应用中Hibernate是通过spring 来管理它的session。如果在你的应用中没有使用OpenSessionInViewFilter或者OpenSessionInViewInterceptor。session会在transaction结束后关闭。
例如:项目的事务如下配置:
<!-- 配置事务管理器 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
<!-- 配置事务传播特性,配置add,update和delete开头的方法,事务的传播特性为required -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="*" read-on
</tx:attributes>
</tx:advice>
<!-- 配置哪些类的方法需要进行事务管理,当前com.ssh2demo.li.Service下的所有类和子包的类的方法需要进行事务管理,还需要参考tx:advice的设置 -->
<aop:config>
<aop:pointcut id="allManagerMethod"
expr
<aop:advisor advice-ref="txAdvice"
pointcut-ref="allManagerMethod" />
</aop:config>
在执行完com.ssh2demo.li.Service下的某个方法后,Session就关闭掉了。
如果应用中使用了OpenSessionInViewFilter,所有打开的session会被保存在一个线程变量里。在线程退出前通过OpenSessionInViewFilter或者OpenSessionInViewInterceptor断开这些session。
也就是允许在每次的整个request的过程中使用同一个hibernate session,可以在这个request任何时期lazy loading数据。 这其实就是hibernate的延迟加载功能。
看看OpenSessionInViewFilter里的几个方法:
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,FilterChain filterChain)
throws ServletException, IOException {
SessionFactory sessionFactory = lookupSessionFactory();
logger.debug("Opening Hibernate Session in OpenSessionInViewFilter");
Session session = getSession(sessionFactory);
TransactionSynchronizationManager.bindResource(
sessionFactory, new SessionHolder(session));
try {
filterChain.doFilter(request, response);
}
finally {
TransactionSynchronizationManager.unbindResource(sessionFactory);
logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");
closeSession(session, sessionFactory);
}
}
protected Session getSession(SessionFactory sessionFactory)
throws DataAccessResourceFailureException {
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
session.setFlushMode(FlushMode.NEVER);
return session;
}
protected void closeSession(Session session, SessionFactory sessionFactory)
throws CleanupFailureDataAccessException {
SessionFactoryUtils.closeSessionIfNecessary(session, sessionFactory);
}
可以看到OpenSessionInViewFilter在getSession的时候,会把获取回来的session的 flush mode 设为FlushMode.NEVER。然后把该sessionFactory绑定到 TransactionSynchronizationManager,使request的整个过程都使用同一个session,在请求过后再接除该 sessionFactory的绑定,最后closeSessionIfNecessary根据该 session是否已和transaction绑定来决定是否关闭session。在这个过程中,若HibernateTemplate 发现自当前session有不是readOnly的 transaction,就会获取到FlushMode.AUTO Session,使方法拥有写权限。
也即是,如果有不是readOnly的transaction就可以由Flush.NEVER转为Flush.AUTO,拥有 insert,update,delete操作权限,如果没有transaction,并且没有另外人为地设flush model的话,则 doFilter的整个过程都是Flush.NEVER。所以受transaction保护的方法有写权限,没受保护的则没有。
对最上面项目的事务可以看出:
以add,update,delete开头的方法拥有可写的事务,如果当前有某个方法,如命名为imp
session.setFlushMode(FlushMode.AUTO);
session.save(user);
session.flush();
尽 管Open Session In View看起来还不错,其实副作用不少。
看回上面OpenSessionInViewFilter的 doFilterInternal方法代码,这个方法 实际上是被父类的doFilter调用的,因此,我们可以大约了解的 OpenSessionInViewFilter调用流程:
1.request(请求)
2.open session并开始 transaction
3.controller
4.View(Jsp)
5.结束transaction 并 close session。
一切看起来很正确,尤其是在本地开发测试的时候没出现问题,但试想下如果流程中的某一步被阻塞的话,那在这期间connection就一直被占用而不释 放。最有可能被阻塞的就是在写Jsp这步,一方面可能是页面内容大,response.write的时间长,另一方面可能是网速慢,服务器与用户间传输时 间久。当大量这样的情况出现时,就有连接池连接不足,造成页面假死现象。Open Session In View是个双刃剑,放在公网上内容多流量大的网站请慎用。