Java JDK动态代理在拦截器和声明式接口中的应用小结

 更新时间:2025年01月21日 11:31:58   作者:码到三十五  
Java动态代理技术通过反射机制在运行时动态生成代理类,实现对目标对象方法的拦截和增强,本文给大家介绍Java JDK动态代理在拦截器和声明式接口中的应用小结,感兴趣的朋友跟随小编一起看看吧

一、动态代理概念回顾

Java动态代理技术是基于反射机制的基础。核心在于利用反射机制和接口编程在运行时动态生成代理类,并通过InvocationHandler接口实现代理逻辑的灵活扩展。通过动态代理,Java程序可以在运行时动态地生成代理类,并控制对目标对象的访问,从而实现对目标对象方法的拦截和增强。
其优势在于灵活性、可扩展性、解耦、AOP支持和远程方法调用等方面.

Java动态代理原理主要基于Java的反射机制,通过动态地生成代理类来实现对接口的动态代理。

二、 核心组件

  • java.lang.reflect.Proxy 类:这是Java动态代理的核心类,提供了创建动态代理实例的静态方法。
  • java.lang.reflect.InvocationHandler:该接口定义了一个方法 invoke(Object proxy, Method method, Object[] args),代理实例在调用接口方法时,会调用此方法。

三、工作流程

  • 定义业务接口:首先,需要定义一个或多个业务接口,这些接口将被动态代理。
  • 实现InvocationHandler接口:创建一个实现了InvocationHandler接口的类,在该类的invoke方法中编写代理逻辑。invoke方法会在代理对象调用接口方法时被自动调用。
  • 生成代理实例:通过Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法生成代理实例。这个方法接收三个参数:
    • ClassLoader loader:类加载器,用于加载代理类。
    • Class<?>[] interfaces:一个接口数组,代理类将实现这些接口。
    • InvocationHandler h:当代理对象的方法被调用时,会调用此处理器的invoke方法。
  • 调用代理实例的方法:当调用代理实例的方法时,实际上会调用InvocationHandlerinvoke方法,然后可以在invoke方法中执行自定义逻辑,并调用实际接口实现的方法。
  • 动态生成字节码Proxy.newProxyInstance 方法内部会动态生成一个实现了指定接口的代理类字节码。这个代理类会重写接口中的所有方法,并在方法内部调用InvocationHandlerinvoke方法。JDK生成的代理类名称通常为com.sun.proxy.$ProxyN,其中N是一个递增的数字,表示这是第几个动态生成的代理类。

四、动态代理的应用场景

动态代理的两个最常用见应用场景为 拦截器声明性接口

4.1 搭载器(AOP)

搭载器就是将目标组件劫持,在执行目标组件代码的前后,塞入一些其它代码。比如在正式执行业务方法前,先进行权限校验,如果校验不通过,则拒绝继续执行。对于此类操作,业界已经抽象出一组通用的编程模型:面向切面编程AOP。

接口定义和实现

public interface Performer {
    void play(String subject);
    String introduction();
}
public interface Director {
    List<String> getCreations();
}
public class DefaultActor implements Performer {
    @Override
    public void play(String subject) {
        System.out.println("[DefaultActor]: 默认男演员正在即兴表演《" + subject + "》");
    }
    @Override
    public String introduction() {
        return "李白·上李邕: 大鹏一日同风起,扶摇直上九万里。假令风歇时下来,犹能颠却沧溟水。世人见我恒殊调,闻余大言皆冷笑。宣父尚能畏后生,丈夫未可轻年少。";
    }
}
public class DefaultDirector implements Director {
    @Override
    public List<String> getCreations() {
        List<String> creations = new ArrayList<>();
        creations.add("《霸王别姬》");
        creations.add("《活着》");
        return creations;
    }
}

拦截器核心类

拦截器核心类实现了InvocationHandler接口,并在invoke方法中插入拦截逻辑。

