谈谈反射机制,动态代理基于什么原理

简介: 谈谈反射机制,动态代理基于什么原理

谈谈反射机制,动态代理基于什么原理



Java 反射机制?


反射机制是Java语言提供的一种基础功能,赋予程序在运行时自省(introspect,官方用语)的能力。通过反射我们可以直接操作类或者对象,比如获取某个对象的类定义,获取类声明的属性和方法,调用方法或者构造对象,甚至可以运行时修改类定义。

反射提供的 Accessibleobject.setAccessible(boolean flag)。它的子类也大都重写了这个方法,这里的所谓 accessible可以理解成修饰成员的 public、 protected、 private,这意味着我们可以在运行时修改成员访问限制。

setAccessible 的应用场景非常普遍,遍布我们的日常开发、测试、依赖注入等各种框架中。比如,在O/R Mapping框架中,我们为一个Java实体对象,运行时自动生成 setter 和 getter 的逻辑。


class Employee {
    private int id;
    private String name;
    private int age;
    public Employee() {
    }
    public Employee(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
    private void setId(int id) {
        this.id = id;
    }
    private int judge(int id) {
        return this.id - id;
    }
    private String sayHalo(String name) {
        return "Halo" + name;
    }
}
public class PrivateTest {
    public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Employee em = new Employee(1, "Alex", 22);
        // 获取Class对象
        Class<?> emClass = em.getClass();
        // 获取特定的声明了的方法
        Method judgeMethod = emClass.getDeclaredMethod("judge", new Class[] { Integer.TYPE });
        // setAccessible(boolean flag)使所有成员可以访问,访问之前设置
        judgeMethod.setAccessible(true);
        // 获取所有声明的方法
        Method[] allMethods = emClass.getDeclaredMethods();
        // AccessibleObject.setAccessible(AccessibleObject[] array,boolean flag)
        //批量给访问权限
        AccessibleObject.setAccessible(allMethods, true);
        // 下面就可以通过反射访问了
        System.out.println(judgeMethod.invoke(em, new Object[] { 3 }));
        // or...
        for (Method method : allMethods) {
        }
    }
}

动态代理


动态代理是一种方便运行时动态构建代理、动态处理代理方法调用的机制,很多场景都是利用类似机制做到的,比如用来包装RPC调用、面向切面的编程(AOP) 实现动态代理的方式很多,比如JDK自身提供的动态代理,就是主要利用了上面提到的反射机制。还有其他的实现方式,比如利用传说中更高性能的字节码操作机制,类似ASM、 Cglib(基于ASM)、 Javassist等

反射,引入运行时自省能力,赋予了Java语言令人意外的活力,通过运行时操作元数据或对象,Java可以灵活地操作运行时才能确定的信息。而动态代理,则是延伸岀来的一种广泛应用于产品开发中的技术,很多繁琐的重复编程,都可以被动态代理机制优雅地解决。


动态代理解决了什么问题?


首先,它是一个代理机制。设计模式中的代理模式,代理可以看作是对调用目标的一个包装,这样我们对目标代码的调用不是直接发生的,而是通过代理完成。其实很多动态代理场景,我认为也可以看作是装饰器( Decorator)模式的应用,通过代理可以让调用者与实现者之间解耦。比如进行RPC调用,框架内部的寻址、序列化、反序列化等。


JDK 动态代理


JDK  动态代理例子,实现了对应的 InvocationHandler ,然后以接口 Hello 为纽带,为被调用目标构建代理对象,进而应用程序就可以用代理对象间接运行调用目标的逻辑。


interface Hello{
    void sayHello();
}
class HelloImpl implements Hello{
    public void sayHello() {
        System.out.println("Hello World");
    };
}
class MyInvocationHandler implements InvocationHandler{
    private Object target;
    public MyInvocationHandler(Object target){
        this.target = target;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Invocking sayHello");
        Object result = method.invoke(target, args);
        return result;
    }
}
public class MyDyamicProxy {
    public static void main(String[] args) {
         HelloImpl hello = new HelloImpl();
         MyInvocationHandler hadler = new MyInvocationHandler(hello);
         // 构造代码示例
         Hello myHello = (Hello) Proxy.newProxyInstance(HelloImpl.class.getClassLoader(), HelloImpl.class.getInterfaces(), hadler);
         // 调用代理方法
         myHello.sayHello();
    }
}

但是 JDK 动态代理有个局限,它只能为接口创建代理,返回的代理对象也只能转换到接口类型,如果一个类没有接口,或者希望代理非接口中定义的方法。那 JDK 动态代理无法实现。


JDK 动态代理优点


  • 最小化依赖关系,减少依赖意味着简化开发和维护, JDK 本身支持,比 CGLIb 可靠
  • 平滑进行 JDK 版本升级,而字节码库通常需要进行更新保证最新 Java 上能够使用。
  • 代码实现简单


CGLIB 动态代理


CGLIb 动态代理使用的是字节码技术, 采用的方式是创建代理类的子类,继承的方式

