Spring框架学习三:基于XML文件的方式对bean进行配置
IoC配置bean的方式:
1. 基于XML文件的方式
2. 基于注解的方式
我们先来看基于XML文件对bean进行配置:
在XML文件声明 Bean 时,Spring 配置文件的根元素来源于 Spring beans 命名空间所定义的 <beans> 元素。在 <beans> 元素内,你可以放置所有的 Spring 配置信息,包括 <bean> 元素的声明。但是 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 | 启用Spring MVC的能力,例如面向注解的控制器、视图控制器和拦截器 |
oxm | 支持 Spring 的对象到 XML 映射的配置 |
tx | 提供声明式事务配置 |
util | 提供各种工具类元素 |
Spring 支持3种依赖注入的方式
- 属性注入
- 构造器注入
- 工厂方式注入(不推荐使用)
以例子来说明
1 //创建测试类 2 public class Person 3 { 4 private String name; 5 private int age; 6 private double height; 7 private Address address; 8 9 //生成 getter、setter方法,生成带参和不带参的构造器,重写 toString 10 }
//创建测试类
public class Address
{
private String city;
public String getCity() { return city; } public void setCity(String city) { this.city = city; } @Override public String toString() { return "Address [city=" + city + "]"; } }
//创建执行类
public class PersonTest { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml"); Person p = (Person) ctx.getBean("p"); System.out.println(p); } }
属性注入
属性注入 即通过 setXxx 方法注入bean的属性值或依赖的对象,属性注入使用 <property> 元素,使用 name 属性指定Bean的属性名称,value 属性或 <value> 子节点指定属性值,属性注入是实际应用中最常用的注入方式。
<!-- 配置一个bean --> <!-- id也可以换成name,使用name时可以使用特殊字符来为name赋值,普遍使用id --> <bean id="p" class="com.bupt.springtest.beanXml.Person"> <!-- 为属性name赋值为tom --> <!-- 通过属性注入: 通过 setter 方法注入属性值 --> <property name="name" value="tom"/> </bean> <!-- 打印得到的对象结果为:Person [name=tom, age=0, height=0.0, address=null] -->
构造方法注入
通过构造方法注入Bean的属性值或依赖的对象,它保证了Bean实例在实例化后就可以使用
构造器注入在 <constructor-arg> 元素里声明属性,<constructor-arg> 中没有 name 属性
通过构造器注入时,类中必须要有与参数个数和类型对应的构造方法
<!-- 通过构造器注入属性值 --> <bean id="p" class="com.bupt.springtest.beanXml.Person"> <!-- 在 Bean 中必须有对应的构造器,即必须在Person类中含有只有一个参数name的构造器 --> <constructor-arg value="tom"/> </bean>
<!-- 打印得到的对象结果为:Person [name=tom, age=0, height=0.0, address=null] -->
<!-- 若一个 bean 有多个构造器, 初始化的参数个数或者顺序都不同时,可以根据 index 或 type 进行更加精确的定位 --> <bean id="p" class="com.bupt.springtest.beanXml.Person"> <constructor-arg value="23" index="1"/> <constructor-arg value="tom" index="0"/> <constructor-arg value="178.2" type="double"/> </bean> <!-- 打印结果为:Person [name=tom, age=23, height=178.2, address=null] -->
<!-- 若value中包含有特殊字符可以使用<![CDATA[]]>把字面值包裹起来 --> <bean id="p" class="com.bupt.springtest.beanXml.Person"> <constructor-arg> <value><![CDATA[<tom>]]></value> </constructor-arg> </bean> <!-- 打印对象结果为:Person [name=<tom>, age=0, height=0.0, address=null] -->
<!-- 可以使用专用的 <null/> 元素标签为Bean的字符串或其他对象类型的属性注入null值 -->
<bean id="address" class="com.bupt.springtest.beanXml.Address">
<!-- 为 Address 的 city 属性赋值为 null, 若某一个 bean 的属性值不是 null, 使用时需要为其设置为 null -->
<property name="city"><null/></property>
</bean>
引用其它Bean
在Bean的配置文件中,可以通过 <ref> 元素或 ref 属性为Bean的属性或构造器参数指定对其他Bean的引用
<bean id="address" class="com.bupt.springtest.beanXml.Address"> <property name="city" value="Beijing"/> </bean> <bean id="p" class="com.bupt.springtest.beanXml.Person"> <property name="name" value="tom"/> <property name="age" value="20"/> <property name="height" value="178.3"/> <!-- 通过 ref 属性值指定当前属性指向哪一个 bean --> <property name="address" ref="address"/> </bean> <!-- 打印结果为:Person [name=tom, age=20, height=178.3, address=Address [city=Beijing]] -->
内部Bean
也可以在属性或构造器里包含Bean的声明,这种Bean称为内部Bean。内部Bean不能使用在任何其他地方,它定义时仅仅给一个特定的属性使用
内部Bean声明直接包含在 <property> 或 <constructor-arg> 元素里,不需要设置任何 id 或 name 属性
<bean id="p" class="com.bupt.springtest.beanXml.Person"> <property name="name" value="tom"/> <property name="age" value="20"/> <property name="height" value="178.3"/> <!-- 内部 bean, 类似于匿名内部类对象.不能被外部的 bean 来引用, 也没有必要设置 id 属性 --> <property name="address"> <bean class="com.bupt.springtest.beanXml.Address"> <property name="city" value="shanghai"/> </bean> </property> </bean> <!-- 打印结果为:Person [name=tom, age=20, height=178.3, address=Address [city=shanghai]] -->
集合属性
在Spring中可以通过一组内置的xml标签(如:<list>、<set> 或<map>)来配置集合属性
配置 java.util.List 类型的属性,需要指定 <list> 标签,在标签包里,包含一些元素。这些标签可以通过 <value> 指定简单的常量值,通过 <ref> 指定对其他Bean的引用,通过 <bean> 指定内置Bean定义,通过 <null/> 指定空元素,甚至可以内嵌其他集合。
数组的定义和List一样,都使用 <list>
配置 java.util.Set 需要使用 <set> 标签,定义元素的方法与List一样
修改Person类中定义的 address 属性,将其类型由 Address 改为 List<Address>
<bean id="address1" class="com.bupt.springtest.beanXml.Address"> <property name="city" value="beijing"/> </bean> <bean id="p" class="com.bupt.springtest.beanXml.Person"> <property name="name" value="tom"/> <property name="address"> <!-- 使用list节点为List类型的属性赋值 --> <list> <!-- 通过 <ref> 指定对其他Bean的引用 --> <ref bean="address1"/> <!-- 通过 <bean> 指定内置Bean定义 --> <bean class="com.bupt.springtest.beanXml.Address"> <property name="city" value="shanghai"/> </bean> </list> </property> </bean> <!-- 打印结果为:Person [name=tom, age=23, height=178.2, address=[Address [city=beijing], Address [city=shanghai]]] -->
java.util.Map通过 <map> 标签定义,<map> 标签里可以使用多个 <entry> 作为子标签,每个条目包含一个键—值对。
必须在 <key> 标签里定义键
因为键和值的类型没有限制,所以可以自由地为他们指定 <value>、<ref>、<bean>、<null/>元素
可以将Map的键和值作为 <entry> 的属性定义:简单常量使用 key 和 value 来定义,Bean 引用通过 key-ref 和 value-ref 属性定义
修改Person类中定义的address属性,将其类型由 Address 改为 Map<String, Address>
<bean id="address1" class="com.bupt.springtest.beanXml.Address"> <property name="city" value="beijing"/> </bean> <bean id="address2" class="com.bupt.springtest.beanXml.Address"> <property name="city" value="shanghai"/> </bean> <!-- 配置 Map 属性值 --> <bean id="p" class="com.bupt.springtest.beanXml.Person"> <property name="name" value="tom"/> <property name="address"> <!-- 使用 map 节点及 map 的 entry 子节点配置 Map 类型的成员变量 --> <map> <entry key="A" value-ref="address1"/> <entry key="B" value-ref="address2"/> </map> </property> </bean> <!-- 打印结果为:Person [name=tom, age=0, height=0.0, address={A=Address [city=beijing], B=Address [city=shanghai]}]] -->
使用 <props> 定义 java.util.Properties,该标签使用多个 <prop> 作为子标签,每个 <prop> 标签必须定义 key 属性
//定义一个测试类
public 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 + "]"; } }
public class DataSourceTest { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml"); DataSource dataSource = ctx.getBean(DataSource.class); System.out.println(dataSource); } }
<!-- 配置 Properties 属性值 --> <bean id="dataSource" class="com.bupt.springtest.beanXml.DataSource"> <property name="properties"> <!-- 使用 props 和 prop 子节点来为 Properties 属性赋值 --> <props> <prop key="user">root</prop> <prop key="password">123</prop> <prop key="jdbcUrl">jdbc:mysql:///test</prop> <prop key="driverClass">com.mysql.jdbc.Driver</prop> </props> </property> </bean> <!-- 打印结果为:DataSource [properties={driverClass=com.mysql.jdbc.Driver, user=root, password=123, jdbcUrl=jdbc:mysql:///test}] -->
使用 utility schema 定义集合
使用基本的集合标签定义集合时,不能将集合作为独立的组件定义,导致其他 Bean 无法引用该集合,所以无法在不同 Bean 之间共享集合
可以使用 util schema 里面的集合标签定义独立的集合Bean,需要注意的是,必须在 <beans> 根元素里添加 util schema 定义
编写xml配置之前我们需要导入 util 的命名空间
在配置文件页面点击下方的NameSpaces,勾选添加 util 命名空间
修改Person类中定义的address属性,将其类型由 Address 改为 List<Address>
<bean id="address1" class="com.bupt.springtest.beanXml.Address"> <property name="city" value="beijing"/> </bean> <bean id="address2" class="com.bupt.springtest.beanXml.Address"> <property name="city" value="shanghai"/> </bean> <!-- 配置单独的集合bean,以供多个bean进行引用,需导入util命名空间 --> <util:list id="address"> <ref bean="address1"/> <ref bean="address2"/> </util:list> <bean id="p" class="com.bupt.springtest.beanXml.Person"> <property name="name" value="jack"/> <property name="address" ref="address"/> </bean> <!-- 打印结果为:Person [name=jack, age=0, height=0.0, address=[Address [city=beijing], Address [city=shanghai]]] -->
使用P命名空间
为了简化 XML 文件的配置,越来越多的 XML 文件采用属性而非子元素配置信息,引入p命名空间可以通过 <bean> 元素属性的方式配置 Bean 的属性,使用p命名空间后,基于XML的配置方式将进一步简化。
同样,需要将p命名空间添加到配置文件中,这时我们就可以这样来编写配置文件了
<bean id="p" class="com.bupt.springtest.beanXml.Person" p:name="jason" p:age="30" p:height="180.2" p:address-ref="address"/> <!-- 打印结果为:Person [name=jason, age=30, height=180.2, address=[Address [city=beijing], Address [city=shanghai]]] -->
自动装配XML配置里的Bean
Spring IOC 容器可以自动装配 Bean,需要做的仅仅是在 <bean> 的 autowire 属性里指定自动装配的模式,装配模式有三种:
byType(根据类型自动装配): 若 IOC 容器中有多个与目标 Bean 类型一致的 Bean. 在这种情况下,Spring 将无法判定哪个 Bean 最合适该属性,所以不能执行自动装配
byName(根据名称自动装配): 必须将目标 Bean 的名称和属性名设置的完全相同
constructor(通过构造器自动装配): 当 Bean 中存在多个构造器时,此种自动装配方式将会很复杂,不推荐使用
将Person类中address属性类型改回Address类型
<bean id="address" class="com.bupt.springtest.beanXml.Address"> <property name="city" value="beijing"/> </bean> <!-- 可以使用 autowire 属性指定自动装配的方式,byName 根据 bean 的名字和当前 bean 的 setter 风格的属性名进行自动装配 --> <bean id="p" class="com.bupt.springtest.beanXml.Person" p:name="jerry" autowire="byName"/> <!-- 打印结果为: Person [name=jerry, age=0, height=0.0, address=Address [city=beijing]]-->
<!-- 若想使用byName自动装配,被装配的bean中定义的 id 值需与装配的bean中setXxx中的Xxx风格一致,如本例中id值必须为address,若为其他值,则不能装配 --> <bean id="address1" class="com.bupt.springtest.beanXml.Address"> <property name="city" value="beijing"/> </bean> <!-- 若 id="address1" 则打印结果为: Person [name=jerry, age=0, height=0.0, address=null]--> <!-- 此时,我们可以选择byType来装配 --> <bean id="address1" class="com.bupt.springtest.beanXml.Address"> <property name="city" value="beijing"/> </bean> <bean id="p" class="com.bupt.springtest.beanXml.Person" p:name="kim" autowire="byType"/> <!-- 得到的打印结果同样显示装配成功,打印结果为: Person [name=kim, age=0, height=0.0, address=Address [city=beijing]]-->
<!-- 若IoC容器中有一个以上的类型匹配的 bean,则会抛异常 --> <bean id="address2" class="com.bupt.springtest.beanXml.Address"> <property name="city" value="shanghai"/> </bean> <!-- 添加此bean后,如果使用 byType 的形式来装配则会出现,打印结果异常 -->
自动装配的缺点:
1. 在Bean配置文件里设置 autowire 属性进行自动装配将会装配Bean的所有属性。然而,若只希望装配个别属性时,autowire 属性就不够灵活了。
2. autowire 属性要么根据类型自动装配,要么根据名称自动装配,不能二者兼用。
3. 实际项目中很少使用自动装配,因为清晰的配置文档阅读性更好。
Bean之间的关系:继承和依赖
Spring允许继承bean的配置,被继承的bean称为父bean,继承这个父bean的bean叫做子bean,继承包含以下规则:
1. 子bean从父bean中继承配置,包括bean的属性配置
2. 子bean也可以覆盖从父bean继承过来的配置
3. 父bean可以作为配置模板,也可以作为bean的实例。若只想把父bean作为模板可以设置 <bean> 的abstract属性为true,这样Spring将不会实例化这个bean
4. 并不是 <bean> 元素里的所有属性都会被继承,比如:autowire、abstract等
5. 可以忽略(即不写class属性)父 bean 的 calss 属性,此时的父bean不再被实例化,完全就是一个模板 bean,所以abstract必须设置为true
下面通过代码来理解
<bean id="address" class="com.bupt.springtest.beanXml.Address" p:city="beijing"/> <bean id="p" class="com.bupt.springtest.beanXml.Person" p:name="A" p:age="26" p:height="172.2" p:address-ref="address"/>
<!-- bean 配置的继承:使用bean的parent属性指定继承哪个bean的配置 --> <bean id="p1" class="com.bupt.springtest.beanXml.Person" p:name="B" parent="p"/>
<!-- 分别获取父bean和子bean的实例将其打印,结果为: Person [name=A, age=26, height=172.2, address=Address [city=beijing]] Person [name=B, age=26, height=172.2, address=Address [city=beijing]] -->
<bean id="address" class="com.bupt.springtest.beanXml.Address" p:city="beijing"/>
<!-- 抽象bean:bean的 abstract 属性为 true 的 bean, 这样的 bean 不能被 IoC 容器实例化, 只能用来继承配置。 若某个 bean 的 class 属性没有指定, 则该 bean 必须是一个抽象 bean--> <bean id="p" p:name="A" p:age="26" p:height="172.2" p:address-ref="address" abstract="true"/>
<!-- bean 配置的继承:使用 bean 的 parent 属性指定继承哪个 bean 的配置 --> <bean id="p1" class="com.bupt.springtest.beanXml.Person" p:name="B" parent="p"/>
<!-- 此时IoC容器只会初始化子bean,打印结果为:Person [name=B, age=26, height=172.2, address=Address [city=beijing]] -->
Spring允许用户通过 depends-on 属性设定 bean 前置依赖的 bean,前置依赖的 bean 会在本 bean 实例化之前创建好,如果前置依赖于有多个bean,则可以通过逗号或空格的方式配置bean的名称。
举个实际例子说明 depends-on 属性的作用: DAO bean实例化之前必须要先实例化 Database bean,因为DAO的使用是依赖Database启动的,如果Database不启动,那么DAO即使实例化也是不可用的。在这种情况下,除了在DAO上使用构造函数注入 Database bean 以外,Spring没有任何依赖注入的关系能满足以上条件。但DAO也许根本就不需要Database的实例注入,因为DAO是通过JDBC访问数据库的,它不需要调用Database上的任何属性和方法。在这种情况下,我们就可以使用 depends-on 来定义在DAO被实例化之前去实例化Database。
<bean id="address" class="com.bupt.springtest.beanXml.Address" p:city="shanghai"/>
<!-- 要求配置Person时,必须有一个关联的Address,换句话说person这个bean依赖于Address这个bean --> <bean id="p" class="com.bupt.springtest.beanXml.Person" p:name="jack" depends-on="address"/>
<!-- 打印结果:Person [name=jack, age=0, height=0.0, address=null] -->
Bean的作用域
在Spring中,可以在 <bean> 元素的 scope 属性里设置 bean 的作用域,默认情况下,Spring只为每个在IoC容器里声明的 bean 创建唯一一个实例,整个IoC容器范围内都能共享该实例,所有后续的 getBean() 调用和 bean 引用都将返回这个唯一的 bean 实例。该作用域被称为 singleton,它是所有 bean 的默认作用域。
除了 singleton 作用域外,还有其他几种作用域,说明如下:
类别 | 说明 |
singleton | 在Spring容器中仅存在一个bean实例,bean以单实例的方式存在 |
prototype | 每次调用 getBean() 都会返回一个新的实例 |
request | 每次HTTP请求都会创建一个新的bean,该作用域仅适用于 WebApplicationContext 环境 |
session | 同一个Http Session共享一个bean,不同的Http Session使用不同的 bean。该作用域仅适用于 WebApplicationContext 环境 |
<!-- 使用 bean 的 scope 属性来配置 bean 的作用域 singleton:默认值,容器初始化时(即new ClassPathXmlApplictionContext()时)创建 bean 的实例,在整个容器的生命周期内只创建这一个bean prototype:容器初始化时不创建bean实例,而在每次请求时(即调用getBean()方法时)都创建一个新的bean实例返回 --> <bean id="address" class="com.bupt.springtest.beanXml.Address" p:city="shanghai"/> <bean id="p" class="com.bupt.springtest.beanXml.Person" p:name="jack" depends-on="address" scope="prototype"/>
注意:Spring 有关单例的概念仅限于 Spring 上下文范围内。不想真正的单例,在每个类加载器中保证只有一个实例。Spring 的单例 bean 只能保证在每二个应用上下文中只有一个 bean 的实例。没有人可以阻止你使用传统的方法实例化同一个 bean。
Spring 中 Bean 的初始化时机:
1. 如果你使用 BeanFactory 作为 Spring Bean 的工厂类,则所有的 bean 都是在第一次使用该 bean 的时候初始化。
2. 如果你使用 ApplicationContext 作为 Spring Bean 的工厂类,则又分为如下几种情况:
a. 如果该 bean 的配置为:<bean id = "xxx" class="xxx" scope="singleton" lazy-init="false"/> 时,则 Spring 在容器初始化(容器启动)时就实例化该 bean,并将实例化的 bean 放在一个 map 结构的缓存中,下次使用该 bean 时,直接从缓存中获取。
b. 如果该 bean 的配置为:<bean id = "xxx" class="xxx" scope="singleton" lazy-init="true"/> 时,则 Spring 在第一次使用该 bean 时(调用 getBean())进行初始化。
c. 如果该 bean 的scope = prototype,则不管 lazy-init 的值是什么都是在第一次使用该 bean 时进行实例化。
其中,默认情况下 lazy-init = false
不同创建时机下的优缺点:
1. Spring 容器启动时创建:
优点: Spring 容器与 web 容器整合时,当 web 容器启动的时候就可以初始化 Spring 容器了,如果 Spring 容器内部有错误,则立马就会报错。
缺点:如果 bean 中存放着大量的数据,而且数据的初始化发生在 bean 初始化的时候,这就会导致数据过早加载到内存中。
2. 使用时再创建:
优点:按需求加载数据,什么时候用就什么时候加载。
缺点:不能提早发现问题,增加了排错的代价。
在 bean 中使用外部属性文件
在配置文件里配置 Bean 时,有时需要在 Bean 的配置里混入系统部署的细节信息(例如: 文件路径,数据源配置信息等),而这些部署细节实际上需要和 Bean 配置相分离。Spring 提供了一个 PropertyPlaceholderConfigurer 的 BeanFactory 后置处理器,这个处理器允许用户将 Bean 配置的部分内容外移到属性文件中。可以在 Bean 配置文件里使用形式为 ${var} 的变量。
PropertyPlaceholderConfigurer 从属性文件里加载属性,并使用这些属性来替换变量。Spring 还允许在属性文件中使用 ${propName},以实现属性之间的相互引用。
#src下创建一个名字为db.properties的属性文件,存放连接数据库的信息 user=root password=000 driverClass=com.mysql.jdbc.Driver jdbcUrl=jdbc:mysql:///test
<!-- 增加context命名空间,导入属性文件,类路径下的db.properties文件 --> <context:property-placeholder location="classpath:db.properties"/>
<!-- 导入c3p0数据库连接池 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <!-- 使用外部化属性文件的属性 --> <property name="user" value="${user}"/> <property name="password" value="${password}"/> <property name="driverClass" value="${driverClass}"/> <property name="jdbcUrl" value="${jdbcUrl}"/> </bean>
<!--
启动mysql,导入相应jar包,获取dataSource实例,打印dataSource.getConnection(),
结果为:com.mchange.v2.c3p0.impl.NewProxyConnection@1a794e5 [wrapping: com.mysql.jdbc.JDBC4Connection@329302]
-->
Spring表达式语言:SpEL
Sping表达式语言(简称SpEL):是一个支持运行时查询和操作对象图的强大表达式语言,它为 bean 的属性进行动态赋值提供了便利
SpEL使用 #{...} 作为定界符,所有在大括号中的字符都将被认为是SpEL
通过SpEL我们可以实现以下功能:
1. 通过 bean 的 id 对 bean 进行引用
2. 调用方法以及引用对象中的属性
3. 计算表达式的值
4. 正则表达式的匹配
<!-- 字面量的表示,以下value分别表示为:整数、小数、科学计数法、布尔值 --> <property name="a" value="#{3}"/> <property name="b" value="#{23.4}"/> <property name="c" value="#{10e3}"/> <property name="d" value="#{true}"/> <!-- String可以使用单引号或者双引号作为字符串的定界符号 --> <property name="name" value="#{'tom'}"/> <property name="name" value='#{"tom"}'/>
<!-- 引用其它对象 --> <!-- 通过value属性和SpEL配置bean之间的应用关系 --> <propety name="prefix" value="#{prefixGenerator}"/> <!-- 应用其他对象属性 --> <!-- 通过value属性和SpEL配置suffix属性值为另外一个bean的suffix属性值 --> <property name="suffix" value="#{sequenceGeneraor.suffix}"/> <!-- 调用其它方法,还可以链式操作 --> <!-- 通过value属性和SpEL配置suffix属性值为另外一个bean的方法返回值 --> <property name="suffix" value="#{sequenceGenerator.toString()}" <!-- 方法的连缀 --> <property name="suffix" value="#{sequenceGenerator.toString().toUpperCase()}"/>
<!-- SpEL支持的运算符号:+,-,*,/,%,^ --> <property name="count" value="#{counter.total + 42}"/> <property name="count" value="#{counter.total - 20}"/>
<!-- 调用静态方法或静态属性:通过T()调用一个类的静态方法,它将返回一个Class Object,然后在调用相应的方法和属性 --> <property name="count" value="#{2 * T(java.lang.Math).PI * cirle.radius}"/> <property name="count" value="#{counter.total / counter.num}"/> <property name="count" value="#{counter.total % counter.num}"/> <property name="count" value="#{T{java.lang.Math}.PI * circle.radius ^ 2}"/> <!-- 加号还可以用作字符串的连接 --> <constructor-arg value="#{performer.firstName + ' ' + performer.lastName}"/> <!-- 比较运算符:<,>,==.<=,>=,lt,gt,eq,le,ge --> <property name="equal" value="#{counter.total le 100000}"/> <property name="equal" value="#{counter.total == 100000}"/> <!-- 逻辑运算符号:and,or,not --> <propery name="largeCircle" value="#{shape.kind == 'circle' and shape.perimer gt 10000}"/> <property name="outOfStock" value="#{!product.available}"/> <property name="outOfStock" value="#{not product.available}"/> <!-- if-else 运算符,类似于三目运算符 -->
<!-- 如果歌曲为'Jingle Bells'就弹钢琴,否则演奏萨克斯 --> <constructor-arg value="#{songSelector.selectSong()=='Jingle Bells' ? piano : saxophone}"/>
<!-- 如果kenny.song不为null,那么表达式结果是kenny.song,否则为'Greensleeves' -->
<property name='song' value="#{kenny.song ?: 'Greensleeves'}"/>
<!-- 正则表达式:matches --> <constructor-arg value="#{admin.email matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z{2,4}]'}"/>
IoC容器中的生命周期
Spring IoC容器可以管理 bean 的生命周期,Spring允许在 bean 生命周期特定点执行定制的任务
Spring IoC容器对 bean 的生命周期进行管理的过程:
1. 通过构造器或工厂方法创建 bean 的实例
2. 为 bean 的属性设置值和对其他 bean 的引用
3. 调用 bean 的初始化方法
4. bean 可以使用了
5. 当容器关闭时,调用 bean 的销毁方法
在 bean 的配置声明里设置 init-method 和 destory-method 属性,为 bean 指定初始化和销毁的方法。
//定义一个测试类 public class BeanLifeCycle { private String name; public BeanLifeCycle() { System.out.println("constructor..."); } public String getName() { return name; } public void setName(String name) { this.name = name; System.out.println("setMethod..."); } public void beanInit() { System.out.println("initMethod..."); } public void beanDestroy() { System.out.println("destroyMethod..."); } }
<!-- 配置ApplicationContext.xml,设置 init-method 和 destroy-method --> <bean id="beanLifeCycle" class="com.bupt.springtest.beanXml.BeanLifeCycle" init-method="beanInit" destroy-method="beanDestroy"> <property name="name" value="tom"/> </bean>
public class BeanLifeCycleTest { public static void main(String[] args) { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml"); BeanLifeCycle blc = (BeanLifeCycle) ctx.getBean("beanLifeCycle"); System.out.println(blc); //关闭IoC服务器 ctx.close(); } }
/** * 打印结果: * constructor...(创建bean实例) * setMethod...(设置属性) * initMethod...(调用初始化方法) * com.bupt.springtest.beanXml.BeanLifeCycle@1a5c39e(使用bean) * destroyMethod...(容器关闭,调用销毁方法) */
如果上下文中定义的很多 bean 都拥有相同名字的初始化方法和销毁方法,你不必每一个 bean 都声明初始化和销毁方法,你可是使用 <beans> 元素的 default-init-method 和 default-destory-method 属性
<?xml version="1.0" encoding="utf-8"> <beans xmlns= ..... .....
..... default-init-method="beanInit" default-destroy-method="beanDestroy"> ........ </beans>
创建 bean 的后置处理器
bean 后置处理器允许在调用初始化方法前后对 bean 进行额外的处理,它会对IoC容器中的所有 bean 实例逐一处理,而非单一实例。其典型应用是:检查 bean 属性的正确性或根据特定的标准更改 bean 的属性。对 bean 后置处理而言,需要实现 org.springframework.beans.factory.config 包下的 Interface BeanPostProcessor 。在初始化方法被调用前后,Spring将把每个 bean 实例分别传递给上述接口的以下两个方法:
postProcessorBeforeInitialization(Object bean, String beanName)
postProcessorAfterInitialization(Object bean, String beanName)
//创建一个后置处理器的实现类,实现BeanPostProcessor接口,提供具体实现 public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessorBeforeInitialization..."); return bean; } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessorAfterInitialization..."); return bean; } }
<!-- 配置ApplicationContext.xml,设置 init-method 和 destroy-method --> <bean id="beanLifeCycle" class="com.bupt.springtest.beanXml.BeanLifeCycle" init-method="beanInit" destroy-method="beanDestroy"> <property name="name" value="tom"/> </bean> <!-- 配置 bean 的后置处理器:不需要配置id,IoC容器自动识别为后置处理器 --> <bean class="com.bupt.springtest.beanXml.MyBeanPostProcessor"/>
public class BeanLifeCycleTest { public static void main(String[] args) { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml"); BeanLifeCycle blc = (BeanLifeCycle) ctx.getBean("beanLifeCycle"); System.out.println(blc); //关闭IoC服务器 ctx.close(); } } /** * 打印结果: * constructor... * setMethod... * postProcessorAfterInitialization... * initMethod... * postProcessorBeforeInitialization... * com.bupt.springtest.beanXml.BeanLifeCycle@10ab905 * destroyMethod... */
@Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { //因为后置处理器是对IoC容器所有的 bean 进行处理,所以我们可以加上判断语句,以达到对特定bean的处理 if("beanLifeCycle".equals(beanName)) { //... } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { //可以在方法内重新实例化一个bean,返回或是设置属性,此时,BeanLifeCycle的name属性不再是xml里配置的tom而变成了jack BeanLifeCycle bfc = new BeanLifeCycle(); bfc.setName("jack"); return bfc; }
通过工厂方法配置bean
bean 的配置方式可以通过全类名即反射的方式来配置,也可以通过工厂方法 (静态工厂方法和实例工厂方法) 以及spring为我们提供的 FactoryBean 方法来配置。
调用静态工厂方法创建bean:是将对象创建过程封装到静态方法中。当客户端需要对象时,只需要简单地调用静态方法,而不需要关心创建对象的细节。
要声明通过静态方法创建的bean,需要在bean的class属性里面指定拥有该工厂的方法的类,同时在 factory-method 属性里指定工厂方法的名称。最后使用
<constructor-arg> 元素为该方法传递方法参数。
package com.bupt.springtest.beanFactory; //创建一个bean 实例 public class Address { private String city; private String street; public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } @Override public String toString() { return "Address [city=" + city + ", street=" + street + "]"; } public Address(String city, String street) { this.city = city; this.street = street; } }
package com.bupt.springtest.beanFactory; import java.util.HashMap; import java.util.Map; /** * 静态工厂方法: 直接调用某个类的静态方法就可以返回 bean 的实例 */ public class StaticAddressFactory { private static Map<String, Address> address = new HashMap<>(); static { address.put("A", new Address("beijing", "xidan")); address.put("B", new Address("shanghai", "nanjiinglu")); } //静态工厂方法 public static Address getAddress(String name) { return address.get(name); } }
<!-- 使用静态工厂方法来配置bean,注意不是配置静态工厂方法实例,而是配置bean实例,此例中即配置Address实例 -->
<!--
class属性:指向静态工厂方法的全类名
factory-method:指向静态工厂方法的名字
constructor-arg:如果工厂方法需要传入参数,则使用 constructor-arg 来配置参数
--> <bean id="address" class="com.bupt.springtest.beanFactory.StaticAddressFactory" factory-method="getAddress"> <constructor-arg value="A"/> </bean>
public class FactoryTest { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml"); Address address = (Address) ctx.getBean("address"); System.out.println(address); } }
//打印结果:Address [city=beijing, street=xidan]
实例工厂方法:将对象的创建过程封装到另一个对象实例的方法里。当客户端需要请求对象时,只需要简单地调用该实例方法而不需要关心对象的创建细节。
要声明通过实例工厂方法创建的bean需要:
1. 在bean的 factory-bean 属性里指定拥有该工厂方法的bean;
2. 在 factory-method 属性里指定该工厂方法的名称;
3. 使用 <constructor-arg>元素为工厂方法传递参数。
/** * 实例工厂方法:需要创建工厂本身,再调用工厂的实例方法来返回 bean 的实例 */ public class InstanceAddressFactory { private static Map<String, Address> address = null; public InstanceAddressFactory() { address = new HashMap<>(); address.put("A", new Address("beijing", "changanjie")); address.put("B", new Address("shanghai", "nanjinglu")); } public Address getAddress(String name) { return address.get(name); } }
<!-- 配置工厂的实例 --> <bean id="addressFactory" class="com.bupt.springtest.beanFactory.InstanceAddressFactory"/> <!-- 通过实例工厂方法来配置 bean --> <bean id="address" factory-bean="addressFactory" factory-method="getAddress"> <constructor-arg value="B"/> </bean> <!-- 打印结果:Address [city=shanghai, street=nanjinglu] -->
FactoryBean
Spring中有两类bean,一种是普通的 bean,另一种是工厂 bean,即 FactoryBean
工厂 bean 和普通的 bean 不同,其返回值的对象不是指定类的一个实例,而是该工厂 bean 的getObject()方法所返回的对象
当配置某一个 bean 时需要用到IoC容器中其它的 bean ,这时通过 FactoryBean 去配置是最合适的
public class AddressFactoryBean implements FactoryBean<Address> { private String city; public void setCity(String city) { this.city = city; } //返回 bean 的对象 @Override public Address getObject() throws Exception { return new Address(city, "xindan"); } //指定返回 bean 的类型 @Override public Class<?> getObjectType() { return Address.class; } //返回的实例是否为单例 @Override public boolean isSingleton() { return true; } }
<!-- 通过 FactoryBean 来配置 bean 的实例 class:指向 FactoryBean 的全类名 property:配置 FactoryBean 的属性 实际上返回的实例是 FactoryBean 的 getObject()方法返回的实例 --> <bean id="address" class="com.bupt.springtest.beanFactory.AddressFactoryBean"> <property name="city" value="beijing"/> </bean>
<!-- 打印结果:Address [city=beijing, street=xindan] -->
posted on 2016-06-12 17:09 Traveling_Light_CC 阅读(685) 评论(0) 编辑 收藏 举报