您的位置:首页 > 编程语言 > Java开发

spring装配Bean

2016-06-14 22:24 393 查看

[spring]03_装配Bean

目录

3.1 JavaBean
    3.1.1 JavaBean 是什么
    3.1.2 JavaBean 特点
    3.1.3 JavaBean的生命周期

3.2 声明Bean
    3.2.1 创建Spring配置
    3.2.2 声明JavaBean
    3.2.3 构造器注入
    3.2.4 通过工厂方法创建Bean
    3.2.5 Bean的作用域
    3.2.6 初始化和销毁Bean

3.3 注入Bean属性
    3.3.1 装配集合

3.4 使用表达式装配
    3.4.1 SpEL 的基本原理
    3.4.2 在 SpEL 中筛选集合

3.5 JavaBean的一个简单应用实例
参考资料

回到顶部

3.1 JavaBean

3.1.1 JavaBean 是什么

JavaBean 是一种JAVA语言写成的可重用组件。

为写成JavaBean,类必须是具体的和公共的,并且具有无参数的构造器。

JavaBean 通过提供符合一致性设计模式的公共方法将内部域暴露成员属性。



以下是一个简单的JavaBean类。

定义一个Person类,有 name 和 age 两个属性,以及这两个属性的 get、set 方法。 

package com.demo.web.controllers;

public class Person {

    private String name = "zhangsan";

    private int age = 28;

    public Person() {

    }

    

