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

概述

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/util
http://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的作用域

类型说明
singeltonBean以单例的方式存在
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的方式)

@Autowired
private Car car;

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

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

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

@Autowired
public 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标签类似)
例如:

@Configuration
public 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>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值