Java AOP

目前,java实现AOP的方式有三种:

  •   代理
  •   编译时修改class
  •   类加载时修改class

代理是目前最流行的实现方式,我们主要介绍代理的实现方式。

编译时修改class

        使用 aspectj-maven-plugin 1.14.0 插件在编译时修改class文件,实现AOP

注意:

目前的 aspectj-maven-plugin 1.14.0 最高只支持到 java 16

        

类加载时修改class

        使用agent在类加载时修改class

运行时需要在 VM options 里加入 -javaagent:C:/Users/manyh/.m2/repository/org/aspectj/aspectjweaver/1.9.7/aspectjweaver-1.9.7.jar
    把其中 C:/Users/manyh/.m2/repository 改为你自己 maven 仓库起始地址

代理

        代理又分为jdk动态代理和cglib代理

 JDK代理

        JDK代理是针对于接口代理的,所以目标类和代理类是兄弟关系。

public class JdkProxyDemo {
    interface Foo {
        void foo();
    }

    static class Target implements Foo {
        @Override
        public void foo() {
            System.out.println("target foo");
        }
    }

    //jdk 只能针对接口代理
    public static void main(String[] args) {
        //目标对象
        Target target = new Target();

        ClassLoader loader = JdkProxyDemo.class.getClassLoader(); //代理类通过ClassLoader来实现运行期间自动生成字节码
        Foo instance = (Foo) Proxy.newProxyInstance(loader, new Class[]{Foo.class}, (proxy, method, args1) -> {
            System.out.println("before...");
            //目标.方法(参数)
            //方法.invoke(目标,参数)
            Object result = method.invoke(target, args1);
            System.out.println("after...");
            return result; //让代理返回目标方法执行的结果
        });

        instance.foo();

    }
}

        我们来模拟一下jdk动态代理的实现:

 

// jdk代理类和目标类是兄弟关系,所以页需要实现接口
public class $Proxy0 implements test.Foo {

    // 用来处理方法调用和返回结果
    private test.InvocationHandler h;

    
    public $Proxy0(test.InvocationHandler h) {
        this.h = h;
    }