package guzb.diy.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ProxyForInterceptor implements InvocationHandler {
    private Object target;
    public ProxyForInterceptor(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 IntercepterTestMain {
    public static void main(String[] args) {
        // 创建原始对象
        Performer actor = new DefaultActor();
        Director director = new DefaultDirector();
        // 创建代理对象
        Performer actorProxy = (Performer) Proxy.newProxyInstance(
                Performer.class.getClassLoader(),
                new Class[]{Performer.class},
                new ProxyForInterceptor(actor)
        );
        Director directorProxy = (Director) Proxy.newProxyInstance(
                Director.class.getClassLoader(),
                new Class[]{Director.class},
                new ProxyForInterceptor(director)
        );
        // 通过代理对象调用方法
        actorProxy.play("京剧");
        System.out.println(actorProxy.introduction());
        System.out.println("导演的作品集:");
        directorProxy.getCreations().forEach(System.out::println);
    }
}

4.2 声明是接口

MyBatis中,声明式接口(通过注解@Select@Insert等)允许直接在接口方法上通过注解来定义SQL语句,而不需要编写具体的SQL实现类。这种方式使得代码更加简洁,易于维护。

使用JDK动态代理来模拟MyBatis中的声明式接口。

定义业务接口

先定义一个业务接口,里面包含使用注解定义的SQL操作。

public interface UserMapper {
    @Select("SELECT * FROM user WHERE name = #{name}")
    User findUserByName(String name);
}
// User的POJO类
class User {
    private Integer id;
    private String name;
    // getters and setters
}

这里的@Select注解是自定义的,很简单就不展示了。

编写InvocationHandler
写一个InvocationHandler,它会在运行时解析@Select这些注解,并执行相应的SQL操作。简化版只模拟解析和调用的过程。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyBatisInvocationHandler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 这里假设能够获取到注解并解析它
        // 实际上,你需要一个SQL执行器来执行这里定义的SQL
        System.out.println("Executing method: " + method.getName());
        System.out.println("Parameters: " + Arrays.toString(args));
        // 模拟的SQL执行结果
        User user = new User();
        user.setId(1);
        user.setName((String) args[0]);
        return user;
    }
}

生成代理对象并调用

最后,我们生成UserMapper接口的代理对象,并调用它的方法。

import java.lang.reflect.Proxy;
public class MyBatisProxyDemo {
    public static void main(String[] args) {
        MyBatisInvocationHandler handler = new MyBatisInvocationHandler();
        UserMapper mapper = (UserMapper) Proxy.newProxyInstance(
            UserMapper.class.getClassLoader(),
            new Class[]{UserMapper.class},
            handler
        );
        User user = mapper.findUserByName("Alice");
        System.out.println("Found user: " + user.getName());
    }
}

输出结果

Executing method: findUserByName
Parameters: [Alice]
Found user: Alice

到此这篇关于Java JDK动态代理在拦截器和声明式接口中的应用小结的文章就介绍到这了,更多相关JDK动态代理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot+JWT实现注册、登录、状态续签流程分析

    SpringBoot+JWT实现注册、登录、状态续签流程分析

    这篇文章主要介绍了SpringBoot+JWT实现注册、登录、状态续签【登录保持】,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-06-06
  • SpringBoot配置MySQL5.7与MySQL8.0的异同点详解

    SpringBoot配置MySQL5.7与MySQL8.0的异同点详解

    MySQL 是 Java 开发中最常用的数据库之一,而 Spring Boot 提供了便捷的配置方式,随着 MySQL 8.0 的普及,许多开发者需要从 MySQL 5.7 升级到 8.0,在实际开发中,二者的配置方式既有相似之处,也有一些需要特别注意的不同点,所以本文给大家详细介绍了它们的异同点
    2024-12-12
  • 详解java 三种调用机制(同步、回调、异步)

    详解java 三种调用机制(同步、回调、异步)

    这篇文章主要介绍了java 三种调用机制(同步、回调、异步),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • Spring的@Value如何从Nacos配置中心获取值并自动刷新

    Spring的@Value如何从Nacos配置中心获取值并自动刷新

    这篇文章主要介绍了Spring的@Value如何从Nacos配置中心获取值并自动刷新,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • k8s部署MongDB全过程

    k8s部署MongDB全过程

    文章介绍了如何在Kubernetes集群中部署MongoDB,包括环境准备、创建Secret、创建服务和Deployment,并通过Robo3T工具测试连接
    2025-01-01
  • Spring Boot开启远程调试的方法

    Spring Boot开启远程调试的方法

    这篇文章主要介绍了Spring Boot开启远程调试的方法,帮助大家更好的理解和使用Spring Boot框架,感兴趣的朋友可以了解下
    2020-10-10
  • Java中Stringbuilder和正则表达式示例详解

    Java中Stringbuilder和正则表达式示例详解

    Java语言为字符串连接运算符(+)提供特殊支持,并为其他对象转换为字符串,字符串连接是通过StringBuilder(或StringBuffer)类及其append方法实现的,这篇文章主要给大家介绍了关于Java中Stringbuilder和正则表达式的相关资料,需要的朋友可以参考下
    2024-02-02
  • 基于Spring Batch 配置重试逻辑

    基于Spring Batch 配置重试逻辑

    这篇文章主要介绍了Spring Batch 配置重试逻辑,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • Java commons-httpclient如果实现get及post请求

    Java commons-httpclient如果实现get及post请求

    这篇文章主要介绍了Java commons-httpclient如果实现get及post请求,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-09-09
  • Java简单实现线程池

    Java简单实现线程池

    这篇文章主要为大家详细介绍了Java简单实现线程池,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-04-04

最新评论