博客
关于我
Java提高班(六)反射和动态代理(JDK Proxy和Cglib)
阅读量:459 次
发布时间:2019-03-06

本文共 4094 字,大约阅读时间需要 13 分钟。

Java 反射与动态代理

一、反射

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

1. 获取类(Class)对象

获取类对象有三种方法:

  • 通过 forName() 方法:Class.forName("PeopleImpl")
  • 通过 getClass() 方法:new PeopleImpl().getClass()
  • 直接获取 .class 属性:PeopleImpl.class

2. 类的常用方法

类中提供了许多有用的方法:

  • getName():获取类完整名称;
  • getSuperclass():获取类的父类;
  • newInstance():创建实例对象;
  • getFields():获取当前类和父类的 public 修饰的所有属性;
  • getDeclaredFields():获取当前类(不包含父类)的声明的所有属性;
  • getMethod():获取当前类和父类的 public 修饰的所有方法;
  • getDeclaredMethods():获取当前类(不包含父类)的声明的所有方法;

更多方法还有:getEnclosingClass(), getModifiers(), getParameters(), getReturnType(), getSignum(), 等等。

3. 类方法调用

通过反射调用类中的方法,需要使用 invoke() 方法。方法调用分为三种类型:

  • 静态(static)方法调用;
  • 普通方法调用;
  • 私有方法调用。

3.1 静态方法调用

示例代码:

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

3.2 普通方法调用

示例代码:

public static void main(String[] args) {    Class myClass = Class.forName("example.PeopleImpl");    Object object = myClass.newInstance();    Method method = myClass.getMethod("sayHi", String.class);    method.invoke(object, "老王");}

3.3 调用私有方法

示例代码:

public static void main(String[] args) {    Class myClass = Class.forName("example.PeopleImpl");    Object object = myClass.newInstance();    Method privSayHi = myClass.getDeclaredMethod("privSayHi");    privSayHi.setAccessible(true);    privSayHi.invoke(object);}

4. 总结

  • 在反射中,核心方法是 newInstance() 获取类实例,getMethod() 获取方法,使用 invoke() 进行方法调用,通过 setAccessible(true) 修改私有变量/方法的访问限制。
  • 获取属性/方法时,“Declared”与无“Declared”有区别:“Declared”修饰的方法或属性可以获取本类的所有方法或属性(private 到 public),而无“Declared”修饰的方法或属性只能获取 public 修饰的方法或属性。

二、动态代理

动态代理是一种方便运行时动态构建代理、动态处理代理方法调用的机制,广泛应用于 RPC 调用、面向切面编程(AOP)等场景。

1. JDK Proxy 动态代理

JDK Proxy 是通过实现 InvocationHandler 接口来实现的。代码示例:

interface Animal {    void eat();}class Dog implements Animal {    @Override    public void eat() {        System.out.println("The dog is eating");    }}class Cat implements Animal {    @Override    public void eat() {        System.out.println("The cat is eating");    }}// JDK 代理类class AnimalProxy implements InvocationHandler {    private Object target;    public Object getInstance(Object target) {        this.target = target;        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("调用前");        Object result = method.invoke(target, args);        System.out.println("调用后");        return result;    }}public static void main(String[] args) {    AnimalProxy proxy = new AnimalProxy();    Animal dogProxy = (Animal) proxy.getInstance(new Dog());    dogProxy.eat();}

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

2. Cglib 动态代理

Cglib 是一种基于 ASM 的动态代理框架,它可以对任意类(包括 final 类)生成代理类,覆盖特定方法实现。代码示例:

class Panda {    public void eat() {        System.out.println("The panda is eating");    }}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) {    CglibProxy proxy = new CglibProxy();    Panda panda = (Panda) proxy.getInstance(new Panda());    panda.eat();}

Cglib 的优势是可以直接代理任意类,而 JDK Proxy 只能代理实现接口的类。然而,Cglib 的缺点是对 final 类无法代理,且依赖 ASM 类库。

三、JDK Proxy 与 Cglib 对比

优点:

  • JDK Proxy

    • 最小化依赖关系,减少依赖意味着简化开发和维护,JDK 本身的支持更加可靠。
    • 平滑进行 JDK 版本升级。
  • Cglib

    • 可调用普通类,不需要实现接口。
    • 高性能。

缺点:

  • JDK Proxy

    • 只能代理实现接口的类。
    • 依赖 JDK 本身的反射机制。
  • Cglib

    • 对 final 类无法代理。
    • 依赖 ASM 类库。

总结:在选型时需要综合考虑性能、可靠性、可维护性等因素,标准类库和反射编程的门槛较低,是初次学习动态代理的不错选择。

转载地址:http://zrgfz.baihongyu.com/

你可能感兴趣的文章
Mysql tinyint(1)与tinyint(4)的区别
查看>>
mysql union orderby 无效
查看>>
mysql v$session_Oracle 进程查看v$session
查看>>
mysql where中如何判断不为空
查看>>
MySQL Workbench 使用手册:从入门到精通
查看>>
mysql workbench6.3.5_MySQL Workbench
查看>>
MySQL Workbench安装教程以及菜单汉化
查看>>
MySQL Xtrabackup 安装、备份、恢复
查看>>
mysql [Err] 1436 - Thread stack overrun: 129464 bytes used of a 286720 byte stack, and 160000 bytes
查看>>
MySQL _ MySQL常用操作
查看>>
MySQL – 导出数据成csv
查看>>
MySQL —— 在CentOS9下安装MySQL
查看>>
MySQL —— 视图
查看>>
mysql 不区分大小写
查看>>
mysql 两列互转
查看>>
MySQL 中开启二进制日志(Binlog)
查看>>
MySQL 中文问题
查看>>
MySQL 中日志的面试题总结
查看>>
mysql 中的all,5分钟了解MySQL5.7中union all用法的黑科技
查看>>
MySQL 中的外键检查设置:SET FOREIGN_KEY_CHECKS = 1
查看>>