    @Override
    public void foo() {
        try {
            // 这里调用用户实现的invoke
            h.invoke(this,foo,new Object[0]);
        } catch (RuntimeException | Error e) {
            throw e;
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public int bar() {
        try {
            Object result = h.invoke(this,bar, new Object[0]);
            return (int) result;
        } catch (RuntimeException | Error e) {
            throw e;
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    // 需要得到Method是为了使得可以正确的调用目标类的方法,定义为静态属性是保证只加载一次,节省内存
    static Method foo;
    static Method bar;
    static{
        try {
            foo = test.Foo.class.getMethod("foo");
            bar = test.Foo.class.getMethod("bar");
        } catch (NoSuchMethodException e) {
            throw new NoSuchMethodError(e.getMessage());
        }
    }
}
public class test {

    //定义一个接口,jdk动态代理是通过接口实现的
    interface Foo {
        void foo();
        int bar();
    }

    //定义目标类
    static class Target implements Foo {
        public void foo() {
            System.out.println("target foo");
        }

        @Override
        public int bar() {
            System.out.println("target bar");
            return 100;
        }

    }

    // 用来处理代理实例上的方法调用并返回结果,这里也可以直接使用jdk的,实现一模一样
    interface InvocationHandler {
        Object invoke(Object proxy,Method method, Object[] args) throws Throwable;
    }

    public static void main(String[] param) {
        //实现invoke
        Foo proxy0 = new $Proxy0(new InvocationHandler() {
            @Override
            public Object invoke(Object proxy,Method method, Object[] args) throws Throwable {
                System.out.println("before...");
                return method.invoke(new Target(),args);
            }
        });
        proxy0.foo();
        proxy0.bar();

    }
}

         这是一个简单是实现,jdk的实现和这个也大差不差。

        接下来我们看看jdk动态代理的源码,JDK动态代理有两大核心类,它们都在Java的反射包下(java.lang.reflect),分别为InvocationHandler接口和Proxy类。

  • InvocationHandler接口
public interface InvocationHandler {

    /**
     * Processes a method invocation on a proxy instance and returns
     * the result.  This method will be invoked on an invocation handler
     * when a method is invoked on a proxy instance that it is
     * associated with.
     *
     * @param   proxy the proxy instance that the method was invoked on
     *
     * @param   method the {@code Method} instance corresponding to
     * the interface method invoked on the proxy instance.  The declaring
     * class of the {@code Method} object will be the interface that
     * the method was declared in, which may be a superinterface of the
     * proxy interface that the proxy class inherits the method through.
     *
     * @param   args an array of objects containing the values of the
     * arguments passed in the method invocation on the proxy instance,
     * or {@code null} if interface method takes no arguments.
     * Arguments of primitive types are wrapped in instances of the
     * appropriate primitive wrapper class, such as
     * {@code java.lang.Integer} or {@code java.lang.Boolean}.
     *
     * @return  the value to return from the method invocation on the
     * proxy instance.  If the declared return type of the interface
     * method is a primitive type, then the value returned by
     * this method must be an instance of the corresponding primitive
     * wrapper class; otherwise, it must be a type assignable to the
     * declared return type.  If the value returned by this method is
     * {@code null} and the interface method's return type is
     * primitive, then a {@code NullPointerException} will be
     * thrown by the method invocation on the proxy instance.  If the
     * value returned by this method is otherwise not compatible with
     * the interface method's declared return type as described above,
     * a {@code ClassCastException} will be thrown by the method
     * invocation on the proxy instance.
     *
     * @throws  Throwable the exception to throw from the method
     * invocation on the proxy instance.  The exception's type must be
     * assignable either to any of the exception types declared in the
     * {@code throws} clause of the interface method or to the
     * unchecked exception types {@code java.lang.RuntimeException}
     * or {@code java.lang.Error}.  If a checked exception is
     * thrown by this method that is not assignable to any of the
     * exception types declared in the {@code throws} clause of
     * the interface method, then an
     * {@link UndeclaredThrowableException} containing the
     * exception that was thrown by this method will be thrown by the
     * method invocation on the proxy instance.
     *
     * @see     UndeclaredThrowableException
     */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

      代理实例的调用处理器需要实现InvocationHandler接口,并且每个代理实例都有一个关联的调用处理器。当一个方法在代理实例上被调用时,这个方法调用将被编码并分派到其调用处理器的invoke方法上。

        我们创建的每一个代理实例都要有一个关联的InvocationHandler,并且在调用代理实例的方法时,会被转到InvocationHandler的invoke方法上。

其有三个参数,分别为:

  • proxy:是调用该方法的代理实例。
  • method:是在代理实例上调用的接口方法对应的Method实例。
  • args:一个Object数组,是在代理实例上的方法调用中传递的参数值。如果接口方法为无参,则该值为null。

其返回值为:调用代理实例上的方法的返回值。

  •  Proxy类

       Proxy提供了用于创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。

代理类具有以下属性:

  • 代理类的名称以 “$Proxy” 开头,后面跟着一个数字序号。
  • 代理类继承了Proxy类。
  • 代理类实现了创建时指定的接口(JDK动态代理是面向接口的)。
  • 每个代理类都有一个公共构造函数,它接受一个参数,即接口InvocationHandler的实现,用于设置代理实例的调用处理器。

Proxy提供了两个静态方法,用于获取代理对象。

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        ....
    }
    public static Class<?> getProxyClass(ClassLoader loader,
                                         Class<?>... interfaces)
        throws IllegalArgumentException
    {
        ...
    }

        那么代理类和其对象是怎么创建的呢?

                我们从newProxyInstance()方法开始下手,它的源码里面有这么一段代码,很明显就是在这个方法创建的代理类

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

                getProxyClass0的实现是这样的,可以看出代理接口最多不能超过65535,这段注释的意思是        如果由实现给定接口的给定加载程序定义的代理类存在,这将简单地返回缓存的副本;否则,它将通过ProxyClassFactory创建代理类

    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }

        简单介绍一下WeakCache<K, P, V> 类,该类主要是为代理类进行缓存的。获取代理类时,会首先从缓存中获取,若没有会调用ProxyClassFactory类进行创建,创建好后会进行缓存。             

        // create subKey and retrieve the possible Supplier<V> stored by that
        // subKey from valuesMap
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));

