浏览 10450 次
精华帖 (5) :: 良好帖 (12) :: 新手帖 (1) :: 隐藏帖 (8)
|
|
---|---|
作者 | 正文 |
发表时间:2010-01-25
最后修改:2010-01-31
两种访问系统的路径: 1、request-->filters(spring and struts)-->actions-->AOP(transaction)-->services-->dao-->db 2、timer(run)->AOP(transaction)-->services-->dao-->db 下面是调用链的图示 在WEB.XML配置了OpenSessionInViewFilter,整个请求只用一个SESSION, 在ACTION与SERVICE之间使用SPRING的AOP配置事务拦截器,所以事务范围涵盖整个SERVICE的方法调用, DAO中一般使用SPRING的HibernateTemplate对象操作数据库 首先看OpenSessionInViewFilter在web.xml和事务拦截器相关配置 <!--web.xml--> <filter> <filter-name>openSessionInViewFilter</filter-name> <filter-class> org.springframework.orm.hibernate3.support.OpenSessionInViewFilter </filter-class> <init-param> <param-name>singleSession</param-name> <param-value>true</param-value> </init-param> </filter> /////SRPING配置文件中事务拦截器相关配置 <!-- hibernate 事务管理者 --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <!-- hibernate 事务拦截器 --> <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <!-- 注入事务管理者 --> <property name="transactionManager" ref="transactionManager" /> <property name="transactionAttributes"> <!-- 定义事务属性--> <props> <prop key="*">PROPAGATION_REQUIRED</prop> <prop key="insert*">PROPAGATION_REQUIRED</prop> <prop key="add*">PROPAGATION_REQUIRED</prop> <prop key="save*">PROPAGATION_REQUIRED</prop> <prop key="update*">PROPAGATION_REQUIRED</prop> <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop> <prop key="search*">PROPAGATION_REQUIRED,readOnly</prop> <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop> <prop key="noTran*">PROPAGATION_NOT_SUPPORTED,readOnly</prop> </props> </property> </bean> /////配置service自动代理 <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"> <!-- 代理所有Service --> <list> <value>*Service</value> </list> </property> <property name="interceptorNames"> <list> <value>transactionInterceptor</value> </list> </property> </bean> //下面从FILTER开始分析代码 //这是OpenSessionInViewFilter类的方法,请求到来时,此方法由父类OncePerRequestFilter的doFilter()方法调用, protected void doFilterInternal( HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { //取得SessionFactory实例,实际上就是调用WebApplicationContext.getBean("beanName"), SessionFactory sessionFactory = lookupSessionFactory(request); //在lookupSessionFactory方法里看到原来此FILTER还支持配置sessionFactory为的名字,默认就叫sessionFactory,如果想改成其它名字具体配置方式: //在web.xml中,<init-param><param-name>sessionFactoryBeanName</param-name><param-value>sessionFactory</param-value></init-param> //此次调用有无打开SESSION,有的话在finally块关闭 boolean participate = false; //是否单SESSION模式(上文web.xml中配置了),注意这并不意味着每个请求只用一个connection,下文详解 if (isSingleSession()) {//这里我只分析单SESSION的情况,因为另一种情况不是此filter推荐的用法,就懒得看了 // 单SESSION模式 // 是否已经打开了SESSION,一般来说每个请求一开始是没有打开的 if (TransactionSynchronizationManager.hasResource(sessionFactory)) { participate = true; } else { logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter"); //打开session,注意想在整个请求内部都用SESSION,这一句还不够,还必须有下面一句。getSession方法如何打开SESSION接下来讲解 Session session = getSession(sessionFactory); //把SESSION放入SessionHolder,再用sessionFactory作为KEY,SessionHolder作为VALUE放入MAP,再把此MAP放入ThreadLocal //------不熟ThreadLocal看这,其他人跳过,ThreadLocal有两个主要的方法,get()和set(value),用法和MAP差不多,只是不需要提供KEY, //KEY就是当前线程,这样对于每个线程,只能往ThreadLocal里放入一个对象,在此放入了一个MAP。 //不同的线程可以同时操作这个ThreadLocal对象,放入内容,因为各自有着不同的KEY------// TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session)); } } else {//非单Session模式,本文不讨论此情况 // deferred close mode if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) { // Do not modify deferred close: just set the participate flag. participate = true; }else { SessionFactoryUtils.initDeferredClose(sessionFactory); } } try {//SESSION已打开,往下调用,-->action-->aop-->service-->dao-->db filterChain.doFilter(request, response); } finally {//目标调用完成,关闭SESSION if (!participate) {//如果上文打开了SESSION if (isSingleSession()) { //从ThreadLocal里面把上面放入的MAP移除 SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory); logger.debug("Closing single Hibernate Session in OpenSessionInViewFilter"); //关闭SESSION closeSession(sessionHolder.getSession(), sessionFactory); } else { // deferred close mode SessionFactoryUtils.processDeferredClose(sessionFactory); } } } }getSession方法 //OpenSessionInViewFilter类,还是上面那个FILTER类 protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException { //取SESSION,下文讲解 Session session = SessionFactoryUtils.getSession(sessionFactory, true); //设置提交方式,getFlushMode默认返回FlushMode.NEVER, //NEVER看名字好像是永不提交,其实是外部显式调用FLUSH时才提交的意思,也许是因为名字不好,此变量已过时了,新的名字是FlushMode.MANUAL //如果请求是写操作,会在打开事务时被改成AUTO, FlushMode flushMode = getFlushMode(); if (flushMode != null) { session.setFlushMode(flushMode); } return session; }SessionFactoryUtils.doGetSession方法 //SessionFactoryUtils类,取SESSION,此工具方法在以下两种情况被调用:在FILTER层开SESSION和在DAO层取SESSION去查数据库 //在FILTER层调用时,没有事务(执行此方法的后半部份),在DAO层调用时,由于经过了AOP,开启了事务(执行方法的前半部份) //在没有JTA事务的环境中,此方法只是打开或取得一个SESSION,关于事务的事什么也没有做。 private static Session doGetSession( SessionFactory sessionFactory, Interceptor entityInterceptor, SQLExceptionTranslator jdbcExceptionTranslator, boolean allowCreate) throws HibernateException, IllegalStateException { Assert.notNull(sessionFactory, "No SessionFactory specified"); //上文放入的sessionHolder在这里被取出来,如果调用来源于FILTER,则还没有放入,取出空,如果DAO层的调用,那么就不为空了 SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); ///--------------前半部份-------------------////////////// if (sessionHolder != null && !sessionHolder.isEmpty()) {// DAO中的调用,取得非空值, Session session = null; //如果:事务是否已开始 && sessionHolder中没有SESSION或只有一个默认SESSION if (TransactionSynchronizationManager.isSynchronizationActive() && sessionHolder.doesNotHoldNonDefaultSession()) { //取出SESSION,里面主要判断一下SESSION有没有被关闭 session = sessionHolder.getValidatedSession(); //session不为空 && 事务没有开始, if (session != null && !sessionHolder.isSynchronizedWithTransaction()) { //在AOP中开始事务时,会将这个标志设为真,所以这里不会被执行,至少我还没有发现哪一次执行经过这里了 logger.debug("Registering Spring transaction synchronization for existing Hibernate Session"); TransactionSynchronizationManager.registerSynchronization(//注册一个SESSION new SpringSessionSynchronization(sessionHolder, sessionFactory, jdbcExceptionTranslator, false)); sessionHolder.setSynchronizedWithTransaction(true); // 取出当前SESSION的提交方式 FlushMode flushMode = session.getFlushMode(); if (flushMode.lessThan(FlushMode.COMMIT) && !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) { //如果是小于COMMIT,就是NEVER,那么改成自动 session.setFlushMode(FlushMode.AUTO); //设置之前的提交模式 sessionHolder.setPreviousFlushMode(flushMode); } } } else {//这里也是一般都不会被执行,因为AOP中已经开启了事务 // No Spring transaction management active -> try JTA transaction synchronization. session = getJtaSynchronizedSession(sessionHolder, sessionFactory, jdbcExceptionTranslator); } if (session != null) { return session; } } ////////-------------------后半部份-------------------------- //来自FILTER的调用,一般sessionHolder为空 logger.debug("Opening Hibernate Session"); //调用HIBERNATE打开SESSION Session session = (entityInterceptor != null ? sessionFactory.openSession(entityInterceptor) : sessionFactory.openSession()); // 当前请求是否已开启事务,FILTER中的调用,还没有到AOP层,事务没有打开,一般会执行ELSE 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); if (holderToUse != sessionHolder) { TransactionSynchronizationManager.bindResource(sessionFactory, holderToUse); } } else { // 上面没有找到事务,从sessionFactory中找,如果sessionFactory有配置JTA事务,则注册事务 // 本文也没有配置JTA事务,所以里面什么也没有干,还是没有注册事务 registerJtaSynchronization(session, sessionFactory, jdbcExceptionTranslator, sessionHolder); } // 如果不自动创建 && sessionHolder中没有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"); } //到此就在FILTER中取得了(或者说打开了)SESSION,下面就从ACTION调用SERVICE这一节了。 return session; } //下面开始到了ACTION的操作,action调用service,此service实际上是Spring动态生成的代理类,此代理类调用 //目标SERVICE类对应的方法前,要经过AOP拦截,下面我们对ACTION到SERVICE这中间经过的所有类方法代码分析一遍。 //我的应用在AOP层配置了一个事务拦截器,所以这中间的代码主要操作事务相关。 //动态生成的代理类代码看不到,从动态代理类下面的那个类(JdkDynamicAopProxy)开始吧 //JdkDynamicAopProxy类,此方法被动态代理类调用 //参数说明:proxy动态代理类对象,method目标方法,args目标方法参数 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { MethodInvocation invocation = null; Object oldProxy = null; boolean setProxyContext = false; //targetSource对象包含目标SERVICE对象 TargetSource targetSource = this.advised.targetSource; Class targetClass = null; Object target = null; try { //如果目标类没有重载equals方法 && 目前正在调用目标类的equals方法,那直接调本类重载方法, //这样就不需要再往下走,不需要再开启事务等等多余的操作以至于浪费性能了,下面的几个IF都是做类似的事 if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { // The target does not implement the equals(Object) method itself. return (equals(args[0]) ? Boolean.TRUE : Boolean.FALSE); } if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { // The target does not implement the hashCode() method itself. return new Integer(hashCode()); } if (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) { // Service invocations on ProxyConfig with the proxy config... return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args); } Object retVal = null; //ProxyConfig类的exposeProxy属性,表示是否要将当前的代理对象放入AopContext中, //这样在你的Service中可以用AopContext.currentProxy()取得当前代理,相当于this引用, //不同于this引用的是,调用当前代理会被AOP拦截,而this不会。 //此属性默认为FALSE,如果要打开, //在BeanNameAutoProxyCreator等代理类BEAN的配置中加入<property name="exposeProxy" value="true" /> if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // 取得目标(真实SERVICE对象) target = targetSource.getTarget(); if (target != null) { targetClass = target.getClass(); } // 取得所有拦截器 List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // 如果没有拦截器 if (chain.isEmpty()) { // 直接调用目标 retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args); } else {//有拦截器 // 创建一个调用器对象 invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // 通过调用器往下调用目标或第一个拦截器 retVal = invocation.proceed(); } // 调用的返回结果目标类对象本身,就把它替换成代理类对象 if (retVal != null && retVal == target && method.getReturnType().isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { retVal = proxy; } return retVal; } finally {//释放资源 //主要是从当前线程清除代理类对象 if (target != null && !targetSource.isStatic()) { targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } }递归调用拦截器 //ReflectiveMethodInvocation类,上面执行retVal = invocation.proceed();就进入到了这个方法 //这个方法主要是递归调用各个拦截器,最后调用目标类 public Object proceed() throws Throwable { // 判断是否还有拦截器要调用, if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { //没有拦截器了,调用目标 return invokeJoinpoint(); } //取出拦截器 Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); //判断拦截器型,对不同类型的调用方法不一样,SPRING事务拦截器TransactionInterceptor是MethodInterceptor实现类, //不属于InterceptorAndDynamicMethodMatcher,执行ELSE if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) { return dm.interceptor.invoke(this); } else {//方法匹配失败,跳过,继续递归 return proceed(); } } else { // 调用事务拦截器等,MethodInterceptor--林信良的SPRING手册介绍过个这接口如何使用 return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }事务拦截器 //TransactionInterceptor--SPRING的事务拦截器实现类,这个方法可以根据事务管理器的种类分类if和else上下两部分, //如果配置的是org.springframework.orm.hibernate3.HibernateTransactionManager等非CallbackPreferringPlatformTransactionManager接口实现类 //则看只需要看if部份 //了解这里工作原理很重要: //1、如果是请求发起的调用,流程路线为--FILTER打开Session,并且绑定到了线程信息中(TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session))), //在此方法里标志为旧SESSION,开始事务,调用目标,提交或回滚事务,判断如果是旧SESSION,则事务结束释放连接但不关闭SESSION,如果还用此SESSION再开启事务,再绑定另外的连接(所以上文说单SESSION并不等于同一个连接),最后在FILTER关闭SESSION。 //2、如果不是请求发起的调用,如计划任务Timer.schedule(...)发起的SERVICE调用,流程为--在此方法打开SESSION,标志为新连接,开启事务,调用目标,提交或回滚事务,释放数据库连接,判断如果是新SESSION,则关闭SESSION。 //总结如下: //1、只要有配置了此事务拦截器,底层使用SPRING的HibernateTemplate.getSession()、getSession().connection()取得连接或Spring提供的各种查询,那么SESSION和数据库连接的关闭不需要你操心,但是stateMent和ResultSet要记得关闭。 //2、如果要在Timer的run方法中多次调用SERVICE时,也要实现OpenSessionInViewFilter这样的效果,那么使用如这句,结束时要记得自已关闭SESSION。 //打开SESSION后:TransactionSynchronizationManager.bindResource(getSessionFactory(), new SessionHolder(session)); //最后关闭SESSION: //SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.unbindResource(getSessionFactory()); //SessionFactoryUtils.closeSession(sessionHolder.getSession()); public Object invoke(final MethodInvocation invocation) throws Throwable { // 取得目标类名 Class targetClass = (invocation.getThis() != null ? invocation.getThis().getClass() : null); // 通过类名及方法名,取得事务属性,即<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>这些内容 final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass); //取得类名+方法名全称 final String joinpointIdentification = methodIdentification(invocation.getMethod()); //没有事务属性 || 事务管理者不是CallbackPreferringPlatformTransactionManager类型(事务采用回调方法实现的方式) if (txAttr == null || !(getTransactionManager() instanceof CallbackPreferringPlatformTransactionManager)) { //开始事务并创建事务信息对象(此对象主要包括事务属性和事务状态等信息),就是在这里调用了session.beginTransaction()方法 //将事务信息以当前线程为KEY放入了TransactionAspectSupport.transactionInfoHolder对象中 //注意:到这里如果当前取得到SessionHolder,那么SESSION就被标记成老SESSION,提交事务时就不会关闭之,反之那么session会在打开,并被标为新SESSION,提交事务时会把此SESSION关闭 TransactionInfo txInfo = createTransactionIfNecessary(txAttr, joinpointIdentification); Object retVal = null; try { // 调用下一下拦截器,如果没有下一个拦截器,那么目标类会被调用 retVal = invocation.proceed(); } catch (Throwable ex) { // 发生异常时,在这里回滚,回滚时,除了rollBack,还执行session.clear(),setFlushMode(NEVER)等动作 // 新SESSION会在此被关闭,老的则不会 completeTransactionAfterThrowing(txInfo, ex); throw ex; } finally { //清理当前线程对应的事务信息, cleanupTransactionInfo(txInfo); } //未发生异常,提交事务,如果是新SESSION,会在此被关闭,老的则不会 commitTransactionAfterReturning(txInfo); return retVal; } else { // 回调式的事务管理器,SPRING中只提供了一个WebSphereUowTransactionManager的具体实现, /**CallbackPreferringPlatformTransactionManager接口的注释 * Extension of the {@link org.springframework.transaction.PlatformTransactionManager} * interface, exposing a method for executing a given callback within a transaction. * <p>Implementors of this interface automatically express a preference for * callbacks over programmatic <code>getTransaction</code>, <code>commit</code> * and <code>rollback</code> calls. Calling code may check whether a given * transaction manager implements this interface to choose to prepare a * callback instead of explicit transaction demarcation control.*/ // 大意应该是可以由此接口的具体实现来决定事务的控制,我的E文一般般,有错的就砸砖 try { Object result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager()).execute(txAttr, new TransactionCallback() { public Object doInTransaction(TransactionStatus status) { TransactionInfo txInfo = prepareTransactionInfo(txAttr, joinpointIdentification, status); try { //往下调用 return invocation.proceed(); } catch (Throwable ex) { if (txAttr.rollbackOn(ex)) { // A RuntimeException: will lead to a rollback. if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } else { throw new ThrowableHolderException(ex); } } else { // A normal return value: will lead to a commit. return new ThrowableHolder(ex); } } finally { cleanupTransactionInfo(txInfo); } } }); // Check result: It might indicate a Throwable to rethrow. if (result instanceof ThrowableHolder) { throw ((ThrowableHolder) result).getThrowable(); } else { return result; } } catch (ThrowableHolderException ex) { throw ex.getCause(); } } } //这篇文章在打开和提交事务、同一SESSION多次切换不同事务的细节没有提及,下一篇讲解。 //本人水平很有限,错漏之处难免,请各位指出,并请手下留情...呵呵 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-01-27
这么麻烦么?我记得不用这么多吧?
能讲讲使用openSessionInFilter的好处么? 我以前写的配置,都没有使用这个东西,所以不是很了解 |
|
返回顶楼 | |
发表时间:2010-01-27
fu cktianya 写道 这么麻烦么?我记得不用这么多吧?
能讲讲使用openSessionInFilter的好处么? 我以前写的配置,都没有使用这个东西,所以不是很了解 配置的方法不是只有一种,我这只是其中一种,运行流程是一样的。 openSessionInFilter的好处: 1、在整个请求中只打开了一个SESSION,对象的创建少了。 2、避免页面出现延迟加载异常。 |
|
返回顶楼 | |
发表时间:2010-05-01
楼主的文章写得很详细,受教了,非常期待下一篇
|
|
返回顶楼 | |
发表时间:2010-05-02
楼主:这句话“
如果:事务是否已开始 && sessionHolder中没有SESSION或只有一个默认SESSION”有点模糊啊,我觉得是用AOP声明式事务代理时,在开启事务的时候,已经有了一个新建了一个SessionHolder了,所以 if (TransactionSynchronizationManager.isSynchronizationActive() && sessionHolder.doesNotHoldNonDefaultSession()) 这个条件肯定是满足的。 |
|
返回顶楼 | |
发表时间:2010-05-03
lixia0417 写道 楼主:这句话“
如果:事务是否已开始 && sessionHolder中没有SESSION或只有一个默认SESSION”有点模糊啊,我觉得是用AOP声明式事务代理时,在开启事务的时候,已经有了一个新建了一个SessionHolder了,所以 if (TransactionSynchronizationManager.isSynchronizationActive() && sessionHolder.doesNotHoldNonDefaultSession()) 这个条件肯定是满足的。 在使用JTA的环境中,sessionHolder中的Session不使默认KEY存放,doesNotHoldNonDefaultSession()方法返回值就是false了,条件就不满足了。 |
|
返回顶楼 | |