万字长文!自己手写一个AOP动态代理框架(1)

前面提到过动态代理的有两种简单的实现方式,就是JDK动态代理和CGLIB。
不懂先看前文:SpringAOP的实现之代理模式
自己写嘛当然是乞丐版了,为了实现简单 但是又具有一定的通用性(拒绝对被代理类有实现接口的强硬要求)。所以采用CGLIB的方式实现AOP。
工程目录结构。
在这里插入图片描述

首先要实现注解一样的 AOP,要定义与AOP相关的注解。

定义2个AOP相关的注解标签。

//作用在类上
@Target(ElementType.TYPE)
//运行时有效
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {
    //初期版本仅仅支持按照注解分类AOP
    //比如只切Controller Controller.class
    Class<? extends Annotation> value();
}

如果对一个代理类有多个AOP,那么需要用户规定代理执行的顺序,那么就需要Order注解。

/**
 * 定义切面顺序
 */
//作用在类上
@Target(ElementType.TYPE)
//运行时有效
@Retention(RetentionPolicy.RUNTIME)
public @interface Order {
    //越小优先级别越高
    int value();
}

定义好相关注解,需要定义AOP的基本骨架。这里用到了类似于模板模式。 这个骨架定义了究竟支持哪几种通知方式(前置,后置等等)。

模板模式的妙用:Spring源码(3)-Spring中的ApplicationContext
通过此骨架的定义,可以让子类按需实现自己想要实现的通知。

public abstract class DefaultAspect {
    /**
     * @param targetClass 被代理的目标类型
     * @param method 被代理的方法
     * @param args 方法的参数
     * @throws Throwable
     */
    public void before(Class<?> targetClass, Method method,Object[] args) throws Throwable {}

    /**
     *
     * @param targetClass 被代理的目标类型
     * @param method 被代理的方法
     * @param args 方法的参数
     * @param returnValue 返回值
     * @return 修改后返回值
     * @throws Throwable
     */
    public Object afterReturning(Class<?> targetClass,Method method
            ,Object[] args,Object returnValue) throws Throwable{
        return returnValue;
    }

    /**
     *
     * @param targetClass 被代理的目标类型
     * @param method 被代理的方法
     * @param args 方法的参数
     * @param e 异常
     * @throws Throwable 抛出被代理方法的 异常
     */
    public void afterThrowing(Class<?> targetClass,
                              Method method,Object[] args,Throwable e) throws Throwable{}
}

只支持了前置通知,方法返回后通知,异常时通知。Aroud可以将这三个联合起来就行。
因为三个方法在抽象类里面都是默认的空实现,不会让用户强制实现,可以按需实现。

SpringAOP的实现之代理模式提到,使用CGLIB就需要实现MethodInterceptor去复用自己的增强逻辑,而在这里也是一样的。但是又有点不一样,因为我们可能(一定)会遇到一个被代理类有多个代理,所以这里是一对多的关系。因此不能照搬原来的代码。需要做出额外的操作来辅助完成多个AOP增强一个被代理类。

所以这里需要声明两个属性。

    //被代理的class
    private Class<?> targetClass;
    //切面信息集合 照顾多个AOP的 情况
    private List<AspectInfo> sortedAspectInfoList;

而且,多个AOP的执行顺序是类似于同心圆的穿梭操作。所以多个before和多个after执行的顺序不尽相同。需要做额外的工作。

上面出现的切面信息类应该定义如下。

package com.framework.aop;

/**
 * @Desc
 * @Author FuYouJ
 * @date 2020/5/31 21:55
 */

import com.framework.aop.aspect.DefaultAspect;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 接受注解的值
 */
@Data
public class AspectInfo {
  private int orderIndex;
  private DefaultAspect aspectObject;
  public AspectInfo() {
  }
  public AspectInfo(int orderIndex, DefaultAspect aspectObject) {
    this.orderIndex = orderIndex;
    this.aspectObject = aspectObject;
  }

}

