写一个计算类,计算前后需要打印日志。
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 -->3 The 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 -->3 The methods div begins with[4, 2] The methods div ends with2 -->2
用了动态代理方法,就会发现ArithmeticCalculatorImpl实现类变得清洁很多。并且日志被封装在invoke方法中,修改十分方便。
aop术语:
切面:横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象
通知:切面必须要完成的工作
目标:被通知的对象
代理:向目标对象应用通知之后创建的对象
连接点:程序执行的某个特定位置,如类某个方法调用前,调用后,方法抛出异常后
由两个信息确定:方法表示的程序执行点,想对点表示的方位。
切点:每个类都拥有多个连接点,即连接点是程序类中客观存在的事务。
AOP通过切点定位到特定的连接点:类比:连接点相当于数据库中的记录,切点相当于查询条件。
前置通知:
来源:https://www.cnblogs.com/X-Spider/p/5395687.html