AOP的基础为代理机制。核心代码完成后,我们想给代码增加一些附加功能,比如日志,拦截等,这时不应该去修改核心代码,而是通过获取代理,在核心代码以外增加代码,实现相关功能。同样在我们没有源代码或无法触碰源代码时,也是如此,即面向切面编程AOP。核心功能与附加功能分开,互不干扰,称之为解耦,使开发过程更加方便。
首先要有一个类,能同时生成cglib代理和jdk代理,用户可以给出Class>类或Object对象,直接得到代理,并使用,和得到本身的对象没有区别。
然后我们可以增加附加功能(此处定为拦截)。用户可以提前添加拦截器,在运行时拦截,可以在各个时候(运行前,运行后,出现异常时)拦截,可以多重拦截,用户也可能不想写拦截,那我们就应该写一个简单的默认拦截器,让其使用。
关系图:
拦截可以分为前置拦截,后置拦截,以及异常时拦截。需要确定的信息有:要拦截哪个类中的哪个方法(Class>和method),这是无论哪种拦截都需要的。
前置拦截:在方法运行前就进行拦截,如验证身份信息等,用于判断该方法到底应不应该执行。我们需要一些参数(args,不一定是几个),当身份验证时,要根据传来的身份信息参数来确定。然后我们需要返回是否可以运行方法的信息。
后置拦截:在方法运行完后进行拦截,可以修改运行结果。所以需要一个参数,即运行的结果,而返回的是修改后的结果。
异常拦截:出现异常时,对异常进行处理,无需返回值。
/**
* 此为intercepter 拦截器
* 其中包括拦截的klass类 method方法两个成员
* 还有三个抽象方法:
* 前置拦截器 后置拦截器 处理异常拦截器,负责各个时候要做的拦截工作
* */
public abstract class Intercepter {
private Class> klass;
private Method method;
... 此处省略若干行不重要的代码
/**前置拦截器 运行之前就进行拦截
* @param 参数
* @return 拦截后方法是否运行
* */
public abstract boolean beforeIntercept(Object[] args);
/**后置拦截器 运行后对结果进行拦截
* @param 运行好的结果 将结果给它,进行对结果的拦截
* @return 将拦截处理后的结果返回
* */
public abstract Object afterIntercept(Object result);
/**处理异常拦截器 对出现的异常进行处理
* @param 异常 将异常给它 进行对异常的处理 拦截
* @return 无返回值
* */
public abstract void exceptionIntercept(Throwable e);
这是一个抽象方法,我们需要再做一个Adapter适配器,以方便以后使用,此时要注意:这部分拦截器作为默认拦截器(这个拦截应当简单,适于各种情况,且不影响程序运行),用户以后若自己写拦截器则可以选择性覆盖默认拦截器,也可以直接使用默认拦截器。
public class InterceptAdapter extends Intercepter {
... 此处省略若干行不重要的代码
@Override
public boolean beforeIntercept(Object[] args) { return true; }
@Override
public Object afterIntercept(Object result) { return result; }
@Override
public void exceptionIntercept(Throwable e) {}
}
每一个代理都有自己的拦截器,拦截器不止一个,所以可以做一个拦截器链,如此可形成一个类,该类只有get,set方法即可。
/**此类中 只有Object代理 以及 对此代理的拦截器列表
* 以后处理的proxy均为T类型 这样最后不用强转 (object也可以 最后需要强转)
* */
public class ProxyClass {
//代理和拦截链
private Object proxy;
private List interceptList;
...
}
我们最重要的步骤就是做一个类,产生代理。生成时,在方法运行前后都应使用拦截器列表,进行拦截。
/**
* 这是一个工厂,得到proxy,并将其存入ProxyClass类待用
* 只有一个成员 ProxyClass类
* @see ProxyClass
* */
public class ProxyFactory {
private ProxyClass proxyClass;
public ProxyFactory() {
}
public ProxyClass getProxy() {
return proxyClass;
}
/**通过调用内部方法 得到cglib代理 并将代理存入ProxyClass的proxy中
* @param 需要的类
* @param
将处理好的代理按存在map中key = className,value = ProxyClass,这样用户使用时,直接可以根据类来得到相应的代理以及拦截器,比较方便。这一个类是面向用户的,之前的都是底层的,为这个类做铺垫。因为面向用户,所以不确定用户给的是什么,那就要全面考虑。
以cglib代理为例,用户无论给class还是object,都要能得到代理,用户还需要添加移除拦截器。
/**里面有代理的map<类名称 string, 代理类 proxyClass>
* */
public class BeanFactory {
private final static Map proxyMap;//这个map只能有一套
static {
proxyMap = new HashMap<>();
}
public BeanFactory() {
}
/**得到cglib代理
* */
public T getCglibProxy(Class> klass) throws Exception {
return getCglibProxy(klass, klass.newInstance());
}
/**得到cglib代理
* */
public T getCglibProxy(Object object) throws Exception {
return getCglibProxy(object.getClass(), object);
}
/**private 得到cglib代理
* 判断 map中是否有该类的proxy 若有 则取出 若没有 则使用proxyFactory的getCglibProxy方法
* 取得代理 存入map 并返回
* */
@SuppressWarnings("unchecked")
private T getCglibProxy(Class> klass, Object object) throws Exception {
ProxyClass proxyClass = proxyMap.get(klass.getName());
if(proxyClass != null) {
return proxyClass.getProxy();
}
ProxyFactory factory= new ProxyFactory();
Object proxy = factory.getCglibProxy(klass, object);
proxyMap.put(klass.getName(), factory.getProxy());
return (T) proxy;
}
/**向某个类中添加某一拦截器
* 如果拦截器作用的klass与提供的klass不符 则直接返回
* 通过klass得到map中的proxyClass 并使用其addIntercepter添加拦截器
* @param 类
* @param 需要添加的拦截器
* */
public void addIntercepter(Class> klass, Intercepter intercepter) throws IntercepterException {
if(!intercepter.getKlass().equals(klass)) {
return;
}
proxyMap.get(klass.getName()).addIntercepter(intercepter);
}
/**移除某个类的某一拦截器
* 通过klass得到map中的proxyClass 并使用其中的removeIntercepter方法移除某个拦截器
* @param 类
* @param 需要移除的拦截器
* */
public void removeIntercepter(Class> klass, Intercepter intercepter) throws IntercepterException {
proxyMap.get(klass.getName()).removeIntercepter(intercepter);
}
/**得到jdk代理
* */
@SuppressWarnings("unchecked")
public T getJdkProxy(Class> klass) throws Exception {
return (T) getJdkProxy(klass, klass.newInstance());
}
/**得到jdk代理
* */
@SuppressWarnings("unchecked")
public T getJdkProxy(Object object) throws Exception {
return (T) getJdkProxy(object.getClass(), object);
}
/**private 得到jdk代理
* 判断 map中是否有该类的proxy 若有 则取出 若没有 则使用proxyFactory的getJdkProxy方法
* 取得代理 存入map 并返回
* */
@SuppressWarnings("unchecked")
private T getJdkProxy(Class> klass, Object object) throws Exception {
ProxyClass proxyClass = proxyMap.get(klass.getName());
if(proxyClass != null) {
return proxyClass.getProxy();
}
ProxyFactory factory= new ProxyFactory();
Object proxy = factory.getJdkProxy(klass, object);
proxyMap.put(klass.getName(), factory.getProxy());
return (T) proxy;
}
到此,整个过程以及全部处理结束,下面来做尝试。
实验的类是:
public class NormalClass {
private int num;
public NormalClass() {
}
public void setNum(int num) {
this.num = num;
}
public int getNum() {
return num;
}
public final void fun() {
System.out.println("这是一个final方法!");
}
public String normalAction(String str) {
System.out.println(str);
return str;
}
拦截器为:
public class NormalClassIntercepter extends InterceptAdapter {
public NormalClassIntercepter(Class> klass, Method method) {
super(klass, method);
}
public NormalClassIntercepter() {
}
@Override
public boolean beforeIntercept(Object[] args) {
for (Object arg : args) {
if(arg.equals("123")) {
System.out.println("置前拦截:拒绝运行");
return false;
}
System.out.println("置前拦截:" + args);
}
return true;
}
@Override
public Object afterIntercept(Object result) {
System.out.println("处理后的结果:" + result + "*****");
return result;
}
}
主程序:
public static void main(String[] args) throws Exception {
Class> klass = NormalClass.class;
Method method = klass.getDeclaredMethod("normalAction",
new Class>[] {
String.class} );
NormalClassIntercepter intercepter1
= new NormalClassIntercepter(NormalClass.class, method);
NormalClassIntercepter intercepter2
= new NormalClassIntercepter(NormalClass.class, method);
BeanFactory proxyMap = new BeanFactory();
try {
NormalClass normal = proxyMap.getCglibProxy(
NormalClass.class);
proxyMap.addIntercepter(klass, intercepter1);
proxyMap.addIntercepter(klass, intercepter2);
normal.normalAction("123");
} catch (Exception e) {
e.printStackTrace();
}
}
注:此时拦截器的添加十分不便,以后再改进。
实验时有一个异常拦截没有给出,大家自行添加即可。