Spring读书学习笔记(三)——IoC

来源:互联网 发布:淘宝出版物许可证 代办 编辑:程序博客网 时间:2024/06/07 06:22
3.1 IoC概述
IoC(Inverse of Control,控制反转)是Spring容器的内核。

3.1.1 概念

可以用DI(Dependency Injection, 依赖注入)的概念来代替IoC,即让调用类对某一接口实现类的依赖关系由第三方注入,以移除调用类对某一接口实现类的依赖。

3.1.2 IoC类型

从注入方法上看,IoC主要可以划分为三种类型:构造函数注入,属性注入和接口注入。


3.2 相关Java知识

Java运行通过程序化的方式间接对Class进行操作。Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息,如构造函数、属性、方法等。

3.2.1 类装置器

1.类装载器的工作机制

类装载器就是寻找类的节码文件并构造出类在JVM内部表示构造的组件。在Java中,类装载器把类装入JVM中,需要经过以下步骤:

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

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

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

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

解析:将符号引用转化为直接引用

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


JVM运行时会产生3个ClassLoader:根装载器、ExtClassLoader(扩展类装载器)和AppClassLoader(应用类装载器)。根装载器不是ClassLoader的子类,它使用C++语言编写,因而在Java中看不到它,跟装载器负责装载JRE的核心类库。其他两个是ClassLoader的子类,ExtClassLoader负责装载JRE扩展目录ext中的JAR包,AppClassLoader负责装载Classpath路径下的类包。

根装载器是ExtClassLoader的父装载器,ExtClassLoader是AppClassLoader的父装载器。在默认情况下,使用AppClassLoader装载应用程序的类。

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


每个类在JVM中都拥有一个对应的java.lang.Class对象,它提供了类结构信息的描述。数组、枚举、注解及基本Java对象,甚至void都拥有对应的Class对象。Class没有public的构造方法。Class对象是由装载类时由JVM通过调用类装载器中的defineClass方法自动构造的。

3.2.1 Java反射机制

Class反射对象描述类语义结构,可以从Class对象中获取构造函数、成员变量、方法类等类元素的反射对象,并以编程的方式通过这些反射对象对目标类对象进行操作。这些反射对象类在java.reflect包中定义,下面介绍3个主要的反射类。

Constructor:类的构造函数反射类,通过Class#getConstructors()方法可以获取类的所有构造函数反射对象数组。一个主要方法是newInstance(Object[] initargs),通过该方法可以创建一个对象类的实例,相当于new。

Method:类方法的反射类,通过Class#getDeclaredMethods()方法可以获取类的所有方法反射类对象数组Method[]。Method最主要方法是invoke(Object obj, Object... args),其中obj表示操作的目标对象,args为方法入参。

Field:类的成员变量反射类,通过Class#getDeclaredFields()方法可以获取类的成员变量反射对象数组,通过Class#getDeclaredFields(String name)则可以获取某个特定名称的成员变量反射对象。Field类最主要方法是set(Object obj, Object value),其中obj表示操作的目标对象,通过value为目标对象的成员变量设置值。

Java还为包提供了Package反射类,为注解提供了AnnotatedElement反射类。Java反射体系保证了可以通过程序化的方式访问目标类中所有元素,对应private或protected成员变量及方法,只要JVM的安全机制允许,也可以通过反射进行调用。

在访问private或protected成员变量和方法时,必须通过setAccessible(boolean access)方法取消Java语言检查,否则将抛出IllegalAccessException。如果JVM的安全管理器设置了相应的安全机制,那么调用该方法将抛出SecurityException。


3.3 资源访问利器

3.3.1 资源抽象接口

JDK所提供的访问资源的类(java.net.URL, File)等并不能很好地满足各种底层资源的访问需求。鉴于此,Spring设计了一个resource接口,它为应用提供了更强的底层资源访问能力。该接口拥有对应不同资源类型的实现类。

假设有一个文件位于Web应用的类路径下,用户可以通过以下方式对这个文件资源进行访问:

通过FileSystemResource以文件系统绝对路径的方式进行访问。

通过ClassPathResource以类路径的方式进行访问。

通过ServletContextResource以相对于Web应用根目录的反射进行访问。

3.3.2 资源加载

Spring提供了一个强大的加载资源的机制,不但能够通过“classpath:”、“file:”等资源地址前缀识别不同的资源类型,还支持Ant风格带通配符的资源地址。

1.资源地址

