啥?你还不懂Java反射机制与动态代理

createh53个月前 (02-01)技术教程28

Java开发中反射和动态代理放有一定的相关性,但单纯的说动态代理是由反射机制实现的,其实是不够全面不准确的,动态代理是一种功能行为,而它的实现方法有很多。

一、反射

反射机制是Java的核心特性之一,它允许在程序运行时检查类的结构并对其进行操作。这种动态特性为程序提供了极高的灵活性,可以在运行时加载类、调用方法、修改字段值,甚至实例化对象。反射机制被广泛应用于各种框架和库中,如Spring、Hibernate、MyBatis等,它们通过反射实现了高度的动态性和灵活性。

通过反射,开发者可以:

  • 动态加载类及其依赖
  • 动态调用方法,而无需在编译时知道具体方法
  • 动态修改对象的字段,甚至是私有字段

尽管反射为开发者提供了强大的功能,但也伴随着一定的性能开销和安全隐患,因此在实际使用中需要谨慎。

1. 实现方式

想象你是一名厨师,而食谱就是一个类。食谱中记录了所有菜品的制作方法,而你作为厨师通过食谱(类)指导自己的烹饪过程。这就类似于反射机制,你可以在程序运行时查看类的结构并通过类中的方法制作“菜品”。

// 示例:获取类信息和方法
Class recipeClass = Class.forName("com.example.Recipe");
String recipeName = recipeClass.getName();
System.out.println("菜品名称: " + recipeName);


// 动态调用方法
Method cookMethod = recipeClass.getDeclaredMethod("cook");
cookMethod.invoke(recipeObject);  


2. 获取类(Class)对象

在使用反射机制时,第一步是获取Class对象。Java为此提供了三种方式:

1.通过forName() -> 示例:Class.forName(“People”)
2.通过getClass() -> 示例:new People().getClass()
3.class直接获取 -> 示例:PeopleImpl.class

2.1静态方法调用

使用 getMethod(xx) 获取到对应的方法,直接使用 invoke(xx)就可以了。

public static void main(String[] args) {
    Class myClass = Class.forName("example.People");
    // 调用静态(static)方法
    Method getSex = myClass.getMethod("getName");
    getSex.invoke(myClass);
}

2.2 普通方法调用

使用getMethod() 获取方法,可以声明需要传递的参数的类型。

Object object = myClass.newInstance();
Method method = myClass.getMethod("hello",String.class);
method.invoke(object,"word!");

2.3 调用私有方法

调用私有方法的关键是设置 setAccessible(true) 属性,修改访问限制,这样设置之后就可以进行调用

Method greetMethod = clazz.getDeclaredMethod("greet", String.class);
greetMethod.setAccessible(true); // 允许访问私有方法
greetMethod.invoke(personObj, "John Doe");  // 调用方法

3. 应用

反射机制在实际开发中有广泛的应用,尤其是在框架和工具开发中。以下是一些常见的应用场景:

  • 动态加载类与方法例如Spring框架通过反射来动态加载Bean对象并调用其初始化方法。
  • 序列化与反序列化例如JSON库(如Jackson和Gson)通过反射将JSON字符串转换为Java对象
  • 注解处理在Spring等框架中,通过反射扫描类上的注解并执行相应的逻辑。
  • 动态代理例如Java AOP(面向切面编程)利用动态代理实现方法的拦截和增强。


二、动态代理

动态代理是一种方便运行时动态构建代理、动态处理代理方法调用的机制,很多场景都是利用类似机制做到的,比如用来包装 RPC 调用、面向切面的编程(AOP)。

实现动态代理的方式很多,比如 JDK 自身提供的动态代理,就是主要利用了上面提到的反射机制。还有其他的实现方式,比如利用传说中更高性能的字节码操作机制,类似 ASM、cglib(基于 ASM)等。

Java的动态代理主要有两种方式:

1. JDK动态代理

JDK动态代理是通过实现接口的方式生成代理类。Proxy类和InvocationHandler接口是JDK动态代理的核心。

