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

本文共 4170 字,大约阅读时间需要 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/

你可能感兴趣的文章
multisim变压器反馈式_穿过隔离栅供电:认识隔离式直流/ 直流偏置电源
查看>>
mysql csv import meets charset
查看>>
multivariate_normal TypeError: ufunc ‘add‘ output (typecode ‘O‘) could not be coerced to provided……
查看>>
MySQL DBA 数据库优化策略
查看>>
multi_index_container
查看>>
MySQL DBA 进阶知识详解
查看>>
Mura CMS processAsyncObject SQL注入漏洞复现(CVE-2024-32640)
查看>>
Mysql DBA 高级运维学习之路-DQL语句之select知识讲解
查看>>
mysql deadlock found when trying to get lock暴力解决
查看>>
MuseTalk如何生成高质量视频(使用技巧)
查看>>
mutiplemap 总结
查看>>
MySQL DELETE 表别名问题
查看>>
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 种解决方案
查看>>