概述
- aspect object programming 面向切面编程
- 功能:可以实现“业务代码”与“关注点代码”分离
- 关注点代码:就是指重复执行的代码
- 业务代码:核心的业务功能
- 运行期间,执行核心业务代码的时候动态植入关注点代码【代理】
名词解释
- 关注点
- 重复代码就叫关注点
- 切面
- 关注点形成的类,就叫做切面(类)
- 切入点
- 执行目标对象方法,动态植入切面代码
- 可以通过切入点表达式,指定拦截哪些类的哪些方法,给指定的类在运行的时候植入切面类代码
- aop
- 面向切面编程,就是指 对很多功能都有的重复的代码的抽取,再在运行的时候往业务方法上动态植入“切面类代码”。
手动实现AOP编程

1 package cn.fuyi.d_myaop; 2 3 public interface IUserDao { 4 5 void save(); 6 } 7 8 package cn.fuyi.d_myaop; 9 10 import javax.annotation.Resource; 11 12 import org.springframework.stereotype.Component; 13 14 @Component("userDao") 15 public class UserDao implements IUserDao { 16 17 @Override 18 public void save() { 19 System.out.println("=====已经保存数据====-"); 20 } 21 } 22 23 package cn.fuyi.d_myaop; 24 25 import org.springframework.stereotype.Component; 26 27 /** 28 * 抽取重复代码形成的类 29 * @author fuyi 30 * 31 */ 32 @Component("aop") //加入IOC容器 33 public class Aop { 34 35 public void begin() { 36 System.out.println("开启事务"); 37 } 38 39 public void commit() { 40 System.out.println("提交事务"); 41 } 42 } 43 44 package cn.fuyi.d_myaop; 45 46 import java.lang.reflect.Method; 47 48 import org.springframework.cglib.proxy.InvocationHandler; 49 import org.springframework.cglib.proxy.Proxy; 50 51 public class ProxyFactory { 52 53 private static Object target; 54 private static Aop aop; 55 56 public static Object getProxyInstance(Object target_, Aop aop_) { 57 58 target = target_; 59 aop = aop_; 60 61 return Proxy.newProxyInstance(target.getClass().getClassLoader(), 62 target.getClass().getInterfaces(), 63 new InvocationHandler() { 64 65 @Override 66 public Object invoke(Object proxy, Method method, Object[] args) 67 throws Throwable { 68 aop.begin(); 69 //执行目标对象方法 70 Object returnValue = method.invoke(target, args); 71 aop.commit(); 72 return returnValue; 73 } 74 }); 75 } 76 77 } 78 79 bean.xml 80 <!-- 开启注解扫描 --> 81 <context:component-scan base-package="cn.fuyi.d_myaop"></context:component-scan> 82 83 <!-- 调用工厂方法,返回UserDao的代理对象 --> 84 <bean id="userDao_proxy" class="cn.fuyi.d_myaop.ProxyFactory" factory-method="getProxyInstance"> 85 <constructor-arg index="0" ref="userDao"></constructor-arg> 86 <constructor-arg index="1" ref="aop"></constructor-arg> 87 </bean> 88 89 package cn.fuyi.d_myaop; 90 91 import static org.junit.Assert.*; 92 93 import org.junit.Test; 94 import org.springframework.context.ApplicationContext; 95 import org.springframework.context.support.ClassPathXmlApplicationContext; 96 97 public class App { 98 99 private ApplicationContext ac = new ClassPathXmlApplicationContext("cn/fuyi/d_myaop/beans.xml"); 100 101 @Test 102 public void testAop1() throws Exception { 103 IUserDao ud = (IUserDao) ac.getBean("userDao_proxy"); 104 ud.save(); 105 106 } 107 } 108 109 /**Output 110 开启事务 111 =====已经保存数据====- 112 提交事务 113 */
注解方式实现AOP编程
步骤:
1) 先引入aop相关jar文件 (aspectj aop优秀组件)
spring-aop-3.2.5.RELEASE.jar 【spring3.2源码】
aopalliance.jar 【spring2.5源码/lib/aopalliance】
aspectjweaver.jar 【spring2.5源码/lib/aspectj】或【aspectj-1.8.2lib】
aspectjrt.jar 【spring2.5源码/lib/aspectj】或【aspectj-1.8.2lib】
注意: 用到spring2.5版本的jar文件,如果用jdk1.7可能会有问题。需要升级aspectj组件,即使用aspectj-1.8.2版本中提供jar文件提供。
2) bean.xml中引入aop名称空间
3) 开启aop注解
4) 使用注解
@Aspect 指定一个类为切面类
@Pointcut("execution(* cn.itcast.e_aop_anno.*.*(..))") 指定切入点表达式
@Before("pointCut_()") 前置通知: 目标方法之前执行
@After("pointCut_()") 后置通知:目标方法之后执行(始终执行)
@AfterReturning("pointCut_()") 返回后通知: 执行方法结束前执行(异常不执行)
@AfterThrowing("pointCut_()") 异常通知: 出现异常时候执行
@Around("pointCut_()") 环绕通知: 环绕目标方法执行

