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的具体用法还请参考本系列的后续文章......

本文介绍了AOP(面向切面编程),它是对传统OOP的补充,能将横切关注点模块化到切面。阐述了AOP的好处,如便于维护和升级、业务模块更简洁。还介绍了AOP的术语,最后通过简单计算器示例解释其产生背景,包括基本实现、日志需求及动态代理实现。
6546

被折叠的 条评论
为什么被折叠?



