装配Bean

来源:互联网 发布:黑暗之魂淘宝 编辑:程序博客网 时间:2024/04/29 19:14

装配Bean

在 Spring IOC 容器读取 Bean 配置创建 Bean 实例之前, 必须对它进行实例化. 只有在容器实例化后, 才可以从 IOC 容器里获取 Bean 实例并使用.
Spring 提供了两种类型的 IOC 容器实现.

  • BeanFactory: IOC 容器的基本实现.
  • ApplicationContext: 提供了更多的高级特性. 是 BeanFactory 的子接口.
  • BeanFactory 是 Spring 框架的基础设施,面向 Spring 本身;ApplicationContext 面向使用 Spring 框架的开发者,几乎所有的应用场合都直接使用 ApplicationContext 而非底层的 BeanFactory
  • 无论使用何种方式, 配置文件时相同的.

ApplicationContext 的主要实现类:

  • ClassPathXmlApplicationContext:从 类路径下加载配置文件
  • FileSystemXmlApplicationContext: 从文件系统中加载配置文件

ConfigurableApplicationContext 扩展于 ApplicationContext,新增加两个主要方法:refresh() 和 close(), 让 ApplicationContext 具有启动、刷新和关闭上下文的能力。
ApplicationContext 在初始化上下文时就实例化所有单例的 Bean
WebApplicationContext 是专门为 WEB 应用而准备的,它允许从相对于 WEB 根目录的路径中完成初始化工作。

这里写图片描述

装配形式

  • 基于XML文件的形式
  • 基于注解的方式

Bean的配置方式

通过全类名

<bean class="com.helloworld.HelloWorld"></bean>

因为没有给出ID,所以这个Bean将会根据全限定类名来进行命名。在这个例子中,ID为com.helloworld.HelloWorld#0,我们通过ID来引用Bean。因为是全限定类名,猜想Spring是通过反射来创建Bean的,所以我们的Bean需要有一个无参数的构造器。

通过工厂方法

静态工厂方法

调用静态工厂方法创建 Bean是将对象创建的过程封装到静态方法中. 当客户端需要对象时, 只需要简单地调用静态方法, 而不同关心创建对象的细节.
要声明通过静态方法创建的 Bean, 需要在 Bean 的 class 属性里指定拥有该工厂的方法的类, 同时在 factory-method 属性里指定工厂方法的名称. 最后, 使用 <constrctor-arg> 元素为该方法传递方法参数.

public class StaticCarFactory {    static HashMap<String, Car> map = new HashMap<>();    static{        map.put("Audi", new Car("Audi", 300000.0));        map.put("Ford", new Car("Ford", 500000.0));    }    public static Car getCar(String name) {        return map.get(name);    }}<!-- 通过静态工厂方法来配置Bean,注意不是配置静态工厂方法实例,而是配置Bean实例 --><!--     class:指向静态工厂的全类名    factory-method:返回Bean的方法    constructor-arg:如果工厂方法需要传入参数,使用constructor-arg --><bean id="car1"     class="com.factory.StaticCarFactory"    factory-method="getCar">    <constructor-arg value="Audi"></constructor-arg></bean>

实例工厂方法

实例工厂方法: 将对象的创建过程封装到另外一个对象实例的方法里. 当客户端需要请求对象时, 只需要简单的调用该实例方法而不需要关心对象的创建细节.
要声明通过实例工厂方法创建的 Bean
- 在 bean 的 factory-bean 属性里指定拥有该工厂方法的 Bean
- 在 factory-method 属性里指定该工厂方法的名称
- 使用 construtor-arg 元素为工厂方法传递方法参数

public class InstanceCarFactory {    HashMap<String, Car> map = new HashMap<>();    {        map.put("Audi", new Car("Audi", 300000.0));        map.put("Ford", new Car("Ford", 500000.0));    }    public Car getCar(String name) {        return map.get(name);    }}<!-- 配置工厂实例 --><bean id="CarFactory" class="com.factory.InstanceCarFactory"></bean><!-- 实例工厂导入 --><bean id="car2" class="com.factory.InstanceCarFactory"    factory-bean="CarFactory"     factory-method="getCar">    <constructor-arg value="Ford"></constructor-arg></bean>

