《Spring实战 第三版》二

第二章 装配Bean

在Spring中,对象无需自己负责查找或创建与其相关联的其他对象
相反,容器负责把需要相互协作的对象引用赋予各个对象
创建应用对象之间协作关系的行为通常称为装配,这也是依赖注入的本质

声明Bean

Spring是一个基于容器的框架
但是如果没有对Spring进行配置,那它就是一个空容器,不起任何作用
所以我们需要配置Spring来告诉它需要加载哪些Bean和如任何装配这些Bean
这样才能确保它们能够彼此协作

从Spring3.0开始,Spring容器提供了两种配置Bean的方式
一个是:一个或多个XML文件;另一个是:基于Java注解的配置方式
我们首先来关注传统的XML文件配置方式

1.创建Spring配置

在XML文件中声明Bean时,Spring配置文件的根元素来源于Spring beans命名空间所定义的<beans>元素
典型的Spring 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" xmlns:aop="http://www.springframework.org/schema/aop"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
   <!--Bean declaration go here-->
</beans>

在根元素<beans>中,可以放置所有的Spring配置信息,包括<bean>元素的声明

2.声明一个简单的Bean

假设我们有一位杂技师,可以同时抛多个豆袋子

public class Juggler implements Performer{
    private int beanBags=3;
    public Juggler(){}
    public Juggler(int beanBags){
        this.beanBags = beanBags;
    }
    public void perform(){
        System.out.println("JUGGLING "+beanBags+" BEANBAGS!");
    }
}

我们需要在XML配置文件中声明:
<bean id="duke" class="Jugger"></bean>
<bean>元素是Spring中最基本的配置单元,通过该元素Spring将创建一个对象
这里创建了一个由Spring容器管理的名字为duke的Bean,id属性定义了Bean的名字
也作为该Bean在Spring容器中的引用,class属性则告诉我们duke是一个Jugger

当Spring容器加载该Bean时,Spring将使用默认的构造器来实例化duke
即duke将可以同时抛三袋豆袋子

3.通过构造器注入

那么,我们若是想要duke可以抛更多的豆袋子呢?比如抛15个豆袋子该怎么实现呢?

<bean id="duke" class="Jugger">
<constructor-arg value="15">
</bean>

在构造Bena时,我们可以使用<constructor-arg>元素来告诉Spring额外的信息
这时,Jugger将调用另外一个构造方法,value将值传递给了BeanBags

4.通过构造器注入对象引用

如果,我们还想让duke一边抛豆袋子一边朗诵莎士比亚的诗歌呢?
先对Jugger类进行修改

public class PoeticJuggler extends Juggler {
    //这里Poem是一个接口,该接口声明了recite方法
    private Poem poem;
    public PoeticJuggler(int beanBags,Poem poem){
        super(beanBags);
        this.poem = poem;
    }
    public void perform(){
        super.perform();
        System.out.println("While reciting...");
        poem.recite();
    }
}

同时,我们假设有一个类Sonnet29它实现了Poem接口,并定义了recite方法
用recite方法来朗诵莎士比亚的诗歌,并在XML文件中对它进行了声明
<bean id="sonnet29" class="Sonnet29"></bean>

回到问题本身来,那么怎么才能让duke变朗诵诗歌边抛豆袋子呢?
需要在XML文件中做如此声明即可

<bean id="duke" class="PoeticJuggler">
    <constructor-arg value="15"/>
    <constructor-arg ref="sonnet29"/>
</bean>

这里,Poem不是简单类型,不能使用value
需要使用ref属性来实现从构造器注入对象引用

从前面的例子可以看出,通过构造器注入来创建Bean是挺不错的

5.通过工厂方法创建Bean

但是如果你想声明的Bean没有一个公开的构造方法,那怎么办呢?
那就需要了解如何装配通过工厂方法创建的Bean

考虑下这种场景,在Spring中将一个单例类配置为Bean
一般来说,单例类的实例只能通过静态工厂方法来创建