1 package cn.fuyi.e_annoaop; 2 3 public interface IUserDao { 4 5 void save(); 6 } 7 8 package cn.fuyi.e_annoaop; 9 10 import org.springframework.stereotype.Component; 11 12 @Component("userDao") 13 public class UserDao implements IUserDao { 14 15 @Override 16 public void save() { 17 System.out.println("=====已经保存数据====-"); 18 } 19 } 20 21 22 @Component 23 @Aspect // 指定当前类为切面类 24 public class Aop { 25 26 // 指定切入点表单式: 拦截哪些方法; 即为哪些类生成代理对象 27 28 @Pointcut("execution(* cn.fuyi.e_annoaop.*.*(..))") 29 public void pointCut_(){ 30 } 31 32 // 前置通知 : 在执行目标方法之前执行 33 @Before("pointCut_()") 34 public void begin(){ 35 System.out.println("开始事务/异常"); 36 } 37 38 // 后置/最终通知:在执行目标方法之后执行 【无论是否出现异常最终都会执行】 39 @After("pointCut_()") 40 public void after(){ 41 System.out.println("提交事务/关闭"); 42 } 43 44 // 返回后通知: 在调用目标方法结束后执行 【出现异常不执行】 45 @AfterReturning("pointCut_()") 46 public void afterReturning() { 47 System.out.println("afterReturning()"); 48 } 49 50 // 异常通知: 当目标方法执行异常时候执行此关注点代码 51 @AfterThrowing("pointCut_()") 52 public void afterThrowing(){ 53 System.out.println("afterThrowing()"); 54 } 55 56 // 环绕通知:环绕目标方式执行 57 @Around("pointCut_()") 58 public void around(ProceedingJoinPoint pjp) throws Throwable{ 59 System.out.println("环绕前...."); 60 pjp.proceed(); // 执行目标方法 61 System.out.println("环绕后...."); 62 } 63 64 } 65 66 67 <!-- 开启注解扫描 --> 68 <context:component-scan base-package="cn.fuyi.e_annoaop"></context:component-scan> 69 70 <!-- 开启aop注解方式,可不写 71 proxy-target-class 默认为false,如果目标对象实现了接口,就使用JDK代理,否则用Cglib代理 72 --> 73 <aop:aspectj-autoproxy></aop:aspectj-autoproxy> 74 75 76 public class App { 77 78 ApplicationContext ac = 79 new ClassPathXmlApplicationContext("cn/itcast/e_aop_anno/bean.xml"); 80 81 // 目标对象有实现接口,spring会自动选择“JDK代理” 82 @Test 83 public void testApp() { 84 IUserDao userDao = (IUserDao) ac.getBean("userDao"); 85 System.out.println(userDao.getClass()); 86 userDao.save(); 87 } 88 89 // 目标对象没有实现接口, spring会用“cglib代理” 90 @Test 91 public void testCglib() { 92 OrderDao orderDao = (OrderDao) ac.getBean("orderDao"); 93 System.out.println(orderDao.getClass()); 94 orderDao.save(); 95 } 96 }
XML方式实现AOP编程
1) 引入jar文件 【aop 相关jar, 4个】
2) 引入aop名称空间
3)aop 配置
* 配置切面类 (重复执行代码形成的类)
* aop配置
拦截哪些方法 / 拦截到方法后应用通知代码

1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:p="http://www.springframework.org/schema/p" 5 xmlns:context="http://www.springframework.org/schema/context" 6 xmlns:aop="http://www.springframework.org/schema/aop" 7 xsi:schemaLocation=" 8 http://www.springframework.org/schema/beans 9 http://www.springframework.org/schema/beans/spring-beans.xsd 10 http://www.springframework.org/schema/context 11 http://www.springframework.org/schema/context/spring-context.xsd 12 http://www.springframework.org/schema/aop 13 http://www.springframework.org/schema/aop/spring-aop.xsd"> 14 15 <!-- dao 实例 --> 16 <bean id="userDao" class="cn.itcast.f_aop_xml.UserDao"></bean> 17 <bean id="orderDao" class="cn.itcast.f_aop_xml.OrderDao"></bean> 18 19 <!-- 切面类 --> 20 <bean id="aop" class="cn.itcast.f_aop_xml.Aop"></bean> 21 22 <!-- Aop配置 --> 23 <aop:config> 24 <!-- 定义一个切入点表达式: 拦截哪些方法 --> 25 <aop:pointcut expression="execution(* cn.itcast.f_aop_xml.*.*(..))" id="pt"/> 26 <!-- 切面 --> 27 <aop:aspect ref="aop"> 28 <!-- 环绕通知 --> 29 <aop:around method="around" pointcut-ref="pt"/> 30 <!-- 前置通知: 在目标方法调用前执行 --> 31 <aop:before method="begin" pointcut-ref="pt"/> 32 <!-- 后置通知: --> 33 <aop:after method="after" pointcut-ref="pt"/> 34 <!-- 返回后通知 --> 35 <aop:after-returning method="afterReturning" pointcut-ref="pt"/> 36 <!-- 异常通知 --> 37 <aop:after-throwing method="afterThrowing" pointcut-ref="pt"/> 38 39 </aop:aspect> 40 </aop:config> 41 </beans>
切入点表达式
可以对指定的“方法”进行拦截; 从而给指定的方法所在的类生层代理对象。

1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:p="http://www.springframework.org/schema/p" 5 xmlns:context="http://www.springframework.org/schema/context" 6 xmlns:aop="http://www.springframework.org/schema/aop" 7 xsi:schemaLocation=" 8 http://www.springframework.org/schema/beans 9 http://www.springframework.org/schema/beans/spring-beans.xsd 10 http://www.springframework.org/schema/context 11 http://www.springframework.org/schema/context/spring-context.xsd 12 http://www.springframework.org/schema/aop 13 http://www.springframework.org/schema/aop/spring-aop.xsd"> 14 15 <!-- dao 实例 --> 16 <bean id="userDao" class="cn.itcast.g_pointcut.UserDao"></bean> 17 <bean id="orderDao" class="cn.itcast.g_pointcut.OrderDao"></bean> 18 19 <!-- 切面类 --> 20 <bean id="aop" class="cn.itcast.g_pointcut.Aop"></bean> 21 22 <!-- Aop配置 --> 23 <aop:config> 24 25 <!-- 定义一个切入点表达式: 拦截哪些方法 --> 26 <!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.*.*(..))" id="pt"/>--> 27 28 <!-- 【拦截所有public方法】 --> 29 <!--<aop:pointcut expression="execution(public * *(..))" id="pt"/>--> 30 31 <!-- 【拦截所有save开头的方法 】 --> 32 <!--<aop:pointcut expression="execution(* save*(..))" id="pt"/>--> 33 34 <!-- 【拦截指定类的指定方法, 拦截时候一定要定位到方法】 --> 35 <!--<aop:pointcut expression="execution(public * cn.itcast.g_pointcut.OrderDao.save(..))" id="pt"/>--> 36 37 <!-- 【拦截指定类的所有方法】 --> 38 <!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.*(..))" id="pt"/>--> 39 40 <!-- 【拦截指定包,以及其自包下所有类的所有方法】 --> 41 <!--<aop:pointcut expression="execution(* cn..*.*(..))" id="pt"/>--> 42 43 <!-- 【多个表达式】 --> 44 <!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) || execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>--> 45 <!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) or execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>--> 46 <!-- 下面2个且关系的,没有意义 --> 47 <!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) && execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>--> 48 <!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) and execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>--> 49 50 <!-- 【取非值】 --> 51 <!--<aop:pointcut expression="!execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->