一. 写在前面
今天来梳理一下两种代理的基础知识:JDK动态代理和Cglib代理。
本章只做一些简单的例子来说明两种代理的使用以及两种代理的基本知识,关于两种代理的底层实现,将会在后续更新。
由于本人水平有限,经验不足,如有不对之处 欢迎各位指正。
二. JDK动态代理
首先是JDK动态代理,这里直接上代码可以看的更直接一点:
我们先定义一个要代理的接口
public interface SalaryUpdate {
void updateSalary();
void increaseSalary();
}
其次再来个实现类来实现这个接口(被代理类):
public class SalaryUpdateImpl implements SalaryUpdate {
@Override
public void updateSalary() {
System.out.println("工资更新的不多,也就50k,哈哈哈哈"); //被代理的方法
}
@Override
public void increaseSalary() {
System.out.println("工资长得不多,也就100k,哈哈哈哈"); //被代理的方法
}
}
最后是我们的代理类 实现了InvocationHandler 接口:
public class JDKProxy implements InvocationHandler {
private Object target;
public JDKProxy(Object target){
this.target= target;
}
public Object createProxy(Object target){
this.target=target;
//生成代理对象 参数1:被代理对象的类加载器
// 参数2:被代理对象实现的接口(代理类要实现的接口)
// 参数3:设置回调对象,当代理对象的方法被调用时会调用 回调对象的 invoke方法
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
@Override
// proxy : 目标对象的代理实例
// method : 代理实例调用的方法
// args :代理实例方法参数值的数组
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before: 事务开始");
method.invoke(this.target,args);
System.out.println("after :提交事务");
return null;
}
}
接下来 是我们的测试类
public class TestProxy {
public static void main(String[] args) {
SalaryUpdate target =new SalaryUpdateImpl();
JDKProxy jdkProxy = new JDKProxy(target);
SalaryUpdate jdkProxyProxy = (SalaryUpdate) jdkProxy.createProxy(target);
jdkProxyProxy.increaseSalary();
}
}
可以看到 我们的动态代理是怎么实现的(被代理类:SalaryUpdateImpl
,代理类:JDKProxy
):
JDKProxy 实现了 InvocationHandler接口 并实现了 invoke方法,我们可以在这个方法中
对代理类进行加强,就好比我们的封装的 Transaction 事务,首先在执行被代理类(SalaryUpdateImpl)的方法之前,开启事务,执行方法之后,提交事务一样。(大体逻辑是这样的,这里只是简单举例一下,实际要比这复杂)
接下来 我们详细看一下,在JDKProxy中 传递的 target 就是我们要代理的对象:
通过Proxy.newProxyInstance()
方法创建我们被代理的对象所实现的接口,其中第一个参数 是我们的 被代理对象的getClassLoader
,第二个参数是我们的 被代理对象所实现的接口 getInterfaces()
,第三个参数是一个 InvocationHandler
对象, 也就是 JDKProxy。
这样 我们通过传入被代理类对象就可以创建一个代理对象,来帮我实现被代理类想要实现的接口对象,
注意 上面的Proxy.newProxyInstance()
方法生成的是接口对象 而不是 被代理类对象。
我们总结一下 JDK动态代理:
1、因为利用JDKProxy生成的代理类实现了接口,所以目标类中所有的方法在代理类中都有。
2、生成的代理类的所有的方法都拦截了目标类的所有的方法。而拦截器中invoke方法的内容正好就是代理类的各个方法的组成体。
3、利用JDKProxy方式必须有接口的存在。
4、invoke方法中的三个参数可以访问目标类的被调用方法的API、被调用方法的参数、被调用方法的返回类型。
三. Cglib代理
前面 被代理类的接口(SalaryUpdate
)和被代理类的实现类(SalaryUpdateImpl
)不变,代理类改变了一下
public class CglibProxy implements MethodInterceptor {
private Object object;
public CglibProxy(Object object){
this.object=object;
}
public Object createProxy(){
Enhancer enhancer =new Enhancer();
enhancer.setSuperclass(this.object.getClass()); // 设置父类
enhancer.setCallback(this); //调用本身为回调对象
return enhancer.create(); //创建生成代理对象
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("你好好干了,接下来给你涨工资"); //相当于开启事务
Object invoke = method.invoke(this.object, objects); //执行目标方法
System.out.println("明年继续加油,再给你多涨点"); //相当于 关闭事务
return invoke;
}
接下来是我们的测试类:
public class TestProxy {
public static void main(String[] args) {
SalaryUpdate salaryUpdate = new SalaryUpdateImpl();// 生成被代理类对象
CglibProxy cglibProxy =new CglibProxy(salaryUpdate);
SalaryUpdateImpl CglibProxy = (SalaryUpdateImpl)cglibProxy.createProxy();
CglibProxy.increaseSalary();
}
}
可以看到我们的CglibProxy
实现了MethodInterceptor
接口,但是创建代理类的过程却不一样了,Cglib代理是通过Enhancer
类创建代理对象的,同时生成的对象可以是接口对象也可以是实现类对象,但是JDK动态代理创建的代理类只能是接口对象。
我们总结一下Cglib代理:
1、 CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
2、 用CGlib生成代理类是目标类的子类。
3、 用CGlib生成 代理类不需要接口
4、 用CGLib生成的代理类重写了父类的各个方法。
5、 拦截器中的intercept方法内容正好就是代理类中的方法体
最后总一下两种代理模式的简单总结:
- 如果目标对象实现了接口,spring使用JDK动态类代理。
优点:因为有接口,所以使系统更加松耦合
缺点:为每一个目标类创建接口- 若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。
优点:因为代理类与目标类是继承关系,所以不需要有接口的存在。
缺点:因为没有使用接口,所以系统的耦合性没有使用JDK的动态代理好。