java中的代理
代理
什么是代理模式?
deepSeek 给我的答案,如下:
代理模式(Proxy Pattern)是一种设计模式,属于结构型模式。在代理模式中,一个类代表另一个类的功能,以便控制客户对原始对象的访问,或者为原始对象提供一个替代品。
在代理模式中,通常会涉及以下三个角色:
* 抽象主题(Subject):定义了RealSubject和Proxy的共用接口,这样就在任何使用RealSubject的地方都可以使用Proxy。
* 真实主题(RealSubject):定义了代理所表示的真实对象,代理角色通常要引用真实主题,以便在需要的时候将请求转发给它。
* 代理(Proxy):保存一个引用使得代理可以访问实体,并提供一个与Subject的接口相同的接口,这样代理就可以用来代替实体。代理角色通常在将请求提交给真实主题之前或之后,执行某个操作,比如权限检查、缓存、记录日志等。

两种代理类型
| 特性/模式 | 静态代理 | 动态代理 |
|---|---|---|
| 定义 | 编译时确定代理类和原始类的关系,通常由程序员手动编写。 | 运行时动态创建代理类,不需要手动编写代理类。 |
| 实现方式 | 代理类和原始类实现相同的接口或继承自相同的父类,代理类中持有原始类的实例。 | 利用反射机制,如Java中的java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler。 |
| 灵活性 | 代理类和原始类的关系固定,不够灵活。 | 可以在运行时动态创建,更加灵活。 |
| 维护成本 | 每个需要代理的类都需要一个代理类,可能导致类数量膨胀,增加维护成本。 | 不需要为每个类手动编写代理类,减少了代码量,降低了维护成本。 |
| 接口变更 | 接口增加方法时,代理类和原始类都需要修改。 | 接口变更不会影响代理类的创建,只需要修改InvocationHandler的实现。 |
| 使用场景 | 适用于代理类数量较少且稳定的场景。 | 适用于代理类数量较多或频繁变化的场景。 |
两种动态代理
| 特性/模式 | JDK代理 | CGLIB代理 |
|---|---|---|
| 实现方式 | JDK代理利用反射机制,通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口实现。 |
CGLIB代理通过底层的字节码技术,为目标类生成一个子类,并在子类中覆盖非final的方法,进而实现方法拦截和增强处理。 |
| 接口要求 | 目标类必须实现一个或多个接口,JDK代理基于这些接口创建代理实例。 | 目标类不需要实现接口,CGLIB代理通过继承目标类来创建代理实例。 |
| 性能 | JDK代理的性能相对较低,因为它依赖于反射机制。 | CGLIB代理的性能相对较高,因为它使用了底层的字节码技术。 |
| final方法 | JDK代理不能拦截final方法,因为final方法不能被覆盖。 | CGLIB代理同样不能拦截final方法。 |
| 构造函数 | JDK代理不会拦截构造函数。 | CGLIB代理可以在子类中拦截构造函数。 |
| 使用场景 | 当目标类已经实现了某个接口,或者你希望代码更轻量级时,适合使用JDK代理。 | 当目标类没有实现任何接口,或者你希望在运行时生成代理类以进行方法增强时,适合使用CGLIB代理。 |
| 兼容性 | JDK代理是Java语言的一部分,因此不需要额外的依赖。 | CGLIB代理需要额外的库(如Apache Commons Collections)支持。 |
jdk 动态代理
示例:
package demo.base.proxy;
import org.junit.Test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Demo1 {
@Test
public void test() {
//真实角色
Rent host = new Host();
//动态生成对应的代理类!
DynamicProxyHandler pih = new DynamicProxyHandler(host);
Rent proxy = (Rent) Proxy.newProxyInstance(this.getClass().getClassLoader(),
host.getClass().getInterfaces(), pih);
//执行方法
proxy.rent();
}
public static class DynamicProxyHandler implements InvocationHandler {
private final Object rent;
public DynamicProxyHandler(Object rent) {
this.rent = rent;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
Object result = method.invoke(rent, args);
fare();
return result;
}
//看房
public void seeHouse() {
System.out.println("带房客看房");
}
//收中介费
public void fare() {
System.out.println("收中介费");
}
}
public interface Rent {
public void rent();
}
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房屋出租");
}
}
}
cglib 动态代理
cglib 代理是通过继承实现,故注意继承需要考虑final问题,final类型不能有子类,所以CGLIB不能代理final类型。
若如下 SimpleService 使用final 修饰,会报错:
java.lang.IllegalArgumentException: Cannot subclass final class demo.base.proxy.CgLibDemo$SimpleService
同样的,final方法是不能重载的,所以也不能通过CGLIB代理,遇到这种情况不会抛异常,而是会跳过final方法只代理其他方法。
示例:
package demo.base.proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.junit.Test;
import java.lang.reflect.Method;
/**
* @author lijunjie134
* @date 2025/2/19
*/
public class CgLibDemo {
// 服务类
static class SimpleService {
public void sayHello() {
System.out.println("Hello, this is the original method.");
}
}
// CGLIB代理的拦截器
static class MyMethodInterceptor implements MethodInterceptor {
private Object target;
public MyMethodInterceptor(Object target) {
this.target = target;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 在调用原始方法之前执行的操作
System.out.println("Before method call.");
// 调用原始方法
Object result = method.invoke(target, args);
// 在调用原始方法之后执行的操作
System.out.println("After method call.");
return result;
}
}
@Test
public void test() {
// 创建一个Enhancer对象
Enhancer enhancer = new Enhancer();
// 设置要代理的目标类
enhancer.setSuperclass(CgLibDemo.SimpleService.class);
// 设置回调方法(拦截器)
enhancer.setCallback(new CgLibDemo.MyMethodInterceptor(new CgLibDemo.SimpleService()));
// 创建代理对象
CgLibDemo.SimpleService proxy = (CgLibDemo.SimpleService) enhancer.create();
// 使用代理对象调用方法
proxy.sayHello();
}
}
本文来自博客园,作者:执大象,转载请注明原文链接:https://www.cnblogs.com/li-junjie/articles/18724280

浙公网安备 33010602011771号