如果要在方法执行前或后或抛出异常后加上一个自己的拦截器,或者一个环绕拦截器,在拦截器中执行一些操作,比如执行一些数据库操作,记录一些信
息,这些操作通过调用一个服务类的方法来执行,这个方法也在spring事务管理拦截器的管理之下,那么这个记录方法需要在另一个事务中进行,而不是与被
拦截方法在同一个事务中,不然如果被拦截方法抛出异常需要回滚时,所作的记录也会被回滚,当然有时候确实需要同时回滚,那就要放在同一个事务中。
这和自己的拦截器和事务管理的拦截器的执行顺序有一定关系,spring事务管理拦截器是一个环绕通知,在被拦截方法执行前启动事务,执行后完成
事务,如果自己的拦截器被spring事务管理拦截器包围在里面,那么在自己的拦截器运行时,spring已经启动了一个事务,如果你的记录信息方法需要
与被拦截方法同在一个事务中,将你的记录信息方法的事务传播属性设为默认的REQUIRED就可以了;
如果你记录信息的方法需要单独的一个事务环境,那就要把事务传播属性设为REQUIRES_NEW了,这样spring事务管理器会新建一个事
务,并且新建一个session连接,因为一个数据库连接不可能同时有两个事务,记录信息完了提交事务并且把新建的session连接关闭,自己的拦截器
退出后继续执行被拦截的方法或它的事务处理。
相反如果自己的拦截器在spring事务管理拦截器的外面,那么记录信息的方法会在一个单独的事务中执行,并提交,不管它的事务传播属性是
REQUIRES_NEW还是REQUIRED,因为与被拦截方法的事务处理没有交叉,并且可以使用同一个session连接如果是
OpenSessionInViewFilter。
所以如果记录信息和被拦截方法要在不同事务中执行,分别提交,那么最好将自己的拦截器设在spring事务管理器拦截器的外面;如果需要将记录信
息和被拦截方法在同一个事务中处理,必须将自己的拦截器被包围在spring事务管理拦截器中,并且记录信息方法的事务传播属性为默认的
REQUIRED。
设置拦截器的执行顺序可以让拦截器处理类实现org.springframework.core.Ordered接口,在spring配置文件的
AOP设置中设定自己的拦截器和spring事务管理拦截器的执行顺序,将自己的拦截的序号排在spring事务管理的前面,就可以将该拦截器放到事务管
理拦截器的外面执行了,对于before通知方式会先于事务管理拦截器执行,对于after returning和after和after
throwing通知方式会后于事务管理拦截器的执行,对于arount通知方式会包围事务管理拦截器执行。
下面是一个异常拦截器的例子。
有位朋友提到在spring异常拦截器中更新数据不能够提交,做了一下测试,测试环境基本是这样:一个用户登录的功能,spring对
service中的每个方法进行事务管理,在用户检测的service方法上同时加上一个异常拦截器,当用户不存在或密码不正确时用户检测方法会抛出异
常,异常拦截器捕获到该异常,同时记录一些日志。
spring配置文件相关:
<!-- 事务管理 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="*" propagation="REQUIRES_NEW" rollback-for="Exception"/>
</tx:attributes>
</tx:advice>
<!-- aop代理设置 -->
<aop:config proxy-target-class="true">
<aop:pointcut id="txPointcut" expression="execution(* com.hbs..*Service.*(..))"/>
<aop:pointcut id="logPointcut" expression="execution(* com.hbs.customer..*Service.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" order="1"/>
<aop:aspect id="logAspect" ref="logInterceptor" order="2" >
<aop:after-throwing
pointcut-ref="logPointcut"
method="serviceIntercept" />
</aop:aspect>
</aop:config>
<!-- log拦截器类 -->
<bean id="logInterceptor" class="com.hbs.eventlog.EventLogInterceptor">
<property name="service" ref="logService"></property>
</bean>
拦截器类:
public class EventLogInterceptor implements Ordered {
private int order = 1;
private EventLogService service;
public Object serviceIntercept(ProceedingJoinPoint point) throws Throwable{
if(point instanceof MethodInvocationProceedingJoinPoint){
MethodInvocationProceedingJoinPoint mpoint = (MethodInvocationProceedingJoinPoint)point;
//
}
try {
System.out.println("记录日志开始");
service.eventLog();
System.out.println("记录日志结束");
}catch(Exception ex) {
ex.printStackTrace();
}
return null;
}
public void setOrder(int order){
this.order = order;
}
public int getOrder() {
return order;
}
public EventLogService getService() {
return service;
}
public void setService(EventLogService service) {
this.service = service;
}
}
service方法中的事务传播属性都设为要求新建事务,spring事务管理切面拦截器的order设为1,而log拦截器的order设为2,这意味
着这两个要同时执行时,先执行事务拦截器,后执行log拦截器,由于事务管理是一个环绕通知(around),实际上是log拦截器被包围在事务管理拦截
器中。
从中可以看出,log异常拦截器在用户登录的事务回滚之前截获异常,在记录日志时,日志记录的service方法也在spring的事务管理之
下,用户登录的事务还没有结束,根据REQUIRES_NEW特性,spring会新开一个事务,这时原来的数据库连接已经在一个事务中,一个连接不可能
同时有两个事务,所以同时新创建一个session连接(虽然我使用了OpenSessionInViewFilter,并且session是单例的),
日志记录就在新建的事务和session中进行,完了提交,并且会把新建的session连接关闭。
然后继续进行被中断的用户登录的事务管理操作,由于抛异常spring将用户登录的事务回滚。
这样能够实现预想的功能,但是如果我去掉指定的REQUIRES_NEW,那么log记录的操作会继续在用户登录的事务中进行,最后会被一起回滚。
如果把事务管理的order设为2,log拦截器的order设为1,也就是log拦截器在事务管理拦截器的外面,会在事务管理拦截器前后执行完了再执行log的异常拦截器。
可以看出,用户登录的事务和日志记录的事务是前后两个不相关的事务,并且在日志记录事务中并不需要新建session连接,而是直接用在
OpenSessionInViewFilter中创建的session。实际上这时也并不需要将propagation设为REQUIRES_NEW,
使用默认的REQUIRES也照样能够正常工作。
所以应该将该异常拦截器设在事务管理拦截器的外面,即使用Order接口排在前面。
http://www.iteye.com/topic/71030
分享到:
相关推荐
例如,可能会有一个自定义的MyBatis拦截器用于分页查询,一个Spring AOP切面用于记录操作日志,Spring事务管理确保数据的一致性,而反射工具类可能用于动态加载配置或处理某些通用的反射任务。通过这些组件的组合,...
在深入研究Flex-Spring拦截器时,理解Spring AOP的核心概念和AMF的工作原理是至关重要的。通过这样的集成,开发者可以在保持Flex客户端的灵活性和交互性的同时,利用Spring的强大功能来处理复杂的业务逻辑和系统管理...
在Spring AOP中,拦截器扮演着关键角色。它是一个实现了`org.springframework.aop.MethodBeforeAdvice`、`org.springframework.aop.AfterReturningAdvice`或`org.springframework.aop.ThrowsAdvice`等接口的对象,...
而Spring拦截器则是实现AOP的一种方式,它类似于Java的Servlet过滤器,可以在方法调用前后执行自定义的操作。 AOP拦截器在Spring中主要通过`HandlerInterceptor`接口或者`@AspectJ`注解来实现。下面我们将详细探讨...
Spring AOP 拦截器 Advisor 是 Spring 框架中的一个重要概念,它与切面编程密切相关,用于实现细粒度的控制和增强应用程序的行为。在 Spring AOP 中,Advisor 是一个组合了通知(Advice)和切入点(Pointcut)的对象...
AOP(面向切面编程)是一种编程范式,旨在将横切关注点(如日志、安全、事务...AOP和拦截器的使用大大提高了开发的灵活性和代码的可重用性,同时也使得日志记录、权限验证、事务处理等横切关注点的管理更为集中和高效。
在Spring Boot应用中,面向切面编程(AOP)是一种强大的设计模式,它允许我们以声明式的方式插入代码,比如日志记录、事务管理或权限检查。Aspect是AOP的核心概念,它封装了关注点,使得我们可以将这些关注点与业务...
本实例主要探讨如何在Spring AOP中实现拦截器。 首先,我们来理解Spring AOP的基本概念。AOP的核心是代理,Spring提供了两种代理方式:JDK动态代理和CGLIB代理。JDK动态代理基于接口,当目标对象实现了接口时使用;...
标题中的"ssh+aop+log4j+日志拦截器+注解"涉及到的是Java Web开发中的几个核心组件和技术,这些技术在构建大型、分布式的企业级应用时常常被使用。下面将详细介绍这些知识点: 1. SSH (Spring, Struts, Hibernate)...
在`tx`和`aop`的配合下,Spring会通过AOP代理拦截带有`@Transactional`的方法调用,根据注解中的配置启动一个新的事务,执行方法,如果方法正常结束则提交事务,遇到异常则回滚事务。 在基于Struts1.2和Spring2.0的...
Spring的AOP框架就是基于这些接口构建的,这样开发者可以编写一次拦截器,就可以在多个AOP框架中复用。 这三个jar包在Spring AOP中的角色如下: 1. `aspectjrt.jar`:提供AspectJ的运行时支持,用于实现基于...
Spring AOP(Aspect Oriented Programming,面向切面编程)是Spring框架中的一个重要模块,它提供了在应用代码中添加横切关注点的能力,如日志记录、事务管理、权限验证等,而无需显式修改业务逻辑代码。本文将深入...
在提供的压缩包中,可能包含了一个或多个测试类(Tests),这些测试类通常用来验证AOP拦截器的功能。它们可能包含模拟业务场景的方法,这些方法会被切面拦截并执行相应的通知。通过运行这些测试,我们可以确保AOP...
Spring AOP是Spring框架的一个重要特性,它允许开发者创建具有横切关注点的模块,如日志记录、事务管理等。这些关注点可以被声明性地应用到多个对象上,而无需修改这些对象的代码。Spring AOP通过动态代理实现,有两...
**面向切面编程(AOP)**是Spring的另一个关键特性,它允许开发者定义横切关注点,这些关注点可以是日志记录、事务管理、性能度量等跨多个类或方法的功能。AOP的核心是切面(Aspect)、连接点(Join Point)、通知...
本项目示例涵盖了这些框架的整合使用,以及jQuery AJAX登录、Struts拦截器和Spring AOP(面向切面编程)的应用。 1. **Struts2**:Struts2是一个基于MVC(Model-View-Controller)设计模式的Web应用框架,它简化了...
在Spring AOP中,你可以使用这些接口来实现自定义的拦截器,从而在方法调用前后插入额外的功能,如日志记录、事务管理等。 接着,我们来看`aspectjweaver.jar`。AspectJ是Spring AOP支持的一个强大的第三方AOP框架...
在`intercept`方法中,我们实现了类似Spring拦截器的功能,调用`preHandle`和`postHandle`方法,并根据`preHandle`的结果决定是否执行目标方法。 最后,`afterCompletion`方法的调用通常需要手动管理,因为它涉及到...