JavaSE反射与动态代理详细代码示例

 更新时间:2026年04月01日 10:02:09   作者:无止境ww  
Java反射和动态代理是Java中两个强大的特性,它们允许程序在运行时动态地获取和操作类、方法、字段等信息,以及创建代理对象来实现AOP等功能,这篇文章主要介绍了JavaSE反射与动态代理的相关资料,需要的朋友可以参考下

一、引言

在Java生态中,反射与动态代理是支撑框架设计的基石,无论是Spring中的AOP,还是Mybatis的Mapper接口,都离不开这两项技术。在学习之前先了解反射和动态代理分别可以做什么,简单说反射赋予程序“看清类结构”的功能,通过反射可以知道类中构造方法,成员方法,成员变量等信息;动态代理则实现了“无侵入式增强”,二者相辅相成,让代码具备灵活性与扩展性。

二、Java反射:探索类的“隐藏世界”

2.1 反射的定义

在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时检查或修改其自身的结构和行为。通过反射,你可以查询类的元数据(如类名、方法、字段、注解等),动态地进行创建对象,调用方法,访问字段等操作。

2.2反射的核心API

Java反射的核心操作集中在java.lang.reflect包中,其中关键类包括Class、Method、Field、Constructor,核心操作分为“获取Class对象”“操作类成员”两步

(1)如何获取类对象?

首先在这里定义一个User类

public class User {
    private String name;
    public Integer age;
    public User(){}
    private User(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    private void setAge(Integer age) {
        this.age = age;
    }
}

获取类对象有三种方式分别是:类名.class,对象.getClass(),Class.forName(“全类名”)

        //1.类名.class(编译时确定,最高效)
        Class<?> class1 = User.class;
        System.out.println(class1); //class User
        //2.对象.getClass() (运行时获取,需要实例化)
        User user = new User();
        Class<?> class2 = user.getClass();
        System.out.println(class2); //class User
        //3.Class.forName("全类名") (运行时加载,最灵活)
        Class<?> class3 = Class.forName("User");
        System.out.println(class3); //class User
        //判断上面三种方法获取的是否是同一个对象
        System.out.println(class1.equals(class2)); //true

(2) 利用反射操作类

得到类成员后就可以利用反射操作类的构造方法,成员方法,成员变量等元数据,核心API也很易懂

示例1. 构造器

//1.获取类
        Class<User> userClass = User.class;
        //2.获取构造器
        //2.1获取公共构造器
        Constructor<?>[] publicConstructors = userClass.getConstructors();
        System.out.println(Arrays.toString(publicConstructors)); //[public User()]
        //2.2获取所有构造器
        Constructor<?>[] declaredConstructors = userClass.getDeclaredConstructors();
        System.out.println(Arrays.toString(declaredConstructors)); //[public User(), private User(java.lang.String)]
        //2.3获取一个公共构造器 (可以传递参数,参数是构造器中的形参类)
        Constructor<User> constructor = userClass.getConstructor();
        System.out.println(constructor); //public User()
        //2.4获取一个构造器 (可以传递参数,参数是构造器中的形参类)
        Constructor<User> declaredConstructor = userClass.getDeclaredConstructor(String.class);
        System.out.println(declaredConstructor); // private User(java.lang.String)

然后可以对获得的构造器进行操作:

//2.4获取一个构造器
		Constructor<User> declaredConstructor = userClass.getDeclaredConstructor(String.class);
        System.out.println(declaredConstructor); // private User(java.lang.String)
		//3.Constructor类常用方法
        //3.1获取构造器的权限修饰符
        int modifiers = declaredConstructor.getModifiers();
        System.out.println(declaredConstructor + "的构造方法的修饰符为" + modifiers);//private User(java.lang.String)的构造方法的修饰符为2(对应private)
        //3.2获取构造器的形参类型
        Class<?>[] types = declaredConstructor.getParameterTypes();
        System.out.println(declaredConstructor + "的形参类型有" + Arrays.toString(types)); //private User(java.lang.String)的形参类型有[class java.lang.String]
        //3.3通过Constructor实例化对象
        //因为获取的是私有方法,所以需要暴力反射
        declaredConstructor.setAccessible(true);//将私有构造器设置为可访问状态
        User user = declaredConstructor.newInstance( "无止境"); 
        System.out.println(user.getName()); //无止境

在 Java 中,java.lang.reflect.Constructor类的getModifiers()方法返回一个整数,该整数是构造器修饰符的位掩码(Bitmask)。每个修饰符对应一个二进制位,不同修饰符通过按位或(OR)组合成最终的整数值。

获取字段,方法等API与获取构造器类似,演示一下通过反射操作私有成员

