Spring Boot动态代理分两种,JDK的动态代理和cglib的动态代理
Spring Boot默认使用JDK的动态代理,当类没有实现接口时才使用cglib的动态代理。
- jdk的动态代理
这种代理适用于实现了接口的类
假设现在有一个接口,里面有很多抽象方法
public interface UserService {
public void method1();
public void method2();
public void method3();
public void method4();
}
有一个类
//将被代理的类。看以看到,这个类实现了UserService接口
public class UserImpl implements UserService {
public void query(String name) {
System.out.println("query name = " + name);
}
}
那怎么代理上面的这个类呢,JDK动态代理的思想是生成一个类,让它和被代理对象实现同样的接口,并重写接口的方法。如下所示。(注意:下面这个类是JDK自动生成的)
//它与被代理类实现了相同的接口,即UserService
public class LogUserProxy implements UserService {
//被代理对象将作为生成类的一个属性
private UserService userService;
//构造方法将被代理对象注入进去
public LogUserProxy(UserService userService) {
this.userService = userService;
}
//重写接口的方法,实现代理原方法的功能
public void query(String name) {
//todo 增强逻辑
System.out.println("增强逻辑");
userService.query(name);
//todo 增强逻辑
System.out.println("增强逻辑");
}
}
上面有两个类,UserImpl(被代理的类)和LogUserProxy(自动生成的类)
面向接口编程中常常使用如下形式调用UserImpl(Spring Boot中会自动注入)
UserService service = new UserImpl();
service.query("aaa");
如果这个时候我们将上述代码改为如下形式即可完成代理
UserService service = new LogUserProxy();
service.query("aaa");
2.cglib动态代理
jdk的动态代理只能代理实现了接口的类,但是有些类没有实现接口,那这种类如何被代理呢?
例如被代理类如下所示。
//被代理类
public class Test {
public void cal() {
System.out.println("被代理方法");
}
}
代理方式:
自定义拦截器并实现MethodInterceptor 接口
public class AIntercepter implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//增强逻辑
System.out.println("代理前,增强逻辑");
methodProxy.invokeSuper(o, objects);
//增强逻辑
System.out.println("代理后,增强逻辑");
return null;
}
}
测试:
public class Main1 {
public static void main(String[] args) {
//代理类class文件存入本地磁盘,方便查看中间生成的临时文件
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
//------------------开始代理--------------------
Enhancer enhancer = new Enhancer();
//指定被代理类,生成的类将会继承此类
enhancer.setSuperclass(Test.class);
//指定拦截器
enhancer.setCallback(new AIntercepter());
//生成代理类
Test t = (Test)enhancer.create();
//完成代理
t.cal();
//------------------完成代理--------------------
}
}
enhancer.create()方法会生成代理类,而System.setProperty()会将其保存到D:\code目录下。查看目录,会发现D:\code下生成了三个文件
Test$$EnhancerByCGLIB$$7ce11e59$$FastClassByCGLIB$$fe330774.class
Test$$EnhancerByCGLIB$$7ce11e59.class
Test$$FastClassByCGLIB$$3b8fb982.class
其中第二个文件是代理类,将其反编译,将其中重要的方法展示如下。
//拦截其中的methodProxy.invokeSuper(o, objects);会调用此方法
final void CGLIB$cal$0() {
super.cal();
}
//测试代码中的t.cal()会调用此方法
public final void cal() {
//获取自定义拦截器
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
//调用自定义拦截器的intercept方法
var10000.intercept(this, CGLIB$cal$0$Method, CGLIB$emptyArgs, CGLIB$cal$0$Proxy);
} else {
super.cal();
}
}
整个过程的方法调用逻辑是
Main1中的t.cal()---->Test$$EnhancerByCGLIB$$7ce11e59.class中的cal()方法----->自定义拦截器AIntercepter中的intercept()方法----->被代理类Test中的cal()方法
其中自定义拦截器中实现方法增强
网友评论