创建一个AspectListExecutor类继承于MethodInterceptor,该类主要的工作就是往被代理的对象添加横切逻辑。

@Data
@NoArgsConstructor
public class AspectListExecutor implements MethodInterceptor {
  //被代理的class
    private Class<?> targetClass;
    // targetCalss切面信息集合 照顾多个AOP的 情况
    private List<AspectInfo> sortedAspectInfoList;
    public AspectListExecutor(Class<?> targetClass,List<AspectInfo> aspectInfos){
        this.targetClass = targetClass;
        //直接先排序再赋值
        this.sortedAspectInfoList = sort(aspectInfos);
    }
    //因为需要按照order的顺序执行aop,所以需要对 List<AspectInfo>排序。
        private List<AspectInfo> sort(List<AspectInfo> aspectInfos) {
        //升序排列
       Collections.sort(aspectInfos, new Comparator<AspectInfo>() {
            @Override
            public int compare(AspectInfo o1, AspectInfo o2) {
                return o1.getOrderIndex() - o2.getOrderIndex();
            }
        });
       return aspectInfos;
    }
   /**
     * 按照oder的顺序升序执行
     * @param method
     * @param args
     */
    private void invokeBeforeAdvices(Method method, Object[] args) throws Throwable {
        for (AspectInfo info : sortedAspectInfoList) {
            info.getAspectObject().before(targetClass,method,args);
        }
    }
        /**
     * 发生异常的时候执行 降序
     * @param method
     * @param args
     * @param e
     */
    private void invokeAfterThrowingAdvices(Method method, Object[] args, Exception e) throws Throwable {
        for (int i = sortedAspectInfoList.size() -1; i >=0 ; i--) {
            sortedAspectInfoList.get(i).getAspectObject()
                    .afterThrowing(targetClass,method,args,e);
        }
    }

    /**
     * 如果代理方法正常返回,降序执行afterAdvice
     * @param method
     * @param args
     * @param returnValue
     * @return
     */
    private Object invokeAfterReturningAdvices(Method method, Object[] args, Object returnValue) throws Throwable {
        Object res = null;
        for (int i = sortedAspectInfoList.size()-1; i >=0 ; i--) {
           res =  sortedAspectInfoList.get(i).getAspectObject().afterReturning(targetClass, method, args, returnValue);
        }
        return res;
    }
//织入逻辑的方法
  /**
     *
     * @param o 被增强的对象 
     * @param method 需要拦截的方法
     * @param args 方法参数
     * @param methodProxy 代理方法
     * @return 返回值
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object returnValue = null;
        if (ValidationUtil.isEmpty(sortedAspectInfoList)){
            //就算为空也要执行原来的方法啊
            return methodProxy.invokeSuper(o,args);
        }
        //1.按照order的顺序执行完毕所有切面的before方法
        invokeBeforeAdvices(method,args);
        try {
            //2。 执行被代理的方法
            returnValue = methodProxy.invokeSuper(o,args);
            //3. 如果被代理方法正常返回,按照order的顺序 逆序执行afterReturning
            returnValue =  invokeAfterReturningAdvices(method,args,returnValue);
        }catch (Exception e){
            //执行异常时
            invokeAfterThrowingAdvices(method,args,e);
        }
        return returnValue;
    }

有了一个能往被代理对象里面织入逻辑的武器,还需要使用这个武器的使用者才可以。在CGLIB已经有这样的使用者。就是Enhancer,它可以根据MethodInterceptor和目标类生成代理对象。

创建一个工具类,利用Enhancer创建一个代理现象。

public class ProxyCreator {
    /**
     *创建代理 对象并返回
     * @param targetClass
     * @param interceptor
     * @return
     */
    public static Object createProxy(Class<?> targetClass, MethodInterceptor interceptor){
        Object o = Enhancer.create(targetClass, interceptor);
        return o;
    }
}

