博客
关于我
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/

你可能感兴趣的文章
Nginx:NginxConfig可视化配置工具安装
查看>>
ngModelController
查看>>
ngrok | 内网穿透,支持 HTTPS、国内访问、静态域名
查看>>
ngrok内网穿透可以实现资源共享吗?快解析更加简洁
查看>>
NHibernate学习[1]
查看>>
NHibernate异常:No persister for的解决办法
查看>>
NIFI1.21.0_java.net.SocketException:_Too many open files 打开的文件太多_实际操作---大数据之Nifi工作笔记0051
查看>>
NIFI1.21.0_Mysql到Mysql增量CDC同步中_日期类型_以及null数据同步处理补充---大数据之Nifi工作笔记0057
查看>>
NIFI1.21.0_Mysql到Mysql增量CDC同步中_补充_更新时如果目标表中不存在记录就改为插入数据_Postgresql_Hbase也适用---大数据之Nifi工作笔记0059
查看>>
NIFI1.21.0_NIFI和hadoop蹦了_200G集群磁盘又满了_Jps看不到进程了_Unable to write in /tmp. Aborting----大数据之Nifi工作笔记0052
查看>>
NIFI1.21.0最新版本安装_连接phoenix_单机版_Https登录_什么都没改换了最新版本的NIFI可以连接了_气人_实现插入数据到Hbase_实际操作---大数据之Nifi工作笔记0050
查看>>
NIFI1.21.0通过Postgresql11的CDC逻辑复制槽实现_指定表多表增量同步_增删改数据分发及删除数据实时同步_通过分页解决变更记录过大问题_02----大数据之Nifi工作笔记0054
查看>>
NIFI1.21.0通过Postgresql11的CDC逻辑复制槽实现_指定表多表增量同步_插入修改删除增量数据实时同步_通过分页解决变更记录过大问题_01----大数据之Nifi工作笔记0053
查看>>
NIFI1.21.0通过Postgresql11的CDC逻辑复制槽实现_指定表或全表增量同步_实现指定整库同步_或指定数据表同步配置_04---大数据之Nifi工作笔记0056
查看>>
NIFI1.23.2_最新版_性能优化通用_技巧积累_使用NIFI表达式过滤表_随时更新---大数据之Nifi工作笔记0063
查看>>
NIFI从MySql中增量同步数据_通过Mysql的binlog功能_实时同步mysql数据_根据binlog实现数据实时delete同步_实际操作04---大数据之Nifi工作笔记0043
查看>>
NIFI从MySql中增量同步数据_通过Mysql的binlog功能_实时同步mysql数据_配置binlog_使用处理器抓取binlog数据_实际操作01---大数据之Nifi工作笔记0040
查看>>
NIFI从MySql中增量同步数据_通过Mysql的binlog功能_实时同步mysql数据_配置数据路由_实现数据插入数据到目标数据库_实际操作03---大数据之Nifi工作笔记0042
查看>>
NIFI从MySql中增量同步数据_通过Mysql的binlog功能_实时同步mysql数据_配置数据路由_生成插入Sql语句_实际操作02---大数据之Nifi工作笔记0041
查看>>
NIFI从MySql中离线读取数据再导入到MySql中_03_来吧用NIFI实现_数据分页获取功能---大数据之Nifi工作笔记0038
查看>>