Spring4-快速入门之在IOC容器中装配Bean

来源:互联网 发布:java 创建数组 编辑:程序博客网 时间:2024/05/20 11:48

概述

Bean配置信息:

即Bean的元数据信息,包括Bean的实现类,属性信息,依赖关系和行为配置(生命周期及生命周期过程中的回调函数)

Spring容器内部协作接口

这里写图片描述
首先,容器会根据Bean的配置信息,在容器内部建立Bean定义注册表(一个个BeanDefinition对象),然后根据注册表实例化Bean,并建立Bean和Bean之间的依赖关系,最后将这些准备就绪的Bean放入缓存池中,供外部的应用程序使用

Bean基本配置

基于XML文件的配置方式如下

<bean id="foo1" class="com.spring4.chpter5.Foo"></bean>

除了使用id为Bean命名,还可以使用name为Bean命名,name属性支持多个命名,可以使用空格,分号,或逗号分开

    <bean name="foo1 foo2 foo3" class="com.spring4.chpter5.Foo"></bean>

依赖注入

Spring支持3种方式的注入,分别是属性注入,构造函数注入和工厂方法注入

属性注入

通过属性的setter()方法进行注入,要求Bean必须提供一个默认的构造函数,并为需要注入的属性提供setter()方法

public class Car {    private String brand;    private String color;    private String maxSpeed;    public void setBrand(String brand) {        this.brand = brand;    }    public void setColor(String color) {        this.color = color;    }    public void setMaxSpeed(String maxSpeed) {        this.maxSpeed = maxSpeed;    }    public void introduce() {        System.out.println("brand:" + this.brand + ";color:" + this.color + ";maxSpeed:" + this.maxSpeed);    }}<bean id="car" class="com.spring4.chpter5.Car">        <property name="brand" value="影刺HT+"></property>        <property name="color" value="黑色"></property>        <property name="maxSpeed" value="300"></property>    </bean>

注意,Spring只会检查Bean中是否有对应的setter方法,并不关心是否有该属性,例如上面Car类中有setBrand()方法,但却不一定要有brand属性

构造函数注入

使用构造函数注入,要求Bean必须提供带参数的构造函数。例如上面的Car类,提供一个带参数的构造函数。

public Car(String brand,String color,String maxSpeed) {        this.brand = brand;        this.color = color;        this.maxSpeed = maxSpeed;    }

bean.xml配置如下:

<!-- 根据构造函数注入 -->            <constructor-arg name="color" value="红色"></constructor-arg>            <constructor-arg name="brand" value="游侠9"></constructor-arg>            <constructor-arg name="maxSpeed" value="350"></constructor-arg>

构造函数注入还提供按索引和按参数类型匹配入参的功能,可以使用constructor-arg标签下的type属性和index属性配置。

注意:循环依赖问题

考虑下面的例子:
Car类的构造函数

public Car(String brand,String color,String maxSpeed,Boss boss) {        this.brand = brand;        this.color = color;        this.maxSpeed = maxSpeed;        this.boss = boss;    }

Boss类的构造函数

    public Boss(String name,Car car) {        this.name = name;        this.car = car;    }

bean.xml配置:

<bean id="car" class="com.spring4.chpter5.Car">            <constructor-arg name="color" value="红色"></constructor-arg>            <constructor-arg name="brand" value="游侠9"></constructor-arg>            <constructor-arg name="maxSpeed" value="350"></constructor-arg>            <constructor-arg name="boss" ref="boss"></constructor-arg    </bean>    <bean id="boss" class="com.spring4.chpter5.Boss">        <constructor-arg name="name" value="SSS"></constructor-arg>        <constructor-arg name="car" ref="car"></constructor-arg>            </bean>

由于在使用构造函数注入时,Bean的入参引用的对象必须已经准备就绪,而这里的Car类和Boss类都使用了构造函数注入,而且都互相引用了对方,因此会发生循环依赖问题,两者都会等待对方实例化,就会出现类似线程死锁的问题,SpringIoC容器将不能启动成功。

