Spring原理(二)AOP

AOP编译器增强

  1. 使用AspectJ的ajc编译器进行AOP增强。在编译阶段对字节码进行增强。
  2. 编写一个SpringBoot主类:
@SpringBootApplication
public class AspectTest {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(AspectTest.class, args);
        MyService myService = run.getBean(MyService.class);
        System.out.println(myService.getClass());
        myService.foo();
        run.close();

        // 没有经过Spring进行动态代理,所以可以直接调用
//        new MyService().foo();
    }
}
  1. service类:
@Service
public class MyService {
    public void foo() {
        System.out.println("myService foo...");
    }
}
  1. Aspect进行增强:
/**
 * 没有加@Component注解
 * 没有将切面交给Spring管理
 * 使用ajc编译器进行编译,生成的字节码文件
 */
@Aspect
public class MyAspect {
    /**
     * 在foo方法之前进行切面增强
     */
    @Before("execution(* aop.service.MyService.foo())")
    public void before() {
        System.out.println("before......");
    }
}
  1. 加入aspectJ的maven插件,使用ajc编译器对字节码文件进行增强。(直接运行可能不会进行增强,因为idea使用的是javac编译器)
<!-- AspectJ 编译插件 -->
<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>
                 <!-- use this goal to weave all your main classes -->
                 <goal>compile</goal>
                 <!-- use this goal to weave all your test classes -->
                 <goal>test-compile</goal>
             </goals>
         </execution>
     </executions>
 </plugin>
  1. 需要使用maven的compile进行编译,因为ajc编译器插件在maven中配置的。

在这里插入图片描述

  1. 就可以看到class文件被编译器增强了。

在这里插入图片描述

在这里插入图片描述

  1. 所以我们是在编译的使用进行增强,而不是Spring的动态代理增强。可以直接调用该方法。
@SpringBootApplication
public class AspectTest {
    public static void main(String[] args) {
//        ConfigurableApplicationContext run = SpringApplication.run(AspectTest.class, args);
//        MyService myService = run.getBean(MyService.class);
//        System.out.println(myService.getClass());
//        myService.foo();
//        run.close();

        // 没有经过Spring进行动态代理,所以可以直接调用
        new MyService().foo();
    }
}
  1. Spring动态代理不能对static静态方法进行代理,而编译器增强可以。

AOP在运行时增强

  1. 使用javaagent在java运行时,进行aop增强。注释掉上面的maven编译时插件。
  2. 需要增强的Service类
@Service
public class MyService {
    public void foo() {
        System.out.println("myService foo...");
        bar();
    }

    public void bar() {
        System.out.println("myService bar...");
    }
}
  1. 切面类
/**
 * 没有加@Component注解
 * 没有将切面交给Spring管理
 * 使用javaagent在运行时对字节码进行增强
 */
@Aspect
public class MyAspect {
    /**
     * 对MyService的所有方法进行前置切面增强
     */
    @Before("execution(* aop.service.MyService.*())")
    public void before() {
        System.out.println("before......");
    }
}
  1. 启动类
@SpringBootApplication
public class AspectTest {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(AspectTest.class, args);
        MyService myService = run.getBean(MyService.class);
        System.out.println(myService.getClass());
        myService.foo();

        // 没有经过Spring进行动态代理,所以可以直接调用
//        new MyService().foo();
    }
}
  1. 加入JVM参数javaagent,使用aspectj的jar包:-javaagent:F:/program/apache-maven-3.5.4/repository/org/aspectj/aspectjweaver/1.9.7/aspectjweaver-1.9.7.jar
  2. 还需要在resource目录下新建META-INF/aop.xml文件。
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
    <aspects>
        <!-- aop.aop.MyAspect 切面类-->
        <aspect name="aop.aop.MyAspect"/>
        <weaver options="-verbose -showWeaveInfo">
            <!-- 指定编织的包 -->
            <include within="aop..*"/>
        </weaver>
    </aspects>
</aspectj>
  1. 运行启动类,可以看到两个方法被增强。

在这里插入图片描述

  1. 但是查看字节码文件,并没有在字节码文件中体现增强。

在这里插入图片描述

  1. 所以并没有在编译器进行增强,而是在运行期间对字节码文件进行了增强。
  2. 使用阿里巴巴的arthas工具对运行期的字节码进行反编译查看。
  3. 下载arthas,使用arthas-boot.jar这个jar包启动,java -jar arthas-boot.jar
  4. 使用help可以查看命令帮助。
  5. 使用jad aop.service.MyService命令对MyService的字节码进行反编译,可以看到aspectj在运行期间修改了字节码文件,对方法进行了增强。

在这里插入图片描述

  1. 使用运行期间增强,在方法嵌套调用时,也可以对每个方法进行增强。但是,生成代理对象就不能对嵌套方法进行增强。
  2. 动态替换字节码,参考美团技术:https://tech.meituan.com/2019/09/05/java-bytecode-enhancement.html

在这里插入图片描述

JDK增强

/**
 * 演示JDK代理
 * JDK只能代理接口
 */
public class JdkProxy {

    interface Foo {
        void foo();
    }

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

    public static void main(String[] args) {
        ClassLoader classLoader = JdkProxy.class.getClassLoader();
        /**
         * 参数1:类加载器,因为代理类是在运行时生成的,所以需要类加载器将代理类加载到JVM中
         * 参数2:代理类实现的接口
         * 参数3:handler处理增强的方法
         * 返回一个接口的代理对象
         */

        Target target = new Target();

        Foo proxyInstance = (Foo) Proxy.newProxyInstance(classLoader, new Class[]{Foo.class}, (proxy, method, args1) -> {
            System.out.println("before");
            // 调用目标方法
            Object invoke = method.invoke(target, args1);
            System.out.println("after");
            return invoke;
        });

        proxyInstance.foo();
    }
}

Cglib增强

  1. Cglib创建的是子类对父类进行增强,所以如果父类定义为final,创建子类就会报错。

在这里插入图片描述

  1. 如果需要增强的方法定义为final,那么方法无法被增强。
/**
 * Cglib增强
 */
public class CglibProxy {
    static final class Target {
        public void foo() {
            System.out.println("foo...");
        }
    }

