基于jdk动态代理的Aop实现

1,592 阅读8分钟

基于jdk动态代理的Aop实现

jdk的动态代理是基于接口的,存在一定的局限性,必须提供一个接口以供实现

jdk动态代理入门

首先代理类必须实现InvocationHandler接口

被代理的类调用方法时都通过这个方法内的method.invoke()语句调用

由此我们可以这样实现一个工具类

注意虽然参数proxy和instance拥有相同的方法,但是如果invoke方法的第一个参数不能传入proxy,会导致堆栈溢出。

调用了代理类的invoke方法,而就是InvocationHandler的实例,所以是递归调用,所以就会出现上述所说的java.lang.StackOverflowError错误。

/**
 *
 * @param <T> 由于JDK动态代理的特性,需要一个实现接口,这里用于
 *           @see DynamicProxy#getProxyInstance() 的类型转换
 */
public class DynamicProxy<T> implements InvocationHandler {
 //由于JDK动态代理的特性,需要一个实现接口,这里用于类型转换
    private final Class<T> instanceInterfaceClass;
    //被代理的类
    private final Object instance;
    /**
     * 被代理的类调用方法时都通过这个方法内的method.invoke()语句调用
     * 注意虽然参数proxy和instance拥有相同的方法,但是如果invoke方法的第一个参数不能传入   
     * proxy,会导致堆栈溢出
     * @param proxy 当前代理类
     * @param method 当前方法
     * @param args 当前方法参数列表
     * @return 方法返回值
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object res = null;
        try {
            
            res = method.invoke(instance, args);
            
        } catch (IllegalAccessException e) {
           //处理异常
        }
        return rs;
    }

    /**
     * 获取代理类
     * @return 代理类
     */
    public T getProxyInstance(){
        Class<?> instanceClass = instance.getClass();
        return (T) Proxy.newProxyInstance(instanceClass.getClassLoader(), instanceClass.getInterfaces(), this);
    }

最后通过实例化这个类调用getProxyInstance方法获取代理类

添加某特殊时间节点的回调

既然我们调用的对应的方法添加了代理,那么我们也可以在方法调用前,调用后,抛异常后,返回后添加回调

方法调用前,我们一般可以用于参数验证,或者参数修改,所以我们可以建立一个这样的映射

Object[]->Object[],即它的回调函数(接口为)Function<Object[],Object[]>

调用后(返回值前),我们一般可以处理返回值,修改,也就说我们可以建立这样一个映射

Object -> Object,即它的回调函数(接口为)Function<Object,Object>

抛出异常后,抛出的异常我们可以消费异常信息,所以我们可以建立这样一个映射 Exception ->void

即它的回调函数(接口为)Consumer

返回值后,就可以记录一些信息,理论上也可以使用Consumer接口但是需要自己再创建一个实体类

,所以我选择自己做一个接口

当然我们不能代理所有方法,所以我们可以添加一个控制方法是否被增强的过滤器,建立

Method->boolean的映射,即Function<Method,Boolean>

/**
 * 返回值后的回调,包含被代理的实例,代理类,当前的方法,返回值,参数列表
 */
@FunctionalInterface
public interface AfterResultCallback{
    /**
     *
     * @param instance 被代理的实例
     * @param proxy 当前代理
     * @param method 被增强的方法
     * @param res 返回值
     * @param arg 参数
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    void callback(Object instance, Object proxy, Method method,Object res,Object... arg) throws InvocationTargetException, IllegalAccessException;
}

所以上面的回调工具类我们可以改写为

/**
 *
 * @param <T> 由于JDK动态代理的特性,需要一个实现接口,这里用于
 *           @see DynamicProxy#getProxyInstance() 的类型转换
 */
public class DynamicProxy<T> implements InvocationHandler {
    //异常时的回调
    private Consumer<Exception> exceptionCallback;
    //方法调用时的回调,可以用于修改入参或者验证入参
    private Function<Object[],Object[]> beforeInvokeCallback;
    //在方法执行后返回值之前的回调,可以修改返回值(不能绕开类型检测)
    //也就说原函数返回T类型不能修改为返回R类型(T和R没有继承关系)
    private Function<Object,Object> beforeResultCallback;
    //返回值后的回调
    //Object instance, Object proxy, Method method,Object res,Object... arg)
    private AfterResultCallback afterResultCallback;
    //方法拦截的条件
    private Function<Method,Boolean> methodVerify;

    //由于JDK动态代理的特性,需要一个实现接口,这里用于类型转换
    private final Class<T> instanceInterfaceClass;
    //被代理的类
    private final Object instance;

