Java动态代理和SpringAOP

一、背景

我们使用代理对象替代对真实对象的访问,这样就可以在不修改原目标对象的前提下,扩展目标对象的功能。比如在方法执行前后增加一些自定义操作。

不修改原目标对象有两个好处

  1. 降低代码耦合
  2. 有些对象就是无法修改,如外部类。

在Java中实现代理有两种方式,静态代理 和 动态代理

静态代理,指的是我们手动完成,为每个类手动创建一个代理类,完成增强逻辑。很不方便,一般不用。

动态代理,借助一些工具来实现,比较方便灵活。

二、动态代理常见的实现方式

JDK

Demo

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
interface UserService {
void addUser(String username);
}

class UserServiceImpl implements UserService {
@Override
public void addUser(String username) {
System.out.println("hello, " + username);
}
}

class DebugInvocationHandler implements InvocationHandler {
private final Object target;
public DebugInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before method " + method.getName());
Object result = method.invoke(target, args);
System.out.println("after method " + method.getName());
return result;
}
}

public class DynamicProxyExample {
public static void main(String[] args) {
UserService proxyInstance = (UserService) Proxy.newProxyInstance(
UserServiceImpl.class.getClassLoader(),
UserServiceImpl.class.getInterfaces(),
new DebugInvocationHandler(new UserServiceImpl()));
proxyInstance.addUser("why");
}
}

可以看到使用newProxyInstance来生成一个代理对象,其中需要三个类

  1. interface: UserService,
  2. interface Impl: UserServiceImpl
  3. InvocationHandler Impl:代理逻辑实现类

JDK只能基于接口代理,所以UserService是必备的

CGLIB

JDK只能基于接口代理,而Cglib可以在运行时对字节码进行修改和动态生成。

Demo

非Spring项目可以引入下面这个包

1
2
3
4
5
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Service1 {
public void m1() {
System.out.println("m1方法:");
}
public void m2() {
System.out.println("m2方法");
}
}

public class CglibTest {
@Test
public void test1() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Service1.class);
enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
System.out.println("调用方法:" + method.getName());
return methodProxy.invokeSuper(o, objects);
});
Service1 proxy = (Service1) enhancer.create();
proxy.m1();
proxy.m2();
}
}

通过Enhancer来创建一个组件,SuperClass用于指定需要增强的类,Callback用于指定代理逻辑。

JDK和CGLIB 动态代理对比

  1. 原理方面,JDK基于反射,运行时生成代理对象;CGLIB运行时修改字节码;
  2. 功能方面,JDK只能代理实现了接口的类或者直接代理接口;CGLIB可以代理未实现任何接口的类(但是不能代理final类或者方法);
  3. 效率方面,JDK动态代理更优秀

AspectJ

是一个AOP框架,更全面的面向切面的编程解决方案。支持编译时织入和加载时织入。

三、Spring AOP

最开始了解到AOP这个概念的时候,其实就是通过Spring。

那了解一下Spring具体使用的实现方案。

如果被代理的对象实现了某个接口,那么SpringAOP就会使用JDK动态代理;

如果对于没有实现接口的对象,就通过Cglib生成一个被代理对象的子类来作为代理。

SpringAOPProcess

那SpringAOP和Aspectj有关系吗?

Spring在AOP实现的时候,复用了Aspectj的语法和注解,在底层实现上也可以选择Aspectj。

四、总结

动态代理常见的三种方式分别为:

  1. JDK
  2. CGLIB
  3. ASPECTJ

其中JDKCGLIB属于运行时增强,JDK依赖反射,只能对实现了接口的类进行代理;CGLIB运行时修改字节码,支持对所有非final类的非final方法进行代理;ASPECTJ在类加载前(或编译时)织入字节码。

其中ASPECTJ比较特别,是个全面的AOP框架。

平时使用的SpringAOP,复用了ASPECTJ的声明,底层实现基于JDKCGLIB


Java动态代理和SpringAOP
https://yzaf.top/2024/dynamic-proxy/
作者
why
发布于
2024年4月21日
许可协议