Skip to content
On this page

Spring 第六讲 AOP 动态代理

pom.xml 准备

xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.5</version>
        <relativePath/>
    </parent>

    <groupId>org.laoshiren</groupId>
    <artifactId>spring-bean-init</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>

        <aspectj.version>1.9.7</aspectj.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>


        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>


        <!-- 织入的依赖 -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>${aspectj.version}</version>
        </dependency>

        <!-- 运行时需要的依赖 -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>${aspectj.version}</version>
        </dependency>

    </dependencies>
</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

AOP

动态代理

java
public interface IService {
    void foo();
}

@Slf4j
public class MyService implements IService {

    @Override
    public void foo(){
        log.info(" service.foo() ");
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13

JDK

JDK 动态代理只能代理接口

java
@Slf4j
public class JdkProxyDemo {

    public static void main(String[] args) {
        IService service = new MyService();
        // 用来加载运行时动态生成的字节码
        ClassLoader classLoader = JdkProxyDemo.class.getClassLoader();
        // 需要代理的接口
        Class[] proxyClass = new Class[]{IService.class};
        // 增强的方法
        InvocationHandler invocationHandler = (objectProxy, method, methodArgs) -> {
            log.info("代理前置处理");
            Object invoke = method.invoke(service, methodArgs);
            log.info("代理后置处理");
            return invoke;
        };
        IService proxy = (IService) Proxy.newProxyInstance(classLoader, proxyClass, invocationHandler);
        proxy.foo();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
JDK 代理类内部原理

首先我们先定义一个接口,和对应的实现。

java
interface Foo {
    void foo();

    int bar();
}

@Slf4j
class Target implements Foo {
    @Override
    public void foo() {
        log.info("foo");
    }

    @Override
    public int bar() {
        log.info("bar");
        return 10;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

第一步就是需要JDK动态生成代理(假设下面就是我们生成的类,实际上JDK是通过ASM技术实现的)

java
@AllArgsConstructor
class $Proxy0 implements Foo {
    
    @Override
    public void foo() {
      
    }

    @Override
    public int bar() {
       
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

第二步,我们需代理类内部实现需要扩展我们方法,还需要顶一个接口去处理对应的逻辑

java
interface InvocationHandler {
    Object invoke();
}
1
2
3

第三修改我们的生成的$Proxy0

java
@AllArgsConstructor
class $Proxy0 implements Foo {
    
    private InvocationHandler invocationHandler;
    
    @Override
    public void foo() {
        invocationHandler.invoke();
    }

    @Override
    public int bar() {
        return (int) invocationHandler.invoke();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

然后把这个 交给我们的业务逻辑去处理

java
public class JdkProxyRationale {
    public static void main(String[] args) {
        Foo foo = new $Proxy0(() -> {
            System.out.println("before");
            Object invoke = method.invoke();
            System.out.println("after");
            return invoke;
        });
        foo.foo();
    }
}
1
2
3
4
5
6
7
8
9
10
11

但是很快发现我们不同的方法,可能方法签名或者返回都不一样,可能处理的逻辑也不一样。所以需要对InvocationHandler 进行修改 method 为实际调用的方法,args 为方法入参。

java
interface InvocationHandler {
    Object invoke(Method method, Object[] args) throws Throwable;
}
1
2
3

所以修改后我们的方法就变成

java
@AllArgsConstructor
static class $Proxy0 implements Foo{
    private InvocationHandler handler;

    @Override
    public void foo() {
        try {
            Method foo = Foo.class.getMethod("foo");
            handler.invoke(foo, new Object[0] );
        }  catch (RuntimeException | Error e) {
            throw e;
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public int bar() {
        try {
            Method bar =  Foo.class.getMethod("bar");
            return (int) handler.invoke(bar, new Object[0] );
        } catch (RuntimeException | Error e) {
            throw e;
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

cglib

java
@Slf4j
public class CglibProxyDemo {

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

        MethodInterceptor callback = (o, method, objectsArgs, methodProxy) -> {
            log.info("前置增强");
            Object invoke= null;
            // 方法反射的调用
            // invoke = method.invoke(myService, objects);
            // 避免反射调用 需要目标(Spring)
            // invoke = methodProxy.invoke(myService, objectsArgs);
            // 内部没有用反射 需要代理
            invoke = methodProxy.invokeSuper(o, objectsArgs);
            log.info("后置增强");
            return invoke;
        };
        MyService proxy = (MyService)Enhancer.create(MyService.class, callback);
        proxy.foo();
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

Cglib 基于父子关系的代理,代理类是子类型,目标是父类型。无法代理final 修饰的类,对于final 修饰的方法也不会进行增强。

cglib 代理类内部原理

定义目标类

java
@Slf4j
class Target {

    void save() {
        log.info("save()");
    }

    void save(int num) {
        log.info("save(int num)");
    }

    void save(long num) {
        log.info("save(long num)");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

生成的是子类继承父类的代理

java
@AllArgsConstructor
class CglibProxy extends Target {
    
    // 通过MethodInterceptor 去进行增强
    private MethodInterceptor methodInterceptor;

    static Method save0;
    static Method save1;
    static Method save2;
    // 静态代码块先设置方法
    static {
        try {
            save0 = Target.class.getDeclaredMethod("save");
            save1 = Target.class.getDeclaredMethod("save", int.class);
            save2 = Target.class.getDeclaredMethod("save", long.class);
        } catch (NoSuchMethodException e) {
            throw new NoSuchMethodError(e.getMessage());
        }
    }

    @Override
    void save() {
        try {
            methodInterceptor.intercept(
                    this, // 代理类对象
                    save0,   // 代理方法
                    new Object[0],  // 参数
                    null            // 方法代理
            );
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

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

    @Override
    void save(long num) {
        try {
            methodInterceptor.intercept(this, save2, new Object[]{num}, null);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

调用

java
public class CglibProxyRationale {

    public static void main(String[] args) {
        Target target = new Target();
        CglibProxy proxy = new CglibProxy((o, method, objects, methodProxy) -> {
            System.out.println("info - 前置");
            Object invoke = method.invoke(target, objects);
            System.out.println("info - 后置");
            return invoke;
        });
        proxy.save();
        proxy.save(1);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
MethodProxy

CglibProxy重写的方法都是带增强功能的方法,因为是通过MethodInterceptor 去增强的,我们还需要定义原始方法

java
@AllArgsConstructor
class CglibProxy extends Target {
    
    static MethodProxy saveSuper0Proxy;
    static MethodProxy saveSuper1Proxy;
    static MethodProxy saveSuper2Proxy;

    private static void setMethodProxy(){
        saveSuper0Proxy = MethodProxy.create(Target.class, CglibProxy.class, "()V","save","saveSuper");
        saveSuper1Proxy = MethodProxy.create(Target.class, CglibProxy.class, "(I)V","save","saveSuper");
        saveSuper2Proxy = MethodProxy.create(Target.class, CglibProxy.class, "(J)V","save","saveSuper");
    }

    static {
        setMethod();
        setMethodProxy();
    }


    void saveSuper(){
        super.save();
    }

    void saveSuper(int num){
        super.save(num);
    }

    void saveSuper(long num){
        super.save(num);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

在类初始化的时候将对应的MethodProxy 初始化完成。

methodInterceptor.intercept(this, save1, new Object[]{num}, saveSuper1Proxy); 将刚才的null 值替换成对应的方法代理。在扩展类中就可以使用methodProxy,而不使用类反射去调用。

cglib 如何避免反射调用

代理类内部产生2个代理类(即 FastClass),一个代理类是配合MethodProxyinvoke() 方法使用,另一个是产生配合invokeSuper()使用

java
abstract class FastClass {
    
    public abstract int getIndex(Signature var1);

    public abstract Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException;
}
1
2
3
4
5
6

这个FastClass 什么时候创建呢,在我们使用MethodProxy.create()方法就开始创建了。

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

    // 获取一个目标方法的编号
    public int getIndex(Signature signature) {
        if (signature.equals(s0)) {
            return 0;
        } else if (signature.equals(s1)) {
            return 1;
        } else if (signature.equals(s2)) {
            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;
        }
        throw new RuntimeException("no such method");
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

所以就在方法调用的时候methodProxy.invoke(target, objects) 就可以正常调用传入的target的对应的方法

java
public class MethodProxyRationale {
    
    public static void main(String[] args) {
        Target target = new Target();
        TargetFastClass targetFastClass = new TargetFastClass();
        int saveIndex = targetFastClass.getIndex(new Signature("save", "()V"));
        targetFastClass.invoke(saveIndex, target, new Object[]{});
    }
}
1
2
3
4
5
6
7
8
9

同理代理原始的方法(即 saveSuper) invokeSuper 就是需要通过传入代理类,调用那些super的方法。

cglib 和JDK 对比

cglib 一上来就会产生代理,无需反射调用,而JDK 经过16次反射调用,产生代理类,一个方法就是一个代理。

Released under the MIT License.