    public static void main(String[] args) {
        Target target = new Target();
        /**
         * 参数1:生成代理对象的父类
         * 参数2:代理的执行行为
         */
        Target proxy = (Target) Enhancer.create(Target.class, new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("before");
                // 使用反射执行目标方法
                // Object invoke = method.invoke(target, args);
                // 使用methodProxy该对象,底层不使用反射调用方法,需要传入目标类(Spring使用这种)
                // Object invoke = methodProxy.invoke(target, args);
                // 底层不使用反射,传入的是代理类
                Object invoke = methodProxy.invokeSuper(o, args);
                System.out.println("before");
                return invoke;
            }
        });

        proxy.foo();
    }
}

自定义JDK代理

  1. jdk代理是基于接口的。
/**
 * 自定义JDK代理实现
 */
public class MyJdkProxy {
    interface Foo {
        void foo();
        int bar();
    }

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

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

    interface InvocationHandler {
        Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
    }

    public static void main(String[] args) {
        // 创建一个代理类
        Foo proxy = new $Proxy0(new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("before");
                if (method.getName().equals("bar")) {
                    // 反射调用
                    Object invoke = method.invoke(new Target(), args);
                    System.out.println((int) invoke);
                    return (int) invoke + 50;
                }
                return null;
            }
        });
        proxy.foo();
        System.out.println(proxy.bar());
    }
}
  1. 自定义代理类
/**
 * 直接自定义写死一个代理类
 */
public class $Proxy0 implements MyJdkProxy.Foo {
    private final MyJdkProxy.InvocationHandler invocationHandler;

    static Method foo;
    static Method bar;

    static {
        try {
            // 避免每次调用方法都执行一次反射获取
            foo = MyJdkProxy.Target.class.getMethod("foo");
            bar = MyJdkProxy.Target.class.getMethod("bar");
        } catch (NoSuchMethodException e) {
            throw new NoSuchMethodError(e.getMessage());
        }
    }

    public $Proxy0(MyJdkProxy.InvocationHandler invocationHandler) {
        this.invocationHandler = invocationHandler;
    }

