【参考】
- cglib(baeldung):https://www.baeldung.com/cglib
- cglib学习:https://www.youtube.com/watch?v=hskHMSlvm6U&t=302s
- Dynamic proxies(baeldung):https://www.baeldung.com/java-dynamic-proxies
【官网】
- cglib官网(GitHub):https://github.com/cglib/cglib/wiki
- Dynamic Proxy官网:https://docs.oracle.com/javase/8/docs/technotes/guides/reflection/proxy.html
本文基于Spring Boot 2.7.0版本。
1. cglib相关
1.1 cglib例子
我们先新建一个目标类:Cat.java
public class Cat {
public String hello(String name) {
return "hello " + name;
}
}
新建一个实现cglib的MethodInterceptor
接口的类:
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class HelloMethodInterceptor implements MethodInterceptor {
private Object target;
public HelloMethodInterceptor(Object target) {
this.target = target;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object obj;
try {
System.out.println("before " + method.getName() + " invoke...");
obj = method.invoke(target, objects);
} finally {
System.out.println("after " + method.getName() + " invoke....");
}
return obj;
}
}
开始写测试类,目标是通过cglib的Enhancer
类,将目标Cat类作为Superclass,enhancer可以set多个CallBack(至少一个)。这里的callback会在cglib的proxy类的方法被调用的时候调用。
import org.junit.jupiter.api.Test;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import proxy.dynamic.Dog;
public class CatCGLIBTest {
@Test
public void test() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Cat.class);
enhancer.setCallback(new HelloMethodInterceptor(new Cat()));
Cat proxy = (Cat) enhancer.create();
System.out.println(proxy.hello("cglib"));
}
控制台打印:
在测试方法中打断点可以看到Enhance创建的类为: image.pngbefore hello invoke...
hello method
after hello invoke....
hello cglib
1.2 cglib的相关类为什么在org.springframework包下?
cglib也有自己的包,如:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.4</version>
</dependency>
Spring因为考虑到别的第三方jar也可能会引用cglib(如hibernate),为了避免引起包冲突,Spring直接将cglib的相关类,放到了它自己的spring-core包下,这就是为什么上述的例子有些cglib相关类在org.springframework.cglib.proxy
包下。
1.3 Spring通过cglib创建代理类
CourseServiceImpl类有@Transactional注解,所以最终Spring会通过cglib(可以通过设置使用cglib代理或是JDK动态代理)生成CourseServiceImpl的增强类:
@Service("courseService")
public class CourseServiceImpl implements CourseService {
@Autowired
private CourseRepository courseRepository;
@Transactional
public List<Course> list() {
return courseRepository.findAll();
}
}
通过debug可以看到,在bean = courseService在createBean
的过程中,最终会调用CglibAopProxy
类进行增强:
代码片断如下(位于CglibAopProxy
):
创建cglib的Enhancer
类:
设置Superclass
:
设置Callbacks
,我们上述#1.1的例子中只设置了一个MethodInterceptor
,Spring这里一共设置了7个:
2. JDK Dynamic Proxy相关
2.1 JDK Dynamic Proxy例子
和上述的Cat类一样,这里新建的是Dog,因为JDK动态代理是基于接口的,所以我们创建了接口Dog以及它的实现类DogImpl。
public interface Dog {
String hello(String name);
}
public class DogImpl implements Dog {
public String hello(String name) {
System.out.println("hello method");
return "hello " + name;
}
}
Dynamic在生成proxy的时候,也需要传入一个Handler,即以下的类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DogInvocationHandler implements InvocationHandler {
private Object target;
public DogInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before invoke...");
Object result = method.invoke(target, args);
System.out.println("after method...");
return result;
}
}
开始测试,可以看到Dynamic Proxy依赖的类都在java.lang.reflect下,也就是Dynamic Proxy是JDK原生的代理。
import org.junit.jupiter.api.Test;
import java.lang.reflect.Proxy;
public class DogDynamicProxyTest {
@Test
public void test() {
Dog proxyInstance = (Dog) Proxy.newProxyInstance(
DogDynamicProxyTest.class.getClassLoader(),
new Class[] { Dog.class }, new DogInvocationHandler(new DogImpl()));
System.out.println(proxyInstance.hello("test"));
}
}
控制台打印:
在测试方法中打断点可以看到Enhance创建的类为: image.pngbefore invoke...
hello method
after method...
hello test
2.2 Spring通过JDK Dynamic Proxy创建代理类
高版本的Spring Boot已经默认使用cglib做为默认的动态代理,如果要使用JDK的动态代理,需要在application.yaml
中设置:
spring:
aop:
proxy-target-class: false
通过debug可以看到,在bean = courseService在createBean的过程中,最终会调用JdkDynamicAopProxy
类进行增强:
通过java.lang.reflect.Proxy
创建代理类:
3. Spring Boot 默认的动态代理
可以查看TransactionAutoConfiguration
类,在2.7.0版本中默认用的是cglib(不同版本可能不一样),想要改成JDK动态代理,请查看#2.2的设置:
网友评论