从零开始写javaweb框架笔记20-使框架具备AOP特性-开发AOP框架

来源:互联网 发布:剑网三御姐捏脸数据 编辑:程序博客网 时间:2024/06/16 14:43

       借鉴Spring AOP的风格,写一个基于切面注解的AOP框架,需了解前面的动态代理技术

      一:定义切面注解

     在框架中添加一个名为Aspect的注解,代码如下:

package org.smart4j.framework.annotation;import java.lang.annotation.*;/** * Created by jack on 2017/6/27. * 切面注解 */@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface Aspect {    /**     * 注解     * @return     */    Class<? extends Annotation> value();}

     通过

@Target(ElementType.TYPE)
来设置该注解只能应用在类上。该注解中包含了一个名为value的属性,它是一个注解类,用了定义Controller这类注解。在使用切面之前,我们需要先搭建一个代理框架。


     二:搭建代理框架

   在框架中添加一个名为Proxy的接口,代码如下:

package org.smart4j.framework.proxy;/** * Created by jack on 2017/6/28. * 代理接口 */public interface Proxy {    Object doProxy(ProxyChain proxyChain) throws Throwable;}

   这个Proxy接口中包括了一个doRroxy方法,传入一个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;/** * Created by jack on 2017/6/28. * 代理链 */public class ProxyChain {    private final Class<?> targetClass;    private final Object targetObject;    private final Method targetMethod;    private final MethodProxy methodProxy;    private final Object[] methodParams;    private List<Proxy> proxyList = new ArrayList<Proxy>();    private int proxyIndex = 0;    /**     * 构造函数     * @param targetClass     * @param targetObject     * @param targetMethod     * @param methodProxy     * @param methodParams     * @param proxyList     */    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 Object[] getMethodParams() {        return methodParams;    }    public Class<?> getTargetClass() {        return targetClass;    }    public Method getTargetMethod() {        return targetMethod;    }    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;    }}

        在ProxyChain类中,我们定义了一系列的成员变量,包括targetClass(目标类),targetObject(目标对象),targetMethod(目标方法),methodProxy(方法代理),methodParams(方法参数),此外还包括了proxyList(代理列表),proxyIndex(代理索引),这些成员变量在构造中进行初始化,并提供了几个重要的获值方法。

        需要注意的是MethodProxy这个类,它是CGLib开源项目为我们提供的一个方法代理对象在doProxyChain方法中被使用。

        需要解释的是doProxyChain方法,在该方法中,我们通过proxyIndex来充当代理对象的计数器,若未达到proxyList的上限,则从proxyList中取出相应的Proxy对象,并调用其doProxy方法。在Proxy接口的实现中会提供相应的横切逻辑,并调用doProxyChain方法,随后将再次调用当前ProxyChain对象的doProxyChain方法,直到proxyIndex达到proxyList的上限为止,最后调用methodProxy的invokeSuper方法,执行目标对象的业务逻辑。

        我们必须在pom.xml文件中添加CGLib的Maven依赖:

 

<!--添加cglib类库,实现动态代理-->        <dependency>            <groupId>cglib</groupId>            <artifactId>cglib</artifactId>            <version>2.2.2</version>        </dependency>

       现在我们需要些一个类,让它提供一个创建代理对象的方法,输入一个目标类和一组Proxy接口实现,输出一个代理对象,将该类命名为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;/** * Created by jack on 2017/6/28. * 代理管理器 */public class ProxyManager {    public static <T> T createProxy(final Class<T> 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();            }        });    }}

        使用CGLib提供的Enhance的create方法来创建代理对象,将intercept的参数传入ProxyChain的构造器中即可。

        谁来调用ProxyManager呢,当然是切面类了,因为在切面类中,需要一个目标方法被调用前后增加相应的逻辑。我们有必要写一个抽象类,让它提供一个模板方法,并在该抽象类的具体实现中扩展相应的抽象方法。我们不妨将该抽象类命名为AspectProxy,代码如下:

package org.smart4j.framework.proxy;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.lang.reflect.Method;/** * Created by jack on 2017/6/28. * 切面代理 */public 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 (intercept(cls,method,params)){                before(cls,method,params);                result = proxyChain.doProxyChain();                after(cls,method,params);            }else {                result = proxyChain.doProxyChain();            }        }catch (Exception e){            LOGGER.error("proxy failure ",e);            error(cls,method,params);            throw e;        }finally {            end();        }        return result;    }    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) throws Throwable{    }    public void error(Class<?> cls,Method method,Object [] params) throws Throwable{    }    public void begin(){}    public void end(){}}

      需要注意的是AspectProxy类中的doProxy方法,我们从proxyChain参数中获取了目标类,目标方法与目标参数,随后通过一个try。。。catch。。。finally代码块来实现调用框架,从框架中抽象出一系列的“钩子方法”,这些抽象方法可在AspectProxy的子类中选择性地进行实现,就像下面这样:

      

package org.smart4j.framework.aspect;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.smart4j.framework.annotation.Aspect;import org.smart4j.framework.annotation.Controller;import org.smart4j.framework.proxy.AspectProxy;import java.lang.reflect.Method;/** * Created by jack on 2017/7/17. */@Aspect(Controller.class)public class ControllerAspect extends AspectProxy{    private static final Logger LOG = LoggerFactory.getLogger(ControllerAspect.class);    private long begin;    @Override    public void before(Class<?> cls, Method method, Object[] params) throws Throwable {        LOG.debug("------------begin--------------");        LOG.debug(String.format("class: %s",cls.getName()));        LOG.debug(String.format("method: %s",method.getName()));        begin = System.currentTimeMillis();    }    @Override    public void after(Class<?> cls, Method method, Object[] params) throws Throwable {        LOG.debug(String.format("time: %s",System.currentTimeMillis()-begin));        LOG.debug("------------end--------------");    }}

          我们只需实现before与after方法,就可以在目标方法执行前后添加其他需要执行的代码了。

      那么这样就结束了吗?no,我们还需要在整个框架里使用ProxyManager来创建代理对象,并将该代理对象放入框架底层的Bean Map中,随后才能通过IOC将代理的对象注入到其他对象中。

  

三:加载AOP框架

         按照之前的方式,为了加载AOP框架,我们需要编写一个名为AopHelper的类,然后将其添加到HelperLoader类中。

        在AopHelper中我们需要获取所有的目标类及其被拦截的切面类实例,并通过ProxyManager的createProxy方法来创建代理对象,最后将其放入Bean Map中。

        首先需要为BeanHelper类添加一个setBean方法,用于将Bean实例放入Bean Map中,代码如下:

package org.smart4j.framework.helper;import org.smart4j.framework.util.ReflectionUtil;import java.util.HashMap;import java.util.Map;import java.util.Set;/** * Created by jack on 2017/5/23. * bean 助手类 */public class BeanHelper {    /**     * 定义bean映射,(用于存放bean类与bean实例的映射关系)     */    private static final Map<Class<?>,Object> BEAN_MAP = new HashMap<Class<?>, Object>();    /**     * 静态代码块     */    static {        Set<Class<?>> beanClassSet = ClassHelper.getBeanClassSet();        for (Class<?> beanClass : beanClassSet) {            Object object = ReflectionUtil.newInstance(beanClass);            BEAN_MAP.put(beanClass,object);        }    }    /**     * 获取Bean映射     */    public static Map<Class<?>,Object> getBeanMap(){        return BEAN_MAP;    }    /**     * 获取Bean实例     */    public static <T> T getBean(Class<?> cls){        if (!BEAN_MAP.containsKey(cls)){            throw new RuntimeException("can not get bean by class:"+cls);        }        return (T) BEAN_MAP.get(cls);    }    /**     * 设置Bean实例     * @param cls     * @param obj     */    public static void setBean(Class<?> cls,Object obj){        BEAN_MAP.put(cls,obj);    }}

       然后,由于我们需要扩展AspectProxy抽象类的所有具体类,此外,还需要获取带有Aspect注解的所有类,因此需要在ClassHelper中添加以下两个方法:

     