public class Stage {
    private Stage(){}
    private static class StageSingletonHolder{
        static Stage instance = new Stage();
    }
    public static Stage getInstance(){
        return StageSingletonHolder.instance;
    }
}

这里Stage没有一个公开的构造方法
取而代之的是,静态方法getInstance()每次调用时都返回相同的实例
在Spring中如何把没有公开构造方法的Stage配置为一个Bean呢?
<bean id="theStage" class="Stage" factory-method="getInstance"/>
幸好,bean元素有一个factory-method属性,允许我们调用一个指定的静态方法
从而代替构造方法来创建一个类的实例

6.Bean的作用域

所有的Spring Bean默认都是单例,即当容器分配一个Bean时,总是返回Bean的同一个实例
那么如何让Spring在每次请求时都为Bean产生一个新的实例?
<bean id="duke" class="Jugger" scope="prototype">
除了prototype,Spring还提供了其他几个作用域选项

7.初始化和销毁Bean

当实例化一个Bean时,可能需要执行一些初始化操作
当不再需要Bean时,可能要做一些清除工作

为Bean定义初始化和销毁操作,只需要使用init-method和destroy-method参数来配置bean元素
init-method属性指定了在初始化Bean时要调用的方法
destroy-method属性指定了Bean从容器移除之前要调用的方法

假设有一个礼堂类Auditorium,它需要在活动开始前开灯和活动结束后关灯
为了实现该需求我们可以使用init-method和destroy-method属性来声明auditorium Bean:

<bean id="auditorium" class="Auditorium"
init-method="turnOnLights"
destroy-method="turnOffLights"
/>

auditorium Bean实例化后会立即调用turnOnLights方法
该Bean从容器中移除和销毁前,会调用turnOffLights方法

注入Bean属性

通常JavaBean的属性是私有的,同时拥有一组存储器方法
以setXXX和getXXX形式存在
Spring可以借助属性的set方法来配置属性的值,已实现settet方式的注入

1.setter方式的注入

假设我们有一位才华横溢的音乐家

public class Instrumentalist implements Performer {
    public  Instrumentalist(){}
    @Override
    public void perform() {
        System.out.println("Playing"+song+":");
        instrument.play();
    }
    private  String song;
    //注入歌曲
    public void setSong(String song) {
        this.song = song;
    }
    //Instrument是一个接口
    private Instrument instrument;
    //注入乐器
    public void setInstrument(Instrument instrument){
        this.instrument = instrument;
    }
}

我们在XML文件中对其进行声明,并调用属性的setter方法来注入song

<bean id="kenny" class="Instrumentalist">
<property name="song" value="Jingle Bell"/>
<!--Saxophone类实现了Instrucment接口,且其Bean的ID为saxophone-->
property name="instrument" ref="saxophone"/>
</bean>

一旦Instrumentalist被实例化,Spring就会调用<property>元素所指定属性的setter方法为该属性注入值
我们还可以注入内部Bean,即定义在其他Bean内部的Bean

<bean id="kenny" class="Instrumentalist">
<property name="song" value="Jingle Bell"/>
<property name="instrument">
    <bean class="Saxophone"/>
</property>
</bean>

内部Bean是没有ID属性的,也就是说不能被复用
内部Bean仅适用于一次注入,而且也不能被其它Bean所引用

2.使用Sprin的命名空间p装配属性

Spring的空间命名p还提供了另外一种Bean属性装配的方式
该方式不需要配置如此多的尖括号
只需要在Spring的XML文件中增加这一声明即可
xmlns:p="http://www.springframework.org/schema/p"
通过该声明,可以使用p:作为<bean>元素所有属性的前缀来装配Bean的属性

<bean id="kenny" class="Instrumentalist"
      p:song="Jingle Bell"
      p:instrument-ref="saxophone"
/>

命令空间p的最主要优点是更简洁

3.装配集合

