Skip to content

Spring基础-IoC #24

Description

@cyjxiaozhi

本文以上节的几个问题为基础,进而深化扩散一下问题:

一、首先到底如何理解IoC
这里我去看了张开涛早先的博客,Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”

在传统的对象创建获取上,我们创建对象并且通过对象内部获取相关依赖,而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。
image
image

对于反转,以及哪些反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。

这里个人理解举一个小例子,就像我们刚开始水平一般找工作,我们需要去主动投简历面试,但如果能力很厉害很出名那公司就会主动来找你并且给你提供好的工作,即“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。

接下来我们要知道Spring Bean是什么,因为IoC Container管理的是Spring Bean, 那么Spring Bean是什么呢?

二、IoC和DI是什么关系
DI—Dependency Injection,即依赖注入:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:

●谁依赖于谁:当然是应用程序依赖于IoC容器;

●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;

●谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;

●注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。
“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。

三、IoC的原理
3.1 IoC容器的概念?
IoC容器就是具有依赖注入功能的容器,IoC容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。应用程序无需直接在代码中new相关的对象,应用程序由IoC容器进行组装。在Spring中BeanFactory是IoC容器的实际代表者。

Spring IoC容器如何知道哪些是它管理的对象呢?这就需要配置文件,Spring IoC容器通过读取配置文件中的配置元数据,通过元数据对应用中的各个对象进行实例化及装配。一般使用基于xml配置文件进行配置元数据,而且Spring与配置文件完全解耦的,可以使用其他任何可能的方式进行配置元数据,比如注解、基于java文件的、基于属性文件的配置都可以。

那Spring IoC容器管理的对象叫什么呢?

3.2 Bean的概念
由IoC容器管理的那些组成你应用程序的对象我们就叫它Bean, Bean就是由Spring容器初始化、装配及管理的对象,除此之外,bean就与应用程序中的其他对象没有什么区别了。那IoC怎样确定如何实例化Bean、管理Bean之间的依赖关系以及管理Bean呢?这就需要配置元数据,在Spring中由BeanDefinition代表,后边会详细介绍,配置元数据指定如何实例化Bean、如何组装Bean等。

3.3 例子
image

image

接口和实现都开发好了,那如何使用Spring IoC容器来管理它们呢?这就需要配置文件,让IoC容器知道要管理哪些对象。让我们来看下配置文件chapter2/helloworld.xml(放到resources目录下):
image

现在万一具备,那如何获取IoC容器并完成我们需要的功能呢?首先应该实例化一个IoC容器,然后从容器中获取需要的对象,然后调用接口完成我们需要的功能,代码示例如下:
image
自此我们已经完成了Spring Hello World

3.4 深入理解容器和Bean
在Spring Ioc容器的代表就是org.springframework.beans包中的BeanFactory接口,BeanFactory接口提供了IoC容器最基本功能;而org.springframework.context包下的ApplicationContext接口扩展了BeanFactory,还提供了与Spring AOP集成、国际化处理、事件传播及提供不同层次的context实现 (如针对web应用的WebApplicationContext)。简单说, BeanFactory提供了IoC容器最基本功能,而 ApplicationContext 则增加了更多支持企业级功能支持。ApplicationContext完全继承BeanFactory,因而BeanFactory所具有的语义也适用于ApplicationContext。

对于容器的实现有三种方式:
• XmlBeanFactory:BeanFactory实现,提供基本的IoC容器功能,可以从classpath或文件系统等获取资源;

(1) File file = new File("fileSystemConfig.xml");

       Resource resource = new FileSystemResource(file);

       BeanFactory beanFactory = new XmlBeanFactory(resource);

(2)

      Resource resource = new ClassPathResource("classpath.xml");                 

      BeanFactory beanFactory = new XmlBeanFactory(resource);

• ClassPathXmlApplicationContext:ApplicationContext实现,从classpath获取配置文件;

     BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath.xml");

• FileSystemXmlApplicationContext:ApplicationContext实现,从文件系统获取配置文件。

     BeanFactory beanFactory = new FileSystemXmlApplicationContext("fileSystemConfig.xml");

ApplicationContext接口获取Bean方法简介:

• Object getBean(String name) 根据名称返回一个Bean,客户端需要自己进行类型转换;

• T getBean(String name, Class requiredType) 根据名称和指定的类型返回一个Bean,客户端无需自己进行类型转换,如果类型转换失败,容器抛出异常;