/**     * 获取应用包名下某父类(或接口)的所有子类(或实现类)     * @param superClass     * @return     */    public static Set<Class<?>> getClassSetBySupper(Class<?> superClass){        Set<Class<?>> classSet = new HashSet<Class<?>>();        for (Class<?> cls : CLASS_SET){            if (superClass.isAssignableFrom(cls) && !superClass.equals(cls)){                classSet.add(cls);            }        }        return classSet;    }    /**     * 获取应用包名带有某注解的所有类     * @param annotationClass     * @return     */    public static Set<Class<?>> getClassSetByAnnotation(Class<? extends Annotation> annotationClass){        Set<Class<?>> classSet = new HashSet<Class<?>>();        for (Class<?> cls : CLASS_SET){            if (cls.isAnnotationPresent(annotationClass)){                classSet.add(cls);            }        }        return classSet;    }

   

   完整代码如下:

package org.smart4j.framework.helper;import org.smart4j.framework.annotation.Controller;import org.smart4j.framework.annotation.Service;import org.smart4j.framework.util.ClassUtil;import java.lang.annotation.Annotation;import java.util.HashSet;import java.util.Set;/** * Created by jack on 2017/5/22. * 类操作助手类 */public class ClassHelper {    /**     * 定义类集合,用于存放所加载的类     */    private static final Set<Class<?>> CLASS_SET;    static {        String basePackage = ConfigHelper.getAppBasePackage();        CLASS_SET = ClassUtil.getClassSet(basePackage);    }    /**     * 获取应用包下的所有类     */    public static Set<Class<?>> getClassSet() {        return CLASS_SET;    }    /**     * 获取应用包名下所有Service类     */    public static Set<Class<?>> getServiceClassSet() {        Set<Class<?>> classSet = new HashSet<Class<?>>();        for (Class<?> cls : CLASS_SET) {            if (cls.isAnnotationPresent(Service.class)) {                classSet.add(cls);            }        }        return classSet;    }    /**     * 获取应用包名下所有Controller类     */    public static Set<Class<?>> getControllerClassSet() {        Set<Class<?>> classSet = new HashSet<Class<?>>();        for (Class<?> cls : CLASS_SET) {            if (cls.isAnnotationPresent(Controller.class)) {                classSet.add(cls);            }        }        return classSet;    }    /**     * 获取应用包名下所有Bean类(包括Service,Controller)     */    public static Set<Class<?>> getBeanClassSet() {       Set<Class<?>> beanClassSet = new HashSet<Class<?>>();       beanClassSet.addAll(getServiceClassSet());       beanClassSet.addAll(getControllerClassSet());        return beanClassSet;    }    /**     * 获取应用包名下某父类(或接口)的所有子类(或实现类)     * @param superClass     * @return     */    public static Set<Class<?>> getClassSetBySupper(Class<?> superClass){        Set<Class<?>> classSet = new HashSet<Class<?>>();        for (Class<?> cls : CLASS_SET){            if (superClass.isAssignableFrom(cls) && !superClass.equals(cls)){                classSet.add(cls);            }        }        return classSet;    }    /**     * 获取应用包名带有某注解的所有类     * @param annotationClass     * @return     */    public static Set<Class<?>> getClassSetByAnnotation(Class<? extends Annotation> annotationClass){        Set<Class<?>> classSet = new HashSet<Class<?>>();        for (Class<?> cls : CLASS_SET){            if (cls.isAnnotationPresent(annotationClass)){                classSet.add(cls);            }        }        return classSet;    }}

     有了以上两个工具方法以后,我们就可以在AopHelper类中编写一个带有Aspect注解的所有类,把封装成一个方法:

private static Set<Class<?>> createTargetClassSet(Aspect aspect)throws Exception{        Set<Class<?>> targetClassSet = new HashSet<Class<?>>();        Class<? extends Annotation> annotation = aspect.value();        if (annotation != null && annotation.equals(Aspect.class)){            targetClassSet.addAll(ClassHelper.getClassSetByAnnotation(annotation));        }        return targetClassSet;    }

      获取Aspect注解中设置的注解类,若该注解类不是Aspect类,则可调用ClassHelper的getClassSetByAnnotation方法获取相关的类,并把这些类放入目标类集合中,最终返回这个集合。

     紧接着我们需要获取代理类及其目标类集合之间的映射关系,一个代理类可对应一个或多个目标类。需要强调的是,这里所说的代理类指的是切面类。通过以下代码获取这个映射关系:

 private static Map<Class<?>,Set<Class<?>>> createProxyMap()throws  Exception{        Map<Class<?>,Set<Class<?>>> proxyMap = new HashMap<Class<?>, Set<Class<?>>>();        Set<Class<?>> proxyClassSet = ClassHelper.getClassSetBySupper(AspectProxy.class);        for (Class<?> proxyClass: proxyClassSet) {            if (proxyClass.isAssignableFrom(Aspect.class)){                Aspect aspect = proxyClass.getAnnotation(Aspect.class);                Set<Class<?>> targetClassSet = createTargetClassSet(aspect);                proxyMap.put(proxyClass,targetClassSet);            }        }        return proxyMap;    }

       代理类需要扩展AspectProxy抽象类,还需要带有Aspect注解,只有满足这两个条件,才能根据Aspect注解中所定义的注解属性去获取该注解所对应的目标类集合,然后才能建立代理类与目标类集合之间的映射关系,最终返回这个映射关系

      一旦获取了代理类与目标类集合之间的映射关系,就能根据这个关系分析出目标类与代理对象列表之间的映射关系,就像下面这样:

      

private static Map<Class<?>,List<Proxy>> createTargetMap(Map<Class<?>,Set<Class<?>>> proxyMap) throws Exception{        Map<Class<?>,List<Proxy>> targetMap = new HashMap<Class<?>, List<Proxy>>();        for (Map.Entry<Class<?>,Set<Class<?>>> proxyEntry: proxyMap.entrySet()) {            Class<?> proxyClass = proxyEntry.getKey();            Set<Class<?>> targetClassSet = proxyEntry.getValue();            for (Class<?> targetClass: targetClassSet) {                Proxy proxy = (Proxy) targetClass.newInstance();                if (targetMap.containsKey(targetClass)){                    targetMap.get(targetClass).add(proxy);                }else {                    List<Proxy> proxyList = new ArrayList<Proxy>();                    proxyList.add(proxy);                    targetMap.put(targetClass,proxyList);                }            }        }        return targetMap;    }

     最后,在AopHelper中通过一个静态块来初始化整个AOP框架,代码如下:

static {        try {            Map<Class<?>,Set<Class<?>>> proxyMap = createProxyMap();            Map<Class<?>,List<Proxy>> targetMap = createTargetMap(proxyMap);            for (Map.Entry<Class<?>,List<Proxy>> targetEntry:targetMap.entrySet()) {                Class<?> targetClass = targetEntry.getKey();                List<Proxy> proxyList = targetEntry.getValue();                Object proxy = ProxyManager.createProxy(targetClass,proxyList);                BeanHelper.setBean(targetClass,proxy);            }        }catch (Exception e){        }    }

      获取代理类及其目标类集合的映射关系,进一步获取目标类与代理对象列表的映射关系,进而遍历这个映射关系,从中获取目标类与代理对象列表,调用ProxyManager.createProxy方法获取代理对象,调用BeanHelper.setBean方法,将该代理对象重新放入Bean Map中。

       下面是AopHelper类的所有代码:

package org.smart4j.framework.helper;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.smart4j.framework.annotation.Aspect;import org.smart4j.framework.proxy.AspectProxy;import org.smart4j.framework.proxy.Proxy;import org.smart4j.framework.proxy.ProxyManager;import java.lang.annotation.Annotation;import java.util.*;/** * Created by jack on 2017/7/17. */public final class AopHelper {    private static final Logger LOGGER = LoggerFactory.getLogger(AopHelper.class);    static {        try {            Map<Class<?>,Set<Class<?>>> proxyMap = createProxyMap();            Map<Class<?>,List<Proxy>> targetMap = createTargetMap(proxyMap);            for (Map.Entry<Class<?>,List<Proxy>> targetEntry:targetMap.entrySet()) {                Class<?> targetClass = targetEntry.getKey();                List<Proxy> proxyList = targetEntry.getValue();                Object proxy = ProxyManager.createProxy(targetClass,proxyList);                BeanHelper.setBean(targetClass,proxy);            }        }catch (Exception e){            LOGGER.error("load aop failure",e);        }    }    private static Set<Class<?>> createTargetClassSet(Aspect aspect)throws Exception{        Set<Class<?>> targetClassSet = new HashSet<Class<?>>();        Class<? extends Annotation> annotation = aspect.value();        if (annotation != null && annotation.equals(Aspect.class)){            targetClassSet.addAll(ClassHelper.getClassSetByAnnotation(annotation));        }        return targetClassSet;    }    private static Map<Class<?>,Set<Class<?>>> createProxyMap()throws  Exception{        Map<Class<?>,Set<Class<?>>> proxyMap = new HashMap<Class<?>, Set<Class<?>>>();        Set<Class<?>> proxyClassSet = ClassHelper.getClassSetBySupper(AspectProxy.class);        for (Class<?> proxyClass: proxyClassSet) {            if (proxyClass.isAssignableFrom(Aspect.class)){                Aspect aspect = proxyClass.getAnnotation(Aspect.class);                Set<Class<?>> targetClassSet = createTargetClassSet(aspect);                proxyMap.put(proxyClass,targetClassSet);            }        }        return proxyMap;    }    private static Map<Class<?>,List<Proxy>> createTargetMap(Map<Class<?>,Set<Class<?>>> proxyMap) throws Exception{        Map<Class<?>,List<Proxy>> targetMap = new HashMap<Class<?>, List<Proxy>>();        for (Map.Entry<Class<?>,Set<Class<?>>> proxyEntry: proxyMap.entrySet()) {            Class<?> proxyClass = proxyEntry.getKey();            Set<Class<?>> targetClassSet = proxyEntry.getValue();            for (Class<?> targetClass: targetClassSet) {                Proxy proxy = (Proxy) targetClass.newInstance();                if (targetMap.containsKey(targetClass)){                    targetMap.get(targetClass).add(proxy);                }else {                    List<Proxy> proxyList = new ArrayList<Proxy>();                    proxyList.add(proxy);                    targetMap.put(targetClass,proxyList);                }            }        }        return targetMap;    }}

    别忘了将AopHelper添加到HelperLoader中进行初始化,代码如下:

package org.smart4j.framework;import org.smart4j.framework.helper.*;import org.smart4j.framework.util.ClassUtil;/** * Created by jack on 2017/5/24. * 加载相应类的,帮助类 */public final class HelperLoader {    //初始化,加载类    public static void init(){        Class<?> [] classList = {ClassHelper.class, BeanHelper.class, AopHelper.class,IocHelper.class, ControllerHelper.class,};        for (Class<?> cls: classList) {            ClassUtil.loadClass(cls.getName(),true);        }    }}


   需要注意的是,AopHelper要在IocHelper之前加载,因为首先需要通过AopHelper获取代理对象,然后才能通过IocHelper进行依赖注入,到这里一个简单的AOP就开发完毕了。


github代码中的地址:https://github.com/wj903829182/smartframework




阅读全文
1 0
原创粉丝点击