AOP在spring中是非常重要的一个
在切面类中,有5种通知类型:
aop:before 前置通知
aop:after-returning 后置通知
aop:after 最终通知
aop:after-throwing 异常通知
aop:around 环绕通知
1 <bean id="personDAO" class="com.lee.spring002.aop.xml.PersonDAOImpl"></bean> 2 <bean id="transaction" class="com.lee.spring002.aop.xml.Transaction"></bean> 3 4 <aop:config > 5 <!-- 切入点表达式,作用:确定目标类 --> 6 <!-- spring 会自动检测这个表达式下的类是否是切面,如果是,则不会包含进来 --> 7 <aop:pointcut expression="execution(* com.lee.spring002.aop.xml.PersonDAOImpl.*(..))" id="perform"/> 8 <!-- ref 指向切面 --> 9 <aop:aspect ref="transaction">10 <!-- 前置通知 -->11 <aop:before method="beginTransaction" pointcut-ref="perform"/>12 13 <!-- 14 后置通知15 可以获取目标方法的返回值(前置方法获取不到)16 如果目标方法抛出异常,后置通知则不会继续执行17 -->18 <aop:after-returning method="commit" pointcut-ref="perform" returning="val"/>19 20 <!-- 21 最终通知22 无论目标方法是否抛出异常都将执行此方法23 -->24 <aop:after method="finallyDisplay" pointcut-ref="perform"/>25 26 <!-- 27 异常通知28 -->29 <aop:after-throwing method="exception" pointcut-ref="perform" throwing="content"/>30 31 <!-- 32 环绕通知33 能控制目标方法能否执行34 前置通知和后置通知能在目标方法的前后加代码,但是不能控制方法的执行35 -->36 <aop:around method="arround" pointcut-ref="perform"/>37 </aop:aspect>38 </aop:config>
关于切面的表达式简单说一下:
- 任意公共方法的执行:
execution(public * *(..))
- 任何一个名字以“set”开始的方法的执行:
execution(* set*(..))
AccountService
接口定义的任意方法的执行:execution(* com.xyz.service.AccountService.*(..))
- 在service包中定义的任意方法的执行:
execution(* com.xyz.service.*.*(..))
- 在service包或其子包中定义的任意方法的执行:
execution(* com.xyz.service..*.*(..))
IPersonDAO.java
1 package com.lee.spring002.aop.xml;2 3 public interface IPersonDAO {4 public String savePerson();5 }
PersonDAOImpl.java
1 package com.lee.spring002.aop.xml; 2 3 public class PersonDAOImpl implements IPersonDAO { 4 5 @Override 6 public String savePerson() { 7 System.out.println("PersonDAOImpl - savePerson()"); 8 9 // int a = 1 / 0;10 11 return "save successfully";12 }13 14 }
Transaction.java
1 package com.lee.spring002.aop.xml; 2 3 import org.aspectj.lang.JoinPoint; 4 import org.aspectj.lang.ProceedingJoinPoint; 5 6 /** 7 * 这是个切面 8 * 9 * @author leechenxiang10 * @date 2016年1月12日11 *12 */13 public class Transaction {14 15 public void beginTransaction(JoinPoint jp) {16 System.out.println("连接点名称: " + jp.getSignature().getName());17 System.out.println("目标类名称: " + jp.getTarget().getClass());18 System.out.println("Begin transaction...");19 }20 21 /**22 * 23 * @param jp24 * @param val 目标方法返回值,要与returning对应 25 */26 public void commit(JoinPoint jp, Object val) {27 System.out.println("Transaction commit...");28 29 System.out.println("returning: " + val.toString());30 } 31 32 public void finallyDisplay() {33 System.out.println("finally...");34 }35 36 public void exception(JoinPoint jp, Throwable content) {37 System.out.println("exception: " + content);38 System.out.println("exception: " + content.getMessage());39 }40 41 public void arround(ProceedingJoinPoint pjp) throws Throwable {42 System.out.println("arround...");43 pjp.proceed(); // 调用目标方法,如果这行不写,则目标方法不执行44 }45 }
测试:
1 package com.lee.spring002.aop.xml; 2 3 import org.junit.Test; 4 import org.springframework.context.ApplicationContext; 5 import org.springframework.context.support.ClassPathXmlApplicationContext; 6 7 public class TransactionTest { 8 9 /**10 * 原理:11 * 1、当spring容器启动的时候,加载两个bean,对两个bean进行实例化12 * 2、当spring容器对配置文件解析到<aop:config>的时候13 * 3、把切入点表达式解析出来,按照切入点表达式匹配spring容器内容的bean14 * 4、如果匹配成功,则为该bean创建代理对象15 * 5、当客户端利用context.getBean获取一个对象时,如果该对象有代理对象,则返回代理对象16 * 如果没有代理对象,则返回对象本身17 */18 @Test19 public void testPerson() {20 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");21 IPersonDAO personDAO = (IPersonDAO)context.getBean("personDAO");22 personDAO.savePerson();23 }24 25 }
github地址:https://github.com/leechenxiang/maven-spring002-aop