    /**
     *
     * @param proxy 当前代理类
     * @param method 当前方法
     * @param args 当前方法参数列表
     * @return 方法返回值
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //如果没有拦截条件,或者不满足条件直接执行
        if (methodVerify == null || !methodVerify.apply(method)) {
            return method.invoke(instance, args);
        }
        Object res = null;
        try {
            //如果存在回调,那么调用
            if (beforeInvokeCallback != null) {
                args = beforeInvokeCallback.apply(args);
            }
            res = method.invoke(instance, args);
            if (beforeResultCallback != null){
                res = beforeResultCallback.apply(res);
            }
            if (afterResultCallback != null){
                afterResultCallback.callback(instance, proxy, method, res, args);
            }
        } catch (IllegalAccessException e) {
            if (exceptionCallback != null) {
                exceptionCallback.accept(e);
            }
        }
        return res;
    }

    /**
     * 获取代理类
     * @return 代理类
     */
    public T getProxyInstance(){
        Class<?> instanceClass = instance.getClass();
        return (T) Proxy.newProxyInstance(instanceClass.getClassLoader(), instanceClass.getInterfaces(), this);
    }

    /**
     *
     * @param instanceInterfaceClass 代理接口
     * @param instance 被代理的类
     */
    public DynamicProxy(Class<T> instanceInterfaceClass, Object instance) {
        this.instanceInterfaceClass = instanceInterfaceClass;
        this.instance = instance;
    }

    //以下均为注册回调函数,用builder模式构建
    public DynamicProxy<T> setExceptionCallback(Consumer<Exception> exceptionCallback) {
        this.exceptionCallback = exceptionCallback;
        return this;
    }

    public DynamicProxy<T> setBeforeInvokeCallback(Function<Object[], Object[]> beforeInvokeCallback) {
        this.beforeInvokeCallback = beforeInvokeCallback;
        return this;
    }

    public DynamicProxy<T> setBeforeResultCallback(Function<Object, Object> beforeResultCallback) {
        this.beforeResultCallback = beforeResultCallback;
        return this;
    }

    public DynamicProxy<T> setAfterResultCallback(AfterResultCallback afterResultCallback) {
        this.afterResultCallback = afterResultCallback;
        return this;
    }

    public DynamicProxy<T> setMethodVerify(Function<Method,Boolean> methodVerify) {
        this.methodVerify = methodVerify;
        return this;
    }
}

Aop功能实现

我们已经做好了动态代理的工具类,现在需要考虑如何把增强的方法织入到被增强的方法

切面方法标注

通过注解我们可以在切面标注各个阶段的回调函数再通过反射将对应方法映射为我们上面对应的接口

即对应接口的实现类里面调用被标注的函数实例(method.invoke())

@AfterResult @BeforeInvoke @BeforeResult @ExceptionCallBack

分别对应返回值后,原方法调用前,返回值后,抛出异常后

都是空注解,只用于标注,不携带数据,所以我只举一个例子

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AfterResult {
}
过滤方法模式标注

简单来写,只控制返回值类型和方法名

后期可以扩充为检测方法签名

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MethodPatten {
    //返回值类型
    Class[] res() default{Object.class};
    //方法名,为空则放行所有方法
    String[] methodName() default {""};
}

初始化Aop组件

扫描切面的所有方法将标注注解和方法是建立起关系

 //切面方法
    private Object aspect;
    private Object instance;
    private Class<T> instanceInterface;
    //切面方法与对应注解的关系
    private Map<Class,Method> annotationToMethod;

