Spring AOP

AOP简介
  AOP(Aspect-Oriented Programming,面向切面编程):是一种新的方法论,是对传统OOP的补充
  AOP的主要编程对象时切面(aspect),而切面模块化横切关注点
  在应用AOP编程时,仍然需要定义公共功能,但可以明确的定义这个功能在哪里,以什么方式应用,并且不必修改受影响的类。这样一来横切关注点就被模块化到特殊的对象(切面)里。
  AOP的好处
    每个事务逻辑位于一个位置,代码不分散,便于维护和升级
    业务模块更简洁,只包含核心业务代码

AOP术语
  切面(Aspect):横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象
  通知(Advice):切面必须要完成的工作
  目标(Target):被通知的对象
  代理(Proxy):向目标对象应用通知之后创建的对象
  连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点有两个信息确定:方法表示的程序执行点,相对点表示的方位。例如ArithmeticCalculator#add()方法执行前的连接点,执行点为ArithmeticCalculator#add();方位为该方法执行前的位置。
  切点(pointcut):每个类都拥有多个连接点。例如ArithmeticCalculator的所有方法其实都是连接点,即连接点是程序类中客观存在的事务。AOP通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件,切点通过org.springframework.aop.Pointcut接口进行描述,它使用类和方法作为连接点的查询条件。

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

下面实现一个简单的计算器来解释AOP的产生背景。

接口:ArithmeticCalculator.java,定义加减乘除方法

 1 package com.yl.spring.aop.helloworld;
 2 
 3 public interface ArithmeticCalculator {
 4     
 5     int add(int i, int j);
 6     int sub(int i, int j);
 7     
 8     int mul(int i, int j);
 9     int div(int i, int j);
10 }

实现类:ArithmeticCalculatorImpl.java 实现基本的加减乘除

 1 package com.yl.spring.aop.helloworld;
 2 
 3 public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
 4 
 5     @Override
 6     public int add(int i, int j) {
 7         int result = i + j;
 8         return result;
 9         
10     }
11 
12     @Override
13     public int sub(int i, int j) {
14         int result = i - j;
15         return result;
16     }
17 
18     @Override
19     public int mul(int i, int j) {
20         int result = i * j;
21         return result;
22     }
23 
24     @Override
25     public int div(int i, int j) {
26         int result = i / j;
27         return result;
28     }
29 
30 }

至此,上面两段代码实现了基本的计算器。新的需求时在计算前后增加日志。

最简单的实现就是在每个方法的计算语句前后增加日志代码,即输出语句。具体实现如下:

ArithmeticCalculatorLoggingImpl.java

 1 package com.yl.spring.aop.helloworld;
 2 
 3 public class ArithmeticCalculatorLoggingImpl implements ArithmeticCalculator {
 4 
 5     @Override
 6     public int add(int i, int j) {
 7         System.out.println("the method add begin with[" + i + ", " + j + "]");
 8         int result = i + j;
 9         System.out.println("the method add end with " +result);
10         return result;
11         
12     }
13 
14     @Override
15     public int sub(int i, int j) {
16         System.out.println("the method sub begin with[" + i + ", " + j + "]");
17         int result = i - j;
18         System.out.println("the method sub end with " +result);
19         return result;
20     }
21 
22     @Override
23     public int mul(int i, int j) {
24         System.out.println("the method mul begin with[" + i + ", " + j + "]");
25         int result = i * j;
26         System.out.println("the method mul end with " +result);
27         return result;
28     }
29 
30     @Override
31     public int div(int i, int j) {
32         System.out.println("the method div begin with[" + i + ", " + j + "]");
33         int result = i / j;
34         System.out.println("the method div end with " +result);
35         return result;
36     }
37 
38 }

 但是上述的实现方法增加了业务模块的复杂度,而且修改日志代码时也不方便。

下面在介绍一个新的方法,即动态代理。具体实现如下:

ArithmeticCalculatorLoggingProxy.java

 1 package com.yl.spring.aop.helloworld;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 import java.lang.reflect.Proxy;
 6 import java.util.Arrays;
 7 
 8 import org.springframework.beans.propertyeditors.ClassArrayEditor;
 9 
10 public class ArithmeticCalculatorLoggingProxy {
11     //要代理的对象
12     private ArithmeticCalculator target;
13     
14     public ArithmeticCalculatorLoggingProxy(ArithmeticCalculator target) {
15         this.target = target;
16     }
17     
18     public ArithmeticCalculator getLoggingProxy() {
19         ArithmeticCalculator proxy = null;
20         
21         //代理对象由哪一个类加载器负责加载
22         ClassLoader loader = target.getClass().getClassLoader();
23         //代理对象的类型,即其中有哪些方法
24         Class [] interfaces = new Class[]{ArithmeticCalculator.class};
25         //当调用代理对象其中的方法时,该执行的代码
26         InvocationHandler h =  new InvocationHandler() {
27             /**
28              * proxy:正在返回的那个代理对象,一般情况下,在invoke方法中都不使用该对象
29              * method:正在调用的方法
30              * args:调用方法时,传入的参数
31              */
32             @Override
33             public Object invoke(Object proxy, Method method, Object[] args)
34                     throws Throwable {
35                 //此句话或造成循环调用,最终内存溢出
36                 //System.out.println(proxy.toString());
37                 
38                 
39                 String methodName = method.getName();
40                 //日志
41                 System.out.println("the method " + methodName + " begin with " + Arrays.asList(args));
42                 //执行该方法
43                 Object result = method.invoke(target, args);
44                 //日志
45                 System.out.println("the method " + methodName + " end with " + result);
46                 return result;
47             }
48         };
49         
50         proxy = (ArithmeticCalculator) Proxy.newProxyInstance(loader, interfaces, h);
51         
52         return proxy;
53     }
54 
55 }

至此,动态代理的实现完毕。

测试类:

 1 package com.yl.spring.aop.helloworld;
 2 
 3 public class Main {
 4     public static void main(String[] args) {
 5         
 6         /*//测试日志
 7         ArithmeticCalculator arithmeticCalculator = null;
 8         arithmeticCalculator = new ArithmeticCalculatorLoggingImpl();
 9         
10         int result = arithmeticCalculator.add(1, 2);
11         System.out.println("-->" + result);
12         
13         result = arithmeticCalculator.div(4, 2);
14         System.out.println("-->" + result);*/
15         
16         //动态代理测试
17         ArithmeticCalculator target = new ArithmeticCalculatorImpl();
18         ArithmeticCalculator proxy = new ArithmeticCalculatorLoggingProxy(target).getLoggingProxy();
19         
20         System.out.println(proxy.getClass().getName());
21         
22         int result = proxy.add(1, 2);
23         System.out.println("-->" + result);
24         
25         result = proxy.div(4, 2);
26         System.out.println("-->" + result);
27     }
28 }

AOP的具体用法还请参考本系列的后续文章......

原文地址:https://www.cnblogs.com/dreamfree/p/4093768.html