浅谈Java动态代理的实现

浅谈 Java 动态代理的实现

什么是动态代理?

Java 中的代理分为静态代理和动态代理两种。静态代理需要事先写好代理类,通过程序员手动编写的方式,代理对象和目标对象之间的关系就已经确定了。而动态代理是在程序运行时动态生成的代理对象,不需要事先写好代理类。动态代理可以根据目标对象动态地生成代理对象,无需为每个目标对象都编写代理类,增强代码的可重用性。

实现步骤

动态代理的实现需要以下步骤:

  1. 定义一个接口,为某个类的接口创建一个代理对象。
  2. 创建实现 InvocationHandler 接口的类,它必须实现 invoke() 方法,该方法在代理对象中被调用。
  3. 实现 jdk 动态代理的 Proxy 类,它提供了创建动态代理类的静态方法,同时可以实现多个接口。

示例一

假设有一个接口计算器(Calculator),它有一个 add() 方法,实现两个数字相加。我们使用动态代理为其创建代理对象,实现输出日志的功能。

public interface Calculator {
    int add(int a, int b);
}
public class CalculatorHandler implements InvocationHandler {
    private Calculator calculator;

    public CalculatorHandler(Calculator calculator) {
        this.calculator = calculator;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before method executed.");
        Object result = method.invoke(calculator, args);
        System.out.println("after method executed.");
        return result;
    }
}
public class Demo {
    public static void main(String[] args) {
        Calculator calculator = new CalculatorImpl();
        Calculator proxy = (Calculator) Proxy.newProxyInstance(
                Calculator.class.getClassLoader(),
                new Class[]{Calculator.class},
                new CalculatorHandler(calculator));
        int result = proxy.add(1, 2);
        System.out.println("result: " + result);
    }
}

此时,我们调用代理对象的 add() 方法,控制台将打印出 before method executed. 和 after method executed.,表示代理对象的方法已经被调用。

示例二

假设有一个接口用户服务(UserService),它提供了查找用户和更新用户信息的方法。我们使用动态代理为其创建代理对象,实现缓存用户信息的功能,即先从缓存中查找用户信息,若找不到,则从数据库中查找用户信息并更新缓存,若找到则直接返回缓存中的用户信息。

public interface UserService {
    User findUserById(int id);
    void updateUser(User user);
}
public class UserServiceHandler implements InvocationHandler {
    private UserService userService;
    private Map<Integer, User> cache = new HashMap<>();

    public UserServiceHandler(UserService userService) {
        this.userService = userService;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Integer userId = (Integer) args[0];
        if ("findUserById".equals(method.getName())) {
            User user = cache.get(userId);
            if (user != null) {
                return user;
            }
            user = (User) method.invoke(userService, args);
            cache.put(userId, user);
            return user;
        } else if ("updateUser".equals(method.getName())) {
            method.invoke(userService, args);
            User user = (User) args[0];
            cache.put(user.getId(), user);
            return null;
        } else {
            return method.invoke(userService, args);
        }
    }
}
public class Demo {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        UserService proxy = (UserService) Proxy.newProxyInstance(
                UserService.class.getClassLoader(),
                new Class[]{UserService.class},
                new UserServiceHandler(userService));
        User user1 = proxy.findUserById(1);
        System.out.println("user1: " + user1);
        User user2 = proxy.findUserById(1);
        System.out.println("user2: " + user2);
        user1.setName("John");
        proxy.updateUser(user1);
        User user3 = proxy.findUserById(1);
        System.out.println("user3: " + user3);
    }
}

此时,我们调用代理对象的 findUserById() 方法,第一次输出将从数据库中获取用户信息,第二次输出将从缓存中获取用户信息,第三次输出将从数据库中重新获取用户信息并更新缓存,验证了缓存用户信息的功能。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:浅谈Java动态代理的实现 - Python技术站

(0)
上一篇 2023年5月26日
下一篇 2023年5月26日

相关文章

  • Springboot配置security basic path无效解决方案

    针对“Springboot配置security basic path无效解决方案”,以下是完整的攻略: 1. 问题描述 当我们在Spring Boot项目中将Spring Security集成进来时,有时候会发现配置的basic path无效,即虽然配置了basic path,但在请求时仍然需要登录验证,这种情况该怎么解决呢? 2. 解决方案 2.1 配置W…

    Java 2023年5月20日
    00
  • tomcat部署简单的html静态网页的方法

    下面我将详细讲解“Tomcat部署简单的HTML静态网页的方法”的完整攻略。步骤如下: 步骤一:下载和安装Tomcat 进入Tomcat的官方网站:https://tomcat.apache.org/ 点击左侧的“Downloads”进入下载页面,选择对应版本的Tomcat压缩包进行下载。 解压下载好的Tomcat压缩包。 在Tomcat的bin目录下找到s…

    Java 2023年5月19日
    00
  • Spring Native打包本地镜像的操作方法(无需通过Graal的maven插件buildtools)

    Spring Native是近期才发布的一个新特性,它的主要功能就是将Spring应用程序打包为本地镜像,打包完成后,我们就可以将这个本地镜像部署到不同的环境上,比如Docker、Kubernetes等。 下面是使用Spring Native打包本地镜像的具体步骤: 配置Java环境 首先需要确保已经安装了JDK11版本及以上,然后安装GraalVM相关组件…

    Java 2023年5月19日
    00
  • 腾讯、百度、华为、搜狗和滴滴Android面试题汇总

    腾讯、百度、华为、搜狗和滴滴Android面试题汇总攻略 前言 面试是进入互联网公司的重要一步,而在面试中往往会遇到很多细节和难点。这些细节和难点很大程度上与我们日常的工作不相关,但是却是面试中极度重要的考核点。为此,本文梳理腾讯、百度、华为、搜狗和滴滴等高端公司的面试题目,希望能对你在面试中起到帮助的作用。 分类 面经的题型并不固定,但是它们可以被大致归类…

    Java 2023年5月26日
    00
  • maven报错:Failed to execute goal on project问题及解决

    针对”Maven报错:Failed to execute goal on project”问题,可能导致报错的原因有很多种,但通常表现为类似于以下的错误提示: Failed to execute goal on project xxx: Could not resolve dependencies for project xxx: Failure to fi…

    Java 2023年5月19日
    00
  • Spring集成MyBatis完整实例(分享)

    下面我将详细讲解Spring集成MyBatis的完整攻略,并附上两个示例。 1. 准备工作 在开始之前,需要完成以下准备工作: 安装Java JDK和Maven。 创建一个Spring项目,可以使用Maven构建。 添加Spring、MyBatis相关依赖,如下所示: <dependencies> <!– Spring相关依赖 –&gt…

    Java 2023年5月20日
    00
  • maven打包如何指定jdk的版本

    Maven是一个非常流行的Java项目管理和构建工具。在使用Maven进行代码打包时,我们经常遇到需要指定JDK版本的情况。接下来,我将详细介绍在Maven中如何指定JDK版本。 方式一:在pom.xml文件中指定JDK版本 可以在Maven项目的pom.xml文件中指定JDK版本,这样在构建项目时就可以使用特定版本的JDK。可以使用以下示例代码来指定JDK…

    Java 2023年5月19日
    00
  • java线程池实现批量下载文件

    关于Java线程池实现批量下载文件,可以按照以下步骤进行: 1. 创建线程池 首先需要使用 Executors.newFixedThreadPool() 方法创建一个固定大小的线程池,例如: private static int THREAD_COUNT = 5; // 线程池大小 private static ExecutorService executo…

    Java 2023年5月19日
    00
合作推广
合作推广
分享本页
返回顶部