Spring官方文档学习(一) Ioc容器

一、Ioc容器

1.1 简单了解Ioc容器和Bean

  这一节介绍了Spring框架的控制反转(Ioc)的实现,Ioc也被大家称作依赖注入,依赖注入的主要实现方法包括:将一个对象所依赖的对象通过对象的构造函数的参数传入、或通过对象构造工厂类的构造方法参数传入、或者通过对象属性的set方法传入。Ioc容器在创建Bean的时候通过上述方式注入它们的依赖,这个过程Bean自身不需要自己去构建它所依赖的对象,而是有Ioc容器负责注入,控制反转因此得名。

  Spring框架的Ioc容器的两个基础包是org.springframework.beans和org.springframework.context,其中BeanFactory接口提供了一种可以通过配置来管理程序中的对象的机制,ApplicationContext做为它的子接口添加了以下功能:

1、更方便与Spring的AOP特性集成

2、消息资源处理(用于国际化)

3、事件分发

4、为应用层定制的上下文,如WebApplicationContext专门用于Web应用

  简而言之BeanFacotry提供了基础了框架的基础配置功能,而ApplicationContext添加了针对企业应用的一些特有功能,也可以理解为ApplicationContext是BeanFactory的超集。

  在Spring中那些构成你的应用的由Ioc容器所管理的对象被称为Bean,它的实例化、装配和管理都由Ioc容器负责,包括它所依赖的对象。你可通过配置Ioc容器的元数据来配置这些Bean

1.2 容器概述

  org.springframework.context.ApplicationContext接口作为Spring Ioc容器负责实例化、装配和配置应用中的Beans,它从我们提供的元数据配置中读取指令来完成这些工作,元数据配置包括通过XML文件的形式、JAVA注解的形式和JAVA代码的形式三种,我们可以在元数据中以三种格式独有的方式描述我们需要配置的对象和对象的内部依赖。

  人话(ApplicationContext管理Bean需要我们提供Bean的信息给他,这些信息可以通过 XML、注解、Java带的形式来提供,不管是哪种方式都是Spring规定好的,我们使用它所规定的格式可以描述我们要交给Sping的Ioc来管理的对象以及对象的依赖关系)

  在Spring中ApplicationContext的实现方式有好几种,对于一个单应用,通常使用ClassPathXmlApplicationContext和FileSystemXmlApplication,(通过字面意义就可以了解到对应从ClassPath和文件系统两种路径来获取XML文件的位置),XML是传统的定义元数据的方式,你也可以让Ioc容器通过Java主页或java代码的形式来读取配置,那样只需要很少的XML配置就可以以声明的方式配置元数据  (好啰嗦....)

  在一些应用场景中,不需要通过写代码的形式来实例化一个或多个Spring Ioc的实例,例如一个Web应用只需要在Web.xml中简单的配置八行代码:

  

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

  如果是使用了Eclipse的Spring插件可能只需要点几下鼠标或者几个快捷键就可以搞定

 

  下面的图例展示了Spring是如何工作的,你的程序中需要用到的类对象通过元数据的配置,在ApplicationConext被创建后就可以直接使用了。

  

 

1.2.1  配置元数据

  如上图所示,Spring的Ioc工作需要我们作为应用开发者提供一组配置好的元数据,来告诉Ioc容器实例化、装配、哪些对象以及对象间的依赖。传统上使用XML格式来配置元数据,接下来的几个章节都是通过这种配置方式来介绍Spring Ioc的一些特性。

  基于XML的元数据配置并不是Spring Ioc唯一支持的方式,Spring Ioc本身就解耦于通过怎样的方式来配置元数据,现在大多数开发者选择通过JAVA代码的配置方式来够建Spring应用

  基于注解的配置方式在Spring2.5版本中被引入

  基于Java代码的配置方式在Spring3.0中被引入,这些特性已成为Spring框架的一部分,参见 @Configuration @Bean @Import @DependsOn

 

  通常一个应用中有N多个Bean需要交给Ioc容器来管理,在XML配置中一个Bean由一个<bean/>标签来表示,多个这样的标签被一个顶级的<beans/>标签包裹,如果是通过JAVA来配置 通常使用@Bean来标识一个Bean,它们都应该位于一个被@Configuration标识的类中。

  这些Bean定义对应组成你的应该的那些真实的对象,如你定义的Service层对象、访问控制层对象、表示层对象等,通常情况下不要再容器中定义细粒度对象,因为那通常是DAO或业务逻辑层的责任,但是你可以通过Spring框架集成Aspects来配置那些在容器外定义的对象。下面的例子展示了基于XML配置元数据的基础结构:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="..." class="...">  
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <bean id="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions go here -->

