一、基本概念
1. 作用
1)Java动态代理的优势是实现无侵入式的代码扩展,也就是方法的增强;让你可以在不用修改源码的情况下,增强一些方法;在方法的前后你可以做你任何想做的事情(甚至不去执行这个方法就可以)
2)为其他对象提供一种代理以控制对这个对象的访问
2. 优点与缺点
1)优点
(1)通过在原有调用的逻辑过程中,再抽一个代理类的方式,使调用逻辑的变化尽可能的封装在代理内部中,达到不改变原有被代理类的方法的情况下,增加新的动作和效果
(2)这就是的即便在未来的使用场景中有更多的拓展,改变也依然很难波及被代理类,我们也就可以放心的呗代理类的特定方法进行复用了
(3)主要用来做方法的增强,让你可以在不修改源码的情况下,增强一些方法,在方法执行前后做任何你想做的事情
(4)动态代理,相比于继承,提高了代码的灵活性和重用性,也提高了代码的简洁规范度
2)缺点
(1)静态代理和JDK动态代理类的接口信息以确定特定的方式进行拦截和包装
(2)CGLIB动态代理虽然不需要接口信息,但是它拦截并包装被代理类的所有方法
3)用途
(1)spring AOP
(2)日志、事务
二、静态代理
1. 基本概念
1)由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了
2. 优缺点
1)优点
(1)业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点
2)缺点
(1)代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了
(2)如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度
3. 示例
1)定义一个账户接口 Count, 有方法 查看账户:queryCount(), 修改账户 updateCount();
2)定义实现Count的实现类CountImpl, 里面分别打印 查看账户, 修改账户 (该类也叫委托类,包含业务逻辑)
3)定义一个代理类(增强CountImpl实现类,可以在调用方法前后加东西),实现Count的话方法名字就一样
如:
public class CountProxy implements Count {
private CountImpl countImpl;
/**
* 覆盖默认构造器
*
* @param countImpl
*/
public CountProxy(CountImpl countImpl) {
this.countImpl = countImpl;
}
@Override
public void queryCount() {
System.out.println("事务处理之前");
// 调用委托类的方法;
countImpl.queryCount();
System.out.println("事务处理之后");
}
@Override
public void updateCount() {
System.out.println("事务处理之前");
// 调用委托类的方法;
countImpl.updateCount();
System.out.println("事务处理之后");
}
4)测试
CountImpl countImpl = new CountImpl();
CountProxy countProxy = new CountProxy(countImpl);
countProxy.updateCount();
countProxy.queryCount();
三、动态代理(动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成。代理类和委托类的关系是在程序运行时确定)
1. JDK动态代理(接口方式)
1)api
(1)InvocationHandler(Interface) Object invoke(Object proxy, Method method, Object[] args) throws Throwable
a. proxy: 指代我们所代理的那个真实对象
b. method: 指代的是我们所要调用真实对象的某个方法的Method对象
c. args: 指代的是调用真实对象某个方法时接受的参数
(2)Proxy(Class) public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler) throws IllegalArgumentException
a. loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
b. interfaces: 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
c. handler: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
2)示例
(1)定义两个接口, 交通工具接口Vehicle (drive()方法)和 可充电设备接口Rechargable(recharge()方法)
(2)定义电能车类,实现Rechargable,Vehicle接口
(3)定义代理类,继承InvocationHandler
public class TestProxy implements InvocationHandler {
// 声明要代理的真实对象
private Object subject;
public TestProxy(Object subject) {
this.subject = subject;
}
public Object invoke(Object object, Method method, Object[] args) throws Throwable {
// 在代理真实对象之前 可以添加操作
System.out.println("before rent house");
System.out.println("Method" + method);
// 真实对象调用方法
method.invoke(subject, args);
// 在代理真实对象之后 做操作
System.out.println("after rent house");
return null;
}
(4)测试类
// 要被代理的真实对象
ElectricCar car = new ElectricCar();
// 1.获取对应的ClassLoader
ClassLoader classLoader = car.getClass().getClassLoader();
// 2.获取ElectricCar 所实现的所有接口
Class[] interfaces = car.getClass().getInterfaces();
// 3.设置一个来自代理传过来的方法调用请求处理器,处理所有的代理对象上的方法调用
InvocationHandler handler = new TestProxy(car);
// 创建代理对象,在这个过程中
// a.JDK会通过根据传入的参数信息动态地在内存中创建和.class 文件等同的字节码
// b.然后根据相应的字节码转换成对应的class,
// c.然后调用newInstance()创建实例
Object o = Proxy.newProxyInstance(classLoader, interfaces, handler);
Vehicle vehicle = (Vehicle) o
vehicle.drive();
Rechargable rechargeable = (Rechargable) o;
rechargeable.recharge();
3)动态代理实现步骤(原理)
(1)获取 RealSubject上的所有接口列表
(2)确定要生成的代理类的类名,默认为:com.sun.proxy.$ProxyXXXX
(3)根据需要实现的接口信息,在代码中动态创建 该Proxy类的字节码
(4)对应的字节码转换为对应的class 对象
(5)创建InvocationHandler 实例handler,用来处理Proxy所有方法调用
(6)Proxy 的class对象 以创建的handler对象为参数,实例化一个proxy对象
4)好处
(1)最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理
2. Cglib动态代理(继承方式)
1)基本概念
(1)JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的
(2)他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理
2)示例
(1)定义一个实例类(没有实现接口)ReadBook, 里面方法 addBook(){ 打印 }
(2)定义一个类BookCglib继承MethodInterceptor抽象类(里面就一个intercept方法)(这个接口和JDK动态代理InvocationHandler的功能和角色是一样的),同时里面可以创建代理类的方法
// 创建代理对象
public Object getInstance(Object target) {
this.target = target;
// cglib 中加强器,用来创建动态代理
Enhancer enhancer = new Enhancer();
// 设置要创建动态代理的类
enhancer.setSuperclass(this.target.getClass());
// 设置回调,这里相当于是对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实行intercept()方法进行拦截
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开始");
proxy.invokeSuper(obj, args);
System.out.println("结束");
}
(3)测试
ReadBook book = new ReadBook();
BookCglib cglib = new BookCglib();
ReadBook isAObjectProxy = (ReadBook)cglib.getInstance(book);
isAObjectProxy.addBook();
3)cglib 创建某个类A的动态代理类的模式是
(1)查找A上的所有非final 的public类型的方法定义
(2)将这些方法的定义转换成字节码
(3)将组成的字节码转换成相应的代理的class对象
(4)实现 MethodInterceptor接口,用来处理对代理类上所有方法的请求(这个接口和JDK动态代理InvocationHandler的功能和角色是一样的)
参考网址
Java动态代理机制详解(JDK 和CGLIB,Javassist,ASM)
注:文章是经过参考其他的文章然后自己整理出来的,有可能是小部分参考,也有可能是大部分参考,但绝对不是直接转载,觉得侵权了我会删,我只是把这个用于自己的笔记,顺便整理下知识的同时,能帮到一部分人。
ps : 有错误的还望各位大佬指正,小弟不胜感激