    /**
     * @param aspect 切面类
     * @param instance 被代理实例
     */
    public AOP(Object aspect, Object instance) {
        this.aspect = aspect;
        this.instance = instance;
        this.annotationToMethod = new HashMap<>();
        Method[] methods = aspect.getClass().getMethods();
        //只获取第一个注解作为键,方法作为值
        for (Method method : methods) {
            Annotation[] annotations = method.getAnnotations();
            if (annotations.length > 0){
                //重复注解的只保留第一个扫描到的
                //必须使用annotationType(),如果使用getClass则是获取的代理类
                annotationToMethod.putIfAbsent(annotations[0].annotationType(),method);
            }
        }
    }

获取方法过滤接口的实例
//获取拦截条件
    private Function<Method,Boolean> getMethodVerify(){
        //如果没有MethodPattern指定条件 则全不拦截
        MethodPatten patten = aspect.getClass().getAnnotation(MethodPatten.class);
        if (patten == null) {
            return (m) -> false;
        }

        return (m) -> {
            //验证被增强的方法(被代理的类)的返回值是否符合条件
            boolean isResPass = false;
            for (Class re : patten.res()) {
                if (re.equals(m.getReturnType())){
                    isResPass = true;
                    break;
                }
            }
            //验证被增强的方法(被代理的类)的方法名是否符合条件
            boolean isMethodNamePass =false;
            for (String s : patten.methodName()) {
                //条件注解中指定的方法名为空则直接放行
                if (s.isEmpty() || s.equals(m.getName())){
                    isMethodNamePass = true;
                    break;
                }
            }
            //全通过才放行
            return isMethodNamePass && isResPass;
        };
    }

获取函数调用前回调实例
//对应织入方法必须为同参数列表长度,必须返回值一致
    private Function<Object[],Object[]> getBeforeInvokeCallback(){
        Function<Object[],Object[]> res = null;
        //获取对应方法,没有就跳过
        Method method = annotationToMethod.get(BeforeInvoke.class);
        if (method == null){
            return null;
        }

        res = (arg) -> {
            try {
                //强制防止展开
                //这个method是切面增强的方法,通过调用这个方法对arg处理
                return (Object[]) method.invoke(aspect,(Object)arg);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            return null;
        };
        return res;
    }
   
获取异常抛出回调实例
  private Consumer<Exception> getExceptionCallback(){
        Method method = annotationToMethod.get(ExceptionCallback.class);
        if (method == null){
            return null;
        }
        Consumer<Exception> res = (e) -> {
            try {
                method.invoke(aspect, e);
            } catch (IllegalAccessException illegalAccessException) {
                illegalAccessException.printStackTrace();
            } catch (InvocationTargetException invocationTargetException) {
                invocationTargetException.printStackTrace();
            }
        };
        return res;
    }
  
获取返回值前的回调
 private Function<Object,Object> getBeforeResultCallback(){
        Method method = annotationToMethod.get(BeforeResult.class);
        if (method == null){
            return null;
        }
        Function function = (res) -> {
            try {
                return method.invoke(aspect, res);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            return null;
        };
        return function;
    }
   
获取返回值后的回调
   private AfterResultCallback getAfterResultCallback(){
        Method method = annotationToMethod.get(AfterResult.class);
        if (method == null){
            return null;
        }
        AfterResultCallback afterResultCallback = (proxied,proxy,proxyMethod,res,arg) ->{
            method.invoke(aspect, proxied,proxy,proxyMethod,res,arg);
        };
        return afterResultCallback;
    }

获取代理类
public T getInstance(){
        //利用工具类获取代理类并注册回调函数,返回值为代理类
        DynamicProxy<T> proxy = new DynamicProxy<>(instanceInterface, instance);
        proxy.setMethodVerify(getMethodVerify())
                .setBeforeInvokeCallback(getBeforeInvokeCallback())
                .setExceptionCallback(getExceptionCallback())
                .setBeforeResultCallback(getBeforeResultCallback())
                .setAfterResultCallback(getAfterResultCallback());
        return proxy.getProxyInstance();
    }

Aop功能测试

接口

public interface I {
    Object dosome(Integer integer1,Integer i2);
    default double add(Integer integer1,Integer integer2,Integer i3){
        return integer1+integer2+i3;
    }
}

public class Impl implements I {
    @Override
    public Object dosome(Integer integer1,Integer integer2) {
        return (Object) Integer.max(integer1, integer2);
    }
}

切面

@MethodPatten(res = double.class)
public class aspect{
    @BeforeInvoke
    public Object[] beforeinvoke(Object...objects){
        System.out.println("参数列表为"+ Arrays.toString(objects));
        return objects;
    }
    @BeforeResult
    public Object before(Object o){
        System.out.println("修改为double max");
        return Double.MAX_VALUE;
    }
    @AfterResult
    public void after(Object instance, Object proxy, Method method, Object res, Object...arg){
        System.out.println("instance is "+instance.getClass());
        System.out.println("proxy is "+proxy.getClass());
        System.out.println("method is "+method.getName());
        System.out.println("return value is "+res);
        System.out.println("arg is "+Arrays.toString(arg));
    }
}

测试方法

public static void main(String[] args)throws InterruptedException {
        AOP<I> aop = new AOP<>(new aspect(), new Impl());
        I i = aop.getInstance();
        System.out.println(i.add(100, 10,2000));
        System.out.println("\n"+i.dosome(100, 312));
    }

控制台输出

可以看出来只拦截double返回值的方法

参数列表为[100, 10, 2000]
修改为double max
instance is class MySpring.Aop.Test.Impl
proxy is class com.sun.proxy.$Proxy6
method is add
return value is 1.7976931348623157E308
arg is [100, 10, 2000]
1.7976931348623157E308

312