如果属性的类型是集合,Spring该如何配置呢?
假设有一个非常厉害的人——hank,可以在同一时刻弹奏多种乐器

public class OneManBand implements Performer {
    public  OneManBand(){}

    @Override
    public void perform() {
        for(Instrument instrument:instrucments){
            instrument.play();
        }
    }
    private Collection<Instrument> instrucments;
    //注入Instrument集合
    public void setInstrucments(Collection<Instrument> instrucments) {
        this.instrucments = instrucments;
    }
}

为了让hank可以同时表演多个乐器,我们需要使用<list>来配置元素
<list>可以用来包含一个或者多个值

<bean id="hank" class="OneManBand">
<property name="instrucments">
    <list>
        <ref bean="saxophone"/>
        <ref bean="piano"/>
    </list>
</property>
</bean>

同样地,也可以使用<set>元素来装配集合或者数组类型的属性
无论是还是都可以用来装配类型为java.util.Collection的任意实现或者数组的属性

那么Map集合又该如何装配呢?
将OneManBand的私有属性instrucments进行修改

private Map<String,Instrument> instrucments;
public void setInstrucments(Map<String, Instrument> instrucments) {
    this.instrucments = instrucments;
}

那么对应的XML配置文件该修改为:

<bean id="hank" class="OneManBand">
<property name="instrucments">
    <map>
        <entry key="Saxo" value-ref="saxophone"/>
        <entry key="Piano" value-ref="piano"/>
    </map>
</property>
</bean>

如果所配置的Map的每一个entry的键和值都是String类型时,可以使用java.util.Properties
同样地,将OneManBand的私有属性instrucments进行修改

private Properties instrucments;
public void setInstrucments(Properties instrucments) {
    this.instrucments = instrucments;
}

对应的XML文件应修改为:

<bean id="hank" class="OneManBand">
<property name="instrucments">
    <props>
        <prop key="Saxo">saxophone</prop>
        <prop key="Piano" >piano/</prop>
    </props>
</property>
</bean>
5.装配空值

Spring还可以为装配一个空值
为属性设置null值,只需使用<null/>元素
<property name="..." ><null/></property>

使用表达式装配

到目前为止,我们所有的关于Bean的装配都是在Spring的XML文件中静态定义的
但是,如果我们为属性装配的值只有运行期才能知道,那有如何实现呢?

Spring 3 引入了Spring表达式语言(Spring Expression Language,SpEL)
SpEL通过运行期间执行的表达式将值装配到Bean的属性或者构造器参数中

#{}标记会提示Spring这个标记内容是SpEL表达式
<property name="instrucment" value="#{saxophone}">
在这里我们使用SpEL把一个ID为“saxophone”的Bean装配到了instrument属性中
SpEL能做的不仅仅是引用Bean,还可以使用Bean的引用来获取属性
也可以通过Bean的引用来调用方法

在SpEl中,还可以使用T()运算符来调用类作用域的方法和常数
如获取Java中的Math类的PI
#{(java.lang.Math).PI}
除此之外,还可以使用SpEL来进行运算

原文地址:https://www.cnblogs.com/ASE265/p/12404914.html

时间: 2024-07-31 06:43:42

《Spring实战 第三版》二的相关文章

Jquery知识点总结(二)

JQuery遍历1 传统的for   2 通过each对象调用callback函数 callback回调函数 /*    * JQ提供的技术,实现遍历    * JQ对象函数调用 each(参数 callback) 参数:函数 回调函数    */    //JQ对象,调用函数each(遍历),传递匿名函数    //JQ自己调用,每遍历一次数组,自动调用一次匿名函数    //匿名函数中参数,i是索引,element是遍历到的元素    $options.each(function(i,ele

jquery知识点总结二

