AOP也就是传说中的“面向切面编程”,是一种新的方法论,也是对传统的OOP的补充,AOP的主要的编程对象是切面,切面模块化横切关注点。
以上的就是比较专业的概述了什么是AOP,当然还有一大堆术语像切点,连接点,切面啥的我就不累赘了,这些东西编程的次数多了自然就熟悉了,这里我用比较浅显易懂的方式和大家说一下什么是AOP。
AOP是面向切面的,编写的传统的C++和JAVA程序是面向对象的,也就是OOP了,之所以一开始大家(我也一样)对AOP的概念非常模糊,看了很多博客也不能很清晰的说出了个一二三来,原因很简单,就是没有自己去写一个AOP的程序,编程这块我觉得动手才是最重要的,一会儿我会用一个简单的程序去说明AOP。在此之前呢,还是大概说一下对AOP的理解,当然我不会用术语尽量说的明白。
大家都知道,写出的代码很注重所谓的“重用性”,要是想让自己的程序能够反复的利用,那必须做到一点,每次修改的时候只是修改一部分,而不是脱胎换骨。虽然面向对象的提出解决了很多的问题,但是也不能做的完美无缺,比如:比如我们要对两个数进行数学运算:加减乘除。但是有个要求,那就是加入日志,在运算之前要记录两个数,在运算之后要记录运算结果。为了方便就直接输出相应的信息就好了,那么我们先定义一个接口:
public interface ArithmeticCalculator
{
int add(int i,int j);
int sub(int i,int j);
int mul(int i,int j);
int div(int i,int j);
}
那么我们在去写一个类去实现这个接口(用传统的方法):
public class ArithmeticCalculatorLoggingImpl implements ArithmeticCalculator {
@Override
public int add(int i, int j)
{
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)
{
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)
{
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)
{
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;
}
}
想必在这里大家都能看懂,这很符合要求是不是,但是大家发现一个问题了没有,每个方法中我们都为了日志输出了两次,但是每个方法的输出时候很相似的,如果这时候我想改变输出的语句,是不是每个方法都要重新写一遍,这样是很麻烦的。于是我们就想,能不能在运行的时候插入我们想要的呢?而不是直接在函数和里面写。在外面看起来就像是在一个类中插入了相应的代码,那这个OOP就不能实现了。于是一咬牙一跺脚,AOP出来吧!于是所谓的面向切面编程就诞生了,从这里大家应该对这个所谓的“切面”有所了解了吧,或者说有点感觉了,其实这个“切面”可以理解成针对与一个类中的某个部分(也许不怎么恰当,先将就着看吧)。
说了半天,那么这个程序如果用面向切面的思想来写会是什么样的呢?
首先实现接口的类变了,变成啥了嗯?看看代码:
package calculate;
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
@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;
}
@Override
public int mul(int i, int j)
{
int result = i*j;
return result;
}
@Override
public int div(int i, int j)
{
int result = i/j;
return result;
}
}
很明显,没有了那些繁杂的输出语句,剩下的这些add,sub,mul,div函数可以叫做业务逻辑。那么去掉的那些前置日志,后置日志呢叫啥呢?那就是切面了。好了那么怎么实现之前的功能呢?那就要写一个代理类了。
public class ArithmeticCalculatorLogginProxy
{
//要代理的对象
private ArithmeticCalculator target;
public ArithmeticCalculatorLogginProxy(ArithmeticCalculator target)
{
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
{
String methodName = method.getName();
//日志1
System.out.println("the method "+ methodName + " begins with " + Arrays.asList(args));
//执行方法
Object result = method.invoke(target, args);
//日志2
System.out.println("The method " + methodName + " ends with " + result);
return result;
}
};
proxy = (ArithmeticCalculator)Proxy.newProxyInstance(loader, interfaces, h);
return proxy;
}
}
也许大家对这个proxy东西不怎么熟悉,没事,其实这玩意就是起到一个代理的功能。主要是编写invoke函数来帮我们完成应该完成的任务。这里可以看出,如果再想改变输出语句的内容,只需要该两行函数就好了。是不是很方便!
那么写一个测试程序测试一下呗。
public class Main
{
public static void main(String[] args)
{
/*
ArithmeticCalculator arithmeticCalculator = null;
arithmeticCalculator = new ArithmeticCalculatorLoggingImpl();
*/
ArithmeticCalculator target = new ArithmeticCalculatorImpl();
ArithmeticCalculator proxy = new ArithmeticCalculatorLogginProxy(target).getLoggingProxy();
int result = proxy.add(1, 2);
System.out.println("result------>" + result);
}
}
上面注释的就是测试之前最传统的写法的,得到的结果肯定都是一样的。
这就是所谓的AOP了,不知道看了有没有比之前对AOP有感觉了没有哈哈。
另外说一句,AOP和IOC都是学Spring的时候接触也是才开始学的,毕竟Spring是基于这两个玩意的,好消息是Sping的AOP实现比较简单,不需要像我一样去写一个代理类。封装就是好呀,哈哈