有了以上工作,下面只需要从容器中筛选所有对象,用代理对象替换容器里面的被代理对象。 容器的实现
这里不赘述。后面我会新开文章专门写实现容器。这里 直接贴出 容器的实现代码 。

容器必须是单例,单例模式我以前写过一个不是很好的文章,有心的可以看看帮助理解。
JAVA中N种单例模式简单概括(防反射,克隆,序列化,实例化,多线程安全)

容器实现代码

/**
 * 容器类 单例模式  (线程安全,防止反射 ,序列化)
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Slf4j
public class BeanContainer {
    //判断实例是否已经被加载过
    private boolean loaded = false;
    private final ConcurrentHashMap<Class<?>,Object> beanMap = new ConcurrentHashMap<>();
    /**
     * 加载 bean的注解列表
     * 当一个类被一下几个注解修饰的时候,那么就归容器管理
     */
    private static final List<Class<? extends Annotation>> BEAN_ANNOTATION
            = Arrays.asList(Component.class, Controller.class
            ,Service.class, Repository.class
            ,Aspect.class);
    /**
     * 获取容器实例
     * @return
     */
    public static BeanContainer getInstance(){
        return ContainerHolder.HOLDER.instance;
    }
    private enum  ContainerHolder{
        //一个实例
        HOLDER;
        //一个成员
        private BeanContainer instance;
        private ContainerHolder(){
            instance = new BeanContainer();
        }
    }
    /**
     * 扫描加载所有的bean
     *避免 多线程同时加载
     */
    public synchronized void loadBeans(String packageName){
        if (isLoaded()){
            log.warn("容器已经被加载过了");
            return;
        }
        Set<Class<?>> classSet = ClassUtil.extractPackageClass(packageName);
        if (ValidationUtil.isEmpty(classSet)) {
            log.warn("没有提取到任何的类"+packageName);
            return;
        }
        for (Class<?> clazz : classSet) {
            for (Class<? extends Annotation> annotation : BEAN_ANNOTATION) {
                //如果有这个注解
                if (clazz.isAnnotationPresent(annotation)) {
                    //将m目标类本省作为建 ,目标类的实例作为 值,放在容器中
                    beanMap.put(clazz,ClassUtil.newInstance(clazz,true));
                }
            }
        }
        loaded = true;
    }

    public boolean isLoaded() {
        return loaded;
    }

    public void setLoaded(boolean loaded) {
        this.loaded = loaded;
    }
    public int size(){
        return beanMap.size();
    }


    //增加一个实例
   public Object addBean(Class<?>clazz, Object bean){
       return beanMap.put(clazz,bean);
    }
    //删除
   public Object remove(Class<?>clazz){
       return beanMap.remove(clazz);
    }

    /**
     * 类获取实例
     * @param clazz
     * @return
     */
   public Object getBean(Class<?>clazz){
        return beanMap.get(clazz);
    }

    /**
     * 获取所有的 beanclss集合
     * @return
     */
    public Set<Class<?>> getClasses(){
        return beanMap.keySet();
    }

    /**
     * 获取所有实例
     * @return
     */
   public Set<Object> getBeans(){
        return new HashSet<>(beanMap.values());
    }

    /**
     * 获取某注解 的class
     * @param annotation
     * @return
     */
  public Set<Class<?>> getClassesByAnnotation(Class<? extends Annotation> annotation){
       //获取所有class
        Set<Class<?>> keySet = getClasses();
        if (ValidationUtil.isEmpty(keySet)){
            log.warn("容器为空");
            return null;
        }
        Set<Class<?>> classSet = new HashSet<>();
        for (Class<?> clazz : keySet) {
            if (clazz.isAnnotationPresent(annotation)){
                classSet.add(clazz);
            }
        }
        return classSet.size() > 0?classSet:null;
    }
  public Set<Class<?>> getClassesBySuper(Class<?> interfaceOrClass){
      //获取所有class
      Set<Class<?>> keySet = getClasses();
      if (ValidationUtil.isEmpty(keySet)){
          log.warn("容器为空");
          return null;
      }
      //通过接口筛选
      Set<Class<?>> classSet = new HashSet<>();
      for (Class<?> clazz : keySet) {
          //判断是否是我的子类
          if (interfaceOrClass.isAssignableFrom(clazz)){
              classSet.add(clazz);
              //TODO
          }
      }
      return classSet.size()>0?classSet:null;
  }
}

