写一个计算类,计算前后需要打印日志。
interface ArithmeticCalculator { public int add(int i, int j); public int sub(int i, int j); public int mul(int i, int j); public int div(int i, int j);}
public class ArithmeticCalculatorImpl implements ArithmeticCalculator { @Override public int add(int i, int j) { // TODO Auto-generated method stub System.out.println("The method add begins with[" + i + "," + j + "]"); int result = i + j; System.out.println("The method add ends with " + result); return result; } @Override public int sub(int i, int j) { // TODO Auto-generated method stub System.out.println("The method sub begins with[" + i + "," + j + "]"); int result = i - j; System.out.println("The method sub ends with " + result); return result; } @Override public int mul(int i, int j) { // TODO Auto-generated method stub System.out.println("The method mul begins with[" + i + "," + j + "]"); int result = i * j; System.out.println("The method mul ends with " + result); return result; } @Override public int div(int i, int j) { // TODO Auto-generated method stub System.out.println("The method div begins with[" + i + "," + j + "]"); int result = i / j; System.out.println("The method div ends with " + result); return result; }}
从上面代码可以看出,四个方法的日志有重复的部分。
Main.java
public class Main { public static void main(String[] args) { // TODO Auto-generated method stub ArithmeticCalculator arithmeticCalculator = null; arithmeticCalculator = new ArithmeticCalculatorImpl(); int result = arithmeticCalculator.add(1, 2); System.out.println("-->" + result); result = arithmeticCalculator.div(4, 2); System.out.println("-->" + result); }}
打印结果:
The method add begins with[1,2]The method add ends with 3-->3The method div begins with[4,2]The method div ends with 2-->2
结果没什么问题,可是在加入日志后,原有的业务方法急剧膨胀,每个方法在处理核心逻辑的同时还要兼顾其他多个关注点,并且在日志需求发生变化时,必须修改所有模块。
接下来,我们使用动态代理解决上述问题
代理设计模式的原理:使用一个代理将对象包装起来,然后用该代理对象取代原始对象,任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
public class ArithmeticCalculatorImpl implements ArithmeticCalculator { @Override public int add(int i, int j) { // TODO Auto-generated method stub int result = i + j; return result; } @Override public int sub(int i, int j) { // TODO Auto-generated method stub int result = i - j; return result; } @Override public int mul(int i, int j) { // TODO Auto-generated method stub int result = i * j; return result; } @Override public int div(int i, int j) { // TODO Auto-generated method stub int result = i / j; return result; }}
public class ArithmeticCalculatorLoggingProxy { private ArithmeticCalculator target; public ArithmeticCalculatorLoggingProxy(ArithmeticCalculator target) { super(); this.target = target; } public ArithmeticCalculator getLoggingProxy() { ArithmeticCalculator proxy = null; //代理对象由哪一个类加载器负责加载 ClassLoader loader = target.getClass().getClassLoader(); //代理对象的类型,即其中有哪些方法 Class [] interfaces = new Class[]{ArithmeticCalculator.class}; //当调用代理对象其中的方法时,该执行的代码 InvocationHandler h = new InvocationHandler() { /** * proxy: 正在返回的那个代理对象,一般情况下,在invoke方法中都不使用对象 * method: 正在被调用的方法 * args: 调用方法时,传入的参数 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub String methodName = method.getName(); //日志 System.out.println("The methods " + methodName + " begins with" + Arrays.asList(args)); //执行方法 Object result = method.invoke(target, args); //日志 System.out.println("The methods " + methodName + " ends with" + result); return result; } }; proxy = (ArithmeticCalculator) Proxy.newProxyInstance(loader, interfaces, h); return proxy; }}
Main.java
ArithmeticCalculator target = new ArithmeticCalculatorImpl(); ArithmeticCalculator proxy = new ArithmeticCalculatorLoggingProxy(target).getLoggingProxy(); int result = proxy.add(1, 2); System.out.println("-->" + result); result = proxy.div(4, 2); System.out.println("-->" + result);
打印结果:
The methods add begins with[1, 2]The methods add ends with3-->3The methods div begins with[4, 2]The methods div ends with2-->2
用了动态代理方法,就会发现ArithmeticCalculatorImpl实现类变得清洁很多。并且日志被封装在invoke方法中,修改十分方便。
aop术语:
切面:横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象
通知:切面必须要完成的工作
目标:被通知的对象
代理:向目标对象应用通知之后创建的对象
连接点:程序执行的某个特定位置,如类某个方法调用前,调用后,方法抛出异常后
由两个信息确定:方法表示的程序执行点,想对点表示的方位。
切点:每个类都拥有多个连接点,即连接点是程序类中客观存在的事务。
AOP通过切点定位到特定的连接点:类比:连接点相当于数据库中的记录,切点相当于查询条件。
前置通知: