java反序列化学习—动态代理

1. Java 代理模式

理解代理模式,首先要明确“代理”的含义。下面这张图中的中介,正代表着软件设计中的​​代理角色​​。它代理的对象(称为​​被代理类​​或​​委托类​​)就像是房东,​​只需专注于核心业务逻辑​​(比如管理房屋的基本信息),而将​​衍生或辅助的业务逻辑​​(例如如何租赁、筛选租客、处理合同等)委托给代理(即中介)来完成。

2. 静态代理

以租客找中介向房东租房子为例,想要实现租客找中介,中介转租房东房源,在 Java 中就需要4个文件,分别是房源、房东、中介、租客,其中房源应该是接口,其余三项为类。
HousingResource.java:这是一个接口,可以抽象的理解为房源,作为房源,它有一个方法rent()为租房

public interface HousingResource {
    public void rent();
}

Landlord.java这个类就是房东,作为房东,他需要实现rent.java这一个接口,并且要实现接口的rent()方法。

public class Landlord implements HousingResource{
    @Override
    public void rent() {
        System.out.println("房东出租房了");
    }
}

IntermediaryAgent.java:这个类是中介,也就是代理,他需要有房东的房源,然而我们通常不会继承房东,而会将房东作为一个私有的属性landlord,我们通landlord.rent()`来实现租房的方法.

// 中介
// 中介
public class  IntermediaryAgent {

    private Landlord landlord;

    public IntermediaryAgent(){}
    public IntermediaryAgent(Landlord landlord){
        this.landlord = landlord;
    }

    public void rent(){
        System.out.println("中介转租");
        landlord.rent();
    }
}

Tenant.java: 这个类是租客去找中介看房。

public class Tenant {
    public static void main(String[] args) {
        Landlord landlord = new Landlord();
        IntermediaryAgent proxy = new IntermediaryAgent(landlord);
        proxy.rent();
    }
}

至此静态代理租房就完成了。

但是,我们中介不可能只有这一个行为,有一些行为是中介可以做的,而房东不能做的,比如看房,收中介费等等。所以我们要在 Proxy.java当中实现这些功能。

// 中介
public class  IntermediaryAgent {

    private Landlord landlord;

    public IntermediaryAgent(){}
    public IntermediaryAgent(Landlord landlord){
        this.landlord = landlord;
    }

    public void rent(){
        System.out.println("中介转租");
        landlord.rent();
        fare();
        contract();
    }
    // 看房
    public void seeHouse(){
        System.out.println("中介带你看房");
    }

    // 收中介费
    public void fare(){
        System.out.println("收中介费");
    }

    // 签租赁合同
    public void contract(){
        System.out.println("签租赁合同");
    }
}

但我们实际项目中被代理类不可能只存在一个功能,那这样的话,代理类也要实现相关功能,这样的话代码量就很大,下面通过一个简单实现业务中CRUD的日志功能来学习。
UserService.java,这是一个接口,我们定义四个抽象方法。

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}

我们需要一个真实对象来完成这些增删改查操作。
UserServiceImpl.java实现这四个功能。

public class UserServiceImpl implements UserService{

    @Override
    public void add() {
        System.out.println("增加了一个用户");
    }

    @Override
    public void delete() {
        System.out.println("删除了一个用户");
    }

    @Override
    public void update() {
        System.out.println("更新了一个用户");
    }

    @Override
    public void query() {
        System.out.println("查询了一个用户");
    }
}

需求来了,现在我们需要增加一个日志功能,怎么实现!
增加一个代理类来处理日志。
UserServiceProxy.java处理日志

// 代理
public class UserServiceProxy implements UserService{
    private UserServiceImpl userService;

 public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
 }

    public void add() {
        log("add");
 userService.add();
 }

    public void delete() {
        log("delete");
 userService.delete();
 }

    public void update() {
        log("update");
 userService.update();
 }

    public void query() {
        log("query");
 userService.query();
 }

    // 增加日志方法
 public void log(String msg){
        System.out.println("[Debug]使用了 " + msg +"方法");
 }
}

再写一个启动类看看效果

public class Client {
    public static void main(String[] args) {
        UserServiceImpl userService = new UserServiceImpl();

 UserServiceProxy proxy = new UserServiceProxy();
 proxy.setUserService(userService);
 proxy.add();

 }
}

可以发现当业务功能多的时候,代码量会很大,不方便,且每多一个房东便要多一个中介,这时便需要一个简便的代理方法,这就是动态代理。

3. 动态代理

动态代理的角色和静态代理的一样。需要一个实体类,一个代理类,一个启动器。
JDK的动态代理需要了解两个类核心 : InvocationHandler 调用处理程序类和 Proxy 代理类
InvocationHandler是由代理实例的调用处理程序实现的接口
每个代理实例都有一个关联的调用处理程序。

当在代理实例上调用方法的时候,方法调用将被分派到其调用处理程序的invoke()方法。

参数:
proxy– 调用该方法的代理实例
method-所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
args-包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean。

Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
动态代理类 (以下简称为代理类 )是一个实现在类创建时在运行时指定的接口列表的类,具有如下所述的行为。 代理接口是由代理类实现的接口。 代理实例是代理类的一个实例。

返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。

参数
loader– 类加载器来定义代理类
interfaces– 代理类实现的接口列表
h– 调度方法调用的调用处理函数

动态代理的代码实现
要写动态代理的代码,需要抓牢两个要点
①:我们代理的是接口,而不是单个用户。
②:代理类是动态生成的,而非静态定死。
首先是我们的接口类

interface Hello {
    String sayHello(String name);
}

接口实现类

public class HelloImpl implements Hello{
    @Override
    public String sayHello(String name) {
        System.out.println("[真实对象] 方法执行");
        return "Hello, " + name;
    }
}

动态代理的实现类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;

public class LogHandler implements InvocationHandler {
    private final Object target;
    public LogHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 前置增强
        System.out.println("[代理] 调用方法: " + method.getName() +
                " 参数: " + Arrays.toString(args));

        // 调用真实对象的方法(关键步骤)
        Object result = method.invoke(target, args);

        // 后置增强
        System.out.println("[代理] 返回结果: " + result);
        return result;
    }
}

最后编写我们的启动类>

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Client {
    public static void main(String[] args) {
        Hello real = new HelloImpl();
        InvocationHandler handler = new LogHandler(real);
        Hello proxy = (Hello) Proxy.newProxyInstance(real.getClass().getClassLoader(), new Class[]{Hello.class},handler);
        System.out.println("[客户端] 调用代理方法");
        String result = proxy.sayHello("World");

        System.out.println("[客户端] 最终结果: " + result);

    }
}

来看看效果

posted @ 2025-06-12 00:08  nago  阅读(34)  评论(0)    收藏  举报