依赖注入代码

/**
 * 依赖注入的服务
 */
@Slf4j
public class DependencyInjector {
    private BeanContainer beanContainer;
    public DependencyInjector(){
        beanContainer = BeanContainer.getInstance();
    }

    /**
     * 执行IOC
     */
    public void doIoc(){
        //遍历容器class对象
        if (ValidationUtil.isEmpty(beanContainer.getClasses())){
            log.warn("容器为空");
            return;
        }
        for (Class<?> clazz : beanContainer.getClasses()) {
            //遍历每个对象的 成员变量
            Field[] fields = clazz.getDeclaredFields();
            if (ValidationUtil.isEmpty(fields)){
                continue;
            }
            //找到自动注入的注解
            for (Field field : fields) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    Autowired autowired = field.getAnnotation(Autowired.class);
                    String autowiredValue = autowired.value();
                    //获取类型
                    Class<?> fieldClass = field.getType();
                    //再容器中找到
                    Object fieldValue =  getFiledInstance(fieldClass,autowiredValue);
                    if (fieldValue == null){
                        throw new RuntimeException("注入失败,容器中不存在该类型,名字:"+autowiredValue);
                    }else {
                        //注入
                        Object targetBean = beanContainer.getBean(clazz);
                        //通过反射注入
                        ClassUtil.setField(field,targetBean,fieldValue,true);
                    }
                }
            }
        }
    }

    /**
     * 根据class对象 获取容器管理的类或者实现类
     * @param fieldClass
     * @param autowired
     * @return
     */
    private Object getFiledInstance(Class<?> fieldClass, String autowired) {
        Object fieldValue = beanContainer.getBean(fieldClass);
        //入如果直接获取到了就返回
        if (fieldValue != null) {
            return fieldValue;
        }else {
            //找实现类
             Class<?> implClass =  getImplementClass(fieldClass,autowired);
             if (implClass != null){
                 return beanContainer.getBean(implClass);
             }else {
                 return null;
             }
        }
    }

    /**
     * 获取接口的实现类
     * @param fieldClass
     * @param autowired
     * @return
     */
    private Class<?> getImplementClass(Class<?> fieldClass, String autowired) {
        Set<Class<?>> classsSet = beanContainer.getClassesBySuper(fieldClass);
        if (ValidationUtil.isEmpty(classsSet) == false){
            //如果有多个实现类呢? 通过 Qualifier 设置名称
            if (ValidationUtil.isEmpty(autowired)){
                //说明没有精确指定
                if (classsSet.size() == 1){
                    return classsSet.iterator().next();
                }else {
                    //抛出异常
                    throw  new RuntimeException("发现多个继承类!却又没有指定名字"+fieldClass.getName());
                }
            }else {
                for (Class<?> clazz : classsSet) {
                    //如果类的类名等于指定的名字
                    if (autowired.equals(clazz.getSimpleName())){
                        return clazz;
                    }
                }
            }
        }
        return null;
    }
}

需要从容器里面筛选出符合要被代理的对象,对其替换。

public class AspectWeaver {
  private BeanContainer beanContainer;
  //获取容器的单例
    public AspectWeaver(){
        beanContainer = BeanContainer.getInstance();
    }
    //AOP主要方法
     public  void doAop(){
     //获取所有的切面类
      //1.获取所有的切面类
        Set<Class<?>> aspectSet = beanContainer.getClassesByAnnotation(Aspect.class);
        //没有需要代理的类,也就是没有切面类。直接返回 此方法是非空验证,可自行实现。
           if (ValidationUtil.isEmpty(aspectSet)){ return; }
        //2.将切面类按照不同的织入目标进行切分 比如切Controller的在一边,切Service的 在一边。用Map保存  比如 controller  有多个切面类要去增强他。所以用List保存
        Map<Class<? extends Annotation>, List<AspectInfo>> categorizedMap = new HashMap<>();
        //遍历 所有的切面类
         for (Class<?> aspectClass : aspectSet) {
         //对切面类进行校验,校验切面的一般规范。比如 不能切自己(死循环)
            boolean b =  verifyAspect(aspectClass);
            if (b){
            //对于校验通过的根据要切的目标去归类保存
            //比如Service的 切面增强 有一个列表
            //比如Controller 切面增强 有一个列表
            //列表是因为 往往 不止一个切面类要去增强某被代理类
                categorizeAspect(categorizedMap,aspectClass);
            }else {
            //对于不符合切面规范的,抛出异常
                throw  new RuntimeException("当前切面类必须持有@Aspect 和 @Order 和继承自DefaultAspect");
            }
        }
        //Map都是空的 也没啥做的 直接返回 无事可做
         if (ValidationUtil.isEmpty(categorizedMap)){return;}
//注意注意 这里map百年里的是KEY  假设 整个应用程序所有的切面只对service和controller进行增强,所以此时的key就是service和controller
//然后根据key获取符合条件的切面类列表,全部进行织入。
         for (Class<? extends Annotation> aClass : categorizedMap.keySet()) {
            //执行织入 代理对象替换了容器的被代理对象
            //这样字从 容器取得对象就是已经增强后的对象了
              List<AspectInfo> aspectInfoList = categorizedMap.get(aClass);
              //对controller 和service织入
              weaveByCategory(aClass, aspectInfoList);
        }
     }
 /**
     * 验证切面 类是否满足要求
     * 必须要有两个切面标签
     * 必须继承自DefaultAspect.class 必须按照这个骨架执行啊
     * Aspect的属性不能是他本身
     * @param aspectClass
     * @return
     */
    private boolean verifyAspect(Class<?> aspectClass) {
            return aspectClass.isAnnotationPresent(Aspect.class)
                    && aspectClass.isAnnotationPresent(Order.class)
                    && DefaultAspect.class.isAssignableFrom(aspectClass)
                    && aspectClass.getAnnotation(Aspect.class).value() != Aspect.class;
    }
/**
     *将切面类 按照不同的切面目标进行分类
     * @param categorizedMap
     * @param aspectClass
     */
    private void categorizeAspect(Map<Class<? extends Annotation>, List<AspectInfo>> categorizedMap, Class<?> aspectClass) {
        Order orderTag = aspectClass.getAnnotation(Order.class);
        Aspect aspectTag = aspectClass.getAnnotation(Aspect.class);
        DefaultAspect aspect = (DefaultAspect) beanContainer.getBean(aspectClass);
        AspectInfo aspectInfo = new AspectInfo(orderTag.value(),aspect);
        if (categorizedMap.containsKey(aspectTag.value()) == false) {
            //如果第一次出现
            List<AspectInfo> aspectInfoList = new ArrayList<>();
            aspectInfoList.add(aspectInfo);
            categorizedMap.put(aspectTag.value(),aspectInfoList);
        }else {
            //已有列表
            categorizedMap.get(aspectTag.value())
                    .add(aspectInfo);
        }
    }
/**
     * 
     * @param aspectAnnotationClass 要被切的注解所对应的类 比如@service的xxxxService
     * @param aspectInfos 他所对应的集合
     */
    private void weaveByCategory(Class<? extends Annotation> aspectAnnotationClass, List<AspectInfo> aspectInfos) {
     //获取被例如 @service标记的所有类
        Set<Class<?>> aspectClassSet = beanContainer.getClassesByAnnotation(aspectAnnotationClass);
        if (ValidationUtil.isEmpty(aspectClassSet)){return;}
        //遍历 为每个被代理的类生成动态代理对象
        for (Class<?> targetClass : aspectClassSet) {
            AspectListExecutor aspectListExecutor = new AspectListExecutor(targetClass,aspectInfos);
            Object proxyBean = ProxyCreator.createProxy(targetClass, aspectListExecutor);
            //将代理对象放到容器 替换被代理的实例
            beanContainer.addBean(targetClass,proxyBean);
        }
    }
}

以上就是一个简单版本的根据注解来分类切面的实现。

测试

编写一个测试的controller,具有返回值。

@Controller
public class TestAopController {
    public int say(){
        System.out.println("我是controller的本地方法");
        return 1;
    }
}

为他编写两个AOP类。顺序分别是0和1

package com.fuyouj.aspect.test;

import com.framework.aop.annotation.Aspect;
import com.framework.aop.annotation.Order;
import com.framework.aop.aspect.DefaultAspect;
import com.framework.core.annotation.Controller;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Method;

/**
 * @Desc
 * @Author FuYouJ
 * @date 2020/6/2 0:40
 */
@Aspect(value = Controller.class)
@Order(0)
@Slf4j
public class TestAopOne extends DefaultAspect {
    @Override
    public void before(Class<?> targetClass, Method method, Object[] args) throws Throwable {
       log.info("我是切面逻辑before order0,我增强的目标类是{},我增强的目标方法是,方法参数{}"
        ,targetClass.getSimpleName(),method.getName(),args);
    }

    @Override
    public Object afterReturning(Class<?> targetClass, Method method, Object[] args, Object returnValue) throws Throwable {
        Integer res = (Integer) returnValue;
        log.info("我是切面逻辑after order0,我增强的目标类是{},我增强的目标方法是,方法参数{}"
                ,targetClass.getSimpleName(),method.getName(),args);
        res++;
        return res;
    }
}
package com.fuyouj.aspect.test;

import com.framework.aop.annotation.Aspect;
import com.framework.aop.annotation.Order;
import com.framework.aop.aspect.DefaultAspect;
import com.framework.core.annotation.Controller;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Method;

/**
 * @Desc
 * @Author FuYouJ
 * @date 2020/6/2 0:40
 */
@Aspect(value = Controller.class)
@Order(1)
@Slf4j
public class TestAopTwo extends DefaultAspect {
    @Override
    public void before(Class<?> targetClass, Method method, Object[] args) throws Throwable {
       log.info("我是切面逻辑before order1,我增强的目标类是{},我增强的目标方法是{},方法参数{}"
        ,targetClass.getSimpleName(),method.getName(),args);
    }

    @Override
    public Object afterReturning(Class<?> targetClass, Method method, Object[] args, Object returnValue) throws Throwable {
        Integer res = (Integer) returnValue;
        log.info("我是切面逻辑after order1,我增强的目标类是{},我增强的目标方法是{},方法参数{}"
                ,targetClass.getSimpleName(),method.getName(),args);
        res++;
        return res;
    }
}

编写测试类

public class TestAop {
    @Test
    public void testAopOne(){
        BeanContainer beanContainer = BeanContainer.getInstance();
        //加载了所有类
        beanContainer.loadBeans("com.fuyouj");
        //AOP
        new AspectWeaver().doAop();
        // ioc
        //这里的细节 先AOP,再自动注入。
        new DependencyInjector().doIoc();
        TestAopController controller = (TestAopController) beanContainer.getBean(TestAopController.class);
        int say = controller.say();
        System.out.println(say+"======");
    }
}

按照逻辑,俩个切面类的顺序分别是0和1,所以 执行顺序因该是
0的beore,1的before,1的after,0的after.并且原来额返回结果经过两次修改,应该是2。

在这里插入图片描述
附上Controller注解。


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}

后序预告
AOP2.0: 支持execution表达式
自己手写一个Spring框架

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章