工厂方法注入

非静态工厂方法:
public Car getCar() {        Car car = new Car();        return car;    }
<bean id="carFactory" class="com.spring4.chpter5.CarFactory"></bean>    <bean id="car2" factory-bean="carFactory" factory-method="getCar"></bean>

静态工厂方法:

public static Car getCar2() {        Car car = new Car();        return car;    }
<bean id="car3" class="com.spring4.chpter5.CarFactory" factory-method="getCar2"></bean>

注入方式的考量

选择构造函数注入的理由:

1.构造函数注入可以保证一些重要的属性在Bean实例化好之前就设置好,避免因为一些重要属性没有提供而导致一个无用Bean实例的情况
2.不需要为每个属性都设置setter方法,减少方法的数量
3.可以更好的封装类变量,避免外部错误的调用

不选择构造函数注入的理由:

1.如果类的属性过多,bean标签下的constructor-arg标签也会增多,可读性差
2.灵活性不强,如果在有些属性是可选的情况下,通过构造函数注入需要传入null作为默认值
3.不利于类的继承和扩展,因为子类也需要引用父类的构造函数
4.会造成循环依赖问题

部分注入参数介绍

1.当注入的属性包含了XML的特殊字符时,可以使用

<property name="brand"><value><![CDATA[影刺HT+&游侠999]]></value></property>

2.引用其他Bean
通过ref标签引用其他的Bean,ref标签包含两个属性,bean和parent,bean属性表示引用当前容器中的Bean,如果没有,则引用父容器中的Bean;parent属性表示引用父容器中的Bean。
父容器bean.xml:

<bean id="car" class="com.spring4.chpter5.Car">        <property name="brand"><value><![CDATA[影刺HT+&游侠999]]></value></property>        <property name="color" value="黑色"></property>        <property name="maxSpeed" value="300"></property>    </bean>

子容器bean2.xml:

<bean id="car" class="com.spring4.chpter5.Car">        <property name="brand"><value>New棉花糖Black></value></property>        <property name="color" value="黑色"></property>        <property name="maxSpeed" value="300"></property>    </bean>    <bean id="boss" class="com.spring4.chpter5.Boss">        <constructor-arg name="name" value="SSS"></constructor-arg>        <constructor-arg name="car">            <!-- <ref bean="car"/> --> <!-- 打印结果为brand:New棉花糖Black>;color:黑色;maxSpeed:300 -->            <ref parent="car"/> <!-- 打印结果为brand:影刺HT+&游侠999;color:黑色;maxSpeed:300 -->        </constructor-arg>          </bean>

3.内部Bean
内部Bean和Java的匿名内部类相似,没有名字,也不能被其他Bean引用,只能在声明处为外部Bean提供实例注入

<bean id="boss" class="com.spring4.chpter5.Boss">            <bean class="com.spring4.chpter5.Car">                <property name="brand"><value>New棉花糖></value></property>                <property name="color" value="绿色"></property>                <property name="maxSpeed" value="280"></property>            </bean>        </constructor-arg>          </bean>

4.null值
通过标签为属性注入null值

<bean id="boss" class="com.spring4.chpter5.Boss">            <bean class="com.spring4.chpter5.Car">                <property name="brand"><value>New棉花糖></value></property>                <property name="color" value="绿色"></property>                <property name="maxSpeed"><null></null></property>            </bean>        </constructor-arg>          </bean>

5.级联属性
以圆点(.)的方式定义级联属性

<property name="car.brand" value="黑色甲虫9"></property>

6.定义集合属性
例如:为Boss类添加一个List类型的favorites属性

public class Boss {    private List<String> favorites = new ArrayList<String>();    ...}

Spring配置如下:

<property name="favorites">    <list>        <value>吃饭</value>        <value>睡觉</value>        <value>打豆豆</value>    </list></property>

注意:不仅仅是List类型的属性,数组类型(int[],String[])的属性也可以通过这种方式进行注入
Map类型和Set类型的注入方式与List类似