    public void foo() {
        try {
            Method foo = MyJdkProxy.Foo.class.getMethod("foo");
            invocationHandler.invoke(this, foo, new Object[0]);
        } catch (RuntimeException | Error e) {
            // 运行时异常直接抛出
            throw e;
        } catch (Throwable e) {
            // 检查异常包装后抛出
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public int bar() {
        try {
            Method bar = MyJdkProxy.Foo.class.getMethod("bar");
            Object invoke = invocationHandler.invoke(this, bar, new Object[0]);
            return (int) invoke;
        } catch (RuntimeException | Error e) {
            // 运行时异常直接抛出
            throw e;
        } catch (Throwable e) {
            // 检查异常包装后抛出
            throw new UndeclaredThrowableException(e);
        }
    }
}

arthas查看JDK生成的代理类

  1. 使用原来JDK代理的demo,和arthas工具反编译代理类,查看jdk生成的代理类。并且程序不能停止。
/**
 * 演示JDK代理
 * JDK只能代理接口
 */
public class JdkProxy {

    interface Foo {
        void foo();
    }

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

    public static void main(String[] args) throws IOException {
        ClassLoader classLoader = JdkProxy.class.getClassLoader();
        /**
         * 参数1:类加载器,因为代理类是在运行时生成的,所以需要类加载器将代理类加载到JVM中
         * 参数2:代理类实现的接口
         * 参数3:handler处理增强的方法
         * 返回一个接口的代理对象
         */

        Target target = new Target();

        Foo proxyInstance = (Foo) Proxy.newProxyInstance(classLoader, new Class[]{Foo.class}, (proxy, method, args1) -> {
            System.out.println("before");
            // 调用目标方法
            Object invoke = method.invoke(target, args1);
            System.out.println("after");
            return invoke;
        });

        // 这里需要打印出类,方便使用arthas工具反编译类
        System.out.println(proxyInstance.getClass());
        proxyInstance.foo();
        
        // 让程序暂停,方便使用arthas工具
        System.in.read();
    }
}

在这里插入图片描述

  1. 打开arthas工具。java -jar arthas-boot.jar

在这里插入图片描述

  1. 连接上java程序后,输入jad aop.service.$Proxy0就可以反编译该代理类。
ClassLoader:
+-sun.misc.Launcher$AppClassLoader@18b4aac2
  +-sun.misc.Launcher$ExtClassLoader@2bdd5e07

Location:

/*
 * Decompiled with CFR.
 *
 * Could not load the following classes:
 *  aop.service.JdkProxy$Foo
 */
package aop.service;

import aop.service.JdkProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

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

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

    static {
        try {
            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("aop.service.JdkProxy$Foo").getMethod("foo", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", 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);
        }
    }
}
  1. 继承了Proxy类,因为InvocationHandler是Proxy类的成员变量,从父类继承过来。
  2. JDK生成的代理类,没有经过源码 -> 编译 -> 字节码的阶段。而是直接生成了代理类的字节码,我们得到的是字节码反编译后的代码。
  3. 运行期间动态生成字节码的底层技术叫ASM,广泛应用于Spring和JDK等框架。

Idea安装ASM插件生成字节码

  1. JDK生成代理类,没有经过源码 -> 编译 -> 字节码的阶段,而是直接生成了代理类的字节码
  2. 运行期间动态生成字节码的技术叫ASM
  3. 在Idea安装一个ASM的插件,ASM Bytecode Outline

在这里插入图片描述

  1. 定义接口,和手写代理类
public interface Foo {
    void foo();
}
/**
 * 这里继承父类Proxy,直接使用父类的成员变量:InvocationHandler h
 */
public class $Proxy0 extends Proxy implements Foo{
    static Method foo;
    static {
        try {
            foo = Foo.class.getMethod("foo");
        } catch (NoSuchMethodException e) {
            throw new NoSuchMethodError(e.getMessage());
        }
    }

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

    @Override
    public void foo() {
        try {
            System.out.println("before...");
            Object invoke = this.h.invoke(this, foo, new Object[]{});
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
}
  1. 将两个类编译一下。

在这里插入图片描述

  1. 然后使用ASM的插件,将代理类的Java源码转化为ASM的代码

在这里插入图片描述

  1. 第二个就是ASM的API可以生成代理类的字节码。

在这里插入图片描述

  1. 将该类粘贴出来,并导入Spring相关ASM的包

在这里插入图片描述

public class $Proxy0Dump implements Opcodes {

    public static byte[] dump() throws Exception {

        ClassWriter cw = new ClassWriter(0);
        FieldVisitor fv;
        MethodVisitor mv;
        AnnotationVisitor av0;

        cw.visit(52, ACC_PUBLIC + ACC_SUPER, "aop/my/asm/$Proxy0", null, "java/lang/reflect/Proxy", new String[]{"aop/my/asm/Foo"});

        cw.visitSource("$Proxy0.java", null);

        {
            fv = cw.visitField(ACC_STATIC, "foo", "Ljava/lang/reflect/Method;", null, null);
            fv.visitEnd();
        }
        {
            mv = cw.visitMethod(ACC_PROTECTED, "<init>", "(Ljava/lang/reflect/InvocationHandler;)V", null, null);
            mv.visitCode();
            Label l0 = new Label();
            mv.visitLabel(l0);
            mv.visitLineNumber(22, l0);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/reflect/Proxy", "<init>", "(Ljava/lang/reflect/InvocationHandler;)V", false);
            Label l1 = new Label();
            mv.visitLabel(l1);
            mv.visitLineNumber(23, l1);
            mv.visitInsn(RETURN);
            Label l2 = new Label();
            mv.visitLabel(l2);
            mv.visitLocalVariable("this", "Laop/my/asm/$Proxy0;", null, l0, l2, 0);
            mv.visitLocalVariable("h", "Ljava/lang/reflect/InvocationHandler;", null, l0, l2, 1);
            mv.visitMaxs(2, 2);
            mv.visitEnd();
        }
        {
            mv = cw.visitMethod(ACC_PUBLIC, "foo", "()V", null, null);
            mv.visitCode();
            Label l0 = new Label();
            Label l1 = new Label();
            Label l2 = new Label();
            mv.visitTryCatchBlock(l0, l1, l2, "java/lang/Throwable");
            mv.visitLabel(l0);
            mv.visitLineNumber(28, l0);
            mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            mv.visitLdcInsn("before...");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
            Label l3 = new Label();
            mv.visitLabel(l3);
            mv.visitLineNumber(29, l3);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitFieldInsn(GETFIELD, "aop/my/asm/$Proxy0", "h", "Ljava/lang/reflect/InvocationHandler;");
            mv.visitVarInsn(ALOAD, 0);
            mv.visitFieldInsn(GETSTATIC, "aop/my/asm/$Proxy0", "foo", "Ljava/lang/reflect/Method;");
            mv.visitInsn(ICONST_0);
            mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
            mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/reflect/InvocationHandler", "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;", true);
            mv.visitVarInsn(ASTORE, 1);
            mv.visitLabel(l1);
            mv.visitLineNumber(32, l1);
            Label l4 = new Label();
            mv.visitJumpInsn(GOTO, l4);
            mv.visitLabel(l2);
            mv.visitLineNumber(30, l2);
            mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]{"java/lang/Throwable"});
            mv.visitVarInsn(ASTORE, 1);
            Label l5 = new Label();
            mv.visitLabel(l5);
            mv.visitLineNumber(31, l5);
            mv.visitTypeInsn(NEW, "java/lang/reflect/UndeclaredThrowableException");
            mv.visitInsn(DUP);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/reflect/UndeclaredThrowableException", "<init>", "(Ljava/lang/Throwable;)V", false);
            mv.visitInsn(ATHROW);
            mv.visitLabel(l4);
            mv.visitLineNumber(33, l4);
            mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
            mv.visitInsn(RETURN);
            Label l6 = new Label();
            mv.visitLabel(l6);
            mv.visitLocalVariable("e", "Ljava/lang/Throwable;", null, l5, l4, 1);
            mv.visitLocalVariable("this", "Laop/my/asm/$Proxy0;", null, l0, l6, 0);
            mv.visitMaxs(4, 2);
            mv.visitEnd();
        }
        {
            mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
            mv.visitCode();
            Label l0 = new Label();
            Label l1 = new Label();
            Label l2 = new Label();
            mv.visitTryCatchBlock(l0, l1, l2, "java/lang/NoSuchMethodException");
            mv.visitLabel(l0);
            mv.visitLineNumber(15, l0);
            mv.visitLdcInsn(Type.getType("Laop/my/asm/Foo;"));
            mv.visitLdcInsn("foo");
            mv.visitInsn(ICONST_0);
            mv.visitTypeInsn(ANEWARRAY, "java/lang/Class");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", false);
            mv.visitFieldInsn(PUTSTATIC, "aop/my/asm/$Proxy0", "foo", "Ljava/lang/reflect/Method;");
            mv.visitLabel(l1);
            mv.visitLineNumber(18, l1);
            Label l3 = new Label();
            mv.visitJumpInsn(GOTO, l3);
            mv.visitLabel(l2);
            mv.visitLineNumber(16, l2);
            mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]{"java/lang/NoSuchMethodException"});
            mv.visitVarInsn(ASTORE, 0);
            Label l4 = new Label();
            mv.visitLabel(l4);
            mv.visitLineNumber(17, l4);
            mv.visitTypeInsn(NEW, "java/lang/NoSuchMethodError");
            mv.visitInsn(DUP);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/NoSuchMethodException", "getMessage", "()Ljava/lang/String;", false);
            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/NoSuchMethodError", "<init>", "(Ljava/lang/String;)V", false);
            mv.visitInsn(ATHROW);
            mv.visitLabel(l3);
            mv.visitLineNumber(19, l3);
            mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
            mv.visitInsn(RETURN);
            mv.visitLocalVariable("e", "Ljava/lang/NoSuchMethodException;", null, l4, l3, 0);
            mv.visitMaxs(3, 1);
            mv.visitEnd();
        }
        cw.visitEnd();

        return cw.toByteArray();
    }
}
  1. 调用该类的dump静态方法,就可以生成字节码文件。
public class TestProxy {
    public static void main(String[] args) throws Exception {
        // 直接调用该类的静态方法,就可以得到二进制的字节码
        byte[] dump = $Proxy0Dump.dump();

        FileOutputStream fileOutputStream = new FileOutputStream("proxy.class");
        fileOutputStream.write(dump, 0, dump.length);
        fileOutputStream.close();
    }
}
  1. 我们可以看到字节码就是我们编写的代理类。

