精通Spring 4.x企业应用开发实战(第四章 IoC容器)——读书笔记

 

根据个人学习情况,筛选出特别关注的信息,完整内容请看原版,仅仅是个人学习笔记。

目录

一、IoC类型

二、通过容器完成依赖关系的注入

三、Java反射机制

四、类装载器ClassLoader


一、IoC类型

IoC(inverse of Control) 包括两方面内容:控制和反转

主要可以划分为3种类型:构造函数注入、属性注入和接口注入。

故事情节1:有一个剧本《墨攻》MoAttack类,通过调用类的构造函数,不需要关心具体由谁来饰演隔离这个角色,在①处传入饰演者并按照剧本要求完成表演即可。

(1)构造函数注入

/*剧本类*/

public class MoAttack {

private GeLi geli;

//①注入隔离的具体饰演者

public Moattack(GeLi geli) {

this.geli = geli;

}

public void cityGateAsk() {

geli.responseAsk("墨者隔离");

}

}

故事情节2:角色的具体饰演者由导演来安排

/*导演类*/

public class Director{

public void direct() {

//指定角色的饰演者

GeLi geli = new LiuDeHua();

//注入具体饰演者到剧本中

MoAttack moAttack = new MoAttack(geli);

moAttack.cityGateAsk();

}

}

故事情节3:虽然饰演隔离者是影片第一主角,但并非每个场景需要隔离者的出现,在这个情况下通过构造函数的注入方式并不妥当,这时考虑使用属性注入,有选择的通过Setter方法完成调用类所需依赖的注入。

(2)属性注入

/*剧本类*/

public class MoAttack {

private GeLi geli;

public void setGeLi(GeLi geli) {

this.geli = geli;

}

public void cityGateAsk() {

geli.responseAsk("墨者隔离");

}

}

/*导演类*/

public class Director{

public void direct() {

MoAtack moAttack = new MoAttack();

//调用属性Setter方法注入

GeLi geli = new LiuDeHua();

moAttack.setGeLi(geli);

moAttack.cityGateAsk();

}

}

(3)接口注入(不推荐)

为了采取接口注入,需要先声明一个ActorArrangable接口,如下所示:

public interface ActorArrangable{

void injectGeLi(GeLi geli);

}

/*剧本类*/

public class MoAttack implements ActorArrangable{

private GeLi geli;

public void injectGeLi(GeLi geli) {

this.geli = geli;

}

public void cityGateAsk() {

geli.responseAsk("墨者隔离");

}

}

/*导演类*/

public class Director{

public void direct() {

MoAtack moAttack = new MoAttack();

GeLi geli = new LiuDeHua();

moAttack.injectGeLi(geli);

moAttack.cityGateAsk();

}

}

由于通过接口注入需要额外声明一个接口,增加了类的数目,而且实现效果和属性注入无本质区别,所以不提倡采用这种方式。

二、通过容器完成依赖关系的注入

Spring通过配置文件或注解描述类和类之间的依赖关系,自动完成类的初始化和依赖注入工作。以下为Spring实例的配置文件片段:

<?xml version="1.0" encoding="UTF-8" ?>

<bean xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w4.org/2001/XMLSchema-instance"

xmlns:p="http://www.springframework.org/schema/p"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

<!-- 实现类实例化 -->

<bean id="geli" class="LiuDeHua" />

<!-- 通过geli-ref建立依赖关系 -->

<bean id="moAttack" class="com.smart.ioc.Moattack"

p:geli-ref="geli" />

</beans>

通过new XmlBeanFactory("beans.xml")等方式即可启动容器,自动实例化Bean并完成依赖关系的装配,从容器中即可返回准备就绪的Bean实例,后续可直接使用。以上实现是归功于Java语言本身的类反射功能。下面请了解Java语言的反射知识。

三、Java反射机制

代码示例如下:

/*汽车类,测试反射机制调用其方法*/