*清空元素* $('ul').empty() *复制节点* $li.clone().appendTo("ul"); 替换节点 $li.replaceWith("<li>哈哈</li>");---替换某个节点 ("<li>哈哈</li>"). replaceAll($li); ---------- *追加CSS样式* **addClass()---注意点attr("class名称"

jquery知识点梳理

jQuery知识点梳理 一.              jquery选择器 基本选择器 ID选择器 类选择器 标记选择器 分组选择器 通配符选择器 层次选择器 $(“ancestor descendent”):祖先后代选择器    空格 $(“parent > child”):父子选择器   大于 $(“prev + next”):相邻后兄弟元素选择器   加号 $(“prev ~ siblings”):所有后兄弟元素选择器  波浪线 过滤选择器:基本过滤选择器 :first.:last.:od

琐碎的知识点(二)

1.绑定设置时间格式 <%#Eval("time", "{0:yyyy-MM-dd}")%> <%#DateTime.Parse(Eval("time").ToString()).ToString("yyyy-MM-dd")%> 2.Repeater1.Items.Count 是指Repeter1的数据绑定条数 <%#this.rptnewstraining.Items.Count == 0 ?

.NET知识点总结二(笔记整合)

19.什么是类型? 用来定义某一种数据在内存里开辟空间的大小,还可以预置操作此种类型数据的相关方法 20.this关键字在方法中使用时所代表的含义 this指的是当前类的对象,或者父类的类的对象(base只能指向父类的对象) 21.里氏替换原则 子类替换父类所在的位置 22.C#中的数据类型 值类型 简单类型(数字类型(int,short,long,float,double),字符(char),逻辑值(bool)),结构体(struct),枚举(enum) 引用类型 字符串(string),数组

jQuery 入门教程(二): 基本语法

学习jQuery之前需要你有下面几个方面的基本知识 HTML CSS JavaScript jQuery 的基本语法 $(selector).action() $ 符合定义这是一个jQuery语句 (selector) 用来选择某个HTML元素,其语法和CSS的selector语法一样. action() 定义操作该HTML元素的方法. 比如: $(this).hide() – 隐藏当前元素. $("p").hide() – 隐藏所以 <p> 元素. $(".te

20150225 IMX257 设备驱动模型之sysfs文件系统知识点整合(二)

20150225 IMX257 设备驱动模型之sysfs文件系统知识点整合(二) 2012-02-25 李海沿 前天我们实现了一个简单的sysfs的kobject的驱动程序,可是有没有发现很多东西都不懂,原因就是在我们对sysfs和kobject的工作原理不懂,虽然我一直不提倡整天接触那些乏味的知识点,也一直不喜欢谈论太多的知识点,但是有的时候,理论知识是实践的基础,有些基础的知识点还是不得不提,下面进入正题. 一.sysfs介绍 在linux2.6内核以后,引入了一个新的文件系统sysfs,它

在angular中结合使用jquery的生成二维码插件

最近在开发项目中,需要在ionic框架中使用到jquery的生成二维码功能的插件,起初我直接在templates中写js代码: 发现这段代码根本就不会执行,原因是js代码必须要在该模块的作用域范围内才能生效,于是把这段代码该模块对应的controller中即可: 在学习angular中永远不要忘记作用域这件事.哈哈,看来学习编程的过程中有时需要抛开传统的思维方式才行啊.虽然只是一个小小的问题,但对于初学angular的我还是纠结了好一阵.

jquery效果(二)

jQuery animate() 方法用于创建自定义动画.语法:$(selector).animate({params},speed,callback);必需的 params 参数定义形成动画的 CSS 属性.可选的 speed 参数规定效果的时长.它可以取以下值:"slow"."fast" 或毫秒.可选的 callback 参数是动画完成后所执行的函数名称. $("button").click(function(){ $("div&q

jQuery生成QRCode二维码

<!DOCTYPE html> <html> <head>   <meta charset="utf-8">   <title>jQuery生成QRCode二维码</title> </head> <body> <!-- 存放二维码 --> <div id="qrcode-1"></div> <!-- 存放二维码 --> &l