</beans>

  id 属性是一个字符串,作为Bean在容器中的唯一标识

  class属性定义了Bean的实际类型 这里需要写全类名 (需要加上类所在的包名)

 

1.2.2 实例化一个Ioc容器

  ApplicaitonContext的构造函数接收一组字符串参数,这些参数标识了元数据配置文件的路径,可以是类路径ClassPath或系统路径 local file system path

  

ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

  下面的示例展示了Service层对象的配置文件 services.xml

  

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- services -->

    <bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
        <property name="accountDao" ref="accountDao"/>
        <property name="itemDao" ref="itemDao"/>
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions for services go here -->

</beans>

  下面的示例展示了数据访问层对象的配置文件 daos.xml

  

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="accountDao"
        class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions for data access objects go here -->

</beans>

    在上面的示例中Service层由 PetStoreServiceImpl类和两个数据访问层对象 JpaAccountDap和JpaItemDao组成,<property/> 标签中的name属性表示类的一个属性的名称,ref数据表示对一个对象的引用,这两个属性组合在一起表示 PetStoreServiceImpl中的一个属性的值是另一个对象的引用。

 

  基于XML元数据配置排版

  将bean根据所处逻辑层的不同,分别定义在不同的XML文件中是一个很好的习惯和方法,你可以通过ApplicationContext的构造函数将这些XML文件的路径传入(上面章节有提到),同时你可以通过<import/>标签在一个XML文件中导入其他的XML中定义的内容,这样你只需要在ApplicationContext的构造函数中传入这个包含<import/>标签的XML文件的路径就可以了。用法如下:

 

<beans>
    <import resource="services.xml"/>
    <import resource="resources/messageSource.xml"/>
    <import resource="/resources/themeSource.xml"/>

    <bean id="bean1" class="..."/>
    <bean id="bean2" class="..."/>
</beans>

  

 如上例所示,其他的Bean定义被导入 分别来自三个文件 services.xml   resources/messageSource.xml   /resources/themeSource.xml,  所有的路径都是相对的当前<impot/>标签所在XML文件的路径,那么services.xml文件必须是跟当前文件在一个路径下,其他的两个文件必须是在当前路径下的一个resource文件夹中,这里开头的  / 是被忽略的,这里用到的是相对路径,因此最好都不要以 /开头,被导入的XML文件必须格式正确无误。

  Note:不建议在Import的时候使用 ../  的方式来引用父路径中的资源

 

1.2.3  使用Ioc容器

  ApplicationContext做为一个高级工厂接口它维护了注册在它里面的各种类型的bean及它们的依赖,可以通过接口函数 getBean(String name,Class<T> requiredType)来获取一个实例化的对象,如下所示:

  

// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);

// use configured instance
List<String> userList = service.getUsernameList();

  你可以通过getBean来获取你的Bean的实例,通过ApplicationContext还提供了一些其他的方法来获取Bean实例,在理想情况下你不应该使用他们,事实上你的应用也不应该使用getBean()方法,这样你的应用就不依赖于Spring API,例如Spring与web框架的集成为各种web框架组件(如控制器和jsf管理的bean)提供了依赖项注入,允许您通过元数据(如自动装配注释)声明对特定bean的依赖项。

 

 