<!-- 注入Set类型的数据 -->        <property name="favorites">            <set>                <value>吃饭</value>                <value>睡觉</value>                <value>打豆豆</value>            </set>        </property>        <!-- 注入Map类型的数据 -->        <property name="jobs">            <map>                <!-- 一个entry代表一个键值对 -->                <entry key="AM" value="MA"></entry>                <entry key="WW" value="CC"></entry>            </map>        </property>

除此之外,List,Set,Map也可以使用Bean作为注入的对象,可通过注入

<list>    <ref bean="beanName" /></list><set>    <ref bean="beanName"/></set><map>    <entry key-ref="beanName" value-ref="beanName"></entry></map>

Properties类型的属性注入
Properties与Map的区别在于,Properties只支持key和value为字符串。

<property name="props">    <props>        <prop key="key1">value1</prop>        <prop key="key2">value2</prop>    </props></property>

通过util命名空间配置集合类型的Bean
如果希望配置一个集合类型的Bean,而不是一个集合类型的属性,可以使用util命名空间进行配置,需要在Spring配置文件中引入util命名空间的声明。

xmlns:util="http://www.springframework.org/schema/util"http://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util-4.0.xsd

配置一个List类型的Bean,可以通过list-class属性显示的指定List的实现类

<util:list id="list1" list-class="java.util.LinkedList">    <value>吃饭</value>    <value>睡觉</value>    <value>打豆豆</value></util:list>

配置一个Set类型的Bean,可以通过set-class属性显示的指定Set的实现类

<util:set id="set1" set-class="java.util.HashSet">    <value>吃饭</value>    <value>睡觉</value>    <value>打豆豆</value></util:set>

配置一个Map类型的Bean,可以通过map-class属性显示的指定Map的实现类

<util:map id="map1" map-class="java.util.HashMap">    <entry key="AM" value="MA"></entry>    <entry key="WW" value="CC"></entry></util:map>

方法注入

1.lookup方法注入
声明一个MagicBoss接口,并声明一个getCar()方法,现在通过lookup方法注入,使每次调用getCar()方法都返回一个新的car Bean

public interface MagicBoss {    public Car getCar();}
<bean id="car" class="com.spring4.chpter5.Car" scope="prototype"><!-- 这里要设置car为prototype -->    <property name="brand"><value>New棉花糖Black></value></property>    <property name="color" value="黑色"></property>    <property name="maxSpeed" value="300"></property></bean><bean id="magicBoss" class="com.mjf.spring4.chpter5.MagicBoss">    <lookup-method name="getCar" bean="car"/></bean>

2.方法替换
Bean实现MethodReplacer接口后,可以使用该接口的方法去替换目标Bean的方法
例如:
Boss1的getCar()方法返回Car1

public class Boss1 {    public Car getCar() {        Car car = new Car();        car.setBrand("Car1");        return car;    }}

Boss2实现了org.springframework.beans.factory.support.MethodReplacer接口,该接口返回Car2

public class Boss2 implements MethodReplacer{    @Override    public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {        Car car = new Car();        car.setBrand("Car2");        return car;    }}

Spring配置如下

<bean id="boss1" class="com.mjf.spring4.chpter5.Boss1">        <!-- 使用boss2的reimplement方法替换boss1的getCar方法  -->    <replaced-method name="getCar" replacer="boss2"></replaced-method></bean><bean id="boss2" class="com.mjf.spring4.chpter5.Boss2"></bean>

Bean之间的继承和依赖

1.继承
通过继承,子bean会继承父bean的所有配置信息

<bean id="car" class="com.spring4.chpter5.Car" scope="prototype">        <property name="brand"><value>New棉花糖Black></value></property>        <property name="color" value="黑色"></property>        <property name="maxSpeed" value="300"></property>    </bean>    <!-- 继承car Bean,brand和maxSpeed属性与car一致 -->    <bean id="car2" class="com.spring4.chpter5.Car" scope="prototype" parent="car">        <property name="color" value="红色"></property>    </bean>

2.依赖
通过depends-on属性,显式的指定Bean前置依赖的Bean,前置依赖的Bean会在本Bean实例化之前创建好

    <!-- boss3会在mycar实例化完成后,在实例化 -->    <bean id="boss3" class="com.spring4.chpter5.Boss" depends-on="mycar"></bean>    <bean id="mycar" class="com.spring4.chpter5.Car"></bean>

整合多个配置文件

通过import标签将多个xml配置文件整合到一起

<import resource="classpath*:com/spring4/chpter5/bean.xml"/>

Bean的作用域

类型 说明 singelton Bean以单例的方式存在 prototype 与singelton相反,每次都会返回新的 Bean request 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境 session 同一个HTTP Session共享一个Bean,不同的HTTP Session使用不同的Bean,该作用域仅适用于WebApplicationContext环境 globalSession 同一个全局Session共享一个Bean,一般用于Portlet应用环境,该作用域仅适用于WebApplicationContext环境

注意:当非web相关作用域的Bean引用web相关作用域的Bean时,需要与Spring的动态代理技术一起使用,如下:

    <!-- 根据代理,判断boss5需要取得那个HTTP请求相关的car Bean -->    <bean id="car5" class="com.spring4.chpter5.Car" scope="request">        <aop:scoped-proxy/><!-- 创建代理 -->    </bean>    <bean id="boss5" class="com.spring4.chpter5.Boss" scope="singleton">        <property name="car" ref="car5"></property><!-- 引用web相关作用域的Bean -->    </bean>

基于注解的配置

1.使用注解定义的Bean
将class定义为Bean的注解类别:

注解名 说明 @Component 将一个class定义为Bean @Repository 用于对Dao实现类进行标注 @Service 用于对Service实现类进行标注 @Controller 用于对Controller实现类进行标注

2.扫描注解定义的Bean
Spring提供了一个Context的命名空间,它提供了通过扫描类包以应用注解定义Bean的方式,如下:

<!-- 声明Context的命名空间 -->xmlns:context="http://www.springframework.org/schema/context"http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-4.0.xsd"><!-- 扫描类包以应用注解定义的Bean --><context:component-scan base-package="com.spring4.chpter5"></context:component-scan>

3.自动装配Bean
使用@Autowired进行自动注入(默认使用byType的方式)

@Autowiredprivate Car car;

将@Autowired的required属性设置为false,即使Spring找不到匹配的Bean,也不会抛出异常
可以使用@Qualifier注解指定注入的Bean的名称

@Autowired@Qualifier("car")//注入名为car的Beanprivate Car car;

@Autowired和@Qualifier除了能注解属性外,还可以注解方法,如下:

@Autowiredpublic Boss(String name,@Qualifier("car") Car car) {    System.out.println("Boss Constructor");    this.name = name;    this.car = car;}

使用@Lazy注解指定延迟依赖注入,注意@Lazy注解必须同时标注在属性和目标Bean上,否则无效。

4.Bean作用范围和生命过程方法
使用@Scope注解指定Bean的作用范围,例如:@Scope(“prototype”)。
使用@PostConstruct和@PreDestory注解指定Bean的初始化及容器销毁前执行的方法,可以标注多个方法。

基于Java类的配置

1.使用Java类提供Bean定义信息
使用@Configuration将一个POJO标注定义为Bean的配置类(我的理解是,同xml配置文件类似,也可以作为bean标签使用)
使用@Bean注解标注方法,提供Bean的定义信息(和bean标签类似)
例如:

@Configurationpublic class AppConf {    @Bean    public Boss1 getBoss1() {        Boss1 boss1 = new Boss1();        return boss1;    }    @Bean    public Car getCar() {        Car car = new Car();        return car;    }}

上面的代码等同于

<bean id="AppConf" class="com.spring4.chpter5.AppConf"></bean>  <bean id="getBoss1" class="com.spring4.chpter5.Boss1"></bean>   <bean id="getCar" class="com.spring4.chpter5.Car"></bean>
原创粉丝点击