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

你可能感兴趣的文章
mutiplemap 总结
查看>>
MySQL Error Handling in Stored Procedures---转载
查看>>
MVC 区域功能
查看>>
MySQL FEDERATED 提示
查看>>
mysql generic安装_MySQL 5.6 Generic Binary安装与配置_MySQL
查看>>
Mysql group by
查看>>
MySQL I 有福啦,窗口函数大大提高了取数的效率!
查看>>
mysql id自动增长 初始值 Mysql重置auto_increment初始值
查看>>
MySQL in 太多过慢的 3 种解决方案
查看>>
Mysql Innodb 锁机制
查看>>
MySQL InnoDB中意向锁的作用及原理探
查看>>
MySQL InnoDB事务隔离级别与锁机制深入解析
查看>>
Mysql InnoDB存储引擎 —— 数据页
查看>>
Mysql InnoDB存储引擎中的checkpoint技术
查看>>
Mysql InnoDB存储引擎中缓冲池Buffer Pool、Redo Log、Bin Log、Undo Log、Channge Buffer
查看>>
MySQL InnoDB引擎的锁机制详解
查看>>
Mysql INNODB引擎行锁的3种算法 Record Lock Next-Key Lock Grap Lock
查看>>
mysql InnoDB数据存储引擎 的B+树索引原理
查看>>
mysql innodb通过使用mvcc来实现可重复读
查看>>
mysql interval显示条件值_MySQL INTERVAL关键字可以使用哪些不同的单位值?
查看>>