• Spring3.x企业开发应用实战读书笔记 —— 第三章IoC容器概述


    声明:    本篇博客绝大多数内容为《Spring3.x企业开发应用实战》一书原内容,所有版权归原书作者所有!,仅供学习参考,勿作他用!

    3.2 相关Java基础知识

        Java语言允许通过程序化的方式间接对Class对象实例操作,Class文件由类装载器装在后,在JVM(Java虚拟机)中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息: 如构造函数、属性和方法等。Java允许用户借由这个Class相关的元信息对象间接调用Class对象的功能,这就为使用程序化方式操作CLass对象开辟了途径。

    3.2.1 简单实例

        我们将从一个简单例子开始探访Java反射机制。

    该类是测试所需要用到的主体类。

    package com.baobaotao.reflect;

    public class Car {
     private String brand;

     private String color;

     private int maxSpeed;

     public Car(){System.out.println("init car!!");}
     public Car(String brand,String color,int maxSpeed){
      this.brand = brand;
      this.color = color;
      this.maxSpeed = maxSpeed;
     }
     public void introduce() {
           System.out.println("brand:"+brand+";color:"+color+";maxSpeed:"+maxSpeed);
     }

     public String getBrand() {
      return brand;
     }

     public void setBrand(String brand) {
      this.brand = brand;
     }

     public String getColor() {
      return color;
     }

     public void setColor(String color) {
      this.color = color;
     }

     public int getMaxSpeed() {
      return maxSpeed;
     }

     public void setMaxSpeed(int maxSpeed) {
      this.maxSpeed = maxSpeed;
     }
    }

    一般情况下,我们会使用如下的代码创建Car的实例:

    Car car = new Car( );

    car.setBrand("some brand");

    或者

    Car car = new Car("some brand","some color",);

    以上两种方式都是采用传统方式直接地调用目标类的方法创建对象,下面我们通过Java反射机制以一种更加通用的方法间接地操作目标类,从而创建对象。


    package com.baobaotao.reflect;

    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;


    public class ReflectTest {
     
     /*
      * 需求:
      *  以反射的方式获取com.baobaotao.reflect.Car类的对象。并设置其实例域的属性,最后输出。   
      */
     public static Car  initByDefaultConst() throws Throwable {
      //通过当前线程对象获取类装载器
      ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
      
      //通过类装载器加载com.baobaotao.reflect.Car类的字节码文件,从而获取到该类的Class对象。
      //类可能找不到,所以需要抛出异常。
      Class clazz = classLoader.loadClass("com.baobaotao.reflect.Car");
      
      //通过该类的默认(无参)构造方法创建对象
      Constructor cons = clazz.getDeclaredConstructor();
      Car car = (Car) cons.newInstance();
      //当然也可以通过该类的Class对象创建该类的实例
      //Car car = (Car) clazz.newInstance();
      
      
      //通过反射方法设置属性
      Method setBrand = clazz.getMethod("setBrand", String.class);
      setBrand.invoke(car, "红旗CA72");
      Method setColor = clazz.getMethod("setColor", String.class);
      setColor.invoke(car,"黑色");
      Method setMaxSpeed = clazz.getMethod("setMaxSpeed", int.class);
      setMaxSpeed.invoke(car, 200);
      
      return car;
     }
     
     public static void main(String[] args) throws Throwable {
      Car car = initByDefaultConst();
      car.introduce();
     }

    }

    结果:

    init car!!
    brand:红旗CA72;color:黑色;maxSpeed:200

        这说明我们完全可以通过编程的方式调用Class的各项功能。这和直接通过构造函数和方法调用类功能的效果是一致的,不过前者是间接调用,后者是直接调用罢了。

        在ReflectTest中,我们使用了几个重要的反射类: ClassLoader、Class、Constructor、Method。

        ClassLoader:    用于将指定路径下的类的字节码文件装载到JVM中。注意,其方法ClassLoader#loadClass的参数一定要是类的全限定名称 —— 包名.类名

        创建目标类的对象:  

            1 可以通过Class类的newInstance方法创建对象。这样创建的对象是通过默认构造器创建的,所以目标类Car中必须相应地给出默认构造器。

            2 通过Constructor类创建。通过目标类对应的Class类的对象的getDeclaredConstructor方法创建Construcor类对象,该类描述了目标类的构造方法的信息。可以通过其newInstance()方法和newInstance(paramTypes)来分别创建不带参的对象和带参的对象。

     如:

            //已经获取目标类对应的Class类的对象clazz

           //得到的未设置实例域的对象(不带参的)

          Car car  = (Car)clazz.getDeclaredConstructor( ).newInstance( );

          //得到的已设置实例域的对象(带参的)

          (Car)clazz.getDeclaredConstructor(String.class,String.class,int.class).newInstance("红旗CA72","黑色",200);

     

    3.2.2 类装载器ClassLoader

        类装载器工作原理

        类装载器就是寻找类的字节码文件并构造出类再JVM内部的表示的对象组件。在Java中,将一个类装载到JVM中需要经过以下步骤:

        1 装载:    查找和导入Class文件;

        2 链接:    执行校验’准备和解析步骤,其中解析步骤是可以选择的:

                a)   校验:    检查载入的Class文件的正确性。

                b)   准备:    给类的静态变量分配内存空间。

                c)    解析:    将符号引用转成直接引用。

    类装载器工作由ClassLoader及其子类负责,ClassLoader是一个重要的Java运行时组件,它负责在运行时查找和装入Class字节码文件。JVM在运行时会产生三个类装载器:  根装载器、ExtClassLoader(扩展类装载器)、和AppClassLoader(系统装载器)。其中,根装载器不是ClassLoader的子类,它由C++编写,因此我们在Java中看不到它。

    默认情况下,使用AppClassLoader装载应用程序的类。我们可以做个试验。

    public static void main(String[] args) throws Throwable {
      ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
      System.out.println("current:"+classLoader);
      System.out.println("parrent:"+classLoader.getParent());
      System.out.println("grandparrent:"+classLoader.getParent().getParent());
     }

    结果;

        current:sun.misc.Launcher$AppClassLoader@73d16e93
        parrent:sun.misc.Launcher$ExtClassLoader@6d06d69c
        grandparrent:null

    通过以上信息,可知装载当前类的装载器为AppClassLoader,父装载器是ExtClassLoader,祖父装载器是根装载器,因为在Java中找不到其句柄,所以直接返回null。

        JVM装载类装载类时使用”全盘负责委托机制“,”全盘负责“指的是当一个类装载器装载类时,默认会选择它该类和其依赖和引用的类,除非显式地声明了使用别的装载器。”委托机制“指的是首先委托父装载器装载目标类的字节码文件,只有在找不到的情况下才从自己的类路径中查找并装载目标类。这是出于安全的考虑。试想,如果某人编写了一个恶意的基础类(如java.lang.String),并装载到了JVM中,这会造成多么可怕的后果!而”全盘委托负责机制“中,基础类永远是由根装载器装载,这便可以杜绝这种现象的发生。

     

    实战经验:

        许多Java开发者都会遇到这样的异常: java.lang.NoSuchMethodError。这便是JVM的“全盘委托机制”造成的。出现该错误说明存在不同版本的类包。

    如commons-lang 2.x.jar和commons-lang3.x.jar都位于类路径中。代码中用到了后者的某个方法,而该方法在前者中并不存在,但是JVM又碰巧从前者中装载类。这便出现了该错误。

     

     


     

     

     

     

     

     

           

       

     

     

     

     

     

     

     

     

     



     

    菊子曰 菊子曰微博群发没烦恼!
    觉得不错请点个赞 若有想法随意评论 问题难解就联系我 互相帮助才是网络
  • 相关阅读:
    JQuery对象操作支持链式法则源码分析
    JQuery + JSON作为前后台数据交换格式实践
    JQuery html API支持解析执行Javascript脚本功能实现-代码分析
    跨域访问实践
    XP下安装MAC OS虚拟系统
    Android APP开发笔记
    CSS浮动与清浮动
    LUA 模块化编程例子
    JavaScript解决命名冲突的一种方法
    XML中文本节点存储任意字符的方法
  • 原文地址:https://www.cnblogs.com/UzumakiNaruto/p/5778334.html
Copyright © 2020-2023  润新知