前言
最开始在代理机制的学习中看到了spring的相关功能(AOP),就尝试着自己模拟完成。这样为后面spring的相关源代码的学习能够打下一个很好的基础。由于对spring了解的很少,因此很感谢带领我实现该内容的先驱——教主。
由于本人才疏学浅,如有疏漏可以留言告知,不胜感激。
1 .AOP简介
1.1什么是aop
AOP就是面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。——以上说法来自度娘
就我个人的理解,AOP就是通过代理机制,在不改变原来类的源代码的同时,为其增加一定的功能。
1.2代理机制
代理机制可以说是AOP的核心,目前来说只有通过代理机制我们才能完成aop的相关操作。代理机制代码如下
public <T> T getProxy(Object object){
// 该Object必须是某些接口的实现类的对象
//该方法的返回值是一个代理,它必须被转换成某些接口类
return (T) Proxy.newProxyInstance(
object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 在此处我们通过反射机制来执行方法
method.invoke(object, args);
return null;
}
});
};
这是Java内部的代理机制的JDK代理的相关代码,它要求方法中所传递的对象必须是某个接口的实现类,在我们得到这个代理对象的时候,我们必须要把它变成该对象的所实现的接口类(如果有多个接口,那么代理类可以执行所有接口内部的方法)。
同样还有一个比较常用的是cglib代理形式(注意,这个代理是java不提供的,如有需要请下载相关jar包)。代码如下
@SuppressWarnings("unchecked")
public <T> T getProxy(Object object){
Class<?> klass = object.getClass();
Enhancer enhancer = new Enhancer();
//设置代理对象的父类,在这里我们直接将作为参数传递过来的对象对应的类作为代理对象的父类
enhancer.setSuperclass(klass);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object arg0, Method method, Object[] args,
MethodProxy arg3) throws Throwable {
Object result = method.invoke(object, args);
return result;
}
});
return (T) enhancer.create();
}
上述两种方式都可以完成对象的代理。
其中,JDKlib的代理方式要求的是接口的实现类的对象,而CGLLIB的代理方式是通过继承的方式,即代理对象继承了其需要代理的对象所对应的类。
注意到,无论是JDK方式还GGL方式,都有一个非常相同的部分:
在JDK中
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 在此处我们通过反射机制来执行方法
Object result = method.invoke(object, args);
return result;
}
以及CGL这里:
public Object intercept(Object arg0, Method method, Object[] args,
MethodProxy arg3) throws Throwable {
Object result = method.invoke(object, args);
return result;
}
这里是代理机制的绝对核心,当我们得到一个对象的代理之后,无论是CGL还是JDK方式,当我们调用相关方法(能够被继承的方法(CGL方式),或者接口中写好的方法(JDK方式))这里的代码必然会被执行。这就为我们的AOP打下了基础。
2 切点与切面
由于是通过代理的方式,我们看到在刚才的两个方法中method.invoke(object, args)这条语句,这是被代理的对象的相关方法真正执行的一条语句。因次我们针对这条语句,就可以对相关方法进行一些额外的操作。因为技术的原因,我们难以对被代理的对象的相关方法的内部进行额外操作,所以我们可以引出如下切点——当方法执行之前应该干什么(前置拦截)——当方法执行之后应该干什么(后置拦截)——以及方法出现异常应该干什么(异常拦截)。这三个位置就是三个切点,而在该切点上所需要做的事情就是切面。如下图
因此,我们可以对刚才的代码进行改造:如下
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
return doInvoke(object, method, args);
}
private Object doInvoke(Object obj, Method method, Object[] args)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException{
Object result = null;
//前置拦截
doBefore(method, args);
try {
result = method.invoke(obj, args);
// 后置拦截
doAfter(method, result);
} catch (Throwable e) {
// 异常拦截
doDealException(method, e);
throw e;
}
return result;
}
接下来我们再实现doBefore,doAfter,doDealException这三个方法,就可以完成三个切点拦截。
但是,这是很不合理的,首先我们应该明确的是,当三个方法完成之后,我们调用代理对象的所有的相关方法时都会执行这三个方法(doBefore,doAfter,doDealException)。而所有的方法都进行同一套拦截这显然是不现实的(毕竟doBefore,doAfter,doDealException方法不能重构,在代码跑起来之后已经没办法在修改方法了)。因此,我们需要动态实现这一点,这就是动态代理。
我们希望,每一个方法都有属于自己的doBefore,doAfter,doDealException等方法,而且在前置拦截,后置拦截,异常拦截时,我们能够做到的是执行多个方法。而每一个拦截,可以称他为拦截器链这个拦截器链就是切面。
3 动态代理
首先,我们的大体框架依旧不发生改变。
我们把代理对象和代理对象所对应的拦截器们放到一个类中,这个类为我们提供了相关的前置拦截,后置拦截,异常拦截的处理方法。
并且,该类对外的方法只允许拦截器的添加和删除和得到代理对象,其他一律不允许。
public class MyProxy {
private Object myProxy; //代理对象
/*这是拦截器链,Intercepter中存储了一个方法的三个拦截器,我们通过遍历的方式
来完成对应方法的三种拦截*/
private List<Intercepter> intercepterList;
MyProxy() {
intercepterList = new ArrayList<>();
}
public void addIntercepter(Intercepter intercepter){
if (intercepterList.contains(intercepter)) {
return;
}
intercepterList.add(intercepter);
}
public void removeIntercepter(Intercepter intercepter) {
intercepterList.remove(intercepter);
}
/**
* @param method
* @param args
* @tip 遍历该拦截器链,通过方法找到对应拦截器类,然后执行
*/
boolean doBefore(Method method, Object[] args){
//拦截方法之前
for(Intercepter intercepter : this.intercepterList){
if (!intercepter.getMethod().equals(method)) {
continue;
}
if (intercepter.before(args) == false) {
return false;
}
return false;
}
/**
* @param method
* @param args
* @tip 遍历该拦截器链,通过方法找到对应拦截器类,然后执行
*/
Object doAfter(Method method, Object result){
for(Intercepter intercepter : this.intercepterList){
if (!intercepter.getMethod().equals(method)) {
continue;
}
result = intercepter.after(result);
}
return result;
}
/**
* @param method
* @param args
* @tip 遍历该拦截器链,通过方法找到对应拦截器类,然后执行
*/
void doDealException(Method method, Throwable e){
for (Intercepter intercepter : intercepterList) {
if (!intercepter.getMethod().equals(method)) {
continue;
}
intercepter.dealException(e);
}
}
@SuppressWarnings("unchecked")
public <T> T getMyProxy() {
return (T) myProxy;
}
void setMyProxy(Object myProxy) {
this.myProxy = myProxy;
}
}
这是拦截器的相关代码
//这是一个抽象类,需要自己完成相关方法的内部过程
public abstract class Intercepter {
// 该拦截器拦截的是那个方法
private Method method;
public Intercepter() {
}
public Intercepter( Method method) {
this.method = method;
}
/**
*
* @param args
* @return 当该参数为false时,被拦截的方法不能执行
*/
public abstract boolean before(Object[] args);
/**
*
* @param reault
* @return 该方法必须有返回值,被拦截方法的最终结果是该法执行之后的返回值
*/
public abstract Object after(Object reault);
public abstract void dealException(Throwable e);
blic Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
}
我们最开始的intercept方法就可以变成这样,并且将其变成一个类
/*
由于篇幅问题,部分方法未给出。 由于我们的MyProxy类的并不希望由外部生成,因此给了一个工厂
我们希望,所有的代理都是通过该工厂来产生的。这样能够保证,相关代理的拦截器链内部方法被执行。
*/
public class ProxyFactory {
public ProxyFactory() {
};
public static <T> T getCGLProxy(Object object, Class<T> klass) throws Throwable{
MyProxy myProxy = new MyProxy();
T proxy = cglProxy(object, klass,myPr