在这里插入图片描述

  1. 动态加载字节码
public class TestProxy {
    public static void main(String[] args) throws Exception {
        // 直接调用该类的静态方法,就可以得到二进制的字节码
        byte[] dump = $Proxy0Dump.dump();

//        FileOutputStream fileOutputStream = new FileOutputStream("proxy.class");
//        fileOutputStream.write(dump, 0, dump.length);
//        fileOutputStream.close();

        // 将字节码加载到内存中
        ClassLoader classLoader = new ClassLoader() {
            @Override
            protected Class<?> findClass(String name) throws ClassNotFoundException {
                System.out.println(name);
                return super.defineClass(name, dump, 0, dump.length);
            }
        };

        Class<?> proxyClass = classLoader.loadClass("aop.my.asm.$Proxy0");
        // 拿到代理类的构造方法
        Constructor<?> constructor = proxyClass.getDeclaredConstructor(InvocationHandler.class);
        constructor.setAccessible(true);
        Foo proxy = (Foo) constructor.newInstance(new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("step1...");
                System.out.println("调用目标方法");
                return null;
            }
        });

        proxy.foo();
    }
}

JDK底层对方法反射执行的优化

  1. JDK代理中,使用了很多反射调用方法method.invoke()。JDK底层对反射的执行有一定的优化。
  2. 测试代码:
/**
 * JDK底层对方法反射执行的优化
 */
public class ReflectOptimize {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException {
        TestReflect testReflect = new TestReflect();
        Method foo = TestReflect.class.getMethod("foo", int.class);
        for (int i = 1; i <= 16; i++) {
            // 反射调用方法16次
            foo.invoke(testReflect, i);
            show(foo, i);
        }

        // 暂停程序,使用arthas工具查看
        System.in.read();
    }

    /**
     * 打印JDK底层实现,对反射执行方法的优化
     */
    private static void show(Method foo, int i) throws IllegalAccessException {
        try {
            Class<? extends Method> aClass = foo.getClass();
            Field methodAccessor = aClass.getDeclaredField("methodAccessor");
            methodAccessor.setAccessible(true);
            MethodAccessor accessor = (MethodAccessor) methodAccessor.get(foo);
            if (accessor != null) {
                Class<? extends MethodAccessor> aClass1 = accessor.getClass();
                Field delegate = aClass1.getDeclaredField("delegate");
                delegate.setAccessible(true);
                Object impl = delegate.get(accessor);
                System.out.println("i = " + i + " = , " + impl);
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

    static class TestReflect {
        public void foo(int i) {
            System.out.println("i = " + i + ", foo...");
        }
    }
}
  1. 输出如下:
i = 1, foo...
i = 1 = , sun.reflect.NativeMethodAccessorImpl@7291c18f
i = 2, foo...
i = 2 = , sun.reflect.NativeMethodAccessorImpl@7291c18f
i = 3, foo...
i = 3 = , sun.reflect.NativeMethodAccessorImpl@7291c18f

...

i = 15, foo...
i = 15 = , sun.reflect.NativeMethodAccessorImpl@7291c18f
i = 16, foo...
i = 16 = , sun.reflect.GeneratedMethodAccessor1@6e8cf4c6

  1. 可以看到,15次之前都是使用的JDK反射执行,16次时,生成了一个代理类执行反射方法。
  2. 使用arthas工具查看该代理类,jad sun.reflect.GeneratedMethodAccessor1
  3. 可以看到如下:JDK在15次之后生成一个代理类,直接调用该方法,不使用反射,提高效率

在这里插入图片描述

  1. 源码如下:

在这里插入图片描述

Cglib代理原理

  1. Cglib代理类是目标类的子类
  2. 所以目标类是final的不能代理,方法是final的代理会失效
  3. 创建一个目标类
/**
 * 父类
 */
public class Target {
    public void save() {
        System.out.println("save");
    }

    public void save(int i) {
        System.out.println("save i = " + i);
    }
}
  1. 创建一个代理类
/**
 * 模仿Cglib生成一个代理类
 * 代理类是目标类的子类
 */
public class Proxy extends Target{

    static Method save0;
    static Method save1;

    static {
        try {
            save0 = Target.class.getMethod("save");
            save1 = Target.class.getMethod("save", int.class);
        } catch (NoSuchMethodException e) {
            throw new NoSuchMethodError(e.getMessage());
        }
    }

    // 回调方法
    private MethodInterceptor methodInterceptor;

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

    @Override
    public void save() {
        try {
            methodInterceptor.intercept(this, save0, new Object[0], null);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public void save(int i) {
        try {
            methodInterceptor.intercept(this, save1, new Object[]{i}, null);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
}
  1. 测试类:
public class TestProxy {
    public static void main(String[] args) {
        Proxy proxy = new Proxy();
        Target target = new Target();
        proxy.setMethodInterceptor(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("before...");
                return method.invoke(target, args);
            }
        });

        proxy.save();
        proxy.save(10);
    }
}
  1. 在Cglib中,也有对JDK反射的优化,就是MethodProxy类。
  2. 生成MethodProxy类。
/**
 * 模仿Cglib生成一个代理类
 * 代理类是目标类的子类
 */
public class Proxy extends Target{

    static Method save0;
    static Method save1;

    static MethodProxy methodProxy0;
    static MethodProxy methodProxy1;

    static {
        try {
            save0 = Target.class.getMethod("save");
            save1 = Target.class.getMethod("save", int.class);

            /**
             * 创建两个MethodProxy对象
             * 参数1:目标类class
             * 参数2:代理类class
             * 参数3:()V, 表示没有参数,返回值是void
             * 参数4:增强后的方法
             * 参数5:调用原方法
             */
            methodProxy0 = MethodProxy.create(Target.class, Proxy.class, "()V", "save", "saveSuper");
            // (I)V表示带一个int参数
            methodProxy1 = MethodProxy.create(Target.class, Proxy.class, "(I)V", "save", "saveSuper");

        } catch (NoSuchMethodException e) {
            throw new NoSuchMethodError(e.getMessage());
        }
    }

    // 回调方法
    private MethodInterceptor methodInterceptor;

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

    // >>>>>>>>>>>>>>>>>>>>>>>>调用原方法
    public void saveSuper() {
        super.save();
    }

    public void saveSuper(int i) {
        super.save(i);
    }

    // >>>>>>>>>>>>>>>>>>>>>>>>增强后的方法
    @Override
    public void save() {
        try {
            methodInterceptor.intercept(this, save0, new Object[0], methodProxy0);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public void save(int i) {
        try {
            methodInterceptor.intercept(this, save1, new Object[]{i}, methodProxy1);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
}
  1. 不使用反射调用
public class TestProxy {
    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...");
                // 反射调用
//                method.invoke(target, args);
                // 不使用反射调用
//                Object invoke = methodProxy.invoke(target, args);
                // 不使用反射调用,传入代理类
                Object invoke = methodProxy.invokeSuper(p, args);
                return invoke;
            }
        });

        proxy.save();
        proxy.save(10);
    }
}

MethodProxy原理

  1. JDK反射的性能不好,Cglib使用MethodProxy类的两个方法,代替了反射。
// 传入目标类
Object invoke = methodProxy.invoke(target, args);
// 传入代理类
Object invoke = methodProxy.invokeSuper(p, args);
  1. Cglib底层是如何实现不通过反射调用的?为这两个方法分别生成了两个代理类,代替反射。
  2. 这两个代理类都继承自FastClass类(抽象类)。

在这里插入图片描述

  1. 模拟传入目标类的方法使用的代理类。
public class TargetFastClass {

    /**
     * 定义两个签名
     */
    static Signature s0 = new Signature("save", "()V");
    static Signature s1 = new Signature("save", "(I)V");

    /**
     * 规定方法签名的下标如下:
     * save()           0
     * save(int i)      1
     * @param signature 方法签名
     * @return
     */
    public int getIndex(Signature signature) {
        if (s0.equals(signature)) {
            return 0;
        } else if (s1.equals(signature)) {
            return 1;
        }

        return -1;
    }

    /**
     * 就是methodProxy.invoke(target, args)方法
     * 传递过来执行的方法
     * @param index 方法下标,可以确定调用的那个方法
     * @param target 目标类
     * @param args 参数
     * @return
     */
    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 {
            throw new RuntimeException("未找到方法");
        }
    }
}
  1. 模拟传入代理的方法执行。
/**
 * 基于代理类的MethodProxy执行
 */
public class ProxyFastClass {
    /**
     * 定义两个签名
     */
    static Signature s0 = new Signature("saveSuper", "()V");
    static Signature s1 = new Signature("saveSuper", "(I)V");

    /**
     * 规定方法签名的下标如下:
     * saveSuper()           0
     * saveSuper(int i)      1
     * @param signature 方法签名
     * @return
     */
    public int getIndex(Signature signature) {
        if (s0.equals(signature)) {
            return 0;
        } else if (s1.equals(signature)) {
            return 1;
        }

        return -1;
    }

    /**
     * 就是methodProxy.invoke(target, args)方法
     * 传递过来执行的方法
     * @param index 方法下标,可以确定调用的那个方法
     * @param proxy 代理类
     * @param args 参数
     * @return
     */
    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 {
            throw new RuntimeException("未找到方法");
        }
    }
}
  1. 在执行这两个方法时,会创建FastClass的两个代理类,执行本身的方法,避免反射。
// 传入目标类
Object invoke = methodProxy.invoke(target, args);
// 传入代理类
Object invoke = methodProxy.invokeSuper(p, args);

在这里插入图片描述

Spring代理选择

/**
 * 两个切面的概念
 * aspect:
 *  通知1(advice) + 切点1(pointcut)
 *  通知2(advice) + 切点2(pointcut)
 *
 * advisor:
 *  更细粒度的切面,包含一个通知和切点
 */
public class MyAdvisor {
    public static void main(String[] args) {
        // 准备一个切点
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* foo())");

        // 准备一个通知:环绕通知
        MethodInterceptor advice = new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                System.out.println("before");
                // 调用目标
                Object res = invocation.proceed();
                System.out.println("after");
                return res;
            }
        };

        // 准备切面
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);