• T getBean(Class requiredType) 根据指定的类型返回一个Bean,客户端无需自己进行类型转换,如果没有或有多于一个Bean存在容器将抛出异常;

• Map<String, T> getBeansOfType(Class type) 根据指定的类型返回一个键值为名字和值为Bean对象的 Map,如果没有Bean对象存在则返回空的Map。

让我们来看下IoC容器到底是如何工作。在此我们以xml配置方式来分析一下:

一、准备配置文件:就像前边Hello World配置文件一样,在配置文件中声明Bean定义也就是为Bean配置元数据。

二、由IoC容器进行解析元数据: IoC容器的Bean Reader读取并解析配置文件,根据定义生成BeanDefinition配置元数据对象,IoC容器根据BeanDefinition进行实例化、配置及组装Bean。

三、实例化IoC容器:由客户端实例化容器,获取需要的Bean。

3.5 IoC的配置使用

3.5.1 XML配置的结构

image 1、标签主要用来进行Bean定义;

2、alias用于定义Bean别名的;

3、import用于导入其他配置文件的Bean定义,这是为了加载多个配置文件,当然也可以把这些配置文件构造为一个数组(new String[] {“config1.xml”, config2.xml})传给ApplicationContext实现进行加载多个配置文件,那一个更适合由用户决定;这两种方式都是通过调用Bean Definition Reader 读取Bean定义,内部实现没有任何区别。标签可以放在下的任何位置,没有顺序关系。

3.5.2 Bean的配置
Spring IoC容器目的就是管理Bean,这些Bean将根据配置文件中的Bean定义进行创建,而Bean定义在容器内部由BeanDefinition对象表示,该定义主要包含以下信息:

●全限定类名(FQN):用于定义Bean的实现类;

●Bean行为定义:这些定义了Bean在容器中的行为;包括作用域(单例、原型创建)、是否惰性初始化及生命周期等;

●Bean创建方式定义:说明是通过构造器还是工厂方法创建Bean;

●Bean之间关系定义:即对其他bean的引用,也就是依赖关系定义,这些引用bean也可以称之为同事bean 或依赖bean,也就是依赖注入。

Bean定义只有“全限定类名”在当使用构造器或静态工厂方法进行实例化bean时是必须的,其他都是可选的定义。难道Spring只能通过配置方式来创建Bean吗?回答当然不是,某些SingletonBeanRegistry接口实现类实现也允许将那些非BeanFactory创建的、已有的用户对象注册到容器中,这些对象必须是共享的,比如使用DefaultListableBeanFactory 的registerSingleton() 方法。不过建议采用元数据定义。

3.5.3 Bean的命名
有多种命名方式:1.不指定id,只配置必须的全限定类名,由IoC容器为其生成一个标识,客户端必须通过接口“T getBean(Class requiredType)”获取Bean;
image

2.指定id,必须在Ioc容器中唯一
image

3.指定name,这样name就是“标识符”,必须在Ioc容器中唯一
image

4.指定id和name,id就是标识符,而name就是别名,必须在Ioc容器中唯一;
image

5.指定多个name,多个name用“,”、“;”、“ ”分割,第一个被用作标识符,其他的(alias1、alias2、alias3)是别名,所有标识符也必须在Ioc容器中唯一;
image

6.使用标签指定别名,别名也必须在IoC容器中唯一
image

3.5.6 实例化Bean
Spring IoC容器如何实例化Bean呢?传统应用程序可以通过new和反射方式进行实例化Bean。而Spring IoC容器则需要根据Bean定义里的配置元数据使用反射机制来创建Bean。在Spring IoC容器中根据Bean定义创建Bean主要有以下几种方式:

一、使用构造器实例化Bean:这是最简单的方式,Spring IoC容器即能使用默认空构造器也能使用有参数构造器两种方式创建Bean,如以下方式指定要创建的Bean类型:
image
image

二、静态工厂方法实例化
image

三、实例工厂方法实例化
image

四、使用Bean注解实例化
image

五、通过ApplicationContext的getBean方法实例化
image
通过以上例子我们已经基本掌握了如何实例化Bean了,大家是否注意到?这几种方式只是配置不一样,从获取方式看完全一样,没有任何不同。这也是Spring IoC的魅力,Spring IoC帮你创建Bean,我们只管使用就可以了

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions