AOP实现原理

AOP 底层实现方式之一是代理,由代理结合通知和目标,提供增强功能。

除此以外,aspectj 提供了两种另外的 AOP 底层实现:

  • 第一种是通过 ajc 编译器在编译 class 类文件时,就把通知的增强功能,织入到目标类的字节码中

  • 第二种是通过 agent 在加载目标类时,修改目标类的字节码,织入增强功能

AOP 实现之 ajc 编译器

定义一个目标类

public class MyService {

    // 静态方法
    public static void foo1() {
        System.out.println("foo1()");
    }

    // 实例方法
    public void foo2() {
        System.out.println("foo2()");
        this.foo3();
    }
    
    public void foo3() {
        System.out.println("foo3()");
    }
}

定义一个切面类

@Aspect
public class MyAspect {

    @Before("execution(* com.spring.aspectj.MyService.*())")
    public void before() {
        System.out.println("before()");
    }
}

在pom.xml引入 acj编译器插件

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.14.0</version>
                <configuration>
                    <complianceLevel>1.8</complianceLevel>
                    <source>8</source>
                    <target>8</target>
                    <showWeaveInfo>true</showWeaveInfo>
                    <verbose>true</verbose>
                    <Xlint>ignore</Xlint>
                    <encoding>UTF-8</encoding>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

测试

/**
 * 版本选择了 java 8, 因为目前的 aspectj-maven-plugin 1.14.0 最高只支持到 java 16
 * 一定要用 maven 的 compile 来编译, idea 不会调用 ajc 编译器
 */
public class AspectDemo {

    public static void main(String[] args) {
        MyService.foo1();
        MyService myService = new MyService();
        myService.foo2();
    }
}

AOP 实现之 agent 类加载

/**
 *
 * 运行时需要在 VM options里加入:
 * -javaagent:D:/development/tools/maven/repos/org/aspectj/aspectjweaver/1.9.19/aspectjweaver-1.9.19.jar
 * 把其中D:/development/tools/maven/repos/ 改成你自己的 maven 仓库的起始路径
 */
public class AspectDemo {

    public static void main(String[] args) {
        MyService.foo1();
        MyService myService = new MyService();
        myService.foo2();
    }
}

agent类加载增强方式不需要在pom.xml引入 acj编译器插件,但需要在 VM options 里添加一个参数 :

简单比较的话:

  • aspectj 在编译和加载时,修改目标字节码,性能较高
  • aspectj 因为不用代理,能突破一些技术上的限制,例如对构造、对静态方法、对 final 也能增强
  • 但 aspectj 侵入性较强,且需要学习新的 aspectj 特有语法,因此没有广泛流行

AOP 实现之 proxy

jdk 动态代理

基本使用

public class JdkProxyDemo {

    interface Foo {
        void foo();
    }

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

    /**
     * newProxyInstance的3个参数:
     *      ClassLoader loader: 类加载器,用来加载在运行期间动态生成的字节码
     *      Class<?>[] interfaces: 要代理的接口
     *      InvocationHandler h: 代理增强的逻辑
     *
     *  InvocationHandler中invoke方法的3个参数:
     *      Object proxy: 代理类自己
     *      Method method: 要执行的目标方法
     *      Object[] args: 执行目标传入的参数
     */
    public static void main(String[] param) {
        // 目标对象
        Target target = new Target();
        // 代理对象
        Foo proxy = (Foo) Proxy.newProxyInstance(
                Target.class.getClassLoader(), new Class[]{Foo.class},
                (p, method, args) -> {
                    System.out.println("proxy before...");
                    Object result = method.invoke(target, args);
                    System.out.println("proxy after...");
                    return result;
                });

        // 调用代理
        proxy.foo();
    }
}

 jdk 动态代理要求目标必须实现接口,生成的代理类实现相同接口,因此代理与目标之间是平级兄弟关系

模拟 jdk 动态代理

public class JdkProxyDemo2 {

    interface Foo {
        void foo();

        int bar();
    }

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

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

    public static void main(String[] param) {
        // 创建代理,这时传入 InvocationHandler
        Foo proxy = new $Proxy0(new InvocationHandler() {
            // 进入 InvocationHandler
            // proxy: 代理类本身
            // method: 要执行的目标方法
            // args: 执行method 传入的参数
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 功能增强
                System.out.println("before...");
                // 反射调用目标方法
                return method.invoke(new Target(), args);
            }
        });

        //调用代理方法
        proxy.foo();
        proxy.bar();
    }
}

 模拟代理实现

