• Java 反射和动态代理


    Java 反射和动态代理

    1. 类加载机制

    1.1 类加载过程

    大体来说,可以分为三个阶段:加载 $rightarrow$ 链接 $rightarrow$ 初始化。具体过程如图:1

    类加载时机:

    1.创建类的实例,也就是new一个对象

    2.访问某个类或接口的静态变量,或者对该静态变量赋值

    3.调用类的静态方法

    4.反射

    5.初始化一个类的子类(会首先初始化子类的父类)

    6.虚拟机启动时标明的启动类,即文件名和类名相同的那个类

    1.2 加载

    ​ 将class字节码文件内容加载到内存中,并将这些静态数据转换为方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据(Class Metadata)的返回入口。

    Class对象代表啥:

    ​ 每当一个类加载到内存中后,这个类便成为运行时类,虚拟机会在堆区创建一个有关这个类的Class对象。

    1.1 类加载过程

    1.3 类加载器

    • Bootstrap Class loader
    • Extension Class loader
    • Application Class loader

    双亲委托模型

    ​ 如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式。

    ​ 即每个儿子都很懒,每次有活就丢给父亲去干,直到父亲说这件事我也干不了时,儿子自己才想办法去完成。

    为什么采用这种模式

    • 避免类的重复加载
    • 保证安全, Java中定义的核心类不会被随意替换

    2. 反射

    ​ 反射到底是干啥的?答:不用new也可以获取到一个对象的实例。可以在运行时构造任意一个类的对象,可以在运行时处理注解、获取泛型信息等。

    ​ 反射相关的apijava.lang.reflect包下。以下是通过反射调用show方法的代码清单。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    package com.nefu.reflect;

    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;

    public class {
    public () {

    }
    public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {

    Class<?> clazz = Class.forName("com.nefu.reflect.Main");
    //2. 创建一个Main类的实例对象
    Object obj = clazz.getDeclaredConstructor().newInstance();
    Method show = clazz.getDeclaredMethod("show");
    //3. 避免权限不够
    show.setAccessible(true);
    System.out.println(show.getReturnType());
    //4. 调用obj的show方法
    show.invoke(obj);
    }
    public void show() {
    System.out.println("hello world");
    }
    }

    3. 动态代理

    ​ 代理类可以增强被代理类对象方法。

    3.1 静态代理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public class Test01 {
    public static void main(String[] args) {
    NikeClothFactory nikeClothFactory = new NikeClothFactory();
    NikeClothFactoryProxy proxy = new NikeClothFactoryProxy(nikeClothFactory);
    proxy.invoke();
    }
    大专栏  Java 反射和动态代理class="line">}
    interface ClothFactory{
    void product();
    }
    ///被代理类
    class NikeClothFactory implements ClothFactory{

    public void product() {
    System.out.println("Nike 开始生产....");
    }
    }
    //代理类
    class NikeClothFactoryProxy{
    ClothFactory clothFactory;

    public NikeClothFactoryProxy(ClothFactory clothFactory) {

    this.clothFactory = clothFactory;
    }

    public void invoke() {
    System.out.println("前置处理");
    clothFactory.product();
    System.out.println("后置处理");
    }
    }

    ​ 以上代码可以看出,当再有一个类实现ClothFactory接口,我们得继续编写一个对应的代理类进行增强处理。静态代理在编译期就确定了代理对象

    3.2 动态代理

    ​ 在Java中,动态代理实现有JDK自带的动态代理,CGLib动态代理。 通过动态代理,可以无需声明代理类。是使用反射和字节码的技术,在运行期创建指定接口或类的子类(即动态代理类)以及其实例对象的技术。通过动态代理技术可以无侵入地对代码进行增强。

    ​ 两种动态代理的最大的区别是:JDK动态代理要求被代理对象必须基于接口来实现。动态代理类和被代理类必须实现同一个接口。动态代理只能对接口中声明的方法进行代理。对那些没有实现接口的bean。JDK动态代理无法代理。而CGLib通过继承被代理类的方式实现代理。

    ​ 在JDK动态代理中,主要调用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。依然以静态代理中的ClothFactory为例,编写动态代理。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;

    //被代理类
    class PumaClothFactory implements ClothFactory{

    public void product() {
    System.out.println("Puma 源自南美!");
    }
    }
    //测试类
    public class Test02 {
    public static void main(String[] args) {
    PumaClothFactory obj = new PumaClothFactory();
    ClothFactory proxy = (ClothFactory)Proxy
    .newProxyInstance(obj.getClass().getClassLoader(),
    obj.getClass().getInterfaces(),
    new PumaInvocationHandler(obj));
    proxy.product();
    }
    }
    //每一个动态代理实例都有一个关联的InvocationHandler
    class PumaInvocationHandler implements InvocationHandler{
    ClothFactory obj;
    public PumaInvocationHandler(ClothFactory clothFactory) {
    // TODO Auto-generated constructor stub
    this.obj = clothFactory;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // TODO Auto-generated method stub
    System.out.println("前置处理");
    //真正调用被代理类的方法
    method.invoke(obj, args);
    System.out.println("后置处理");
    return null;
    }
    }

    ​ 可以看出,上述代码中并没有显示的编写代理类,而是调用了Proxy.newProxyInstance方法来动态创建代理类。

  • 相关阅读:
    jquery异步加载json格式的数据
    三角形及选中取消按钮的css代码
    css实现自适应宽度布局
    table表格中实现tbody部分可滚动,且thead部分固定
    table数据表格添加checkbox进行数据进行两个表格左右移动。
    对checkbox 的checked的一些总结
    Java多线程同步器
    Springboot动态获取bean对象工具类
    并发阻塞队列和非阻塞队列详解
    多线程-volatile关键字和ThreadLocal详解
  • 原文地址:https://www.cnblogs.com/lijianming180/p/12410226.html
Copyright © 2020-2023  润新知