和“classpath:”对应的还有另一种“classpath*:”前缀。假设有多个JAR包或文件系统类路径都拥有一个相同的包名。“classpath:”只会在第一个加载的包的类路径下查找,而“classpath*”会扫描所有这些JAR包及类路径下出现的此类路径。

Ant风格的资源地址支持3种匹配符。

?:匹配文件名中的一个字符。

*:匹配文件名中的任意字符。

**:匹配多层路径。


3.4 BeanFactory和ApplicationContext

Spring通过一个配置文件描述Bean和Bean之间的依赖关系,利用Java语言的反射功能实例化Bean并建立Bean之间的依赖关系。

Bean工厂(com.springframework.beans.factory.BeanFactory)是Spring框架最核心的接口,它提供了高级IoC的配置机制。BeanFactory使管理不同类型的Java对象成为可能,应用上下文(com.springframework.context.ApplicationContext)建立在BeanFactory集成之上,提供了更多面向应用的功能,它提供了国际化支持和框架事件体系,更易于创建实际应用。一般称BeanFactory为IoC容器,称ApplicationContext为应用上下文,有时直接将ApplicationContext称为Spring容器。

BeanFactory是Spring框架的基础设施,面向Spring本身;ApplicationContext面向使用Spring框架的开发者,几乎所有的应用场合都可以直接使用ApplicationContext而非底层的BeanFactory。

3.4.1 BeanFactory介绍

BeanFactory是一个类工厂,但与传统的类工厂不同,传统的类工厂仅负责构造一个或几个类的实例;而BeanFactory是类的通用工厂,它可以创建并管理各种类的对象。这些可被创建和管理的对象本身并没有什么特别之处,仅是一个POJO,Spring称这些Java对象为Bean。JavaBean要满足一定规范,如必须提供一个默认不带参的构造函数、不依赖于某一特定的容器等,但Spring中的Bean比JavaBean更宽泛一些,所有可以被Spring容器实例化并管理的Java类都可以成为Bean。

1.BeanFactory的类体系结构

Spring为BeanFactory提供了多种实现,建议使用XmlBeanDefinitionReader/DefaultListableBeanFactory替代。

3.4.2 ApplicationContext类体系结构

1.ApplicationContext类体系结构

ApplicationContext的主要实现类是ClassPathXmlApplicationContext和FileSystemXmlApplicationContext,前者默认从类路径加载配置文件,后者默认从文件系统中装载配置文件。

2.WebApplicationContext类体系结构

WebApplicationContext是专门为Web应用准备,它允许从相对于Web根目录的路径中装配文件完成初始化工作。从WebApplicationContext可以获得ServletContext的引用,整个Web应用上下文对象将作为属性放到ServletContext中,以便Web应用环境可以访问Spring应用上下文。

在非Web应用的环境下,Bean只有singleton和prototype两种作用域。WebApplicationContext为Bean添加了三个新的作用域:request,session和global session。

3.WebApplicationContext初始化

在web.xml中配置自启动的Servlet或定义Web容器监听器(ServletContextListener),都可以完成启动Spring Web应用上下文的工作。

3.4.3 父子容器

通过HierarchicalBeanFactory接口,Spring的IoC容器可以建立父子层级关联的容器体系,子容器可以访问父容器中的Bean,但父容器不能访问子容器的Bean。在容器内,Bean的id必须是唯一的,但子容器可以拥有一个和父容器id相同的Bean。


3.5 Bean的生命周期

3.5.1 BeanFactory中Bean的生命周期

1.图解



4.5.2 ApplicationContext中Bean的生命周期

Bean在应用上下文中的生命周期和在BeanFactory中的生命周期类似,不同的是如果Bean实现了org.springframework.context.ApplicationContextAware接口,则会增加一个调用该接口方法setApplicationContext()的步骤。

如果在配置文件中声明了工厂后处理器接口BeanFactoryPostProcess的实现类,则应用上下文在装载配置文件之后、初始化Bean实例之前将调用这些BeanFactoryPostProcessor对配置信息进行加工处理。

ApplicationContext和BeanFactory另一个最大的不同之处在于:前者会利用Java反射机制自动识别出配置文件中定义的BeanPostProcessor、InstantiationAwareBeanPostProcessor和BeanFactoryPostProcessor,并自动将它们注册到应用上下文中;而后者需要再代码中通过手工调用addBeanPostProcessor()方法进行注册。这也是我是你在应用开发中普遍使用ApplicationContext而很少使用BeanFactory的原因之一。

在ApplicationContext中,只需在配置文件中通过<bean>定义工厂后处理器和Bean后处理器,它们就会按预期方式运行。