        /**
         * proxyTargetClass = false 目标类实现了接口(setInterfaces参数设置),用JDK代理
         * proxyTargetClass = false 未实现接口,用Cglib代理
         * proxyTargetClass = true  总是使用Cglib代理
         */
        // 工厂创建代理,Spring会选择JDK或Cglib的代理
        ProxyFactory proxyFactory = new ProxyFactory();
        // 目标类
        Target target = new Target();
        // 给代理设置目标类
        proxyFactory.setTarget(target);
        // 设置目标类实现了那些接口:不设置的话,就不知道实现了接口
        proxyFactory.setInterfaces(target.getClass().getInterfaces());
        // 总是使用Cglib生成代理类
        proxyFactory.setProxyTargetClass(true);
        // 加入切面
        proxyFactory.addAdvisor(advisor);

        // 创建代理对象
        I1 proxy = (I1) proxyFactory.getProxy();
        // 查看使用的那种代理
        System.out.println(proxy.getClass());
        proxy.foo();
        proxy.bar();
    }

    interface I1 {
        void foo();
        void bar();
    }

    static class Target implements I1 {

        @Override
        public void foo() {
            System.out.println("foo()...");
        }

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

切点匹配和@Transcation解析

  1. 匹配切点底层都实现了MethodMatcher接口,用来执行方法的匹配。
/**
 * AspectJExpressionPointcut:切点的匹配
 * 以及Transactional注解的解析
 */
public class PointcutTest {
    public static void main(String[] args) throws NoSuchMethodException {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        // 设置切点,使用方法匹配
        pointcut.setExpression("execution(* bar())");
        // false
        System.out.println(pointcut.matches(T1.class.getMethod("foo"), T1.class));
        // true
        System.out.println(pointcut.matches(T1.class.getMethod("bar"), T1.class));

        AspectJExpressionPointcut pointcut2 = new AspectJExpressionPointcut();
        // 使用注解匹配
        pointcut2.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");
        // true
        System.out.println(pointcut2.matches(T1.class.getMethod("foo"), T1.class));
        // false
        System.out.println(pointcut2.matches(T1.class.getMethod("bar"), T1.class));

        /**
         * Spring对事务的注解Transactional,不是上面的方法进行判断。
         * 因为@Transactional可以标注在类上,也可以标注在目标类实现的接口上,也可以进行事务增强
         * 模拟Spring对@Transactional的解析,继承该类StaticMethodMatcherPointcut
         */
        StaticMethodMatcherPointcut pointcut3 = new StaticMethodMatcherPointcut() {
            @Override
            public boolean matches(Method method, Class<?> targetClass) {
                // 判断该方法上有没有标注Transactional注解
                // 使用Spring工具类MergedAnnotations
                MergedAnnotations annotations = MergedAnnotations.from(method);
                if (annotations.isPresent(Transactional.class)) {
                    return true;
                }

                // 判断类上: TYPE_HIERARCHY参数表示需要查找父类以及接口
                annotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
                if (annotations.isPresent(Transactional.class)) {
                    return true;
                }
                return false;
            }
        };

        System.out.println("-------------------");
        // true
        System.out.println(pointcut3.matches(T2.class.getMethod("foo"), T2.class));
        // true
        System.out.println(pointcut3.matches(T3.class.getMethod("foo"), T3.class));
    }

    static class T1 {
        @Transactional
        public void foo() {};
        public void bar() {};
    }

    @Transactional
    static class T2 {
        public void foo() {};
    }

    @Transactional
    interface I1 {
    }

    static class T3 implements I1{
        public void foo() {};
    }
}

Advisor和Aspect的解析过程

/**
 * 模拟两种切面解析的过程:
 * 1. AnnotationAwareAspectJAutoProxyCreator是一个BeanPostProcessor的后置处理器
 * 2. 在初始化后期,对bean进行增强
 * 3. 先判断bean有没有匹配的切面
 * 4. 有的话就创建代理对象增强
 */
public class MyAdvisorAndAspect {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        GenericApplicationContext applicationContext = new GenericApplicationContext();
        applicationContext.registerBean("aspect1", Aspect1.class);
        applicationContext.registerBean("config", Config.class);
        // 解析Configuration注解以及Bean注解等
        applicationContext.registerBean(ConfigurationClassPostProcessor.class);
        // 加入一个BeanPostProcessor的后置处理器,在初始化后期,解析Aspect注解,并创建代理
        applicationContext.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);

        // 创建Bean
        applicationContext.refresh();

        // 查看容器中有那些Bean
//        for (String beanDefinitionName : applicationContext.getBeanDefinitionNames()) {
//            System.out.println(beanDefinitionName);
//        }

        /**
         * AnnotationAwareAspectJAutoProxyCreator类
         * 第一个重要方法:findCandidateAdvisors,找到满足条件的切面
         * 一部分是低级的Advisor,自己编写获得,如advisor3
         * 一部分是高级的,由Aspect解析获取
         */
        AnnotationAwareAspectJAutoProxyCreator creator = applicationContext.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
        Class<? extends AnnotationAwareAspectJAutoProxyCreator> aClass = creator.getClass();
        Class<?> superclass = aClass.getSuperclass();
        Class<?> superclass1 = superclass.getSuperclass();
        Method findCandidateAdvisors = superclass1.getDeclaredMethod("findEligibleAdvisors", Class.class, String.class);
        findCandidateAdvisors.setAccessible(true);
        System.out.println(findCandidateAdvisors);
        /**
         * 两个参数:
         * Target1.class:将这个类相关的切面返回
         * target1:该类没有交给容器,暂时无用
         */
        List<Advisor> advisorList = (List<Advisor>) findCandidateAdvisors.invoke(creator, Target1.class, "target1");
        for (Advisor advisor : advisorList) {
            System.out.println(advisor);
        }

        /**
         * 返回了4个切面:我们一共加了两个切面,为什么返回四个
         *
         * Spring给所有代理都要加的切面
         * org.springframework.aop.interceptor.ExposeInvocationInterceptor.ADVISOR
         * 加的那个低级的切面
         * org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [aop.aop.MyAdvisorAndAspect$Config$1@385e9564]
         * 高级切面的一个方法,转化为一个低级切面
         * InstantiationModelAwarePointcutAdvisor: expression [execution(* foo())]; advice method [public void aop.aop.MyAdvisorAndAspect$Aspect1.before()]; perClauseKind=SINGLETON
         * 高级切面的方法
         * InstantiationModelAwarePointcutAdvisor: expression [execution(* foo())]; advice method [public void aop.aop.MyAdvisorAndAspect$Aspect1.after()]; perClauseKind=SINGLETON
         */

        /**
         * 第二个重要方法:wrapIfNecessary
         * 判断是否需要创建代理
         * 内部调用了第一个方法findCandidateAdvisors,如果返回了有切面,就需要创建代理
         */
        Class<?> superclass2 = superclass1.getSuperclass();
        Method wrapIfNecessary = superclass2.getDeclaredMethod("wrapIfNecessary", Object.class, String.class, Object.class);
        wrapIfNecessary.setAccessible(true);
        // 如果有切面就根据Target1创建一个代理
        Object target1 = wrapIfNecessary.invoke(creator, new Target1(), "target1", "target1");
        Object target2 = wrapIfNecessary.invoke(creator, new Target2(), "target1", "target1");
        // 判断是否创建代理,target2没有切面,不会创建代理
        System.out.println(target1.getClass()); // class aop.aop.MyAdvisorAndAspect$Target1$$EnhancerBySpringCGLIB$$1062fea0
        System.out.println(target2.getClass());

        // 调用代理的方法,增强
        ((Target1)target1).foo();
    }

    static class Target1 {
        public void foo() {
            System.out.println("target1 foo");
        }
    }

    static class Target2 {
        public void bar() {
            System.out.println("target2 bar");
        }
    }

    /**
     * 定义一个高级的切面类
     */
    @Aspect
    static class Aspect1 {
        @Before("execution(* foo())")
        public void before() {
            System.out.println("aspect1 before");
        }

        @After("execution(* foo())")
        public void after() {
            System.out.println("aspect1 after");
        }
    }

    /**
     * 创建一个低级的切面,Spring底层使用的切面,一般用户不用
     */
    @Configuration
    static class Config {
        @Bean
        public Advisor advisor3(MethodInterceptor advice3) {
            // 创建一个切点
            AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
            pointcut.setExpression("execution(* foo())");

            return new DefaultPointcutAdvisor(pointcut, advice3);
        }

        @Bean
        public MethodInterceptor advice3() {
            return new MethodInterceptor() {
                @Override
                public Object invoke(MethodInvocation invocation) throws Throwable {
                    System.out.println("advice3 before");
                    Object proceed = invocation.proceed();
                    System.out.println("advice3 after");
                    return proceed;
                }
            };
        }
    }
}

