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:[email protected]
    parrent:[email protected]
    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又碰巧从前者中装载类。这便出现了该错误。

 菊子曰微博群发没烦恼!

时间: 2024-10-22 19:12:40

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

MySQL Cookbook读书笔记第三章

1,查询指定列/从指定列中查询 若需要指定每一列在查询结果中出现的先后顺序,可以在select语句中指定(dstuser在dsthost之后): 查询显示用户所关注的列: 2,指定查询行 使用where关键字可以查询符合条件限制的数据例如:查找srchost为venus或者srchost以s开头的行记录: 使用操作符like进行模式匹配,其中%表示通配符,代表一个差多为任意值的字符串. where使用操作符and可以查询多个条件: 条件并集: 3,格式化显示查询结果 当查询语句中没有重命名列名,

Linux内核设计与实现读书笔记——第三章

Linux内核设计与实现读书笔记——第三章 进程管理 20135111李光豫 3.1进程 1.进程即处于执行期的程序,并不局限于一个可执行的代码,是处于执行期程序以及其相关资源的总称. 2.Linux系统中,对于进程和线程并没有明显的区分,线程是一种特殊的进程. 3.Linux系统中,常用fork()进程创建子进程.调用fork()进程的成之为其子进程的父进程. 4.fork()继承实际上由clone()系统调用实现.最后通过exit()退出执行. 3.2任务描述符及任务结构 1.任务队列实质上

Android群英传神兵利器读书笔记——第三章:Android Studio奇技淫巧

Android群英传神兵利器读书笔记--第三章:Android Studio奇技淫巧 这篇文章篇幅较长,可以使用版权声明下面的目录,找到感兴趣的进行阅读 目录 3.1 Android Studio使用初探 Project面板 Stucture面板 Android Monitor Keymap Tip of the Day 快速查找 Search Action 演示模式 3.2 Android Studio使用进阶 操作与导航 快速重构 代码模板 内置模板 自定义代码注释模板 代码分析 在Andr

《java并发编程的艺术》读书笔记-第三章Java内存模型(二)

一概述 本文属于<java并发编程的艺术>读书笔记系列,第三章java内存模型第二部分. 二final的内存语义 final在Java中是一个保留的关键字,可以声明成员变量.方法.类以及本地变量.可以参照之前整理的关键字final.这里作者主要介绍final域的内存语义. 对于final域,编译器和处理器要遵守两个重排序规则: 在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序. 初次读一个包含final域的对象的引用,与随后初次读这

&lt;java并发编程的艺术&gt;读书笔记-第三章java内存模型(一)

一概述 本文属于<java并发编程的艺术>读书笔记系列,继续第三章java内存模型. 二重排序 2.1数据依赖性 如果两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖性.数据依赖分下列三种类型: 名称 代码示例 说明 写后读 a = 1;b = a; 写一个变量之后,再读这个位置. 写后写 a = 1;a = 2; 写一个变量之后,再写这个变量. 读后写 a = b;b = 1; 读一个变量之后,再写这个变量. 上面三种情况,只要重排序两个操作的执行顺序,

Python读书笔记-第三章,四章

第三章 1. 字符串格式化 >>>format="hello %s  %s world" >>>values=('world','hot') >>>print format % values  #也可以接收单个字符串也 也可以用%f %d这类的类似与c的printf 匹配多个参数的时候应该用圆括号 >>>'%s plus %s equals %s'  %(1,1,2) >>>'%010.2f' %

《R语言实战》读书笔记--第三章 图形初阶(二)

3.4添加文本.自定义坐标轴和图例 很多作图函数可以设置坐标轴和文本标注.比如标题.副标题.坐标轴标签.坐标轴范围等.需要注意的是并不是所有的绘图函数都有上述的参数,需要进行验证.可以将一些默认的参数进行移除,用ann = FALSE来操作. 3.4.1标题 title函数. title(main = NULL, sub = NULL, xlab = NULL, ylab = NULL, line = NA, outer = FALSE, ...) #上面的outer是指标题是否在图形边界之外,

《R语言实战》读书笔记--第三章 图形初阶

3.1使用图形 可以使用pdf等函数将图形直接保存在文件中.在运用attach和detach函数的使用中经常出现错误,比如命名重复的问题,所以,应该尽量避免使用这两个函数. plot是一般的画图函数,hist是直方图,boxplot是箱型图.这些函数会覆盖前面的图形,如何创建多个图形便于同时查看呢?方法有三: 1.创建新图形之前先打开一个新的图形窗口,每一幅新图形会出现在最近的图形窗口中. dev.new() 语句1 dev.new() 语句2 -- 上面的语句,每显现一幅图形之前会新开一个窗口

Maven实战读书笔记(三):Maven依赖

3.1 依赖的配置 一个依赖声明可以包含下面元素: <dependencies> <dependency> <groupId></groupId> <artifactId></artifactId> <version></version> <type></type> <scope></scope> <optional></optional>