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


    Java的动态代理

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

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

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

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

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

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

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

    不使用代理

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

    1. package com.qingfeng.servlet;
    2. public interface UserService {
    3. String login(String username, String password);
    4. void searchUser();
    5. boolean deleteUser();
    6. }

    然后编写一个实现类:

    1. package com.qingfeng.servlet;
    2. public class UserServiceImpl implements UserService{
    3. @Override
    4. public String login(String username,String password) {
    5. long beforeTime = System.currentTimeMillis();
    6. try {
    7. Thread.sleep(2000);
    8. if ("admin".equals(username) && "123456".equals(password)) {
    9. return "login success";
    10. }
    11. return "用户名或者密码错误";
    12. }
    13. catch (Exception e) {
    14. e.printStackTrace();
    15. } finally {
    16. long afterTime = System.currentTimeMillis();
    17. System.out.println("login time:" + (afterTime - beforeTime) + "ms");
    18. }
    19. return "用户名或者密码错误";
    20. }
    21. @Override
    22. public void searchUser() {
    23. long beforeTime = System.currentTimeMillis();
    24. try {
    25. Thread.sleep(2000);
    26. System.out.println("查询用户--");
    27. }
    28. catch (Exception e) {
    29. e.printStackTrace();
    30. } finally {
    31. long afterTime = System.currentTimeMillis();
    32. System.out.println("search time:" + (afterTime - beforeTime) + "ms");
    33. }
    34. }
    35. @Override
    36. public boolean deleteUser() {
    37. long beforeTime = System.currentTimeMillis();
    38. try {
    39. Thread.sleep(2000);
    40. System.out.println("删除用户");
    41. return true;
    42. }
    43. catch (Exception e) {
    44. e.printStackTrace();
    45. } finally {
    46. long afterTime = System.currentTimeMillis();
    47. System.out.println("delete time:" + (afterTime - beforeTime) + "ms");
    48. }
    49. return false;
    50. }
    51. }

    再定义一个测试类:

    1. package com.qingfeng.servlet;
    2. public class Test {
    3. public static void main(String[] args) {
    4. UserServiceImpl usi = new UserServiceImpl();
    5. String res = usi.login("admin", "admin");
    6. System.out.println(res);
    7. usi.searchUser();
    8. usi.deleteUser();
    9. }
    10. }

    执行结果:

    以上就是典型的静态代理

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

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

    如何解决这些问题呢?

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

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

    1. public void Test(function()){
    2. long startTime = System.currentTimeMillis();
    3. function();
    4. long endTime = System.currentTimeMillis();
    5. System.out.println(endTime - startTime);
    6. }

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

    动态代理

    动态代理实现图:

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

    代码实现:

    定义的接口不变:

    1. package com.qingfeng.servlet;
    2. public interface UserService {
    3. String login(String username, String password);
    4. void searchUser();
    5. boolean deleteUser();
    6. }

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

    1. package com.qingfeng.servlet;
    2. public class UserServiceImpl implements UserService{
    3. @Override
    4. public String login(String username,String password) {
    5. try {
    6. if ("admin".equals(username) && "123456".equals(password)) {
    7. return "login success";
    8. }
    9. return "用户名或者密码错误";
    10. }
    11. catch (Exception e) {
    12. e.printStackTrace();
    13. }
    14. return "用户名或者密码错误";
    15. }
    16. @Override
    17. public void searchUser() {
    18. try {
    19. Thread.sleep(2000);
    20. System.out.println("查询用户--");
    21. }
    22. catch (Exception e) {
    23. e.printStackTrace();
    24. }
    25. }
    26. @Override
    27. public boolean deleteUser() {
    28. try {
    29. Thread.sleep(2000);
    30. System.out.println("删除用户");
    31. return true;
    32. }
    33. catch (Exception e) {
    34. e.printStackTrace();
    35. }
    36. return false;
    37. }
    38. }

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

    1. package com.qingfeng.servlet;
    2. public class Test {
    3. public static void main(String[] args) {
    4. // 把业务对象直接做成一个代理对象返回,代理对象的类型也是 UserService
    5. UserService usi = ProxyUtil.gotoProxy(new UserServiceImpl()); // 修改的地方
    6. String res = usi.login("admin", "admin");
    7. System.out.println(res);
    8. usi.searchUser();
    9. usi.deleteUser();
    10. }
    11. }

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

    1. package com.qingfeng.servlet;
    2. import java.lang.reflect.InvocationHandler;
    3. import java.lang.reflect.Method;
    4. import java.lang.reflect.Proxy;
    5. public class ProxyUtil {
    6. public static UserService gotoProxy(final UserService obj) {
    7. // 返回一个代理对象
    8. return (UserService) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {
    9. @Override
    10. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    11. // 参数一:代理对象本身
    12. // 参数二:正在被代理的方法
    13. // 参数三:代理方法传入的参数
    14. long startTime = System.currentTimeMillis();
    15. Object result = method.invoke(obj, args);
    16. long endTime = System.currentTimeMillis();
    17. System.out.println(method.getName() + "方法耗时:" + (endTime - startTime) + "ms");
    18. // 把业务执行的结果返回给调用者
    19. return result;
    20. }
    21. });
    22. }
    23. }

    我们用到的核心类:

    1. /*
    2. * public static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)
    3. * 参数一: 类加载器,负责加载代理类到内存中使用
    4. * 参数二: 获取被代理的对象的全部接口
    5. * 代理是要实现全部接口的全部方法,所以要知道所有的接口
    6. * 参数三: 代理的核心处理逻辑,如何实现的问题
    7. * InvocationHandler 触发方法
    8. */

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

    运行结果:

  • 相关阅读:
    C# 辗转相除法求最大公约数
    医院检验信息系统源码 医院检验LIS系统源码 LIS源码
    网管软件有什么?网管软件推荐
    使用Spyder进行动态网页爬取:实战指南
    网规配置案例分析——国庆
    当我们做后仿时我们究竟在仿些什么(一)
    ChatRule:基于知识图推理的大语言模型逻辑规则挖掘11.10
    数值分析在线考试系统(asp.net+SqlServer)
    blender 快捷键 常见问题
    [carla入门教程]-4 carla中的地图(附:鸟瞰图和道路图)
  • 原文地址:https://blog.csdn.net/qq_64201116/article/details/128183945