public class Car {

private String brand;

private String color;

private int maxSpeed;

public Car(){}

public Car(String brand, String color, int maxSpeed) {

this.brand = brand;

this.color = color;

this.maxSpeed = maxSpeed;

}

//省略get和set方法

...........................

public void instroduce() {

System.out.println("brand:" + brand + "|color:" + color + "|maxSpeed:" + maxSpeed);

}

}

/* 测试反射机制的类 */

public class ReflectTest {

public static Car initByDefaultConst() throws Throwable {

//① 通过类装载器获取Car类对象

ClassLoader loader = Thread.currentThread().getContextClassLoader();

//查看类加载器

System.out.println("current loader:" + loader);

System.out.println("parent loader:" + loader.getParent());

System.out.println("grandparent loader:" + loader.getParent().getParent());

Class clazz = loader.loadClass("com.imooc.demo.reflect.Car");

//② 获取类的默认构造器对象并通过它实例化Car

Constructor cons = clazz.getDeclaredConstructor((Class[])null);

Car car = (Car) cons.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, 280);

return car;

}

public static void main(String[] args) throws Throwable {

Car car = initByDefaultConst();

car.instroduce();

}

}

输出结果:

current loader:sun.misc.Launcher$AppClassLoader@18b4aac2

parent loader:sun.misc.Launcher$ExtClassLoader@7cc355be

grandparent loader:null

brand:红旗CA72|color:黑色|maxSpeed:280

四、类装载器ClassLoader

类装载器就是寻找类的节码文件并构造出类在JVM内部标识对象的组件。在Java中,类加载器把一个类装入JVM中,经过以下步骤:

(1)装载:查找和导入Class文件。

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

2-1 校验:检查载入Class文件数据的正确性。

2-2 准备:给类的静态变量分配存储空间。

2-3 解析:将符号引用转换成直接引用。

(3)初始化:对类的静态变量、静态代码块执行初始化工作。

  • 类加载工作由ClassLoader及其子类负责。ClassLoader负责在运行时查找和装入Class字节码文件。JVM在运行时会产生3个ClasLoader:根装载器、ExtClassLoader(扩展类装载器)和AppClassLoader(应用类装载器)。其中根加载器不是ClassLoader的子类,使用C++语言编写,在Java中看不到它,根装载器负责装载JRE的核心类库,如JRE目标下的rt.jar、charsets.jar等。ExtClassLoader和AppClassLoader都是ClassLoader的子类,其中ExtClassLoader负责装载JRE扩展母乳ext中的JAR类包;AppClassLoader负责装载Classpath路径下的类包。
  • 这3个类装载器之间存在父子层级关系,即根装载器是在ExtClassLoader的父装载器,ExtClassLoader是AppClassLoader的父装载器。默认情况下,使用AppClassLoader装载应用程序的类。

代码示例如下:

public classClassLoaderTest{

public static void main(String[] args) {

//① 通过类装载器获取Car类对象

ClassLoader loader = Thread.currentThread().getContextClassLoader();

//查看类加载器

System.out.println("current loader:" + loader);

System.out.println("parent loader:" + loader.getParent());

System.out.println("grandparent loader:" + loader.getParent().getParent());

}

}

输出结果:

current loader:sun.misc.Launcher$AppClassLoader@18b4aac2

parent loader:sun.misc.Launcher$ExtClassLoader@7cc355be

grandparent loader:null

JVM装载类时使用"全盘负责委托机制","全盘负责"是指当一个ClassLoader装载一个类时,除非显示使用另一个ClassLoader,该类所依赖及引用的类也应由这个ClassLoader载入;"委托机制"是指先委托父装载器寻找目标类,只有在找不到的情况下才从自己的类路径中查找并装载目标类。这一点是从安全角度考虑的。

比如说:我们单独写一个java.lang.String:

package java.lang;

public class String {

public String(String msg) {

System.out.println("String");

System.out.println(msg);

}

}

那么我们在其他类调用java.lang.String会出现什么结果呢,比如

System.out.println(new String("myString")); //代码写完会标识报错

猜你喜欢

转载自blog.csdn.net/feixiang3447/article/details/82154319