代理的创建时机

  1. 在初始化之后创建代理对象。(无循环依赖的时候)
  2. 在依赖注入之前创建代理对象。(有循环依赖的时候)
/**
 * 演示代理创建的时机
 */
public class ProxyCreateTime {
    public static void main(String[] args) {
        GenericApplicationContext applicationContext = new GenericApplicationContext();
        applicationContext.registerBean(ConfigurationClassPostProcessor.class);
        applicationContext.registerBean(Config.class);
        applicationContext.refresh();
        applicationContext.close();
    }

    /**
     * 代理的创建时机:
     * 1. 在初始化之后创建代理(无循环依赖)
     * 2. 在依赖注入之前创建代理(有循环依赖),并暂存于二级缓存
     *
     * 依赖注入和初始化方法不应该被增强
     */

    @Configuration
    static class Config {
        /**
         * 解析低级切面Advisor和高级切面Aspect的注解
         * 并且创建代理对象
         * @return
         */
        @Bean
        public AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator() {
            return new AnnotationAwareAspectJAutoProxyCreator();
        }

        /**
         * 解析Autowired
         * @return
         */
        @Bean
        public AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor() {
            return new AutowiredAnnotationBeanPostProcessor();
        }

        /**
         * 解析PostConstruct
         * @return
         */
        @Bean
        public CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor() {
            return new CommonAnnotationBeanPostProcessor();
        }

