Java安全--篇三-动态代理

本文通过一个具体的案例介绍了Java动态代理的应用。对比了静态代理与动态代理的区别,详细展示了如何利用Java反射机制创建动态代理对象,并实现对方法执行时间的统计。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java的动态代理

理论知识比较少,都是例子。更容易理解,可以看完之后再看看理论知识。

这个可以算是反射之后的下一步学习的东西吧,涉及到到的基础

  • 类加载器(知道反射如何得到就行)
    • clazz.getClass().getClassLoader()
  • 反射

我们通过一个案例问题来引出动态代理:

我们现在需要满足网站的三个功能:

  1. 用户登陆
  2. 用户查找
  3. 用户删除

并且我们需要统计每个功能的执行程序所用的时间。

不使用代理

首先编写一个接口来模拟三个功能:

package com.qingfeng.servlet;

public interface UserService {
    String login(String username, String password);
    void searchUser();
    boolean deleteUser();
}

然后编写一个实现类:

package com.qingfeng.servlet;

public class UserServiceImpl implements UserService{
    @Override
    public String login(String username,String password) {
        long beforeTime = System.currentTimeMillis();
        try {
            Thread.sleep(2000);
            if ("admin".equals(username) && "123456".equals(password)) {
                return "login success";
            }
            return "用户名或者密码错误";
        }
        catch (Exception e) {
            e.printStackTrace();
        } finally {
            long afterTime = System.currentTimeMillis();
            System.out.println("login time:" +  (afterTime - beforeTime) + "ms");
        }
        return "用户名或者密码错误";
    }

    @Override
    public void searchUser() {
        long beforeTime = System.currentTimeMillis();
        try {
            Thread.sleep(2000);
            System.out.println("查询用户--");
        }
        catch (Exception e) {
            e.printStackTrace();
        } finally {
            long afterTime = System.currentTimeMillis();
            System.out.println("search time:" +  (afterTime - beforeTime) + "ms");
        }
    }



    @Override
    public boolean deleteUser() {
        long beforeTime = System.currentTimeMillis();
        try {
            Thread.sleep(2000);
            System.out.println("删除用户");
            return true;
        }
        catch (Exception e) {
            e.printStackTrace();
        } finally {
            long afterTime = System.currentTimeMillis();
            System.out.println("delete time:" +  (afterTime - beforeTime) + "ms");
        }
        return false;
    }
}

再定义一个测试类:

package com.qingfeng.servlet;

public class Test {
    public static void main(String[] args) {
        UserServiceImpl usi = new UserServiceImpl();
        String res = usi.login("admin",  "admin");
        System.out.println(res);
        usi.searchUser();
        usi.deleteUser();
    }
}

执行结果:

以上就是典型的静态代理。

但是我们不难发现一些问题:

  • 代码冗余,每个方法里面都有统计时间的代码
  • 每个方法里面的功能不纯粹,不仅要解决业务功能还要解决统计时间的问题
  • 如果新添加一个方法依旧要在代码中写入统计时间的代码

如何解决这些问题呢?

其实发现有冗余代码我们的目的肯定就是整合代码,使用类似函数的东西来实现重复代码的重合,就如下图所示:

用伪代码实现起来就是这样:

public void Test(function()){
    long startTime = System.currentTimeMillis();
    function();
    long endTime = System.currentTimeMillis();
    System.out.println(endTime - startTime);
}

我们希望这里的function可以是我们想要执行的任意方法,其实这个思想就是用动态代理来实现的

动态代理

动态代理实现图:

使用动态代理就可以达到如图的效果,把类里面的方法统计时间的功能交给代理,自己只用实现真实逻辑即可。

代码实现:

定义的接口不变:

package com.qingfeng.servlet;

public interface UserService {
    String login(String username, String password);
    void searchUser();
    boolean deleteUser();
}

实现类可以把统计时间的代码全都删去:

package com.qingfeng.servlet;

public class UserServiceImpl implements UserService{
    @Override
    public String login(String username,String password) {
        try {
            if ("admin".equals(username) && "123456".equals(password)) {
                return "login success";
            }
            return "用户名或者密码错误";
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        return "用户名或者密码错误";
    }

    @Override
    public void searchUser() {
        try {
            Thread.sleep(2000);
            System.out.println("查询用户--");
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }



    @Override
    public boolean deleteUser() {
        try {
            Thread.sleep(2000);
            System.out.println("删除用户");
            return true;
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }
}

测试类的代码我们在第一行做一个微改,把要执行的方法交给代理:

package com.qingfeng.servlet;

public class Test {
    public static void main(String[] args) {
        // 把业务对象直接做成一个代理对象返回,代理对象的类型也是 UserService
        UserService usi = ProxyUtil.gotoProxy(new UserServiceImpl()); // 修改的地方
        String res = usi.login("admin",  "admin");
        System.out.println(res);
        usi.searchUser();
        usi.deleteUser();
    }
}

然后我们编写一个动态代理的Java文件,这个是动态代理的关键

package com.qingfeng.servlet;

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


public class ProxyUtil {
    public static UserService gotoProxy(final UserService obj) {
        // 返回一个代理对象
        return (UserService) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 参数一:代理对象本身
                // 参数二:正在被代理的方法
                // 参数三:代理方法传入的参数
                long startTime = System.currentTimeMillis();
                Object result = method.invoke(obj, args);
                long endTime = System.currentTimeMillis();
                System.out.println(method.getName() + "方法耗时:"  + (endTime - startTime) + "ms");
                // 把业务执行的结果返回给调用者
                return result;

            }
        });
    }
}

我们用到的核心类:

/*

* public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

* 参数一: 类加载器,负责加载代理类到内存中使用

* 参数二: 获取被代理的对象的全部接口

* 代理是要实现全部接口的全部方法,所以要知道所有的接口

* 参数三: 代理的核心处理逻辑,如何实现的问题

* InvocationHandler 触发方法

*/

我们可以注意到这里的类型是Object,但是Java里面大家都是Object类型的,所以这里可以用类替代

运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值