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 @Override13 public int sub(int i, int j) {14 int result = i - j;15 return result;16 }17 18 @Override19 public int mul(int i, int j) {20 int result = i * j;21 return result;22 }23 24 @Override25 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 @Override15 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 @Override23 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 @Override31 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 @Override33 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的具体用法还请参考本系列的后续文章......