java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > JavaSE反射与动态代理

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

作者:无止境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)核心组件

(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用法

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

(1)核心组件

(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用法

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

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

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

(1)反射的实际应用

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

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

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

您可能感兴趣的文章:
阅读全文