    //1.获取类
        Class<User> userClass = User.class;
        //2.实例私有构造器
        Constructor<User> privateConstructor = userClass.getDeclaredConstructor(String.class);
        privateConstructor.setAccessible(true);
        User user = privateConstructor.newInstance("无止境");
        //3.调用私有化方法setAge
        Method setAge = userClass.getDeclaredMethod("setAge", Integer.class);
        setAge.setAccessible(true);
        setAge.invoke(user, 18);
        System.out.println(user.getName() + "的年龄为" + user.getAge()); //无止境的年龄为18
        //4.获取私有属性 name
        Field nameField = userClass.getDeclaredField("name");
        nameField.setAccessible(true);
        String name = (String)nameField.get(user);
        System.out.println("利用反射获取的私有属性" + name); //利用反射获取的私有属性无止境

Method类中invoke()方法比较常用,传递的参数表示1:调用方法的对象, 2:调用方法传递的参数;返回值就是原方法返回值

三、动态代理,无侵入式增强的实现利器

3.1动态代理定义

动态代理是一种设计模式,允许程序在运行时动态生成代理类,代理类会包裹目标对象,在不修改目标代码的情况下,对目标方法进行增强。Java中动态代理主要分为两种JDK动态代理和CGLIB动态代理

3.2JDK动态代理(接口增强)

(1)核心组件

  • InvocationHandler:定义代理逻辑,所有代理方法的调用都会委托给该接口的invoke方法。
  • Proxy:用于创建动态代理类和实例的工厂

(2)实现步骤

