上一篇我们完成搭建了一个简单的MVC框架,使其可以通过DispatchServlet来处理所有的请求【从零写javaweb框架】(八)请求转发器,这一篇会实现AOP功能。
一.什么是AOP
AOP(Aspect-Oriented Programming)是对面向对象编程(OOP)的一种补充,可以译为面向切面编程。其中切面是AOP的一个术语,表示从业务逻辑中分离出来的横切逻辑,比如性能监控,日志记录、权限控制等,这些逻辑都可从核心的业务逻辑代码中抽离出去。也就是说,通过AOP可以解决代码耦合问题,是职责更加单一。
我个人的理解:AOP的作用通俗一点说就是可以在方法的前后附加上其他代码,每次调用此方法之前或之后(又或者前后都有),都会先后执行附加的代码。
二.设计模式之代理模式
如果想继续深入了解AOP的实现,我们需要先了解静态代理模式和动态代理模式,这里附上我以前写的两篇博文(如果之前已经了解过代理模式了,那么可以直接跳过看下一步):
静态代理模式:设计模式_4:代理模式
动态代理模式:设计模式_25:动态代理模式
三.开发AOP框架
紧接着我之前写的框架,现在要开始实现AOP了,首先定义一个切面注解:
package org.smart4j.framework.annotation;
import java.lang.annotation.*;
/**
* desc : 切面注解
* Created by Lon on 2018/2/3.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {
/**
* 注解
*/
Class<? extends Annotation> value();
}
这个注解的value是一个class,表示我们要对哪个标有此注解类里的方法进行切入(其实就是要代理哪个类),我们可以注解在一个类上面,表示此类是一个切面类。用法:@Aspect(Controller.class),它表示我们要对标有Controller(这个注解是我们之前编写的,如果忘记了可以回头看一看)注解的类进行切入。
然后继续在框架中添加一个名为Proxy的接口:
package org.smart4j.framework.proxy;
/**
* desc : 代理接口
* Created by Lon on 2018/2/3.
*/
public interface Proxy {
/**
* 执行链式代理
*/
Object doProxy(ProxyChain proxyChain) throws Throwable;
}
这个Proxy接口中有一个doProxy方法,参数是一条代理链proxyChain,这条代理链的作用是把多个代理串起来,并一个个依次执行,执行顺序取决于添加到链上的先后顺序,ProxyChain代码:
package org.smart4j.framework.proxy;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* desc : 代理链
* Created by Lon on 2018/2/3.
*/
public class ProxyChain {
// 要代理的目标类
private final Class<?> targetClass;
// 要代理的目标对象
private final Object targetObject;
// 要代理的目标方法
private final Method targetMethod;
// 目标方法的代理(这个是CGLib为我们提供的)
private final MethodProxy methodProxy;
// 要代理的目标方法的方法参数
private final Object[] methodParams;
// 代理列表
private List<Proxy> proxyList = new ArrayList<Proxy>();
// 代理索引
private int proxyIndex = 0;
// ProxyChain构造函数
public ProxyChain(Class<?> targetClass, Object targetObject, Method targetMethod,
MethodProxy methodProxy, Object[] methodParams, List<Proxy> proxyList) {
this.targetClass = targetClass;
this.targetObject = targetObject;
this.targetMethod = targetMethod;
this.methodProxy = methodProxy;
this.methodParams = methodParams;
this.proxyList = proxyList;
}
public Class<?> getTargetClass() {
return targetClass;
}
public Method getTargetMethod() {
return targetMethod;
}
public Object[] getMethodParams() {
return methodParams;
}
/**
* 如果尚未达到proxyList上限,则从proxyList上拿取相应的对象,执行它的doProxy方法,
* 在Proxy接口的实现中会提供相应的横切逻辑,并调用doProxyChain方法,随后将再次调用
* 当前ProxyChain对象的doProxyChain方法,知道proxyIndex达到proxyList上限为止,
* 到最后再调用methodProxy的invokeSuper方法,执行目标对象的业务逻辑。
*/
public Object doProxyChain() throws Throwable{
Object methodResult;
if (proxyIndex < proxyList.size()) {
methodResult = proxyList.get(proxyIndex++).doProxy(this);
} else {
methodResult = methodProxy.invokeSuper(targetObject, methodParams);
}
return methodResult;
}
}
我们下面一步还用到了动态代理,所以要往maven加入CGLib:
<!-- CGLib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
现在写一个类,用它来动态创建代理对象,命名为ProxyManager:
package org.smart4j.framework.proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.util.List;
/**
* desc : 代理管理器
* Created by Lon on 2018/2/5.
*/
public class ProxyManager {
@SuppressWarnings("unchecked")
public static <T> T createProxy(final Class<?> targetClass, final List<Proxy> proxyList){
return (T) Enhancer.create(targetClass, new MethodInterceptor() {
public Object intercept(Object targetObject, Method targetMethod,
Object[] methodParams, MethodProxy methodProxy) throws Throwable {
return new ProxyChain(targetClass,targetObject, targetMethod,methodProxy,methodParams,proxyList).doProxyChain();
}
});
}
}
接下来需要通过切面类来调用ProxyManager,写一个抽象类,让它提供一个模板方法,并在该抽象类的具体实现中扩展相应的抽象方法:
package org.smart4j.framework.proxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
/**
* desc : 切面代理类
* Created by Lon on 2018/2/6.
*/
public abstract class AspectProxy implements Proxy{
private static final Logger LOGGER = LoggerFactory.getLogger(AspectProxy.class);
public Object doProxy(ProxyChain proxyChain) throws Throwable{
Object result = null;
Class<?> cls = proxyChain.getTargetClass();
Method method = proxyChain.getTargetMethod();
Object[] params = proxyChain.getMethodParams();
begin();
try {
if (this.intercept(cls, method, params)){
this.before(cls, method, params);
result = proxyChain.doProxyChain();
this.after(cls, method, params, result);
} else {
result = proxyChain.doProxyChain();
}
} catch (Throwable e){
LOGGER.error("proxy failure", e);
error(cls, method, params, e);
throw e;
} finally {
end();
}
return result;
}
public void begin(){
}
public boolean intercept(Class<?> cls, Method method, Object[] params) throws Throwable{
return true;
}
public void before(Class<?> cls, Method method, Object[] params) throws Throwable{
}
public void after(Class<?> cls, Method method, Object[] params, Object result) throws Throwable{
}
public void error(Class<?> cls, Method method, Object[] params, Throwable e) {
}
public void end(){
}
}
在doProxy方法中,我们从proxyChain参数中获取了目标类、目标方法与方法参数、之后用try/catch/finally代码块来实现调用框架,从框架中抽象出一系列的钩子方法,这些抽象方法可在AspectProxy的子类中有选择性地进行实现。
下一篇会写加载AOP框架