JAVA动态代理

1 动态代理介绍、准备功能

假设现在有一个大明星叫杨超越,它有唱歌和跳舞的本领,作为大明星是要用唱歌和跳舞来赚钱的,但是每次做节目,唱歌的时候要准备话筒、收钱,再唱歌;跳舞的时候也要准备场地、收钱、再唱歌。杨超越越觉得我擅长的做的事情是唱歌,和跳舞,但是每次唱歌和跳舞之前或者之后都要做一些繁琐的事情,有点烦。于是杨超越就找个一个经济公司,请了一个代理人,代理杨超越处理这些事情,如果有人想请杨超越演出,直接找代理人就可以了。如下图所示

我们说杨超越的代理是中介公司派的,那中介公司怎么知道,要派一个有唱歌和跳舞功能的代理呢?

解决这个问题,Java使用的是接口,杨超越想找代理,在Java中需要杨超越实现了一个接口,接口中规定要唱歌和跳舞的方法。Java就可以通过这个接口为杨超越生成一个代理对象,只要接口中有的方法代理对象也会有。

接下来我们就先把有唱歌和跳舞功能的接口,和实现接口的大明星类定义出来。

2 生成动态代理对象

下面我们写一个为BigStar生成动态代理对象的工具类。这里需要用Java为开发者提供的一个生成代理对象的类叫Proxy类。

通过Proxy类的newInstance(...)方法可以为实现了同一接口的类生成代理对象。 调用方法时需要传递三个参数,该方法的参数解释可以查阅API文档,如下。

java 复制代码
public class ProxyUtil {
    public static Star createProxy(BigStar bigStar){
       /* newProxyInstance(ClassLoader loader,
                Class<?>[] interfaces,
                InvocationHandler h)
                参数1:用于指定一个类加载器
                参数2:指定生成的代理长什么样子,也就是有哪些方法
                参数3:用来指定生成的代理对象要干什么事情
                */
        // Star starProxy = ProxyUtil.createProxy(s);
        // starProxy.sing("好日子") starProxy.dance()
        Star starProxy = (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
                new Class[]{Star.class}, new InvocationHandler() {
                    @Override // 回调方法
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 代理对象要做的事情,会在这里写代码
                        if(method.getName().equals("sing")){
                            System.out.println("准备话筒,收钱20万");
                        }else if(method.getName().equals("dance")){
                            System.out.println("准备场地,收钱1000万");
                        }
                        return method.invoke(bigStar, args);
                    }
                });
        return starProxy;
    }
}

调用我们写好的ProxyUtil工具类,为BigStar对象生成代理对象

java 复制代码
public class Test {
    public static void main(String[] args) {
        BigStar s = new BigStar("杨超越");
        Star starProxy = ProxyUtil.createProxy(s);
​
        String rs = starProxy.sing("好日子");
        System.out.println(rs);
​
        starProxy.dance();
    }
}

运行测试类,结果如下图所示

3 动态代理应用

现有如下代码

java 复制代码
/**
 *  用户业务接口
 */
public interface UserService {
    // 登录功能
    void login(String loginName,String passWord) throws Exception;
    // 删除用户
    void deleteUsers() throws Exception;
    // 查询用户,返回数组的形式。
    String[] selectUsers() throws Exception;
}

下面有一个UserService接口的实现类,下面每一个方法中都有计算方法运行时间的代码。

java 复制代码
/**
 * 用户业务实现类(面向接口编程)
 */
public class UserServiceImpl implements UserService{
    @Override
    public void login(String loginName, String passWord) throws Exception {
        long time1 = System.currentTimeMillis();
        if("admin".equals(loginName) && "123456".equals(passWord)){
            System.out.println("您登录成功,欢迎光临本系统~");
        }else {
            System.out.println("您登录失败,用户名或密码错误~");
        }
        Thread.sleep(1000);
        long time2 = System.currentTimeMillis();
        System.out.println("login方法耗时:"+(time2-time1));
    }
​
    @Override
    public void deleteUsers() throws Exception{
        long time1 = System.currentTimeMillis();
        System.out.println("成功删除了1万个用户~");
        Thread.sleep(1500);
        long time2 = System.currentTimeMillis();
        System.out.println("deleteUsers方法耗时:"+(time2-time1));
    }
​
    @Override
    public String[] selectUsers() throws Exception{
        long time1 = System.currentTimeMillis();
        System.out.println("查询出了3个用户");
        String[] names = {"张全蛋", "李二狗", "牛爱花"};
        Thread.sleep(500);
        long time2 = System.currentTimeMillis();
        System.out.println("selectUsers方法耗时:"+(time2-time1));
        return names;
    }
}

