静态代理
是指在程序运行前就已经存在的编译好的代理类是为静态代理。实现静态代理有四个步骤:
①定义业务接口;
②被代理类实现业务接口;
③定义代理类并实现业务接口;
④最后便可通过客户端进行调用。(这里可以理解成程序的main方法里的内容)
package com.zhb.jdk.proxy;
public class UserServiceProxy implements IUserService {
// 被代理对象
private IUserService target;
// 通过构造方法传入被代理对象
public UserServiceProxy(IUserService target) {
this.target = target;
}
@Override
public void add(String name) {
System.out.println("准备向数据库中插入数据");
target.add(name);
System.out.println("插入数据库成功");
}
}
静态代理缺点:
①代理类和被代理类实现了相同的接口,导致代码的重复,如果接口增加一个方法,被代理类和代理类都要实现实现这个方法,增加了代码维护的难度。
②代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。比如上面的例子,只是对用户的业务功能(IUserService)进行代理,如果是商品(IItemService)的业务功能那就无法代理,需要去编写商品服务的代理类。
动态代理:
在程序运行期间根据需要动态创建代理类及其实例来完成具体的功能。
动态代理主要分为JDK动态代理和cglib动态代理两大类
JDK动态代理
我们并没有像静态代理那样去自己定义一个代理类,并实例化代理对象。实际上,动态代理的代理对象是在内存中的,是JDK根据我们传入的参数生成好的。
cglib动态代理
JDK代理要求被代理的类必须实现接口,有很强的局限性。而CGLIB动态代理则没有此类强制性要求。简单的说,CGLIB会让生成的代理类继承被代理类,并在代理类中对代理方法进行强化处理(前置处理、后置处理等)。在CGLIB底层,其实是借助了ASM这个非常强大的Java字节码生成框架。
代理方式 | 实现 | 优点 | 缺点 | 特点 |
---|---|---|---|---|
JDK静态代理 | 代理类与委托类实现同一接口,并且在代理类中需要硬编码接口 | 实现简单,容易理解 | 代理类需要硬编码接口,在实际应用中可能会导致重复编码,浪费存储空间并且效率很低 | 好像没啥特点 |
JDK动态代理 | 代理类与委托类实现同一接口,主要是通过代理类实现InvocationHandler并重写invoke方法来进行动态代理的,在invoke方法中将对方法进行增强处理 | 不需要硬编码接口,代码复用率高 | 只能够代理实现了接口的委托类 | 底层使用反射机制进行方法的调用 |
CGLIB动态代理 | 代理类将委托类作为自己的父类并为其中的非final委托方法创建两个方法,一个是与委托方法签名相同的方法,它在方法中会通过super调用委托方法;另一个是代理类独有的方法。在代理方法中,它会判断是否存在实现了MethodInterceptor接口的对象,若存在则将调用intercept方法对委托方法进行代理 | 可以在运行时对类或者是接口进行增强操作,且委托类无需实现接口 | 不能对final类以及final方法进行代理 | 底层将方法全部存入一个数组中,通过数组索引直接进行方法调用 |