Java反射(JDK)与动态代理(CGLIB)详解

 更新时间:2021年08月31日 18:10:07   作者:KKKLxxx  
下面小编就为大家带来一篇浅谈Java反射与动态代理。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧

一、反射

概念:在运行状态中,对于任意的一个类,都能够知道这个类的所有字段和方法,对任意一个对象都能够通过反射机制调用一个类的任意方法

实现方法:JVM在第一次加载某个类时会生成一个Class对象,里面记录了这个类的信息

链接:类加载机制(留坑)

二、动态代理

动态代理的作用:在不改变原代码的基础上增加新的功能,如日志、权限检验等

反射在动态代理中的应用:由于知道原类的字段、方法等信息,才可以通过代理类执行被代理类的方法

动态代理的实现有两种

1、JDK代理

实现方法:通过创建一个代理类,这个代理类继承于一个Proxy类,Proxy类中有一个InvocationHandler接口,这个接口持有被代理对象和一个invoke()方法。创建好代理类对象后,对该对象调用的方法都会交由invoke方法处理。invoke方法接受3个参数:代理对象、方法、参数列表。重写invoke方法便可以在原方法的基础上添加其他逻辑

一个JDK代理的简单实现:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 定义一个接口
interface MyInterface {
    void fun();
}
// 用一个类去实现这个接口
class Person implements MyInterface {
    @Override
    public void fun() {
        System.out.println("Person实现接口方法");
    }
}
// 用一个类实现InvocationHandler接口
class MyInvocationHandler<T> implements InvocationHandler {
    //InvocationHandler持有的被代理对象
    T target;
    public MyInvocationHandler(T target) {
        this.target = target;
    }
    /**
     * proxy:代表动态代理对象
     * method:代表正在执行的方法
     * args:代表调用目标方法时传入的实参
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理执行" + method.getName() + "方法"); // 在原方法的基础上添加其他逻辑
        Object result = method.invoke(target, args); // 通过invoke方法调用原方法
        return result;
    }
}
public class Main {
    public static void main(String[] args) {
        Person person = new Person();
        InvocationHandler invocationHandler = new MyInvocationHandler<>(person);
        MyInterface proxy = (MyInterface) Proxy.newProxyInstance(Person.class.getClassLoader(),
                Person.class.getInterfaces(), invocationHandler);
        proxy.fun();
    }
}

输出结果:

代理执行fun方法
Person实现接口方法

缺陷:只能代理接口方法,因为JDK代理需要继承一个Proxy类,又由于Java的单继承机制,导致代理类无法继承父类的函数,只能实现接口

2、CGLIB代理

原理与JDK代理类似,区别在于CGLIB代理创建的代理类直接继承于被代理类,所以可以实现被代理类的方法而非仅仅接口方法

一个简单的CGLIB代理实现:

public class Main {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Car.class);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy)
                    throws Throwable {
                System.out.println("before");
                Object res = methodProxy.invokeSuper(obj, args);
                System.out.println("after");
                return res;
            }
        });
        Car car = (Car) enhancer.create();
        car.print();
    }
}
class Car {
    void print() {
        System.out.println("执行原方法");
    }
}

由于CGLIB并非JDK自带,所以需要通过Maven引入一个依赖

<dependency>
    <groupId>org.sonatype.sisu.inject</groupId>
    <artifactId>cglib</artifactId>
    <version>3.1.1</version>
</dependency>

输出结果:

before
执行原方法
after

3、JDK代理与CGLIB代理对比

1、JDK代理只能实现接口方法,而CGLIB代理既可以实现接口方法也可以实现类中自带的方法

2、性能上,在JDK1.8,CGLIB3.1.1的环境上,每次创建一个代理类并执行同样的方法

当执行10000次,JDK代理用时85ms,而CGLIB代理用时190ms,明显JDK代理性能更佳;

当执行1000000(一百万)次时,两种代理耗时几乎相等;

当执行10000000次时,CGLIB代理已经优于JDK代理。

所以在执行次数少时,JDK代理性能更好;反之CGLIB代理性能更好(但是重复执行多于1000000次的任务几乎没有吧

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!

相关文章

  • Java实现RedisUtils操作五大集合(增删改查)

    Java实现RedisUtils操作五大集合(增删改查)

    本文主要介绍了Java实现RedisUtils操作五大集合,文中通过示例代码介绍的非常详细,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-07-07
  • 基于java中BlockingQueue的使用介绍

    基于java中BlockingQueue的使用介绍

    本篇文章小编为大家介绍,基于java中BlockingQueue的使用介绍。需要的朋友参考下
    2013-04-04
  • IDEA Ultimate2020.2版本配置Tomcat详细教程

    IDEA Ultimate2020.2版本配置Tomcat详细教程

    这篇文章主要介绍了IDEA Ultimate2020.2版本配置Tomcat教程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09
  • Mybatis中and和循环or混用操作(or转换成in)

    Mybatis中and和循环or混用操作(or转换成in)

    这篇文章主要介绍了Mybatis中and和循环or混用操作(or转换成in),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • Maven项目执行生命周期相关操作时出现错误:does not match a valid id pattern问题

    Maven项目执行生命周期相关操作时出现错误:does not match a 

    当pom文件中的gav标签格式错误,如出现中文或空格,会导致与有效的id模式不匹配错误,gav标签应仅包含数字、字母和下划线,解决方法是修改标签中的中文为英文,删除多余空格,并刷新pom文件,例如,将中文"测试"改为英文"test"
    2024-09-09
  • 查看本地启动SpringBoot的本地端口号的几种方式

    查看本地启动SpringBoot的本地端口号的几种方式

    这篇文章主要介绍了查看本地启动SpringBoot的本地端口号的几种方式,文中通过代码示例讲解的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-10-10
  • 基于socket和javaFX简单文件传输工具

    基于socket和javaFX简单文件传输工具

    这篇文章主要介绍了基于socket和javaFX简单文件传输工具的相关资料,需要的朋友可以参考下
    2016-02-02
  • 关于在Java中使用预定义类

    关于在Java中使用预定义类

    这篇文章主要介绍了关于在Java中使用预定义类,预定义类就是Java类库(或第三方库)中已经定义好的类,例如,Math 类和 Date 类,需要的朋友可以参考下
    2023-05-05
  • JAVA Web.xml加载顺序过程详解

    JAVA Web.xml加载顺序过程详解

    这篇文章主要介绍了JAVA Web.xml加载顺序过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-12-12
  • Jrebel License Server 激活 IDEA-Jrebel-在线-离线-均适用(推荐)

    Jrebel License Server 激活 IDEA-Jrebel-在线-

    这篇文章主要介绍了Jrebel License Server 激活 IDEA-Jrebel-在线-离线-均适用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12

最新评论