观察上面代码发现有什么问题吗?

我们会发现每一个方法中计算耗时的代码都是重复的,我们可是学习了动态代理的高级程序员,怎么能忍受在每个方法中写重复代码呢!况且这些重复的代码并不属于UserSerivce的主要业务代码。

所以接下来我们打算,把计算每一个方法的耗时操作,交给代理对象来做。

先在UserService类中把计算耗时的代码删除,代码如下

java 复制代码
/**
 * 用户业务实现类(面向接口编程)
 */
public class UserServiceImpl implements UserService{
    @Override
    public void login(String loginName, String passWord) throws Exception {
        if("admin".equals(loginName) && "123456".equals(passWord)){
            System.out.println("您登录成功,欢迎光临本系统~");
        }else {
            System.out.println("您登录失败,用户名或密码错误~");
        }
        Thread.sleep(1000);
    }
​
    @Override
    public void deleteUsers() throws Exception{
        System.out.println("成功删除了1万个用户~");
        Thread.sleep(1500);
    }
​
    @Override
    public String[] selectUsers() throws Exception{
​
        System.out.println("查询出了3个用户");
        String[] names = {"张全蛋", "李二狗", "牛爱花"};
        Thread.sleep(500);
​
        return names;
    }
}

然后为UserService生成一个动态代理对象,在动态代理中调用目标方法,在调用目标方法之前和之后记录毫秒值,并计算方法运行的时间。代码如下

java 复制代码
public class ProxyUtil {
    public static UserService createProxy(UserService userService){
        UserService userServiceProxy
            = (UserService) Proxy.newProxyInstance(
            ProxyUtil.class.getClassLoader(),
            new Class[]{UserService.class}, 
            new InvocationHandler() {
                                                                                        @Override
            public Object invoke(                                                                             Object proxy, 
                              Method method, 
                                  Object[] args) throws Throwable {                             if(
                    method.getName().equals("login") ||                                             method.getName().equals("deleteUsers")||
                    method.getName().equals("selectUsers")){
                    //方法运行前记录毫秒值         
                    long startTime = System.currentTimeMillis();
                    //执行方法
                    Object rs = method.invoke(userService, args);
                    //执行方法后记录毫秒值
                    long endTime = System.currentTimeMillis();
​
                    System.out.println(method.getName() + "方法执行耗时:" + (endTime - startTime)/ 1000.0 + "s");
                    return rs;
               }else {
                    Object rs = method.invoke(userService, args);
                    return rs;                                                                }
           }                                                                 });
        //返回代理对象
        return userServiceProxy;
    }
}

在测试类中为UserService创建代理对象

java 复制代码
/**
 * 目标:使用动态代理解决实际问题,并掌握使用代理的好处。
 */
public class Test {
    public static void main(String[] args) throws Exception{
        // 1、创建用户业务对象。
        UserService userService = ProxyUtil.createProxy(new UserServiceImpl());
​
        // 2、调用用户业务的功能。
        userService.login("admin", "123456");
        System.out.println("----------------------------------");
​
        userService.deleteUsers();
        System.out.println("----------------------------------");
​
        String[] names = userService.selectUsers();
        System.out.println("查询到的用户是:" + Arrays.toString(names));
        System.out.println("----------------------------------");
​
    }
}

执行结果如下图所示

动态代理对象的执行流程如下图所示,每次用代理对象调用方法时,都会执行InvocationHandler中的invoke方法。

相关推荐
Scott9999HH7 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔7 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社8 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海8 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖9 小时前
JDK 26 新特性详解
java·开发语言
马优晨10 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区11 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大12 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai12 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说12 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json