        进去apply方法找到ProxyClassFactory类,ProxyClassFactory是Proxy类的一个静态内部类,该类用于生成代理类。

        看到这行代码,生成代理类就是这个方法无疑了

            /*
             * Generate the specified proxy class.
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);

        将这个字节数组显示出来就是最终的代理类了,然后在通过下面这个方法实现类加载,这个方法是一个本地方法

return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);

cglib代理

        CGLIB 能够代理接口和普通的类,但是被代理的类不能被 final 修饰,且接口中的方法不能使用 final 修饰。

和 jdk 动态代理原理查不多

  1. 回调的接口换了一下,InvocationHandler 改成了 MethodInterceptor

  2. 调用目标时有所改进,见下面代码片段

    1. method.invoke 是反射调用,必须调用到足够次数才会进行优化

    2. methodProxy.invoke 是不反射调用,它会正常(间接)调用目标对象的方法(Spring 采用)

    3. methodProxy.invokeSuper 也是不反射调用,它会正常(间接)调用代理对象的方法,可以省略目标对象

public class CglibProxyDemo {
    static class Target {
        public void foo() {
            System.out.println("target foo");
        }
    }

    public static void main(String[] param) {
        Target target = new Target();

        //这里的代理对象的Target的子类,所以Target不能为final
        Target proxy = (Target) Enhancer.create(Target.class, (MethodInterceptor) (p, method, args, methodProxy) -> {
            System.out.println("before...");
//            Object invoke = method.invoke(target, args); //用发射调用目标
//            Object invoke = methodProxy.invoke(target, args); //内部没有用反射,需要目标,spring使用的方式
            Object invoke = methodProxy.invokeSuper(p, args); //内部没有用反射,需要代理
            System.out.println("after...");
            return invoke;
        });

        proxy.foo();
    }
}

        现在我们需要研究的就是CGLIB是怎么避免反射调用的(反射调用耗费性能)

我们来进行模拟一下

先创建一个Target,做为目标类

public class Target {
    public void save() {
        System.out.println("save()");
    }

    public void save(int i) {
        System.out.println("save(int)");
    }

    public void save(long j) {
        System.out.println("save(long)");
    }
}

代理类

public class Proxy extends Target {

    // 相当于 jdk代理里面的InvocationHandler
    private MethodInterceptor methodInterceptor;

    public void setMethodInterceptor(MethodInterceptor methodInterceptor) {
        this.methodInterceptor = methodInterceptor;
    }

    static Method save0;
    static Method save1;
    static Method save2;
    static MethodProxy save0Proxy;
    static MethodProxy save1Proxy;
    static MethodProxy save2Proxy;
    static {
        try {
            save0 = Target.class.getMethod("save");
            save1 = Target.class.getMethod("save", int.class);
            save2 = Target.class.getMethod("save", long.class);
            save0Proxy = MethodProxy.create(Target.class, Proxy.class, "()V", "save", "saveSuper");
            save1Proxy = MethodProxy.create(Target.class, Proxy.class, "(I)V", "save", "saveSuper");
            save2Proxy = MethodProxy.create(Target.class, Proxy.class, "(J)V", "save", "saveSuper");
        } catch (NoSuchMethodException e) {
            throw new NoSuchMethodError(e.getMessage());
        }
    }

    // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 带原始功能的方法
    public void saveSuper() {
        super.save();
    }
    public void saveSuper(int i) {
        super.save(i);
    }
    public void saveSuper(long j) {
        super.save(j);
    }
    // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 带增强功能的方法
    @Override
    public void save() {
        try {
            methodInterceptor.intercept(this, save0, new Object[0], save0Proxy);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public void save(int i) {
        try {
            methodInterceptor.intercept(this, save1, new Object[]{i}, save1Proxy);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public void save(long j) {
        try {
            methodInterceptor.intercept(this, save2, new Object[]{j}, save2Proxy);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
}
public static void main(String[] args) {
        Proxy proxy = new Proxy();
        Target target = new Target();
        proxy.setMethodInterceptor(new MethodInterceptor() {
            @Override
            public Object intercept(Object p, Method method, Object[] args,
                                    MethodProxy methodProxy) throws Throwable {
                System.out.println("before...");
//                return method.invoke(target, args); // 反射调用
                // FastClass
                return methodProxy.invoke(target, args); // 内部无反射, 结合目标用
//                return methodProxy.invokeSuper(p, args); // 内部无反射, 结合代理用
            }
        });

        proxy.save();
        proxy.save(1);
        proxy.save(2L);
    }

在使用methodProxy的invoke和invokeSuper时生成两个FastClass类

public class ProxyFastClass {
    static Signature s0 = new Signature("saveSuper", "()V");
    static Signature s1 = new Signature("saveSuper", "(I)V");
    static Signature s2 = new Signature("saveSuper", "(J)V");

    // 获取代理方法的编号
    /*
        Proxy
            saveSuper()              0
            saveSuper(int)           1
            saveSuper(long)          2
        signature 包括方法名字、参数返回值
     */
    public int getIndex(Signature signature) {
        if (s0.equals(signature)) {
            return 0;
        } else if (s1.equals(signature)) {
            return 1;
        } else if (s2.equals(signature)) {
            return 2;
        }
        return -1;
    }

