Spring 学习之路(九):Spring 中的AOP(二):事务通知
2018-03-26 23:05
465 查看
AspectJ
目前,spring 框架中我们可以使用基于 AspectJ 注解或者是基于XML配置的 AOP(主流是使用 AspectJ ,简单,方便)如何配置AspectJ
简单理解,AspectJ 就是一个支持 aop 的第三方组件,spring 提供了很好的支持,我们只需要将 对应的 jar 包加入我们的项目即可(对应 jar 包可以在我的源代码下载)
如图:
配置文件中声明 使用 AspectJ 注解
引入aop命名空间
使 AspectJ 注解生效
AspectJ 注解工作流程
在 Spring 中声明 AspectJ 切面, 只需要在 IOC 容器中将切面声明为 Bean 实例. 当在 Spring IOC 容器中初始化 AspectJ 切面之后, Spring IOC 容器就会为那些与 AspectJ 切面相匹配的 Bean 创建代理
在 AspectJ 注解中, 切面只是一个带有 @Aspect 注解的 Java 类
通知是标注有某种注解的简单的 Java 方法
对以上流程不清楚的话,我们直接看代码
前置通知
//把该类声明为一个切面:需要把该类放入到ioc容器中,然后再声明为一个切面 @Aspect @Component public class LogginAspect { // 声明该方法是一个前置通知,在目标方法开始之前执行 @Before("execution(void com.zc.cris.beans.spring.aop.impl.Chinese.*(String))") public void beforeMethod(JoinPoint joinPoint) { // 获取方法签名和参数集合 System.out.println(joinPoint.getSignature().getName() + "-----" + Arrays.asList(joinPoint.getArgs())); System.out.println("我是方法的前置通知"); } - 测试代码 @Test void testProxy() { //创建ioc容器 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //获取bean People bean = context.getBean(People.class); // System.out.println(bean); // System.out.println(bean.getClass().getName()); //System.out.println(bean instanceof Chinese); //false //使用bean bean.eat("筷子"); bean.say("中文"); }
console:
后置通知
// 声明该方法是一个后置通知,在目标方法执行后执行(无论目标方法是否发生异常) // 且后置通知无法访问目标方法的返回值 @After("execution(* com.zc.cris.beans.spring.aop.impl.*.*(String))") public void afterMethod(JoinPoint joinPoint) { System.out.println("我是方法的后置通知"); } - 测试代码同上
console:
返回通知
// 声明该方法为返回通知:方法正常执行结束后执行的代码 // 返回 通知是可以访问到方法的返回值的! /*切点表达式表示执行任意类的任意方法. 第 一个 * 代表匹配任意修饰符及任意返回值, 第二个 * 代表任意类的对象, 第三个 * 代表任意方法, 参数列表中的 .. 匹配任意数量的参数 */ @AfterReturning(value = "execution(public void com.zc.cris.beans.spring.aop.impl.*.*(..))", returning = "result") public void afterRetruning(JoinPoint joinPoint, Object result) { System.out.println("我是方法的返回通知" + joinPoint.getSignature().getName() + "^^^^" + Arrays.asList(joinPoint.getArgs() + "我是方法的返回值" + result)); } - 测试代码同上
console:
异常通知
//目标方法出现异常才会指定的代码 //可以访问到异常对象,且可以指定出现特定的异常(NullPointException)才会执行 @AfterThrowing(value = "execution(public void com.zc.cris.beans.spring.aop.impl.*.*(..))", throwing="e") public void afterThrowing(JoinPoint joinPoint, Exception e) { System.out.println("我是目标方法发生异常才执行的通知:"+e.getMessage()); } - 测试代码同上
console:
通过以上四种通知类型的应用我们大致了解Spring 的aop通过 AspectJ 组件是如何完成的,让我们再 修改之前的代理类,加深理解
环绕通知
//环绕通知:必须携带 ProceedingJoinPoint 类型的参数 //环绕通知类似于动态代理的全过程:ProceedingJoinPoint 类型的参数可以决定目标方法的执行, //环绕通知必须要有返回值,返回值其实就是目标方法的返回值 @Around(value = "execution(public void com.zc.cris.beans.spring.aop.impl.*.*(..))") public Object around(ProceedingJoinPoint pjt) { Object result = null; String methodName = pjt.getSignature().getName(); try { //前置通知 System.out.println("我是环绕通知的前置通知!!!!!"); //执行目标方法 result = pjt.proceed(); //返回通知 System.out.println("我是环绕通知的后置通知"); } catch (Throwable e) { //异常通知 System.out.println("我是环绕通知的异常通知"+e.getMessage()); throw new RuntimeException(e); } //后置通知 System.out.println("我是环绕通知的后置通知"); return result; }
console:
切面的优先级
假如我们现在有两个切面类,一个负责参数验证,一个负责日志记录,那么我们如何确定这两个切面类谁先执行,谁后执行呢?
//使用 @Order(1) 注解指定切面的优先级,数字越小,优先级越高 @Order(1) @Aspect @Component public class ValidationAspect { @Before(value = "execution(* com.zc.cris.beans.spring.aop.impl.*.*(..))") public void validate(JoinPoint joinPoint) { System.out.println("------- validation-----"+ Arrays.asList(joinPoint.getArgs())); } } @Order(2) //把该类声明为一个切面:需要把该类放入到ioc容器中,然后再声明为一个切面 @Aspect @Component public class LogginAspect {
console:
切面表达式的重用
通过上面的测试,我们发现每个通知的注解里都需要写相同的切面表达式,这明显不符合我们的风格,著名编程大师马丁·富勒 就曾经说过,代码有很多种坏味道,而重复是最坏的一种,事实上通过一个小小的注解就可以搞定
/* * 定义一个方法,专门用来声明切入点表达式,一般的,该方法中不需要再写任何代码 * 使用@Pointcut 注解来声明 * 后面的其他通知直接使用该方法名来引用当前的切入点表达式即可 */ @Pointcut("execution(* com.zc.cris.beans.spring.aop.impl.*.*(String))") public void declaredJointPointExpresson() {}; // 声明该方法是一个前置通知,在目标方法开始之前执行 @Before("declaredJointPointExpresson()") @Before(value = "com.zc.cris.beans.spring.aop.impl.LogginAspect.declaredJointPointExpresson()")
我们通过@Pointcut 注解对切面表达式进行了重构,当前类的通知或者其他包的类的通知,都可以使用,以达到简洁,高效的目的
通过xml配置文件来配置spring的事务通知(不推荐,看完代码你就知道为什么了,了解即可)
applicationContext.aopXML.xml
<bean id="chinese" class="com.zc.cris.beans.spring.aop.impl.xml.Chinese"></bean> <bean id="validationAspect" class="com.zc.cris.beans.spring.aop.impl.xml.ValidationAspect"></bean> <bean id="logginAspect" class="com.zc.cris.beans.spring.aop.impl.xml.LogginAspect"></bean> <!-- 配置aop --> <aop:config> <!-- 定义切入点表达式 --> <aop:pointcut expression="execution(* com.zc.cris.beans.spring.aop.impl.xml.*.*(String))" id="pointCut"/> <!-- 定义一个切面对象 --> <aop:aspect ref="logginAspect" order="2"> <!-- 定义各种通知 --> <aop:before method="beforeMethod" pointcut-ref="pointCut"/> <aop:after method="afterMethod" pointcut-ref="pointCut"/> <aop:after-returning method="afterRetruning" returning="result" pointcut-ref="pointCut"/> <aop:after-throwing method="afterThrowing" throwing="e" pointcut-ref="pointCut"/> <!-- <aop:around method="around"/> --> </aop:aspect> <aop:aspect ref="validationAspect" order="1"> <aop:before method="validate" pointcut-ref="pointCut"/> </aop:aspect> </aop:config>
取消我们aop切面类上的所有切面注解,然后进行测试发现console打印的和之前测试一毛一样
源代码点我
相关文章推荐
- SpringMVC + Spring + MyBatis 学习笔记:SpringMVC和Spring一同工作的时候,AOP事务管理不起作用的解决方法
- [原创]java WEB学习笔记106:Spring学习---AOP的通知 :前置通知,后置通知,返回通知,异常通知,环绕通知
- spring源码学习之路---深入AOP(终)
- spring aop事务通知(切面异常处理)
- spring源码学习之路---AOP初探
- Spring.NET学习笔记14——AOP的通知类型(基础篇)
- 学习笔记:spring与hibernate整合(采用aop来管理事务来实现声明式事务)
- Spring 学习之路(八):Spring 中的AOP(一):aop初步了解
- 18.01.24,web学习第四十五天,还有半年,努力吧青年 Spring第三天 aop事务配置+Spring整合jdbc操作
- Spring AOP学习笔记(3):AOP返回通知&异常通知&环绕通知
- SSH与SSM学习之SSH整合09——Spring的aop事务
- Spring学习(九)-AOP切面通知
- Spring学习笔记四(AOP中的通知参数和注解开发)
- spring源码学习之路---深入AOP(终)
- [Spring]Spring AOP学习笔记(4)--Spring 事务
- 【spring源码学习】spring的aop目标对象中进行自我调用,且需要实施相应的事务定义的解决方案
- spring学习笔记(23)基于tx/aop配置切面增强事务
- Spring 4.0 学习日记(8) ---AOP切面注解实现五种通知
- SPRING源码学习之路(三)——<aop:config>自动代理的实现
- JAVAEE之Spring学习(三)---通过aop切面实现事务处理