  1. 定义接口:目标类需实现接口
  2. 实现InvocationHandler:处理方法调用逻辑
  3. 生成代理对象:通过Proxy.newProxyInstance()创建代理

(3)示例代码

定义接口:目标类需实现接口

//目标接口
public interface UserService {
    void addUser(String name);
    void deleteUser(Integer id);
}
//目标实现类
public class UserServiceImpl implements UserService {
   @Override
    public void addUser(String name) {
        System.out.println("执行添加姓名为 : " + name + " 用户的操作");
    }
    @Override
    public void deleteUser(Integer id) {
        System.out.println("执行删除id为 : " + id + " 用户的操作");
    }
}

实现InvocationHandler:处理方法调用逻辑

public class LogInvocationHandler implements InvocationHandler {
    private Object target; //目标对象
    public LogInvocationHandler(Object target) {
        this.target = target;
    }
    /**
     *
     * @param proxy 代理对象
     * @param method 目标方法
     * @param args 方法参数
     * @return 方法返回值
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //前置日志,记录日志
        System.out.println("【日志】 开始调用方法 : " + method.getName() + ",方法参数为 : " + Arrays.toString(args));
        //调用目标方法
        Object result = method.invoke(target, args);
        //后置日志,记录方法完成
        System.out.println("【日志】方法调用结束 : " + method.getName());
        return result;
    }
}

生成代理对象:通过Proxy.newProxyInstance()创建代理

public static void main(String[] args) {
        //1.创建目标对象
        UserServiceImpl target = new UserServiceImpl();
        //2.创建InvocationHandler实例
        LogInvocationHandler handler = new LogInvocationHandler(target);
        //3.生成动态代理对象
        UserService proxy = (UserService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(), //类加载器
                target.getClass().getInterfaces(), //目标实现的接口
                handler
        );
        //4.调用代理对象的方法
        proxy.addUser("无止境");
        proxy.deleteUser(1001);
    }

最后控制台输出结果

【日志】 开始调用方法 : addUser,方法参数为 : [无止境]
执行添加姓名为 : 无止境 用户的操作
【日志】方法调用结束 : addUser
【日志】 开始调用方法 : deleteUser,方法参数为 : [1001]
执行删除id为 : 1001 用户的操作
【日志】方法调用结束 : deleteUser

至此就完成了一个简单的日志增强效果

(4)核心API用法

  • public Object invoke(Object proxy, Method method, Object[] args):三个参数分别是代理对象本身(通常不需要使用);被调用的目标方法;方法参数数组。
  • public static Object newProxyInstance(
    ClassLoader loader,
    Class<?>[] interfaces,
    InvocationHandler h
    ):里面要传递三个参数分别是类加载器(通常使用目标接口的类加载器);目标接口数组(至少一个接口);代理逻辑处理器。

3.3CFLIB实现动态代理(类增强)

(1)核心组件

  • MethodInterceptor:拦截目标类的方法调用,定义增强逻辑
  • Enhancer:生成代理类和实例的工具

(2)实现步骤

  1. 引入CGLIB依赖
  2. 定义无接口的目标类
  3. 实现MethodInterceptor
  4. 生成CGLIB代理对象:利用Enhancer.create()方法

(3)示例代码

引入CGLIB依赖(maven)

<dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>
public class OrderService {
    //模拟创建订单方法
    public void createOrder(String orderNo) {
        System.out.println("创建订单,订单号为 : " + orderNo);
    }
}

自定义类实现MethodInterceptor,处理方法调用逻辑

public class TransactionMethodInterceptor implements MethodInterceptor {
    /**
     *
     * @param o 目标对象
     * @param method 目标方法
     * @param objects 方法参数
     * @param methodProxy 方法代理对象
     * @return 方法返回值
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //前置增强:开启事务
        System.out.println("【事务】开启数据事务");
        //调用目标方法(通过methodProxy提高性能)
        Object result = methodProxy.invokeSuper(o, objects);
        //后置增强:提交事务
        System.out.println("【事务】提交数据事务");
        return result;
    }
}

通过Enhancer.create()生成代理对象

public class CglibProxyDemo {
    public static void main(String[] args) {
        //1.创建Enhancer实例(CGLIB核心)
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(OrderService.class); //设置目标类
        enhancer.setCallback(new TransactionMethodInterceptor()); //设置增强器
        //2.生成动态代理对象
        OrderService proxy = (OrderService) enhancer.create();
        //3.调用代理对象方法
        proxy.createOrder("order_112233");
    }
}

最终输出结果:

【事务】开启数据事务
创建订单,订单号为 : order_112233
【事务】提交数据事务

(4)核心API用法

  • public void setSuperclass(Class<?> superclass); 指定目标类
  • public void setCallback(Callback callback); 设置拦截器
  • public Object create(); 生成代理实例
  • public Object intercept(
    Object obj,
    Method method,
    Object[] args,
    MethodProxy proxy
    ):参数分别是obj:代理对象(通常不需要使用);method:被调用的目标方法;args:方法参数数组;proxy:MethodProxy 对象,比反射调用更高效

3.3JDK动态代理和CGLIB动态代理横向对比

组件JDK动态代理CGLIB动态代理
接口InvocationHandlerMethodInterceptor
生成代理方式Proxy.newProxyInstanceEnhancer.create()
目标类型接口
调用目标方法method.invoke(target, args)proxy.invokSuper(obj, args)

四、反射与动态代理的实际应用

(1)反射的实际应用

  • Spring 框架:Spring 使用反射来创建和管理 Bean。例如,通过配置文件(如 XML 或注解)指定要创建的类,Spring 容器利用反射机制根据类名动态加载类,并调用其构造函数创建对象实例。
  • JUnit:JUnit 利用反射来发现和执行测试方法。它通过反射遍历测试类中的方法,根据特定的注解(如@Test)识别出测试方法,并动态调用这些方法来执行测试。例如,在一个测试类中定义了多个测试方法,JUnit 通过反射获取这些方法并依次执行,从而实现对代码的自动化测试。

(2) 动态代理的实际应用

  • Spring AOP:Spring AOP 大量使用动态代理来实现切面功能。例如,在一个企业级应用中,可能需要对业务方法进行日志记录、事务管理等操作。通过动态代理,可以在不修改目标业务类代码的前提下,为目标方法添加这些额外的功能。Spring 会为目标对象创建动态代理对象,当调用代理对象的方法时,会先执行切面逻辑(如记录日志),然后再调用目标对象的实际方法,最后还可以在方法调用后执行一些清理或补充逻辑(如事务提交)。
  • 在数据库事务管理中,动态代理可以用于管理事务的开启、提交和回滚。以一个银行转账操作的业务方法为例,动态代理可以在方法调用前开启事务,方法执行过程中如果没有异常则提交事务,若出现异常则回滚事务。这样可以将事务管理的逻辑从业务代码中分离出来,提高代码的可维护性和复用性。

通过上面的例子可以初步了解反射与动态代理,想要深度学习的话可以多看看框架中的源码,学习优秀的代码风格以及设计模式。

到此这篇关于JavaSE反射与动态代理的文章就介绍到这了,更多相关JavaSE反射与动态代理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java序列化之serialVersionUID的用法解读

    Java序列化之serialVersionUID的用法解读

    Java序列化之serialVersionUID:本文介绍了Java对象的序列化和反序列化过程,强调了serialVersionUID的作用,以及如何生成和使用它来保证版本兼容性,通过实例代码,展示了序列化和反序列化的具体操作
    2025-11-11
  • 详解Springboot @Cacheable 注解(指定缓存位置)

    详解Springboot @Cacheable 注解(指定缓存位置)

    这篇文章主要介绍了详解Springboot @Cacheable 注解(指定缓存位置),使用  @Cacheable  注解就可以将运行结果缓存,以后查询相同的数据,直接从缓存中取,不需要调用方法,需要的朋友可以参考下
    2023-09-09
  • 23种设计模式(7) java代理模式

    23种设计模式(7) java代理模式

    这篇文章主要为大家详细介绍了23种设计模式之java代理模式,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-11-11
  • Java实现象棋算法的示例代码

    Java实现象棋算法的示例代码

    象棋算法包括搜索算法、评估函数和剪枝算法,本文主要介绍了Java实现象棋算法的示例代码,具有一定的参考价值,感兴趣的可以了解一下
    2023-12-12
  • 子类继承父类时构造函数相关问题解析

    子类继承父类时构造函数相关问题解析

    这篇文章主要介绍了子类继承父类时构造函数相关问题解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11
  • Java使用Spire.PDF for Java实现旋转PDF页面

    Java使用Spire.PDF for Java实现旋转PDF页面

    在日常的文档处理工作中,我们经常会遇到 PDF 页面方向不正确的问题,本文将深入探讨如何利用 Java 实现 PDF 页面的旋转操作,感兴趣的小伙伴可以了解下
    2025-09-09
  • Java中Switch Case多个条件处理方法举例

    Java中Switch Case多个条件处理方法举例

    Java中switch语句用于根据变量值执行不同代码块,适用于多个条件的处理,这篇文章主要介绍了Java中Switch Case多个条件处理的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-04-04
  • SpringBoot基于redis自定义注解实现后端接口防重复提交校验

    SpringBoot基于redis自定义注解实现后端接口防重复提交校验

    本文主要介绍了SpringBoot基于redis自定义注解实现后端接口防重复提交校验,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • java设计模式之观察者模式

    java设计模式之观察者模式

    这篇文章主要为大家详细介绍了java设计模式之观察者模式的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-12-12
  • SpringSecurity学习之自定义过滤器的实现代码

    SpringSecurity学习之自定义过滤器的实现代码

    这篇文章主要介绍了SpringSecurity学习之自定义过滤器的实现代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-01-01

最新评论