    // 根据方法编号, 正常调用目标对象方法
    public Object invoke(int index, Object proxy, Object[] args) {
        if (index == 0) {
            ((Proxy) proxy).saveSuper();
            return null;
        } else if (index == 1) {
            ((Proxy) proxy).saveSuper((int) args[0]);
            return null;
        } else if (index == 2) {
            ((Proxy) proxy).saveSuper((long) args[0]);
            return null;
        } else {
            throw new RuntimeException("无此方法");
        }
    }

    public static void main(String[] args) {
        ProxyFastClass fastClass = new ProxyFastClass();
        int index = fastClass.getIndex(new Signature("saveSuper", "()V"));
        System.out.println(index);

        fastClass.invoke(index, new Proxy(), new Object[0]);
    }
}
public class TargetFastClass {
    static Signature s0 = new Signature("save", "()V");
    static Signature s1 = new Signature("save", "(I)V");
    static Signature s2 = new Signature("save", "(J)V");

    // 获取目标方法的编号
    /*
        Target
            save()              0
            save(int)           1
            save(long)          2
        signature 包括方法名字、参数返回值
     */
    public int getIndex(Signature signature) {
        if (s0.equals(signature)) {
            return 0;
        } else if (s1.equals(signature)) {
            return 1;
        } else if (s2.equals(signature)) {
            return 2;
        }
        return -1;
    }

    // 根据方法编号, 正常调用目标对象方法
    public Object invoke(int index, Object target, Object[] args) {
        if (index == 0) {
            ((Target) target).save();
            return null;
        } else if (index == 1) {
            ((Target) target).save((int) args[0]);
            return null;
        } else if (index == 2) {
            ((Target) target).save((long) args[0]);
            return null;
        } else {
            throw new RuntimeException("无此方法");
        }
    }

    public static void main(String[] args) {
        TargetFastClass fastClass = new TargetFastClass();
        int index = fastClass.getIndex(new Signature("save", "(I)V"));
        System.out.println(index);
        fastClass.invoke(index, new Target(), new Object[]{100});
    }
}

我们现在来梳理一下流程,CGLIB在生成代理类时候,为每个目标类的方法都封装了一个MethodProxy,代理类第一次被实例化或者第一次调用其方法时还会生成两个FastClass类,在使用methodProxy调用invoke或者invokeSuper方法时,methodProxy底层会调用对应的FastClass类的对应的方法。这样就避免了反射调用,来提高方法调用性能

这是CGLIB底层MethodProxy的invoke方法的实现

public Object invoke(Object obj, Object[] args) throws Throwable {
        try {
            this.init();
            MethodProxy.FastClassInfo fci = this.fastClassInfo;
            return fci.f1.invoke(fci.i1, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        } catch (IllegalArgumentException var5) {
            if (this.fastClassInfo.i1 < 0) {
                throw new IllegalArgumentException("Protected method: " + this.sig1);
            } else {
                throw var5;
            }
        }
    }
    private static class FastClassInfo {
        FastClass f1;
        FastClass f2;
        int i1;
        int i2;

        private FastClassInfo() {
        }
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值