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

你可能感兴趣的文章
NN&DL4.8 What does this have to do with the brain?
查看>>
nnU-Net 终极指南
查看>>
No 'Access-Control-Allow-Origin' header is present on the requested resource.
查看>>
NO 157 去掉禅道访问地址中的zentao
查看>>
no available service ‘default‘ found, please make sure registry config corre seata
查看>>
no connection could be made because the target machine actively refused it.问题解决
查看>>
No Datastore Session bound to thread, and configuration does not allow creation of non-transactional
查看>>
No fallbackFactory instance of type class com.ruoyi---SpringCloud Alibaba_若依微服务框架改造---工作笔记005
查看>>
No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalanc
查看>>
No mapping found for HTTP request with URI [/...] in DispatcherServlet with name ...的解决方法
查看>>
No mapping found for HTTP request with URI [/logout.do] in DispatcherServlet with name 'springmvc'
查看>>
No module named 'crispy_forms'等使用pycharm开发
查看>>
No module named cv2
查看>>
No module named tensorboard.main在安装tensorboardX的时候遇到的问题
查看>>
No module named ‘MySQLdb‘错误解决No module named ‘MySQLdb‘错误解决
查看>>
No new migrations found. Your system is up-to-date.
查看>>
No qualifying bean of type XXX found for dependency XXX.
查看>>
No resource identifier found for attribute 'srcCompat' in package的解决办法
查看>>
no session found for current thread
查看>>
No toolchains found in the NDK toolchains folder for ABI with prefix: mips64el-linux-android
查看>>