1.3 Bean概述

  Spring Ico容器管理着一个或多个Bean,这些Bean由你提供给容器的元数据配置信息创建而来,(例如通过传递给ApplicationContext的实现类的XML格式定义的元数据)

  在Ioc容器内部这些Bean的信息以 BeanDefinition 对象的形式保存,该对象主要包含以下信息(当然还有其他的信息):

  1、一个带有完整包路径的类名,(bean的实际实现类)

  2、Bean的各种行为配置(如作用域,生命周期等)

  3、Bean的依赖项(Bean要完成某项工作所需要的其他的对象的引用)

  4、Bean对象创建时所需要的一些其他配置

  元数据被转换成组成一个Bean所有需要的各种属性,下面列举了这些属性

 

  

PropertyExplained in…​

Class

Instantiating Beans

Name

Naming Beans

Scope

Bean Scopes

Constructor arguments

Dependency Injection

Properties

Dependency Injection

Autowiring mode

Autowiring Collaborators

Lazy initialization mode

Lazy-initialized Beans

Initialization method

Initialization Callbacks

Destruction method

Destruction Callbacks

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  除了包含如何创建Bean的定义信息之外 ApplicationContext还允许注册已存在的外部类,这是通过getBeanFactory()方法获取ApplicatoonContext的BeanFacotry来完成的,它返回一个BeanFactory的实现类 DefaultListableBeanFacotry, DefaultListableBeanFacotry提供了两个用于注册外部Bean的函数 registerSingleton(..)和registerBeanDefinition(..),  我们的应用通常是不使用的

 

1.3.1 Bean命名

  每个一个Bean可以用有一个或多个名称标识,这个名字必须在容器中是唯一的,通常它只有一个名字,但是我们可以为它起一个别名

  在基于XML的元数据定义中,我们可以通过<bean/>的ID属性或者name属性来给bean起一个独一无二的名字,可以是字符或字符加数字或加一些特殊字符,如果想要增加一个别名,可以在name属性中使用     ,    英文逗号或  ;  英文分号或空格 来分割不同的名字,

如果你没有为Bean提供一个name或id那么Ioc容器会给Bean分配一个独一无二的名字,但是如果你想要在其他的一些地方引用到该Bean对象的时候,必须为起一个名字或写一个id,将名字或ID做为该bean的引用

  bean的命名规则,以小写字母开头的驼峰命名

  给Bean添加别名的好处在于,假如一个bean被应用中的不同的组件所引用,那么根据不同功能的组件分配一个对该组件有意义的名字是是一个很好的习惯,可以通过别名直观的了解到该bean在组件中的作用

1.3.2  实例化Bean

  Bean的定义实质上是如果创建一个或者多个对象的规则,当容器被请求时会扫描这些Bean定义根据定义封装的信息来创建真实的对象。

  当你使用基于XML的元数据配置时,你必须为<bean/>标签的 class属性指定一个 类型,容器根据这个属性来确定实例化什么样的类,在内部它是 BeanDefinition对象的一个属性  该属性是Class类型,主要分以下两种情况

  1、提供Bean对应的Class  容器利用反射直接调用类的构造函数创建实例,相当于java代码 new Object()

  2、提供一个包含静态工厂方法的类,通过调用工厂方法返回创建的对象,工厂类可能返回自身或者其他类型

 

  内部类

  如果是需要配置静态内部类的话,则必须提供内部类的二进制名,例如如果你有一个 在com.exaple包下的 SomeThing 类,这个SomeThing类包含一个静态内部类 OtherThing  那么<bean/>的 class属性应该定义为

  com.example.SomeThing$OtherThing    使用$连接内部类和它外部类

  

 

 

 

  

 

 

 

posted @ 2020-12-18 13:14  挖哈哈哈^-^  阅读(136)  评论(0)    收藏  举报