1. 代理模式
代理模式,给目标对象创建代理对象,在不影响源代码的情况下,实现对目标对象的功能横向扩展。在代理模式中,主要有 4 种角色:
- 抽象角色:一般会使用接口和抽象类来解决。
- 真实角色:被代理的角色,目标对象。
- 代理角色:代理真实角色,一般会做其他附属操作。
- 客户:访问代理角色的人。
分类:
- 静态代理,一个真实角色产生一个代理角色。
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理。
- 基于接口:JDK 动态代理
- 基于类:Cglib 动态代理
- Java 字节码实现:Javasist
优点:
- 可以使真实角色(目标对象)的操作更加纯粹,不用去关注一些公共的业务,公共业务交给了代理角色实现了业务的分工。
- 公共业务发生扩展时,需要集中管理。
缺点:
- 一个真实角色就产生一个代理角色。代码量翻倍(开放效率变低)
2. 静态代理
静态代理,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类。
优点:写法简单,代码量少。
缺点:一个真实角色就产生一个代理角色。
//租房
public interface Rent {
public void rent();
}
//房东,真实角色
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东要出租房子");
}
}
//中介,代理角色
public class Proxy implements Rent{
private Host host;
public Proxy(Host host) {
this.host = host;
}
@Override
public void rent() {
host.rent();
sendHouse();
fare();
hetong();
}
//看房
public void sendHouse() {
System.out.println("中介带你看房!");
}
//收中介费
public void fare() {
System.out.println("收中介费");
}
//签租赁合同
public void hetong() {
System.out.println("签租赁合同!");
}
}
//客户端访问代理角色
public class Client {
public static void main(String[] args) {
//房东要租房子
Host host = new Host();
//代理,中介帮房东租房子,但是,代理角色一般会有一些附属操作
Proxy proxy = new Proxy(host);
//不用面对房东,直接找中介租房
proxy.rent();
}
}
3. JDK动态代理
JDK 动态代理,主要说的是两个接口 InvocationHandler 和 Proxy,都是 reflect 包下面的。
- Proxy:自动生成代理类实例。
- InvocationHandler:调用处理程序并返回一个结果。
优点:不需要生成代理类,可扩展性强,方便后续的更改和操作。
缺点:目标对象必须实现一个或多个接口。
public class ProxyInvocationHandler implements InvocationHandler {
// 被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成得到代理类
public Object getProxy() {
return Proxy.newProxyInstance(
this.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this
);
}
//处理代理实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质,就是使用反射机制实现
log(method.getName());
Object result = method.invoke(target, args);
return result;
}
public void log(String msg) {
System.out.println("执行了" + msg + "方法");
}
}
public class Client {
public static void main(String[] args) {
//真实角色
UserServiceImpl userService = new UserServiceImpl();
//代理角色,不存在
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//1、设置要代理的对象
pih.setTarget(userService);
//2、动态生成代理类
UserService proxy = (UserService) pih.getProxy();
proxy.insert();
}
}
4. Cglib动态代理
需要引入 cglib 依赖。
<dependency>
<groupId>org.sonatype.sisu.inject</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
不需要实现接口,在内存中构建一个子类对象从而实现对目标对象功能扩展,Cglib 包的底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类。
public class CglibProxy implements MethodInterceptor {
// 维护一个目标对象
private Object target;
// 传入一个被代理的对象,真实角色
public CglibProxy(Object target) {
this.target = target;
}
// 返回一个代理对象,是 target 的代理对象
public Object getProxyInstance() {
// 1、创建一个工具类
Enhancer enhancer = new Enhancer();
// 2、设置父类
enhancer.setSuperclass(target.getClass());
// 3、设置回调函数
enhancer.setCallback(this);
// 4、创建子类对象,即代理对象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
watch();
Object resultVal = method.invoke(target, objects);
return resultVal;
}
public void watch() {
System.out.println("看房。。。");
}
}
public class Client {
public static void main(String[] args) {
Host host = new Host();
Host proxyInstance = (Host)new CglibProxy(host).getProxyInstance();
proxyInstance.rent();
}
}
CGLIB代理与JDK动态代理的区别
- jdk动态代理:目标对象需要实现一个或多个接口,使用反射完成,使用了动态生成字节码技术。
- cglib动态代理:可以直接代理类,使用字节码技术,不能对 final类进行继承。使用了动态生成字节码技术。