Implementación de AOP basada en proxy dinámico jdk

Implementación de AOP basada en proxy dinámico jdk

El proxy dinámico de jdk se basa en interfaces, existen ciertas limitaciones, debe proporcionar una interfaz para la implementación

Comenzando con el proxy dinámico jdk

Primero, la clase proxy debe implementar la interfaz InvocationHandler

Cuando la clase delegada llama a un método, se llama a través de la instrucción method.invoke () en este método

A partir de esto, podemos implementar una clase de herramienta como esta

Tenga en cuenta que aunque los parámetros proxy e instancia tienen el mismo método, si el primer parámetro del método invoke no se puede pasar al proxy, provocará un desbordamiento de la pila.

Se llama al método invoke de la clase proxy, que es una instancia de InvocationHandler, por lo que es una llamada recursiva, por lo que aparecerá el java.lang.StackOverflowError mencionado anteriormente.

/**
 *
 * @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);
    }

复制代码

Finalmente, obtenga la clase proxy instanciando esta clase y llamando al método getProxyInstance

Agregar una devolución de llamada para un nodo de tiempo particular

Dado que el método correspondiente que llamamos agrega un proxy, también podemos agregar una devolución de llamada antes de que se llame al método, después de la llamada, después de lanzar una excepción y después de devolver

Antes de llamar al método, generalmente podemos usarlo para la verificación o modificación de parámetros, por lo que podemos establecer un mapeo como este

Object [] -> Object [], es decir, su función de devolución de llamada (la interfaz es) Función <Object [], Object []>

Después de la llamada (antes del valor de retorno), generalmente podemos manejar el valor de retorno y modificarlo, lo que significa que podemos establecer dicha asignación

Objeto-> Objeto, es decir, su función de devolución de llamada (interfaz es) Función <Objeto, Objeto>

Después de que se lanza la excepción, podemos consumir información de excepción para la excepción lanzada, por lo que podemos establecer dicha excepción de mapeo-> void

Es decir, su función de devolución de llamada (la interfaz es) Consumidor

Después de devolver el valor, puede registrar cierta información. En teoría, también puede usar la interfaz de consumidor, pero necesita crear otra clase de entidad usted mismo.

, Así que elegí hacer una interfaz yo mismo

Por supuesto, no podemos usar proxy para todos los métodos, por lo que podemos agregar un filtro para controlar si el método está mejorado y establecer

Método-> mapeo booleano, es decir, Función <Método, Booleano>

/**
 * 返回值后的回调,包含被代理的实例,代理类,当前的方法,返回值,参数列表
 */
@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;
}
复制代码

Por lo tanto, la clase de herramienta de devolución de llamada anterior se puede reescribir como

/**
 *
 * @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;
    }
}

复制代码

Realización de la función Aop

Hemos creado la clase de herramienta de proxy dinámico, ahora debemos considerar cómo tejer el método mejorado en el método mejorado

Anotación de método de aspecto

A través de anotaciones, podemos marcar las funciones de devolución de llamada de cada etapa en el plano de corte, y luego asignar el método correspondiente a la interfaz correspondiente anterior por reflexión.

Es decir, la instancia de función marcada (method.invoke ()) se llama en la clase de implementación de la interfaz correspondiente

@AfterResult @BeforeInvoke @BeforeResult @ExceptionCallBack

Corresponde al valor de retorno, antes de que se llame al método original, después del valor de retorno, después de lanzar una excepción

Todas son anotaciones vacías, solo se usan para anotaciones y no llevan datos, así que solo daré un ejemplo

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AfterResult {
}
复制代码
Anotación de modo de método de filtro

Simplemente escriba, solo controle el tipo de valor de retorno y el nombre del método

Más tarde se puede ampliar a la firma del método de detección

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

复制代码
Inicializar el componente Aop

Todos los métodos de escaneo de sectores marcarán anotaciones y métodos para establecer una relación.

 //切面方法
    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);
            }
        }
    }

复制代码
Obtenga un ejemplo de una interfaz de filtrado de métodos
//获取拦截条件
    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;
        };
    }

复制代码
Obtener instancia de devolución de llamada antes de la llamada a la función
//对应织入方法必须为同参数列表长度,必须返回值一致
    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;
    }
   
复制代码
Obtener una excepción de instancia de devolución de llamada
  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;
    }
  
复制代码
Devolución de llamada antes de obtener el valor de retorno
 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;
    }
   
复制代码
Devolución de llamada después de obtener el valor de retorno
   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;
    }

复制代码
Obtener clase de proxy
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 prueba funcional

Interfaz

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

复制代码

Clase

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

复制代码

Superficie de corte

@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));
    }
}

复制代码

Método de prueba

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));
    }
复制代码

Salida de la consola

Se puede ver que solo se intercepta el método de devolver el valor de retorno doble

参数列表为[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
复制代码

Supongo que te gusta

Origin juejin.im/post/5e917760f265da47eb059ebc
Recomendado
Clasificación