Java 代理模式详解(附案例源代码)

前言

Java代理模式是一种设计模式**,**在 Java 开发中被广泛应用。它允许我们通过添加一个代理对象来控制对另一个对象的访问,从而提供了一种间接访问实际对象的方法。

代理模式可以分为静态代理和动态代理两种。静态代理是在代码实现阶段就确定了代理类与目标类之间的关系,而动态代理是在运行时动态生成代理类。在Java中,使用反射机制来实现动态代理。

静态代理的例子包括创建一个接口和一个实现该接口的类,然后创建一个代理类,该代理类实现了相同的接口并包含目标对象。代理类可以在不修改目标对象的前提下扩展目标对象的功能,但这种方法可能会产生冗余的代理类,不易维护,且一旦接口增加方法,目标对象与代理对象都要进行修改。

动态代理则是在程序运行过程中产生的代理对象,它通过反射机制生成。在Java中,可以使用java.lang.reflect包下的Proxy类和一个InvocationHandler接口来生成动态代理对象。这种方式可以针对接口做代理,而不需要在编译时实现代理类。

1. 静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。

java 复制代码
/**
 * 代理对象,静态代理
 * */
public class Proxy01 implements UserDao {
    //接受保存目标对象
    UserDao target;
    public Proxy01() {
    }
    public Proxy01(UserDao target) {
        this.target = target;
    }
    @Override
    public void getUser() {
        System.out.println("获取user开始");
        target.getUser();
        System.out.println("获取user结束");
    }
}
java 复制代码
public class Proxy01_test {
    public static void main(String[] args) {
        //目标对象
        UserDao target = new UserDaoImpl();
        //代理对象,把目标对象传给代理对象,建立代理关系
        Proxy01 proxy = new Proxy01(target);
        //执行的是代理的方法
        proxy.getUser();
    }
}

当客户端调用代理类的方法时,代理类会通过目标对象来真正执行计算操作。在代理类的方法前或方法后,可以添加一些额外的操作,例如日志记录、性能监控等。

总结:

  • 静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现接口或继承相同 的父类

缺点:

  • 因为代理对象需要和被代理对象实现相同的接口或父类,所以会有太多的代理类
  • 一旦接口中增加了方法后,被代理对象和代理对象都需要维护(非常麻烦,不方便)

2.动态代理

动态代理是一种更加灵活和高效的代理模式,它可以在运行时动态生成代理类,避免了手动编写大量代理类的繁琐工作。在 Java 中,动态代理主要有两种实现方式:JDK 动态代理和 CGLIB 动态代理。

2.1 JDK 动态代理

JDK 动态代理是 Java 标准库提供的一种动态代理实现方式,它基于接口代理实现。在 JDK 动态代理中,我们需要通过 java.lang.reflect.Proxy 类来生成代理对象。

java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

class CalculatorInvocationHandler implements InvocationHandler {
    private final Object target;

    public CalculatorInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method.getName() + "执行方法前...");
        Object result = method.invoke(target, args);
        System.out.println(method.getName() + "执行方法后...");
        return result;
    }
}

public class JdkMain {
    public static void main(String[] args) {
        Calculator calculator = new RealCalculator();
        CalculatorInvocationHandler handler = new CalculatorInvocationHandler(calculator);
        Calculator proxy = (Calculator) Proxy.newProxyInstance(
            calculator.getClass().getClassLoader(),
            calculator.getClass().getInterfaces(),
            handler
        );

        int a = 1, b = 2;
        System.out.println("add: " + proxy.add(a, b));
        System.out.println("sub: " + proxy.sub(a, b));
        System.out.println("mul: " + proxy.mul(a, b));
        System.out.println("div: " + proxy.div(a, b));
    }
}

我们定义了一个 CalculatorInvocationHandler 类来实现java.lang.reflect.InvocationHandler 接口。当客户端调用代理对象的方法时,JDK 动态代理会自动调用 invoke 方法,并将目标对象方法的调用转发给 RealCalculator 对象。在 invoke 方法前或方法后,我们可以添加一些额外的操作,例如日志记录、性能监控等。

总结:

代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理。

2.2 Cglib动态代理

Cglib动态代理是一种不基于接口的动态代理实现方式,它可以代理没有实现接口的类。在 Cglib动态代理中,我们需要通过 net.sf.cglib.proxy.Enhancer 类来生成代理对象。

没有实现任何接口的类:

java 复制代码
public class UserService {
    public void addUser(String username, String password) {
        System.out.println("add user: " + username + ", " + password);
    }

    public void updateUser(String username, String password) {
        System.out.println("update user: " + username + ", " + password);
    }

    public void deleteUser(String username) {
        System.out.println("delete user: " + username);
    }
}

使用 Cglib动态代理来生成代理对象:

java 复制代码
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

class UserServiceInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println(method.getName() + "执行方法前");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println(method.getName() + "执行方法后");
        return result;
    }
}

public class CglibMain {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserService.class);
        enhancer.setCallback(new UserServiceInterceptor());
        UserService proxy = (UserService) enhancer.create();

        proxy.addUser("Tom", "123456");
        proxy.updateUser("Tom", "tom123456");
        proxy.deleteUser("Tom");
    }
}

Cglib子类代理需要注意的是:

  • 需要引入cglib的jar包
  • 代理的类不能是final,否则报错
  • 目标对象的方法如果有final/static,那么不会被拦截,即不会执行目标对象额外的业务方法。

Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP的框架使用,为其提供方法的interception(拦截),例如大家所熟知的Spring AOP。

相关推荐
转码的小石2 分钟前
12/21java基础
java
高山我梦口香糖9 分钟前
[react]searchParams转普通对象
开发语言·前端·javascript
李小白6610 分钟前
Spring MVC(上)
java·spring·mvc
GoodStudyAndDayDayUp23 分钟前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea
信号处理学渣31 分钟前
matlab画图,选择性显示legend标签
开发语言·matlab
红龙创客31 分钟前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++
jasmine s41 分钟前
Pandas
开发语言·python
装不满的克莱因瓶1 小时前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb
n北斗1 小时前
常用类晨考day15
java
biomooc1 小时前
R 语言 | 绘图的文字格式(绘制上标、下标、斜体、文字标注等)
开发语言·r语言