需求:有一个接口,里面有多个方法,如今须要记录每一个方法的开始与结束。
//接口以下java
public interface Calculator { int add(int i,int j); int sub(int i,int j); }
//接口实现以下程序员
public class CalculatorImpl implements Calculator{ @Override public int add(int i, int j) { int result = i+j; return result; } @Override public int sub(int i, int j) { int result = i-j; return result; } }
1.常规作法,在每一个方法的开始与结束的地方加上log。以下spring
public class CalculatorImpl implements Calculator{ @Override public int add(int i, int j) { System.out.println("The method add begin["+i+","+j+"]"); int result = i+j; System.out.println("The method add end"+result); return result; } @Override public int sub(int i, int j) { System.out.println("The method sub begin["+i+","+j+"]"); int result = i-j; System.out.println("The method sub end"+result); return result; } }
1.1 测试代码数据库
public class Main { public static void main(String[] args) { Calculator calculator = new CalculatorImpl(); calculator.add(1, 2); calculator.sub(4, 2); } }
1.2 输出结果编程
The method add begin[1,2] The method add end3 The method sub begin[4,2] The method sub end2
2.很明显上述能够知足每一个方法执行先后输出log的要求,可是若是输出的内容稍微改一点,那么将是大面积的修改log,所以上述方法不是很适合大型项目的log需求。
2.1 采用动态代理方法实现,添加代理类框架
package com.test.aop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; public class CalculatorProxy { //要代理的对象 private Calculator target; //返回代理对象 public Calculator getProxy(){ Calculator proxy = null; //代理对象由哪个类加载器负责加载 ClassLoader loader = target.getClass().getClassLoader(); //代理对象的类型,即其中有哪些方法 Class [] interfaces = new Class[]{Calculator.class}; //当调用代理对象的方法时,执行该代码 InvocationHandler h = new InvocationHandler() { /** * proxy:正在执行的代理对象,通常不使用该对象 * method:正在被调用的方法 * args:调用方法时传入的参数 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); //日志开始 System.out.println("The method"+methodName+" begin "+Arrays.asList(args)); //执行方法 Object oj = method.invoke(target, args); //日志结束 System.out.println("The method"+methodName+" end "+oj); return oj; } }; proxy = (Calculator) Proxy.newProxyInstance(loader, interfaces, h); return proxy; } public CalculatorProxy(Calculator target) { this.target = target; } }
2.2 main方法ide
public class Main { public static void main(String[] args) { Calculator target = new CalculatorImpl(); Calculator proxy = new CalculatorProxy(target).getProxy(); int result = proxy.add(1, 2); System.out.println(result); result = proxy.sub(4, 2); System.out.println(result); } }
2.3输出测试
The methodadd begin [1, 2] The methodadd end 3 3 The methodsub begin [4, 2] The methodsub end 2 2
3.如上的方法彻底能够知足需求,可是不可能每一个程序员都懂动态代理,加之spring框架自己也提供了相应的解决方法,即AOP(面向切面编程)。举个简单的实例,一个业务逻辑由以下部分组成:
1>验证参数
2>前置日志
3>方法,即核心实现
4>后置日志
AOP经常使用术语:
1.切面:如上业务逻辑组成部分,每一个点都是一个切面。
2.通知:切面完成的功能。
3.目标:被通知的对象,即如上业务逻辑组成部分中的 方法。
4.代理:它是一个对象,它的做用是通知目标对象。
5.链接点:指的是程序的某个位置,例如:某个方法被调用以前、调用以后,或是抛出异常的位置。
6.切点:每一个类都拥有多个链接点,AOP经过切点定位到指定的链接点。例如:链接点至关于数据库中的记录(记录有多条),切点至关于查询条件,它能查询到某一条记录。开始调用切面的时候叫作切点,简单的理解为在哪里开始调用日志、事务的地方。this