import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class CglibProxyHandler implements MethodInterceptor {
    /**
     * 维护目标对象
     */
    private Object target;
    public Object getProxyInstance(final Object target) {
        this.target = target;
        // Enhancer类是CGLIB中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展
        Enhancer enhancer = new Enhancer();
        // 将被代理的对象设置成父类
        enhancer.setSuperclass(this.target.getClass());
        // 回调方法,设置拦截器
        enhancer.setCallback(this);
        // 动态创建一个代理类
        return enhancer.create();
    }
    @Override
    public Object intercept(Object object, Method method, Object[] args,
            MethodProxy methodProxy) throws Throwable {
        System.out.println("代理先进行谈判……");
        // 唱歌需要明星自己来唱
        Object result = methodProxy.invokeSuper(object, args);
        System.out.println("演出完代理去收钱……");
        return result;
    }
 }
public class Client {
    public static void main(String[] args) {
        Star realStar = new RealStar();
        Star proxy = (Star) new CglibProxyHandler().getProxyInstance(realStar);
        proxy.sing();
    }
 }


CGLIB 动态代理优点


  • 调用对象可以不实现接口,可以打破 JDK  动态代理的限制
  • 只关心操作的类,不必为其他相关类增加工作量
  • 高性能

从性能角度,有人曾经得出结论说JDKProxγ比cgliB或者 Javassist慢几十倍。坦白说,不去争论具体的 benchmark细节,在主流JDK版本中,JDK Proxy在典型场景可以提供对等的性能水平,数量级的差距基本上不是广泛存在的。而且,反射机制性能在现代JDK中,自身已经得到了极大的改进和优化,同时,JDK很多功能也不完全是反射,同样使用了ASM进行字节码操作。


动态代理的应用


动态代理应用非常广泛,虽然最初多是因为RPC等使用进入我们视线,但是动态代理的使用场景远远不仅如此,它完美符合 Spring AOP等切面编程。简单来说它可以看作是对OOP的一个补充,因为OOP对于跨越不同对象或类的分散、纠缠逻辑表现力不够,比如在不同模块的特定阶段做一些事情,类似日志、用户鉴权、全局性异常处理、性能监控,甚至事务处理等,你可以參考下面这张图。

相关文章
|
算法 Java C++
试题 算法训练 回文数
试题 算法训练 回文数
115 0
|
8月前
|
算法 安全 数据安全/隐私保护
根据空域图信息构造飞机航线图以及飞行轨迹模拟matlab仿真
本程序基于MATLAB2022A实现空域图信息的飞机航线图构建与飞行轨迹模拟。空域图是航空领域的重要工具,包含航线、导航点、飞行高度层等信息。程序通过航路网络建模(节点为机场/导航点,边为航线段)构建航线图,并依据飞行规则规划航线。飞行轨迹模拟包括确定起飞点与目的地、设置航路点及飞行高度层,确保飞行安全。完整程序运行结果无水印,适用于航空飞行计划制定与研究。
219 16
|
缓存 并行计算 Ubuntu
Jetson 学习笔记(十一):jetson agx xavier 源码编译ffmpeg(3.4.1)和opencv(3.4.0)
本文是关于在Jetson AGX Xavier上编译FFmpeg(3.4.1)和OpenCV(3.4.0)的详细教程,包括编译需求、步骤、测试和可能遇到的问题及其解决方案。还提供了Jetson AGX Xavier编译CUDA版本的OpenCV 4.5.0的相关信息。
436 4
Jetson 学习笔记(十一):jetson agx xavier 源码编译ffmpeg(3.4.1)和opencv(3.4.0)
|
存储 安全 Linux
|
弹性计算 缓存 数据库
2024年阿里云2核4G服务器一年多少钱?一年165元
2024年阿里云2核4G服务器一年多少钱?一年165元,2核4G配置1个月多少钱?2核4G服务器30元3个月、轻量应用服务器2核4G4M带宽165元一年、企业用户2核4G5M带宽199元一年
|
测试技术
软件测试工程师日常工作中需要拒绝哪些工作?
软件测试工程师日常工作中需要拒绝哪些工作?
152 0
|
传感器 机器学习/深度学习 算法
光流(Optical Flow)
光流分析是计算机视觉中的一个重要问题,可以用于许多应用,例如物体跟踪、运动估计、结构重建、虚拟现实等。
830 0
|
存储 SQL NoSQL
15_MySQL存储过程与存储函数(一)
15_MySQL存储过程与存储函数(一)
281 0
|
存储 算法
枚举类型和联合体类型
枚举类型和联合体类型
200 0
枚举类型和联合体类型
|
JavaScript 开发者
讲解 v-model 实现表单元素的数据双向绑定|学习笔记
快速学习讲解 v-model 实现表单元素的数据双向绑定
195 0
 讲解 v-model 实现表单元素的数据双向绑定|学习笔记

热门文章

最新文章