上一篇我们完成搭建了一个简单的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框架