public class CalculatorProxy implements InvocationHandler {
    private final Object target;


    public CalculatorProxy(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 static Object newProxyInstance(Object target) {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new CalculatorProxy(target));
    }
}

注意:JDK Proxy 只能代理实现接口的类(即使是extends继承类也是不可以代理的)。

2. CGlib代理

JDK 动态代理机制只能代理实现了接口的类,CGlib 是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对 final 修饰的类进行代理。

CGlib 的调用通过实现 MethodInterceptor 接口的 intercept 方法,调用 invokeSuper 进行动态代理的,可以直接对普通类进行动态代理。

class CglibProxy implements MethodInterceptor {
    private Object target; // 代理对象
    public Object getInstance(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        // 设置父类为实例类
        enhancer.setSuperclass(this.target.getClass());
        // 回调方法
        enhancer.setCallback(this);
        // 创建代理对象
        return enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("调用前");
        Object result = methodProxy.invokeSuper(o, objects); // 执行方法调用
        System.out.println("调用后");
        return result;
    }
}


public static void main(String[] args) {
    // CGLIB 动态代理调用
    CglibProxy proxy = new CglibProxy();
    Panda panda = (Panda)proxy.getInstance(new Panda());
    panda.eat();
}


3. 应用

动态代理主要用于 AOP 场景,例如日志、权限控制、事务管理等。动态代理还常用于 RPC 框架中实现远程服务调用的透明化。例如,Spring AOP 使用动态代理来实现切面编程,dubbo使用动态代理来实现远程调用。

三. 总结

Java的反射机制和动态代理为开发者提供了强大的动态功能,使得程序可以在运行时灵活地处理类、对象及其行为。虽然这些特性极大地增强了灵活性和可扩展性,但也带来了性能开销和安全隐患。因此,反射和动态代理的使用应该谨慎,尤其是在对性能有较高要求的场景下。

通过对反射机制和动态代理的深入理解,两者在实际开发中常常各自发挥作用,或者结合使用,以实现复杂的动态行为和灵活的系统设计。


如果这篇文章对您有所帮助,或者有所启发的话,帮忙给个赞 谢谢!!!

相关文章

Python之面向对象:私有属性是掩耳盗铃还是恰到好处

引言声明,今天的文章中没有一行Python代码,更多的是对编程语言设计理念的思考。上一篇文章中介绍了关于Python面向对象封装特性的私有属性的相关内容,提到了Python中关于私有属性的实现是通过“...

Java基础——面试官:你来说说反射如何获取私有对象的属性和方法

最近,@Python大星 的朋友小鹿参加了一场#Java#面试。有一道题是这样的 >>>【面试官问:你来说说反射如何获取私有对象的属性和方法?】问题的答案我们文章中揭晓,先看下反射的...

思考:Java对象之生(java中对象的概念的理解)

内存、性能是程序永恒的话题,实际开发中关于卡顿、OOM也经常是打不完的两只老虎,关于卡顿、OOM的定位方法和工具比较多,这篇文章也不打算赘述了,本章主要是来整理一下JVM的内存模型以及Java对象的生...

java匿名内部类的定义以及使用场景

匿名内部类定义 匿名内部类是Java中一种没有显式声明名称的内部类。它们在创建时被同时声明和实例化,通常用于创建一次性使用的类。它们的特点是:无名称: 无法像普通类一样被其他代码引用。一次性: 通常只...

内部类Java(内部类定义)

概念将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。什么时候使用内部类一个事物内部还有一个独立的事物,内部的事物脱离外部的事物无法独立使用比如汽车只有一个发动机,电脑只有一...

java 核心技术-12版 卷Ⅰ- 6.1.4 静态和私有方法

原文6.1.4 静态和私有方法在 Java 8 中,允许在接口中增加静态方法。理论上讲,没有任何理由认为这是不合法的。只是这似乎有违于将接口作为抽象规范的初衷。目前为止,通常的做法都是将静态方法放在伴...