public class $Proxy0 extends Proxy implements JdkProxyDemo2.Foo {

    public $Proxy0(InvocationHandler h) {
        super(h);
    }

    // 进入代理方法
    public void foo() {
        try {
            // 回调 InvocationHandler
            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);
        }
    }

    static Method foo;
    static Method bar;
    static {
        try {
            foo = CglibProxyDemo2.Foo.class.getMethod("foo");
            bar = CglibProxyDemo2.Foo.class.getMethod("bar");
        } catch (NoSuchMethodException e) {
            throw new NoSuchMethodError(e.getMessage());
        }
    }
}

用arthas工具查看jdk生成的代理对象

final class $Proxy0
extends Proxy
implements JdkProxyDemo1.Foo {
    private static final Method m0;
    private static final Method m1;
    private static final Method m2;
    private static final Method m3;

    public $Proxy0(InvocationHandler invocationHandler) {
        super(invocationHandler);
    }

    static {
        try {
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m3 = Class.forName("com.spring.proxy.JdkProxyDemo1$Foo").getMethod("foo", new Class[0]);
            return;
        }
        catch (NoSuchMethodException noSuchMethodException) {
            throw new NoSuchMethodError(noSuchMethodException.getMessage());
        }
        catch (ClassNotFoundException classNotFoundException) {
            throw new NoClassDefFoundError(classNotFoundException.getMessage());
        }
    }

    public final boolean equals(Object object) {
        try {
            return (Boolean)this.h.invoke(this, m1, new Object[]{object});
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final String toString() {
        try {
            return (String)this.h.invoke(this, m2, null);
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final int hashCode() {
        try {
            return (Integer)this.h.invoke(this, m0, null);
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final void foo() {
        try {
            this.h.invoke(this, m3, null);
            return;
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    private static MethodHandles.Lookup proxyClassLookup(MethodHandles.Lookup lookup) throws IllegalAccessException {
        if (lookup.lookupClass() == Proxy.class && lookup.hasFullPrivilegeAccess()) {
            return MethodHandles.lookup();
        }
        throw new IllegalAccessException(lookup.toString());
    }
}

方法反射优化

// 运行时请添加 --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/jdk.internal.reflect=ALL-UNNAMED
public class TestMethodInvoke {
    public static void main(String[] args) throws Exception {
        Method foo = TestMethodInvoke.class.getMethod("foo", int.class);
        for (int i = 1; i <= 17; i++) {
            show(i, foo);
            Thread.sleep(1000);
            foo.invoke(null, i);
        }
        System.in.read();
    }

    // 方法反射调用时, 底层 MethodAccessor 的实现类
    private static void show(int i, Method foo) throws Exception {
        Method getMethodAccessor = Method.class.getDeclaredMethod("getMethodAccessor");
        getMethodAccessor.setAccessible(true);
        Object invoke = getMethodAccessor.invoke(foo);
        if (invoke == null) {
            System.out.println(i + ":" + null);
            return;
        }
        Field delegate = Class.forName("jdk.internal.reflect.DelegatingMethodAccessorImpl").getDeclaredField("delegate");
        delegate.setAccessible(true);
        System.out.println(i + ":" + delegate.get(invoke));
    }

    public static void foo(int i) {
        System.out.println(i + ":" + "foo");
    }
}

打印结果

1:null
1:foo
2:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
2:foo
3:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
3:foo
4:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
4:foo
5:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
5:foo
6:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
6:foo
7:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
7:foo
8:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
8:foo
9:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
9:foo
10:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
10:foo
11:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
11:foo
12:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
12:foo
13:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
13:foo
14:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
14:foo
15:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
15:foo
16:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
16:foo
17:jdk.internal.reflect.GeneratedMethodAccessor2@22d8cfe0
17:foo

前16次MethodAccessor接口的实现类是NativeMethodAccessorImpl,为反射调用,性能较低。第17次开始,为了把反射调用优化为正常调用,生了一个代理类来完成。

用arthas工具查看GeneratedMethodAccessor2的实现,

public class GeneratedMethodAccessor2
extends MethodAccessorImpl {
    /*
     * Loose catch block
     */
    public Object invoke(Object object, Object[] objectArray) throws InvocationTargetException {
        char c;
        block9: {
            if (objectArray.length != 1) {
                throw new IllegalArgumentException();
            }
            Object object2 = objectArray[0];
            if (object2 instanceof Byte) {
                c = ((Byte)object2).byteValue();
                break block9;
            }
            if (object2 instanceof Character) {
                c = ((Character)object2).charValue();
                break block9;
            }
            if (object2 instanceof Short) {
                c = (char)((Short)object2).shortValue();
                break block9;
            }
            if (object2 instanceof Integer) {
                c = (char)((Integer)object2).intValue();
                break block9;
            }
            throw new IllegalArgumentException();
        }
        try {
            TestMethodInvoke.foo((int)c);   // 非反射调用
            return null;
        }
        catch (Throwable throwable) {
            throw new InvocationTargetException(throwable);
        }
        catch (ClassCastException | NullPointerException runtimeException) {
            throw new IllegalArgumentException(super.toString());
        }
    }
}

jdk的代理优化:前16次为反射调用,被调用第17次时,每次方法调用都会生成一个代理对象。

cglib 代理

基本使用

public class CglibProxyDemo {

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

        public void bar() {
            System.out.println("target bar");
        }
    }

    /**
     * MethodInterceptor中的intercept方法参数说明:
     * Object obj: 代理类自己
     * Method method: 要执行的目标方法
     * Object[] args: 目标方法传入的参数
     * MethodProxy proxy: 可以避免反射调用
     */
    public static void main(String[] param) {
        // 目标对象
        Target target = new Target();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Target.class);
        // 设置Callbacks,是一个数组,可以设置多个方法拦截器
        enhancer.setCallbacks(new Callback[]{(MethodInterceptor) (p, method, args, methodProxy) -> {
            System.out.println("proxy before...");
            Object result = method.invoke(target, args);      // 用方法反射调用目标
//            Object result = methodProxy.invoke(target, args);   // methodProxy可以避免反射调用,内部没有反射 (spring用的这种方式)
//            Object result = methodProxy.invokeSuper(p, args); // 内部没有反射,不需要目标对象实例,需要代理对象
            System.out.println("proxy after...");
            return result;
        }, NoOp.INSTANCE});

        // 设置方法拦截过滤器,不同的方法可以选择不同的方法拦截器
        enhancer.setCallbackFilter(method -> {
            if (method.getName().equals("foo")) {
                return 0;
            }
            return 1;
        });

        // 创建代理对象
        Target t = (Target) enhancer.create();

        t.foo();  // foo被代理增强了
        t.bar();  // bar方法不会被代理增强
    }
}

cglib 不要求目标实现接口,它生成的代理类是目标的子类,重写目标方法,代理与目标之间是子父关系。final类和final方法不能被cglib增强

模拟 cglib 代理

定义一个目标类

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

 模拟 cjlib 代理实现

public class Proxy extends Target {

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

cjlib代理实现原理和和 jdk 动态代理原理查不多

  • 回调的接口换了一下,InvocationHandler 改成了 MethodInterceptor
  • 调用目标时有所改进
    • method.invoke 是反射调用,必须调用到足够次数才会进行优化
    • methodProxy.invoke 是不反射调用,它会正常(间接)调用目标对象的方法(Spring 采用)
    • methodProxy.invokeSuper 也是不反射调用,它会正常(间接)调用代理对象的方法,可以省略目标对象

cglib 如何避免反射

当调用 MethodProxy 的 invoke 或 invokeSuper 方法时, 会动态生成两个类:ProxyFastClass 配合代理对象一起使用, 避免反射;TargetFastClass 配合目标对象一起使用, 避免反射 (Spring 用的这种)。

 TargetFastClass 

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

TargetFastClass 记录了 Target 中方法与编号的对应关系:

  • save(long) 编号 2

  • save(int) 编号 1

  • save() 编号 0

  • 首先根据方法名和参数个数、类型,用 switch 和 if 找到这些方法编号

  • 然后再根据编号去调用目标方法,又用了一大堆 switch 和 if, 但避免了反射

ProxyFastClass

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

 ProxyFastClass 记录了 Proxy 中方法与编号的对应关系,不过 Proxy 额外提供了下面几个方法

  • saveSuper(long) 编号 2,不增强,仅是调用 super.save(long);

  • saveSuper(int) 编号 1,不增强,仅是调用 super.save(int);

  • saveSuper() 编号 0,不增强,仅是调用 super.save();

  • 查找方式与 TargetFastClass 类似。

为什么有这么麻烦的一套东西呢?

  • 避免反射, 提高性能,代价是一个代理类配两个 FastClass 类,代理类中还得增加仅调用 super 的一堆方法;

  • 用编号处理方法对应关系比较省内存,另外,最初获得方法顺序是不确定的,这个过程没法固定死。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值