    public Person(String name, int age) {

        this.name = name;

        this.age = age;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    public int getAge() {

        return age;

    }

    public void setAge(int age) {

        this.age = age;

    }

}

3.1.2 JavaBean 特点

JavaBean 是一个 public 类型的类
JavaBean 含无参数的构造函数
JavaBean 提供 set 和 get 方法 

 

3.1.3 JavaBean的生命周期

传统JavaBean生命周期

传统的Java应用,Bean的生命周期很简单。

使用new进行实例化,然后该Bean就可以使用了。程序结束后,Java会自动进行垃圾回收。

 

Spring容器中的JavaBean生命周期
在Spring容器中的Bean的生命周期要复杂多了,步骤如下:

(1)Spring对Bean进行实例化。

(2)Spring将值和Bean的引用注入进Bean对应的属性中。

(3)如果 Bean 实现了 BeanNameAware 接口,Spring将 Bean 的ID传递给 setBeanName() 接口方法。

(4)如果 Bean 实现了 BeanFactoryAware 接口,Spring将调用 setBeanFactory() 接口方法,将BeanFactory 容器实例传入。

(5)如果 Bean 实现了 ApplicationContextAware 接口,Spring将调用 setApplicationContext() 接口方法,将应用上下文的引用传入。

(6)如果 Bean 实现了 BeanPostProcessor 接口,Spring将调用它们的 post-ProcessBeforeInitialization接口方法。

(7)如果 Bean 实现了 InitializingBean 接口,Spring将调用它们的 afterPropertiesSet 接口方法。类似地,如果 Bean 使用 init-method 声明了初始化方法,该方法也会被调用。

(8) 如果 Bean 实现了 BeanPostProcessor接口,Spring将调用它们的 post-ProcessAfterInitialization接口方法。

(9)此时此刻,Bean 已经准备就绪,可以被应用程序是用来,它们将一直驻留在应用上下文中,直到该应用上下文被销毁。

(10)如果 Bean 实现了 DisposableBean 接口,Spring将调用它的 destroy() 接口方法。同样,如果Bean使用 destroy-method 声明了销毁方法,该方法也会被调用。
 
回到顶部

3.2 声明Bean

创建应用对象之间协作关系的行为通常被称为装配,这也是依赖注入的本质。

Spring是一个基于容器的框架。但是如果没有配置Spring,那它就是一个空容器,当然也毫无用处。

所以需要配置Spring,以告诉容器需要加载哪些Bean和如何装配这些Bean。
从Spring3.0开始,Spring容器提供了两种配置Bean的方式。

使用XML文件作为配置文件
基于Java注解的配置方式 

注:本文先不介绍注解的配置方式,而是重点介绍传统的XML配置方式。

 

3.2.1 创建Spring配置

以下是一个典型的XML配置文件(一般为<servlet名>-servlet.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

    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <!-- Bean declarations go here -->
</beans>

在 <beans> 元素内,可以放置所有的Spring配置信息。

实际上,beans这个标签是 Spring的一种命名空间。

Spring框架自带了10个命名空间,如下表所示:

命名空间
用途
aop
为声明切面以及将@AspectJ注解的类代理为Spring切面提供了配置元素
beans
支持声明Bean和装配Bean,是Spring最核心也是最原始的命名空间
context
为配置Spring应用上下文提供了配置元素,包括自动检测盒自动装配Bean、注入非Spring直接管理的对象
jee
提供了与Java EE API的集成,例如JNDI和EJB
jms
为声明消息驱动的POJO提供了配置元素
lang
支持配置由Groovy、Jruby或BeanShell等脚本是实现的Bean
mvc
启用SpringMVC的能力,例如面向注解的控制器、试图控制器和拦截器
oxm
支持Spring的对象到XML映射配置
tx
提供声明式事务配置
util
提供各种各样的工具类元素,包括把集合配置为Bean、支持属性占位符元素
除了Spring框架自带的命名空间,Spring Portfolio的许多成员,例如Spring security、Spring Web Flow和Spring Dynamic Modules,同样提供了他们自己的命名空间配置。

3.2.2 声明JavaBean

以上文提到的Person 类为例,定义了JavaBean后,还需要在xml文件中声明,形式如下:

<bean id="person" class="com.demo.web.controllers.Person"/>
这个标签的意思就是,创建一个 com.demo.web.controllers.Person 类的对象person。
当Spring容器加载这个Bean的时候,会使用默认构造器来实例化person对象,相当于:

com.demo.web.controllers.Person person = new com.demo.web.controllers.Person();
备注实际上,Spring是使用反射机制来创建Bean的。

3.2.3 构造器注入

很多应用场景下,我们希望在初始化实例时,传入关键性的参数,这就需要带参数的构造函数了。

为了解决这个问题,在构造Bean的时候,可以使用 <constructor-arg> 元素来指定构造器参数。

<bean id="person" class="com.demo.web.controllers.Person">

    <constructor-arg value="lisi" />

    <constructor-arg value="28" />
</bean>
如果不使用这个标签,spring将使用默认构造函数。

以上参数都是直接传入值,如果想传入对象引用,那要怎么做呢?

请参考下面的例子:

首先定义一个Car类

package com.demo.web.controllers;

public class Car {

    private String model;

    

    public Car() {

    }

    

    public Car(String model) {

        this.model = model;

    }

    

    public void run() {

        System.out.println(model + " 正在行驶");

    }

}

再定义一个Driver类

package com.demo.web.controllers;

public class Driver {

    private Car car;

    

    public Driver() {

    }

    public Driver(Car car) {

        this.car = car;

    }

    

    public void drive() {

        car.run();

    }

}

接下来,我们可以在XML中配置一个Car的变量

<bean id="car" class="com.demo.web.controllers.Car">

    <constructor-arg value="宝马5系" />
</bean>

有了Car的变量car,我们就可以在声明Driver变量时,使用 <constructor-arg> 标签引用它。

<bean id="driver" class="com.demo.web.controllers.Driver">

    <constructor-arg ref="car" />
</bean>
 

3.2.4 通过工厂方法创建Bean

有时候一个类并没有public型的构造方法(典型的如单例模式里的类),对于这种情况如何在spring中实例化呢?

这时候静态工厂方法是实例化对象的唯一方法。Spring支持通过 <bean> 元素的 factory-method 属性来装配工厂创建的Bean。

package com.demo.web.controllers;

public class Singleton {

    static Singleton instance = new Singleton();

    private Singleton() {

    }

    public static Singleton getInstance() {

        return instance;

    }

}

为了在Spring中将Singleton配置为Bean,可以按照下面的方式来配置

<bean id="instance" class="com.demo.web.controllers.Singleton"

        factory-method="getInstance" />
 

3.2.5 Bean的作用域

所有的Spring Bean默认都是单例。当容器分配一个Bean时,它总是返回Bean的同一个实例。

但有时我们需要每次请求时都获得唯一的Bean实例,如何做到呢?

当在Spring中配置 <bean> 元素时,我们可以为Bean声明一个作用域。为了让Spring在每次请求时都为Bean产生一个新的实例,我们只需要配置Bean的scope属性为 prototype即可。

<bean id="person" class="com.demo.web.controllers.Person" scope="prototype" />
除了prototype,Spring还提供了其他几个作用域选项,如下:

作用域
定义
singleton
在每一个Spring容器中,一个Bean定义只有一个对象实例(默认)
prototype
运行Bean的定义可以被实例化任意次(每次调用都创建一个实例)
request
在一次HTTP请求中,每个Bean定义对应一个实例,该作用域仅在基于Web的Spring上下文(例如Spring MVC)中才有效
session
在一个HTTP Session中,每个Bean定义对应一个实例,该作用域仅在基于Web的Spring上下文(例如Spring MVC)中才有效
global-session
在一个全局HTTP Session中,每个Bean定义对应一个实例,该作用域仅在Portlet上下文中才有效
注:Spring的单例Bean只能保证在每个应用上下文中只有一个Bean的实 例。 

3.2.6 初始化和销毁Bean

当实例化一个Bean时,可能需要执行一些初始化操作来确保该Bean处于可用状态。同样地,当不再需要Bean,将其从容器中移除时,我们可能还需要按 顺序执行一些清除工作。为了满足初始化和销毁Bean的需求,Spring提供了Bean生命周期的钩子方法。

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

假设,为一个灯泡的功能写一个类。

package com.demo.web.controllers;

public class Light {

    public void turnOn() {

        // ...
    }

    public void turnOff() {

        // ...
    }

}

接着,我们在XML中做如下配置

<bean id="light" class="com.demo.web.controllers.Light"

        init-method="turnOn" destroy-method="turnOff" />
这样,就能保证让灯泡类在点亮之前调用turnOn(),结束时调用turnOff()。

回到顶部

3.3 注入Bean属性

通常,JavaBean中的属性都是私有的,同时提供一组get、set方法。 

(1)注入简单值

在Spring中,除了用前面介绍的构造器注入方式,还可以使用 <property> 元素配置 Bean 的属性。 

还是以Person类为例

<bean id="person" class="com.demo.web.controllers.Person">

    <property name="name" value="wangwu" />

    <property name="age" value="30" />
</bean>
 

(2)引用其他Bean

还记得我们在构造器注入部分提到的Driver和Car类的例子吗,如果使用property的方式,则按如下方式表达

<bean id="baoma" class="com.demo.web.controllers.Car">

    <constructor-arg value="宝马5系" />
</bean>

<bean id="driver" class="com.demo.web.controllers.Driver">

    <property name="car" ref="baoma" />
</bean>

(3)内部注入

内部注入是通过直接声明一个 <bean> 元素作为 <property>元素的子节点而定义的。

<bean id="driver" class="com.demo.web.controllers.Driver">

    <property name="car">

        <bean class="com.demo.web.controllers.Car" />

    </property>
</bean>

(4)使用Sping的命名空间p装配属性

Spring还提供了一个命名空间p以简化<property>元素的装配方式。 

命名空间p的schema URI为 http://www.springframework.org/schema/p。
如果要使用命名空间p ,需要在Spring的XML配置中增加一段声明

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:p="http://www.springframework.org/schema/p" 

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://www.springframework.org/schema/beans

    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
有了以上声明,我们就可以使用p:作为<bean>元素所有属性的前缀来装配Bean的属性。

用p:重新定义上节Driver类的配置, 如下:

<bean id="baoma" class="com.demo.web.controllers.Car">

    <constructor-arg value="宝马5系" />
</bean>

<bean id="driver" class="com.demo.web.controllers.Driver"

    p:car-ref="baoma"/>
p:car-ref="baoma" 表示要装配car 属性,-ref后缀表示这是一个引用而不是字面值。"baoma"是这个属性的引用对象。

3.3.1 装配集合

Spring不仅可以装配单个值,也可以装配集合。

Spring提供4种类型的集合配置元素。

集合元素
用途
对应实际数据类型
<list>
装配list类型的值,允许重复
数组或java.util.Collection
<set>
装配set类型的值,不允许重复
<map>
装配map类型的值,名称和值可以是任意类型
java.util.Map
<props>
装配properties类型的值,名称和值必须都是String型
java.util.Properties
 

(1)<list> 和 <set>

<list> 和 <set> 都可以用来装配类型为
java.util.Collection 的任意实现或数组的属性。
两者的区别在于:list 允许成员重复; set 必须保证成员不重复。

需要注意的是,不是说如果属性是java.util.Set 类型,则用户必须用 <set> 装配。



定义一个 instruments 属性,它是一个Collection,保存各种乐器 

<property name="instruments">

    <list>

        <ref bean="guitar" />

        <ref bean="piano" />

        <ref bean="piano" />

    </list>
</property>

<property name="instruments">

    <set>

        <ref bean="guitar" />

        <ref bean="piano" />

        <ref bean="piano" /><!-- 自动忽略重复的属性 -->

    </set>
</property>

 

(2)<map>

<map> 元素声明了一个 java.util.Map 类型的值。每个 <entry> 元素定义
Map 的一个成员。
<entry> 元素由一个键和一个值组成,键和值可以是简单类型,也可以是其他Bean的引用。

属性
用途
key
指定map中entry的键为String
key-ref
指定map中entry的键为Spring上下文中其他Bean的引用
value
指定map中entry的值为String
value-ref
指定map中entry的值为Spring上下文中其他Bean的引用


<property name="instruments">

    <map>

        <entry key="GUITAR" value-ref="guitar">

        <entry key="PIANO" value-ref="piano">

    </map>
</property>
 

(3)<prop>

如 果 Map 的每一个 entry 的键和值都为 String 类型时,可以考虑使用 java.util.Properties 代替 Map。Properties 类提供了和 Map 大致相同的功能,但是它限定了键和值必须为 String
类型。
<props> 元素构建了一个 java.util.Properties 值,这个 Properties 的每一个成员由 <prop> 定义。


<property name="instruments">

    <props>

        <prop key="GUITAR">guitar sound</prop>

        <prop key="PIANO">piano sound</prop>

    </props>
</property>

回到顶部

3.4 使用表达式装配

Spring 3引入了Spring表达式语言(Spring Expression Lanuage, SpEL)。

它通过运行期执行的表达式将值装配到 Bean 的属性或构造器参数中。 

 

3.4.1 SpEL 的基本原理

SpEL 表达式的首要目标是通过计算获得某个值。最简单的SpEL求值或许是对字面值、Bean的属性或某个类的常量进行求值。 

字面值

<property> 元素的 value 属性中使用 #{} 界定符把这个值装配到 Bean 的属性中


<property name="count" value="#{5}" />
此外,也可以与非SpEL 表达式的值混用
<property name="count" value="The value is #{5}" />

引用Bean、Properties 和方法

SpEL 表达式可以通过 ID 引用其他 Bean。



<property name="fruit" value="#{apple}" />

这和以下语句的功能等价

<property name="fruit" ref="apple" />
除了直接引用其他Bean,也可以引用 Bean 对象的属性和方法



<property name="song" value="#{singer.song}" />
<property name="song" value="#{singer.selectSong().toUpperCase()}" />
需要注意的是,singer.selectSong().toUpperCase()存在一个问题,如果selectSong()方法返回的是null, 那么SpEL表达式求值时会抛出一个 NullPointerException 异常。

为了避免这种情况,可以使用null-safe存取器(?.)



<property name="song" value="#{singer.selectSong()?.toUpperCase()}" />
使用 ?. 代替 . 来访问 toUpperCase() 方法。?. 可以确保只有当 selectSong() 不为 null 时才去调用 toUpperCase() 方法。

操作类

现在,我们了解了在 SpE L中,如何去调用 Bean 对象,以及对象的属性和方法。

但是,如何去访问类的静态方法或常量引用呢?

在 SpEL 中,使用 T() 运算符去调用类作用域的方法和常量。



以下演示了如何调用 java.lang.Math 类中的静态方法和属性。 

<property name="multiplier" value="#{T{java.lang.Math}.PI}" />
<property name="multiplier" value="#{T{java.lang.Math}.random}" />

在 SpEL 值上执行运算操作

SpEL提供了几种运算符,这些运算符可以用在SpEL表达式中的值上。

运算符类型
运算符
算术运算
+、-、*、/、%、^
关系运算
<、>、==、<=、>=、lt、gt、eq、le、ge
逻辑运算
and、or、not、|
条件运算
?: (ternary)、?: (Elvis)
正则表达式
matches

3.4.2 在 SpEL 中筛选集合 

假设针对前面的Person类,我们定义一个List集合,如下: 

<util:list id="persons">

    <bean class="com.demo.web.controllers.Person" p:name="zhangsan" p:age="17"/>

    <bean class="com.demo.web.controllers.Person" p:name="lisi" p:age="24"/>

    <bean class="com.demo.web.controllers.Person" p:name="wangwu" p:age="30"/>

    <bean class="com.demo.web.controllers.Person" p:name="zhaoliu" p:age="16"/>

    <bean class="com.demo.web.controllers.Person" p:name="liuqi" p:age="23"/>
</util:list>

访问集合成员

可以使用 [] 运算符来访问集合成员



<property name="choosePerson" value="#{persons[T{java.lang.Math}.random() * persons.size()]]}" />
以上表示,随机选取一个人。

也可以按下面方式选取

<property name="choosePerson" value="#{persons['zhangsan']}" /><!-- 选取集合中叫张三的人 -->
<property name="choosePerson" value="#{persons[2]}" /><!-- 选取集合中第二个人 -->

查询集合成员

如果想要在persons集合中查询年龄大于18岁的人。 

在SpEL中,只需使用一个查询运算符(.?[]) 就可以简单做到,如下所示:

<property name="adults" value="#{persons.?[age gt 18]}" />
查询运算符会创建一个新的集合,集合中只存放符合括号中表达式的成员。 

SpEL 还提供两种运算符:.^[] 和 .$[],从集合中查询出第一个匹配项和最后一个匹配项。 

投影集合

在SpEL中, 提供了投影运算符(.![])将集合中每个成员的特定属性放入一个新的集合中。

<property name="personNames" value="#{persons.![name]}" />
以上,将所有人的名字取出来,建立一个新的集合personNames。

 

回到顶部

3.5 JavaBean的一个简单应用实例

以下 以 [Spring]01_ 环境配置 中的 HelloWorld 工程为基础,演示一下 JavaBean 的简单使用方法。

打开 HelloWorld 工程。

(1)新建一个 java 文件,名为 Person.java,完整内容如下:



package com.demo.web.controllers;

public class Person {

    private String name = "zhangsan";

    private int age = 28;

    public Person() {

    }

    public Person(String name, int age) {

        this.name = name;

        this.age = age;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    public int getAge() {

        return age;

    }

    public void setAge(int age) {

        this.age = age;

    }

}

(2)修改 index.jsp 文件,完整内容如下:



<!-- 导入要引用的类 -->
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"

    import="com.demo.web.controllers.Person" pageEncoding="UTF-8"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Spring Hello World</title>
</head>
<body>

    <!-- 

    <h1>${message}</h1>

     -->

    <jsp:useBean id="person" class="com.demo.web.controllers.Person" scope="session" />

    <!-- 获取对象属性 -->

    name: <jsp:getProperty name="person" property="name" /><br>

    age: <jsp:getProperty name="person" property="age" /><br>

    <!-- 设置对象属性 -->

    <jsp:setProperty name="person" property="name" value="lisi" />

    <jsp:setProperty name="person" property="age" value="30" /><!-- 这里有自动的类型转换:String2int-->

    <jsp:setProperty name="person" property="age" value="<%=20 + 20%>" /><!-- value值还可以用表达式 -->

    <!-- 再次获取对象属性 -->

    <br>

    name after change: <jsp:getProperty name="person" property="name" /><br>

    age after change: <jsp:getProperty name="person" property="age" /><br>

    <!-- 用表达式输出对象属性 -->

    <br>

    name again: <%=person.getName()%><br>

    <!-- 脚本段声明对象 -->

    <br>

    another person:

    <%

        Person person2 = new Person(); //这里不能把对象叫做person,提示重复的局部变量,进一步说明这种方法和上面 的方法,本质上是一样的

        out.println(person2.getName());

    %>

    

    <!-- 再声明一个Person类对象,注意id必须和前面的不同,声明的是同一个类的不同的对 象-->

    <jsp:useBean id="person3" class="com.demo.web.controllers.Person" scope="session"/>
</body>
</html>

(3)运行

结果如下:



name: zhangsan

age: 28

name after change: lisi

age after change: 40

name again: lisi

another person: zhangsan

回到顶部

参考资料

Spring实战(第3版) 
http://www.cnblogs.com/mengdd/p/3151701.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: