概述

  • 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 */
View Code

 

注解方式实现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.2\lib】

      aspectjrt.jar   【spring2.5源码/lib/aspectj】或【aspectj-1.8.2\lib】

    注意: 用到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 }
View Code

 

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>  
View Code

 

切入点表达式

  可以对指定的“方法”进行拦截;  从而给指定的方法所在的类生层代理对象。

 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()) &amp;&amp; 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"/>-->
View Code