        /**
         * 加入一个低级的切面
         */
        @Bean
        public Advisor advisor(MethodInterceptor advice) {
            // 创建切点和切点表达式
            AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
            pointcut.setExpression("execution(* foo())");
            return new DefaultPointcutAdvisor(pointcut, advice);
        }

        @Bean
        public MethodInterceptor advice() {
            return new MethodInterceptor() {
                @Override
                public Object invoke(MethodInvocation invocation) throws Throwable {
                    System.out.println("before...");
                    return invocation.proceed();
                }
            };
        }

        @Bean
        public Target1 target1() {
            return new Target1();
        }

        @Bean
        public Target2 target2() {
            return new Target2();
        }
    }

    /**
     * Target1有foo方法,会被切面增强,会在初始化之后创建代理
     */
    static class Target1 {
        public Target1() {
            System.out.println("target1 construct");
        }

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

        /**
         * 构造循环依赖
         */
//        @Autowired
//        public void setTarget2(Target2 target2) {
//            System.out.println(target2.getClass());
//        }

        @PostConstruct
        public void init() {
            System.out.println("target1 init...");
        }
    }

    /**
     * Target2没有foo方法,不会被切面增强,不会创建代理对象
     */
    static class Target2 {
        public Target2() {
            System.out.println("target2 construct");
        }

        @Autowired
        public void setTarget1(Target1 target1) {
            // 打印注入target1,是代理类还是原生类
            System.out.println(target1.getClass());
        }

        @PostConstruct
        public void init() {
            System.out.println("target2 init...");
        }
    }
}

高级切面转低级切面

  1. 开发的时候一般使用高级切面Aspect,Spring底层会转化为低级切面。
/**
 * 高级的Aspect切面转化为Spring内部使用的低级的Advisor切面
 */
public class Aspect2Advisor {

    static class Aspect {
        @Before("execution(* foo())")
        public void before1() {
            System.out.println("before1");
        }

        @After("execution(* foo())")
        public void After() {
            System.out.println("After");
        }
    }

    public static void main(String[] args) {
        // 反射调用切面方法时的目标对象,封装成一个工厂类
        AspectInstanceFactory factory = new SimpleAspectInstanceFactory(Aspect.class);

        /**
         * 拿到切面所有的方法
         */
        for (Method declaredMethod : Aspect.class.getDeclaredMethods()) {
            // 解析前置通知方法,转化为Advisor类
            if (declaredMethod.isAnnotationPresent(Before.class)) {
                // 如果该方法有前置通知注解
                Before before = declaredMethod.getAnnotation(Before.class);
                // 拿到注解上的切点表达式
                String execution = before.value();
                // 创建一个切点
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(execution);
                // 创建前置通知类
                AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(declaredMethod, pointcut, factory);
                // 创建切面
                DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
                System.out.println(advisor);
            }

            // 解析后置通知方法,转化为Advisor类
            if (declaredMethod.isAnnotationPresent(After.class)) {
                // 如果该方法有后置通知注解
                After after = declaredMethod.getAnnotation(After.class);
                // 拿到注解上的切点表达式
                String execution = after.value();
                // 创建一个切点
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(execution);
                // 创建前置通知类
                AspectJAfterAdvice advice = new AspectJAfterAdvice(declaredMethod, pointcut, factory);
                // 创建切面
                DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
                System.out.println(advisor);
            }
        }
    }
}

在这里插入图片描述

所有切面转化为环绕通知后调用链执行

/**
 * 高级的Aspect切面转化为Spring内部使用的低级的Advisor切面
 * 前置通知、后置通知、异常通知、返回通知都会被转化为环绕通知MethodInterceptor
 * 然后使用调用链MethodInvocation执行
 */
public class Aspect2Advisor {

    static class Aspect {
        @Before("execution(* foo())")
        public void before1() {
            System.out.println("before1");
        }

        @After("execution(* foo())")
        public void after() {
            System.out.println("After");
        }

        @AfterReturning("execution(* foo())")
        public void afterReturning() {
            System.out.println("afterReturning");
        }

        /**
         * 环绕通知需要返回,才能执行到目标方法
         * @param joinPoint
         * @return
         * @throws Throwable
         */
        @Around("execution(* foo())")
        public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
            System.out.println("around before");
            Object proceed = joinPoint.proceed();
            System.out.println("around after");

