先来实现一个实例
1.创建接口ArithmeticCalulator
public interface ArtithmeticCaclulator {
int add(int i,int j);
int sub(int i,int j);
int mul(int i,int j);
int div(int i,int j);
}
2.实现ArithmeticCalulatorImpl,并加入日志信息
public class ArithmeticCalulatorImpl implements ArtithmeticCaclulator {
@Override
public int add(int i, int j) {
System.out.println("The method add begin with [" + i + "," + j + "]");
int result = i + j;
System.out.println("The method add end with " + result);
return result;
}
@Override
public int sub(int i, int j) {
System.out.println("The method sub begin with [" + i + "," + j + "]");
int result = i - j;
System.out.println("The method sub end with " + result);
return result;
}
@Override
public int mul(int i, int j) {
System.out.println("The method mul begin with [" + i + "," + j + "]");
int result = i * j;
System.out.println("The method mul end with " + result);
return result;
}
@Override
public int div(int i, int j) {
System.out.println("The method div begin with [" + i + "," + j + "]");
int result = i / j;
System.out.println("The method div end with " + result);
return result;
}
}
3.Main
public class Main {
public static void main(String[] args) {
ArtithmeticCaclulator artithmeticCaclulator =new ArithmeticCalulatorImpl();
int result=artithmeticCaclulator.add(1, 2);
System.out.println("->>"+result);
result=artithmeticCaclulator.sub(3, 2);
System.out.println("->>"+result);
}
}
4.结果
可以看到这些日记信息都十分相似,而且当我加入这些日志信息后,有时候会和业务代码混淆,难以区分。不利于我们的维护。
解决方法一:
1.动态代理
AritimeticCalulatorLoggingProxy.class
public class AritimeticCalulatorLoggingProxy {
//要代理的对象
private ArtithmeticCaclulator target;
public AritimeticCalulatorLoggingProxy(ArtithmeticCaclulator target) {
this.target = target;
}
public ArtithmeticCaclulator getLoggingProxy() {
ArtithmeticCaclulator proxy=null;
//代理对象由哪个类加载器加载
ClassLoader loader=target.getClass().getClassLoader();
//代理对象的类型 包括其中的方法
Class [] interfaces =new Class[] {ArtithmeticCaclulator.class};
//当调用代理对象的方法时 指向的代码
InvocationHandler h =new InvocationHandler() {
/**
* proxy:正在返回的那个代理对象 一般情况下 在invoke方法总都不使用该对象
* 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 with "+ Arrays.asList(args));
//执行方法
Object result = method.invoke(target, args);
//日志
System.out.println("The method "+ methodname + " end with " + result);
return result;
}
};
proxy=(ArtithmeticCaclulator)Proxy.newProxyInstance(loader, interfaces, h);
return proxy;
}
}
我们把公共代码写在InvocationHandler里面。同时去除ArithmeticCalulatorImpl的日志信息。
修改Main方法 ,通过代理调用方法
public class Main {
public static void main(String[] args) {
ArtithmeticCaclulator target=new ArithmeticCalulatorImpl();
ArtithmeticCaclulator proxy= new AritimeticCalulatorLoggingProxy(target).getLoggingProxy();
int result=proxy.add(1, 2);
System.out.println("->>"+result);
result=proxy.sub(3, 2);
System.out.println("->>"+result);
}
}
结果:
我们可以引入这样一种思想,将业务代码和日志代码以某种形式分离开来,由于他们是同一层的(横向的),所有我们可以像上面的例子用Proxy分离。但是实际中肯定不能这么做,一是维护成本高,二是对程序员要求也高。
我们可以利用Aop编程的思想来解决代码分离的问题。
AOP简介
术语
1.切面:我们将日志抽取出来,作为一个切面,将验证抽取出来,作为一个切面,将业务逻辑抽取出来,作为一个切面。这些切面都叫做横切关注点。
2.通知:切面里的每一个方法都是一个通知。它是每个切面必须完成的工作。
3.目标:被通知的对象,这里是add(),mul(),div(),sub()。
4.代理:通知目标后,业务逻辑和日志必须有一个结合,这样的结果就是产生一个代理。
5.连接点:以方法执行为评判标准,执行前后中,异常抛出时等等。。相当于一个被查询的数据。是切点的定位目标
6.切点:aop通过切点定位到连接点,看不见摸不着。相当于查询条件。