FactoryBean

Spring 中有两种类型的 Bean, 一种是普通Bean, 另一种是工厂Bean, 即FactoryBean. 工厂 Bean 跟普通Bean不同, 其返回的对象不是指定类的一个实例, 其返回的是该工厂 BeangetObject 方法所返回的对象

通过FactoryBean来配置Bean实例,Class指向FactoryBean的全类名,但实际返回的是getObject方法返回的实例!

public class CarFactoryBean implements FactoryBean<Car> {    private String brand;    public void setBrand(String brand) {        this.brand = brand;    }    //返回bean实例    @Override    public Car getObject() throws Exception {        return new Car(brand, 300000.0);    }    //返回bean对象    @Override    public Class<?> getObjectType() {        return Car.class;    }    //是否是单例模式    @Override    public boolean isSingleton() {        return true;    }}<bean id="car" class="com.factorybean.CarFactoryBean" >    <property name="brand" value="BMW"></property></bean>

依赖注入的方式

构造器注入

通过构造方法注入Bean 的属性值或依赖的对象,它保证了 Bean 实例在实例化后就可以使用。
构造器注入在 <constructor-arg> 元素里声明属性, <constructor-arg> 中没有 name 属性。

<constructor-arg>元素

举例:
特别地,当使用构造器注入时,相应的Bean类需要有对应的构造函数。

<!--     通过构造方法来配置Bean    可以按【顺序,位置】或【索引】或者【类型匹配】参数    也接受引用 --><constructor-arg ref="car1"/><constructor-arg values="Audi"/><constructor-arg index="0" values="Audi"/><constructor-arg value="Linken" type="java.lang.String"/>

p-命名空间

举例:
Spring 2.5所引入的。导入p-命名空间实现Bean的装配。

<!-- 通过P命名空间为Bean的属性赋值,需要先导入P命名空间 ,相对于传统的配置方式很简洁 --><bean id="ren" class="com.collections.Person"     p:name="RenLei" p:age="23" p:cars-ref="mycars"></bean>

c-命名空间

Spring 3.0所引入的。导入c-命名空间实现Bean的装配。

<!-- 同样也接受引用[c:name-ref]--><bean id="car4" class="com.helloworld.Car"     c:name="Ford"     c:price="1230000.0"    c:speed="120" ></bean>

属性注入

使用属性的setter方法,利用<property>标签设置。同样,可以对字面量,集合属性赋值。属性注入是实际应用中最常用的注入方式。

<property name="name" value="WangHao"/><!-- 可以使用Property的ref属性建立Bean之间的联系 --><property name="car" ref="car1"/><property name="car" >    <ref bean="car1"/></property><!-- 为级联属性赋值,属性需要先初始化 --><property name="car.speed" value="1234"/><!-- property里创建内部Bean --><property name="car">    <!-- 引用内部Bean,不能被外部引用,ID不需要写 -->    <bean class="com.helloworld.Car">        <constructor-arg type="java.lang.String"><null/></constructor-arg>        <constructor-arg value="50000.0" type="double"></constructor-arg>        <constructor-arg value="500" type="int"></constructor-arg>    </bean></property><!-- 装配集合Map  --><property name="map">    <map>        <entry key="AA" value-ref="car1"></entry>        <entry key="BB" value-ref="car2"></entry>    </map></property>

工厂方法注入

很少使用,不推荐使用

补充

关于字面值

字面值:可用字符串表示的值,可以通过 <value> 元素标签或 value 属性进行注入。
基本数据类型及其封装类、String 等类型都可以采取字面值注入的方式
若字面值中包含特殊字符,可以使用 <![CDATA[]]> 把字面值包裹起来。

引用其他Bean

组成应用程序的 Bean 经常需要相互协作以完成应用程序的功能. 要使 Bean 能够相互访问, 就必须在 Bean 配置文件中指定对 Bean 的引用。
Bean 的配置文件中, 可以通过 <ref> 元素或 ref 属性为 Bean 的属性或构造器参数指定对 Bean 的引用。
也可以在属性或构造器里包含 Bean 的声明, 这样的 Bean 称为内部 Bean

这里写图片描述

内部Bean

当 Bean 实例仅仅给一个特定的属性使用时, 可以将其声明为内部 Bean. 内部 Bean 声明直接包含在 <property><constructor-arg> 元素里, 不需要设置任何 idname 属性。

内部 Bean 不能使用在任何其他地方。

null值

可以使用专用的 <null/> 元素标签为 Bean 的字符串或其它对象类型的属性注入 null
举例:

<property name="name"> <null/></property>

级联属性

StrutsHiberante 等框架一样,Spring 支持级联属性的配置。

<!-- 为级联属性赋值,属性需要先初始化 --><property name="car.speed" value="1234"></property>

装配集合

这是c-命名空间p-命名空间无法做到的。

<constructor-arg value="cars" >    <!-- 为集合属性赋值,Set和数组以及List原理相同 -->    <list>        <ref bean="car1"/>        <ref bean="car2"/>        <ref bean="car3"/>        <bean class="com.helloworld.Car">            <constructor-arg type="java.lang.String"><null/></constructor-arg>            <constructor-arg value="50200.0" type="double"></constructor-arg>            <constructor-arg value="520" type="int"></constructor-arg>        </bean>    </list></constructor-arg>

我们看到,在<constructor-arg>装配集合属性,用的是引用,当然也可以用<value>又在集合里创建Bean实例。

Properties 继承Hashtable<Object,Object>。下面这个例子是配置这样的一个Properties

//POJOpublic class DataSource {    private Properties properties;    public Properties getProperties() {        return properties;    }    public void setProperties(Properties properties) {        this.properties = properties;    }    @Override    public String toString() {        return "DataSource [properties=" + properties + "]";    }}<!-- 配置Properties属性值 --> <bean id="proper" class="com.collections.DataSource">  <property name="properties">      <props>          <prop key="user">root</prop>          <prop key="password">102559</prop>          <prop key="database">mysql:jdbc://test</prop>          <prop key="drive">com.mysql.jdbc.Driver</prop>      </props>  </property> </bean>

util

导入util-命名空间可以更便捷的配置集合属性。

<!-- 配置单例的集合Bean,以便多个Bean使用,需要导入util命名空间 --><util:list id="mycars">    <ref bean="car1"/>    <ref bean="car2"/>    <ref bean="car3"/></util:list>

util-命名空间的元素

元素 描述 <util:constant> 引用某个类型的public static域,并将其暴露为Bean <util:list> 创建一个java.util.List类型的Bean,其中包含值或应用 <util:map> 创建一个java.util.Map类型的Bean,其中包含值或应用 <util:properties> 创建一个java.util.Properties类型的Bean <util:property-path> 引用一个Bean的属性,并将其暴露为Bean <util:set> 创建一个java.util.Set类型的Bean,其中包含值或应用

自动装配

Spring IOC 容器可以自动装配 Bean。需要做的仅仅是在 <bean>autowire 属性里指定自动装配的模式。
- byType(根据类型自动装配): 若 IOC 容器中有多个与目标 Bean 类型一致的 Bean。 在这种情况下, Spring 将无法判定哪个 Bean 最合适该属性, 所以不能执行自动装配。
- byName(根据名称自动装配): 必须将目标 Bean 的名称和属性名设置的完全相同.
constructor(通过构造器自动装配): 当 Bean 中存在多个构造器时, 此种自动装配方式将会很复杂。(不推荐使用

autowire 属性要么根据类型自动装配, 要么根据名称自动装配, 不能两者兼而有之。

举例:
Person有三个属性:

  • private String name;
  • private Car car;
  • private Address address;

    创建了Bean CarBean Address两个Bean。在创建Person Bean时,指定autowire属性,Spring IOC会自动装配Bean。使用byName,实例名 必须完全匹配;使用byType,实例必须要求只有一个。

<bean id="address2" class="com.autowire.Address"    p:city="HuBei" p:street="NanJingDongLu"></bean><bean id="car2" class="com.autowire.Car"    p:brand="Audi" p:price="100000.0"></bean><!--     可以使用autowire属性指定自动装配的方式    byName:名称必须完全匹配    byType:只能有一个实例 --><bean id="person" class="com.autowire.Person"    p:name="WangHao" autowire="byType"></bean>

Bean依赖&继承

  • 继承
    1. Spring 允许继承 bean 的配置, 被继承的 bean 称为父 bean. 继承这个父 BeanBean 称为子 Bean
    2. Bean 从父 Bean 中继承配置, 包括 Bean 的属性配置
      Bean 也可以覆盖从父 Bean 继承过来的配置
    3. Bean 可以作为配置模板, 也可以作为 Bean 实例. 若只想把父 Bean 作为模板, 可以设置 <bean>abstract 属性为 true, 这样 Spring 将不会实例化这个 Bean
    4. 并不是 <bean> 元素里的所有属性都会被继承. 比如: autowire, abstract 等.
    5. 也可以忽略父 Beanclass 属性, 让子 Bean 指定自己的类, 而共享相同的属性配置. 但此时 abstract 必须设为 true

举例:
配置上的继承。

<!--     抽象的Bean不能被实例化     如果不指定Class,则Bean必须是抽象的--><bean id="address" class="com.autowire.Address"    p:city="HuBei" p:street="RenMingLu" abstract="true"></bean><!-- 继承Bean后并覆盖了其中的某个属性 --><bean id="address2"     p:street="NanJingDongLu" parent="address"></bean>
  • 依赖
    1. Spring 允许用户通过 depends-on 属性设定 Bean 前置依赖的Bean,前置依赖的 Bean 会在本 Bean 实例化之前创建好
    2. 如果前置依赖于多个 Bean,则可以通过逗号,空格或的方式配置 Bean 的名称
<!-- bean之间的依赖 --><bean id="person2" class="com.autowire.Person"    p:name="WangJun" depend-on="car2" p:address-ref="address2"></bean>

Bean的声明周期

来看一个栗子:
有一个类Book,分别在构造器,setter方法,init方法,destory方法标记输出:

public class Book {    public Book() {        System.out.println("Book Constructor...");    }    private String name;    public void setName(String name) {        this.name = name;        System.out.println("Setting...");    }    public void init() {        System.out.println("Init...");    }    public void destory() {        System.out.println("Destory...");    }    @Override    public String toString() {        return "Book [name=" + name + "]";    }}

创建一个类,实现BeanPostProcessor,在postProcessAfterInitializationpostProcessBeforeInitialization同样标记输出语句。这两个方法传入分别的BeanBean的名称,返回的也是BeanBean 后置处理器允许在调用初始化方法前后对 Bean 进行额外的处理。Bean 后置处理器对 IOC 容器里的所有 Bean 实例逐一处理, 而非单一实例。其典型应用是: 检查 Bean 属性的正确性或根据特定的标准更改 Bean 的属性.

@Override    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {        System.out.println("postProcessAfterInitialization: " + bean + ", " + beanName);        return bean;    }    @Override    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {        System.out.println("postProcessBeforeInitialization: " + bean + ", " + beanName);        return bean;    }

配置Bean:

<bean id="book" class="other.Book" init-method="init" destroy-method="destory">    <property name="name" value="NowCoder"></property></bean><bean class="other.MyBeanPostProcessor"></bean>

输出:

Book Constructor...Setting...postProcessBeforeInitialization: Book [name=NowCoder], bookInit...postProcessAfterInitialization: Book [name=NowCoder], bookBook [name=NowCoder]八月 18, 2017 4:10:38 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@17f052a3: startup date [Fri Aug 18 16:10:36 CST 2017]; root of context hierarchyDestory...

结论:Spring IOC 容器对 Bean 的生命周期进行管理的过程:

  • 通过构造器或工厂方法创建 Bean 实例
  • Bean 的属性设置值和对其他 Bean 的引用
  • Bean 实例传递给 Bean 后置处理器的 postProcessBeforeInitialization 方法
  • 调用 Bean 的初始化方法
  • Bean 实例传递给 Bean 后置处理器的 postProcessAfterInitialization方法
  • Bean 可以使用了
  • 当容器关闭时, 调用 Bean 的销毁方法
原创粉丝点击