            return proceed;
        }
    }

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

    public static void main(String[] args) throws Throwable {
        /**
         * 第一步:将Aspect切面类转化为低级的Advisor
         */

        // 反射调用切面方法时的目标对象,封装成一个工厂类
        AspectInstanceFactory factory = new SimpleAspectInstanceFactory(Aspect.class);
        List<Advisor> list = new ArrayList<>();
        /**
         * 拿到切面所有的方法
         */
        for (Method declaredMethod : Aspect.class.getDeclaredMethods()) {
            // 解析前置通知方法,转化为Advisor类
            if (declaredMethod.isAnnotationPresent(Before.class)) {
                // 如果该方法有前置通知注解
                Before before = declaredMethod.getAnnotation(Before.class);
                // 拿到注解上的切点表达式
                String execution = before.value();
                // 创建一个切点
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(execution);
                // 创建前置通知类
                AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(declaredMethod, pointcut, factory);
                // 创建切面
                DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
                list.add(advisor);
            } else if (declaredMethod.isAnnotationPresent(After.class)) {
                // 如果该方法有后置通知注解
                After after = declaredMethod.getAnnotation(After.class);
                // 拿到注解上的切点表达式
                String execution = after.value();
                // 创建一个切点
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(execution);
                // 创建前置通知类
                AspectJAfterAdvice advice = new AspectJAfterAdvice(declaredMethod, pointcut, factory);
                // 创建切面
                DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
                list.add(advisor);
            }else if (declaredMethod.isAnnotationPresent(AfterReturning.class)) {
                // 如果该方法有后置通知注解
                AfterReturning after = declaredMethod.getAnnotation(AfterReturning.class);
                // 拿到注解上的切点表达式
                String execution = after.value();
                // 创建一个切点
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(execution);
                // 创建前置通知类
                AspectJAfterReturningAdvice advice = new AspectJAfterReturningAdvice(declaredMethod, pointcut, factory);
                // 创建切面
                DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
                list.add(advisor);
            }else if (declaredMethod.isAnnotationPresent(Around.class)) {
                // 如果该方法有后置通知注解
                Around after = declaredMethod.getAnnotation(Around.class);
                // 拿到注解上的切点表达式
                String execution = after.value();
                // 创建一个切点
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(execution);
                // 创建前置通知类
                AspectJAroundAdvice advice = new AspectJAroundAdvice(declaredMethod, pointcut, factory);
                // 创建切面
                DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
                list.add(advisor);
            }
        }

        for (Advisor advisor : list) {
            System.out.println(advisor);
        }

        /**
         * 第二步:将通知统一转化为环绕通知 MethodInterceptor
         */
        Target target = new Target();
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(target);
        /**
         * 某些通知的内部会使用到调用链对象 MethodInvocation
         * 所以在调用链的最外层应该准备好调用链对象,放到当前线程
         * 在设置所有的环绕通知之前,先使用一个环绕通知ExposeInvocationInterceptor
         * 设置调用链对象 MethodInvocation
         * 否则后面通知内部要使用这个调用链对象时,要报错
         */
        proxyFactory.addAdvice(ExposeInvocationInterceptor.INSTANCE);
        proxyFactory.addAdvisors(list);

        System.out.println(">>>>>>>>>>>>>");
        List<Object> methodInterceptorList = proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo"), Target.class);
        for (Object o : methodInterceptorList) {
            System.out.println(o);
        }

        /**
         * 第三步:创建并执行调用链,所有的环绕通知 + 目标方法
         */
        System.out.println(">>>>>>>>>>>>>>>>>>>>>");
        Constructor<ReflectiveMethodInvocation> constructor = ReflectiveMethodInvocation.class.getDeclaredConstructor(Object.class, Object.class, Method.class, Object[].class, Class.class, List.class);
        constructor.setAccessible(true);
        // 创建一个调用链
        MethodInvocation methodInvocation = constructor.newInstance(null, target, Target.class.getMethod("foo"), new Object[0], Target.class, methodInterceptorList);
        methodInvocation.proceed();
//        MethodInvocation methodInvocation = new ReflectiveMethodInvocation(
//                null, target, Target.class.getMethod("foo"), new Object[0], Target.class, methodInterceptorList
//        );
    }
}

自定义执行环绕通知的责任链

/**
 * 自定义责任链,实现环绕通知的责任链
 */
public class MyChain {
    public static void main(String[] args) throws Throwable {
        Target target = new Target();
        ArrayList<MethodInterceptor> list = new ArrayList<>();
        list.add(new Advice1());
        list.add(new Advice2());

        MyInvocation invocation = new MyInvocation(target, Target.class.getMethod("foo"), args, list);
        invocation.proceed();
    }

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

    /**
     * 定义两个环绕通知
     */
    static class Advice1 implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("advice1 before");
            // 这里递归调用下一个环绕通知或目标方法
            Object proceed = invocation.proceed();
            System.out.println("advice1 after");
            return proceed;
        }
    }

    static class Advice2 implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("advice2 before");
            // 这里递归调用下一个环绕通知或目标方法
            Object proceed = invocation.proceed();
            System.out.println("advice2 after");
            return proceed;
        }
    }

    /**
     * 定义一个调用链类
     */
    static class MyInvocation implements MethodInvocation{
        // 调用的目标类
        private Object target;
        // 调用的目标方法
        private Method method;
        // 目标方法的参数
        private Object[] args;
        // 环绕通知列表
        private List<MethodInterceptor> methodInterceptorList;
        // 调用次数
        private int count = 1;

        public MyInvocation(Object target, Method method, Object[] args, List<MethodInterceptor> methodInterceptor) {
            this.target = target;
            this.method = method;
            this.args = args;
            this.methodInterceptorList = methodInterceptor;
        }

        @Override
        public Method getMethod() {
            return method;
        }

        @Override
        public Object[] getArguments() {
            return args;
        }

        @Override
        public Object proceed() throws Throwable {
            // 需要递归调用方法,递归结束条件
            if (count > methodInterceptorList.size()) {
                // 调用环绕通知结束,调用目标方法
                return method.invoke(target, args);
            }

            MethodInterceptor interceptor = methodInterceptorList.get(count++ - 1);
            return interceptor.invoke(this);
        }

        @Override
        public Object getThis() {
            return target;
        }

        @Override
        public AccessibleObject getStaticPart() {
            return method;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值