在上一篇博客中我们介绍了AOP的一些基本概念,并且在博客中的Demo我们没有使用配置文件的形式来实现,在博客的结尾我们我们也抛出了那样写的问题,在这篇博客中我们就用配置文件的方式来来介绍AOP中的五大通知。
前置通知(before):在目标方法执行之前执行
后置通知(after):在目标方执行完成后执行,如果目标方法异常,则后置通知不再执行
异常通知(After-throwing):目标方法抛出异常的时候执行
最终通知(finally);不管目标方法是否有异常都会执行,相当于try。。catch。。finally中的finally环绕通知(round):可以控制目标方法是否执行下面就通过一个栗子来总结一下这几个通知应该如何使用.
上面截图就是demo的基本目录结构,先来说一下jar包,其中aspectjrt.jar和aspectjweaver.jar是springAOP配置文件形式所需要的jar包,其实这和springAOP的由来有联系,因为AOP的思想最原始就是aspect技术,只不过当时开发人员不知道这个技术干什么用,导致该项技术没有什么用武之地,但是到后来spring的作者意识到这种思想的优势的使用,加以整合提出aop的思想,使这项技术到达了巅峰。cglib是代理对象形成的另一种形式。
和上一篇博客对比我们可以发现,我们不需要自己写拦截器,而是在配置文件中实现。
首先来看一下配置文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <!-- 导入目标类 导入切面 进行aop的配置 --> <bean id="personDao" class="com.itheima11.spring.aop.xml.transaction.PersonDaoImpl"></bean> <bean id="transaction" class="com.itheima11.spring.aop.xml.transaction.Transaction"></bean> <aop:config> <!-- 切入点表达式 符合切入点表达式的类要产生代理对象 expression就是切入点表达式 id 标示符 --> <aop:pointcut expression="execution(* com.itheima11.spring.aop.xml.transaction.PersonDaoImpl.*(..))" id="perform"/> <aop:aspect ref="transaction"> <!-- 前置通知 1、在目标方法执行之前 2、前置通知中,方法有一个参数JoinPoint --> <aop:before method="beginTransaction" pointcut-ref="perform"/> <!-- 后置通知 1、在目标方法执行之后 2、能够获取目标方法的返回值 returning="val" val就是通知方法中 后置通知中的参数名称 3、如果目标方法产生异常,则后置通知不再执行 --> <aop:after-returning method="commit" pointcut-ref="perform" returning="val"/> <!-- 异常通知 获取目标方法抛出的异常信息 throwing="ex" --> <aop:after-throwing method="throwingMethod" pointcut-ref="perform" throwing="ex"/> <!-- 最终通知 --> <aop:after method="finallyMethod" pointcut-ref="perform"/> <!-- 环绕通知 能够控制目标方法的执行 环绕通知可以有返回值,这个返回值就是代理对象的方法的返回值 前置通知和后置通知只能在目标方法执行之前和之后加代码,但是不能控制目标方法的执行 --> <aop:around method="aroundMethod" pointcut-ref="perform"/> </aop:aspect> </aop:config> </beans>
核心类切面代码:
package com.itheima11.spring.aop.xml.transaction; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; public class Transaction { /** * 前置通知 参数:JoinPoint 连接点:客户端调用哪个方法,这个方法就是连接点 */ public void beginTransaction(JoinPoint joinPoint) { System.out.println("目标类:" + joinPoint.getTarget().getClass()); System.out.println("目标方法的名称:" + joinPoint.getSignature().getName()); System.out.println("目标方法的参数:" + joinPoint.getArgs().length); System.out.println("begin transaction"); } /** * 后置通知 val接收目标类的目标方法的返回值 */ public void commit(JoinPoint joinPoint, Object val) { System.out.println("返回值:" + val); System.out.println("commit"); } /** * 异常通知 不管是否目标方法是否抛出异常,都会执行这个方法,相当于finally */ public void throwingMethod(JoinPoint joinPoint, Throwable ex) { System.out.println(ex.getMessage()); } /** * 最终通知 */ public void finallyMethod() { System.out.println("finally method"); } /** * 环绕通知 可以控制目标方法的是否执行 */ public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("around method"); return joinPoint.proceed();// 执行目标方法,如果不写行代码 则目标方法不会执行 } }
客户端代码
package com.itheima11.spring.aop.xml.transaction; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * aop的原理 * 1、启动spring容器 * 2、spring容器会为personDao和transaction实例化 * 3、spring容器解析aop:config的配置 * 当spring容器解析到切入点表达式的时候,就会把切入点表达式解析出来 * 会让切入点表达式的类和spring容器中的类进行匹配 * 如果匹配成功,则会为该对象创建代理对象 * 代理对象的方法形成=通知+目标方法 * 如果匹配不成功,则会报错 * 4、在客户端context.getBean时,如果当前的对象有代理对象,则返回代理对象 * 如果没有代理对象返回对象的本身 * 5、在spring内部会检查目标类有没有实现接口 * 如果目标类实现了接口,则采用jdkproxy产生代理对象 * 如果目标类没有实现接口,则采用cglibproxy产生代理对象 * @author zd * */ public class PersonDaoTest { @Test public void testSpring(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); PersonDao personDao = (PersonDao)context.getBean("personDao"); String s = personDao.savePerson(); System.out.println(s); } }
结果:
小编在都将重要的注释写在代码中,请读者自行体会。上面这个demo就是对于springAOP中常用的几个通知的实现,在实际的场景我们需要不同的需求来运用不同的通知来实现。
当我们对这个模式比较熟悉以后,里面比较重要是我们切入点表达式的编写,我们不在用原先的if。。else。。来实现,我们根据需求编写表达式实现。所以我们需要熟悉表达式的编写。
在下面的博客中将会利用这些基本知识,实现具体的业务需求。