7.5 Spring容器中的Bean
7.5.1 Bean的基本定义和Bean别名
<beans.../>元素是Spring配置文件的根元素,该元素可以指定如下属性:
default-lazy-init : 指定该<beans.../> 元素下配置的所有Bean默认的延迟初始化行为。
default-merge : 指定该<beans.../> 元素下配置的所有Bean默认的merge行为。
default-autowire : 指定该<beans.../> 元素下配置的所有Bean 默认的自动装配行为。
default-autowire-candidates: : 指定该<beans.../> 元素下配置的所有Bean 默认是否作为自动装配的候选Bean。
default-init-method : 指定该<beans.../> 元素下配置的所有Bean 默认的初始化方法。
default-destroy-method : 指定该<beans.../> 元素下配置的所有Bean 默认的回收方法。
<beans.../>元素下所能指定的属性都可以在每个<bean.../>子元素中指定----将属性名去掉default即可。区别是:为<bean.../>指定的这些属性,只对特定Bean起作用;如果在<beans.../>元素下指定这些属性,这些属性将会对<beans.../>包含的所有Bean都起作用。当二者所指定的属性不一致时,<bean.../>下指定的属性会覆盖<beans.../>下指定的属性。
<bean.../>元素的id属性具有唯一性,而且是一个真正的XML ID 属性,因此其他XML元素在引用id时,可以利用XML解析器的验证功能。
指定别名有两种方式:
⊙ 定义<bean.../>元素时通过name属性指定别名;如果需要为Bean实例指定多个别名,则可以在name属性中使用逗号、冒号或者空格来分隔多个别名,后面通过任一别名即可访问该Bean实例。
⊙ 通过<alias.../>元素为已有的Bean指定别名。
<alias name="" alias=""/> name:指定一个Bean实例的标识名,表明将为该Bean实例指定别名;alias:指定一个别名。
<bean id="person" class="edu.pri.lime._7_4_5.bean.Person" name="chinese,lime,oracle"/>
<alias name="person" alias="jackson"/>
<alias name="person" alias="tomcat"/>
7.5.2 容器中Bean的作用域
当通过Spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域。
⊙ singleton : 单例模式,在整个Spring IoC容器中,singleton作用域的Bean将只生成一个实例。
⊙ prototype : 每次通过容器的getBean()方法获取prototype作用域的Bean时,都将产生一个新的Bean实例。
⊙ request : 对于一次HTTP请求,request作用域的Bean将只生成一个实例,这意味着,在同一次HTTP请求内,程序每次请求该Bean,得到的总是同一个实例。只有在Web应用中使用Spring时,该作用域才真正有效。
⊙ session : 对于一次HTTP回话,session作用域的Bean将只生成一个实例,这意味着,在同一次HTTP回话内,程序每次请求该Bean,得到的总是同一个实例。只有在Web应用中使用Spring时,该作用域才真正有效。
⊙ global session : 每个全局的HTTP Session对应一个Bean实例。在典型的情况下,仅在使用portlet context的时候有效。只有在Web应用中使用Spring时,该作用域才真正有效。
比较常用的是singleton和prototype两种作用域,对于singleton作用域的Bean,每次请求该Bean都将获得相同的实例。容器负责跟踪Bean实例的状态,负责维护Bean实例的生命周期行为;如果一个Bean被设置成prototype作用域,程序每次请求该id的Bean,Spring都会新建一个Bean实例,然后返回给程序。在这种情况下,Spring容器仅仅使用new关键字创建Bean实例,一旦创建成功,容器就不再跟踪实例,也不会维护Bean实例的状态。
如果不指定Bean的作用域,Spring默认使用singleton作用域。
Java在创建Java实例时,需要进行内存申请;销毁实例时,需要完成垃圾回收,这些工作都会导致系统开销的增加。因此,prototype作用域的Bean的创建、销毁代价比较大。而singleton作用域的Bean实例一旦创建成功,就可以重复使用。
Spring配置文件通过scope属性指定Bean的作用域。
对于request作用域:
<bean id="loginAction" class="edu.pri.lime._7_5_2.bean.LoginAct" scope="request"/>
针对每次HTTP请求,Spring容器会根据loginAction Bean定义创建一个全新的LoginAction Bean实例,且该loginAction Bean 实例仅在当前HTTP Request内有效。因此,如果程序需要,完全可以*更改Bean实例的内部状态;其他请求所获得的loginAction Bean实例无法感受到这种内部状态的改变。当处理请求结束时,request作用域的Bean实例将被销毁。
request、session作用域的Bean只对Web应用才真正有效。实际上通常只会将Web应用的控制器Bean指定成request作用域。
request和session作用域只在Web应用中才有效,并且必须在Web应用中增加额外配置才会生效。为了让request和session两个作用域生效,必须将HTTP请求对象绑定到为该请求提供服务的线程上,这是的具有request和session两个作用域的Bean实例能够在后面的调用链中被访问到。
对于支持Servlet2.4 及更新规范的Web容器,可以在Web应用的web.xml文件中增加如下Listener配置,该Listener负责是request作用域生效。
Web : Listener
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
对于只支持Servlet2.4 以前规范的Web容器,则该容器不支持Listener规范,故无法使用这种配置方式,只能改为使用Filter配置方式。
Web :Filter
<filter>
<filter-name>requestContextFilter</filter-name>
<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>requestContextFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
一旦web.xml中增加了如上任意一种配置,程序就可以在Spring配置文件中使用request或session作用域了。
xml :app_7_5_2.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <!-- 指定使用request作用域 -->
<!-- 这样Spring容器会为每次HTTP请求生成一个Person实例,当请求响应结束时,该实例也随之消失。 -->
<bean id="per" class="edu.pri.lime._7_4_5.bean.Person" scope="request" /> <!-- 默认作用域为singleton -->
<bean id="chinese" class="edu.pri.lime._7_4_5.bean.Person" />
<!-- 指定作用域为prototype -->
<bean id="french" class="edu.pri.lime._7_4_5.bean.Person" scope="prototype" />
<bean id="date" class="java.util.Date" />
<bean id="loginAction" class="edu.pri.lime._7_5_2.bean.LoginAct"
scope="request" /> </beans>
如果Web应用直接使用Spring MVC作为MVC框架,即用SpringDispatcherServlet或DispatcherPorlet来拦截所有用户请求,则无须这些额外的配置,因为Spring DispatcherServlet和DispatcherPortlet已经处理了所有和请求有关的状态处理。
Jsp :
<%
/* 获取Web应用初始化的Spring容器 */
WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext("app_7_5_2.xml");
/* 两次获取容器中id为per的Bean */
Person perA = (Person)ctx.getBean("per");
Person perB = (Person)ctx.getBean("per");
out.println((perA == perB) + "<br/>");
out.println(perA); %>
7.5.3 配置依赖
Java 应用中各组件相互调用的实质可以归纳为依赖管理,根据注入方式的不同,Bean的依赖注入通常有如下两种形式。
设置注入 : 通过<property.../>元素驱动Spring执行setter方法。
构造注入 : 通过<constructor-arg.../>元素驱动Spring执行带参数的构造器。
通常不建议使用配置文件管理Bean的基本类型的属性值;通常只使用配置文件管理容器中Bean与Bean之间的依赖关系。
对于singleton作用域的Bean,如果没有强行取消其预初始化行为,系统会在创建Spring容器时预初始化所有的singleton Bean,与此同时,该Bean所以来的Bean也被一起实例化。
BeanFactory 与 ApplicationContext 实例化容器中Bean的时机不同;BeanFactory等到程序需要Bean实例时才创建Bean;而ApplicationContext在容器创建ApplicationContext实例时,会预初始化容器中所有的singleton Bean。
因为采用ApplicationContext作为Spring容器,创建容器时会同时创建容器中所有singleton作用域的Bean,因此可能需要更多的系统开销。但一旦创建成功,应用后面的响应速度更快,因此,对于普通的Java EE 应用,推荐使用ApplicationContext作为Spring容器。
Spring的作用就是管理Java EE组件,Spring把所有的Java对象都称为Bean。因此完全可以把任何Java类都部署在Spring容器中----只要该Java类具有响应的构造器即可。Spring可以为任何Java对象注入任何类型的属性------只要该Java对象为该属性提供了对应的setter方法即可。
Java 类的成员变量可以是各种数据类型,出了基本类型值、字符串类型值等,还可以是其他Java 实例,也可以是容器中的其他Bean实例,甚至是Java 集合、数组等,所以Spring允许通过如下元素为setter方法、构造器参数指定参数值:
⊙ value
⊙ ref
⊙ bean
⊙ list、set、map 及 props
7.5.4 设置普通属性值------<value>
<value.../>元素用于指定基本类型及其包装、字符串类型的参数值,Spring使用XML解析器来解析出这些数据,然后利用java.beans.PropertyEditor完成类型转换:从java.lang.String类型转换为所需的参数值类型。
Class : ExampleBean
package edu.pri.lime._7_5_4.bean; public class ExampleBean { // 定义一个int型的成员变量
private int integerField;
// 定义一个double型的成员变量
private double doubleField;
// integerField和doubleField的setter和getter值
public int getIntegerField() {
return integerField;
}
public void setIntegerField(int integerField) {
this.integerField = integerField;
}
public double getDoubleField() {
return doubleField;
}
public void setDoubleField(double doubleField) {
this.doubleField = doubleField;
}
@Override
public String toString() {
return "ExampleBean [integerField=" + integerField + ", doubleField=" + doubleField + "]";
}
}
XML :
<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <bean id="exampleBean" class="edu.pri.lime._7_5_4.bean.ExampleBean">
<property name="integerField" value="1" />
<property name="doubleField" value="3.1" />
</bean> </beans>
Class : Test
package edu.pri.lime._7_5_4.main; import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import edu.pri.lime._7_5_4.bean.ExampleBean; public class Test { public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("app_7_5_4.xml");
ExampleBean exb = ctx.getBean("exampleBean",ExampleBean.class);
System.out.println(exb.toString());
}
}
7.5.5 配置合作者Bean------<ref.../>
如果需要为Bean设置的属性值是容器中的另一个Bean实例,则应该使用<ref.../>元素。使用<ref.../>元素时可指定一个bean属性,该属性用于引用容器中其他Bean实例的id属性值。
<!-- 配置名为chinese的Bean。其实现类是edu.pri.lime._7_3_3.bean.impl.Chinese类 -->
<bean id="chinese" class="edu.pri.lime._7_3_3.bean.impl.Chinese">
<!-- 驱动Spring调用Chinese的带一个参数的构造器来创建对象 -->
<constructor-arg ref="stoneAxe"/>
</bean>
<contructor-arg.../>运算也可增加ref属性,从而指定将容器中另一个Bean作为构造器参数。
7.5.6 使用自动装配注入合作者Bean------<beans default-autowire="" .../> 或 <bean autowire="" .../>
Spring 能自动装配Bean与Bean之间的依赖关系,即无须使用ref显示指定依赖Bean,而是有Spring容器检查XML配置文件内容,根据某种规则,为调用者Bean注入被依赖的Bean。
Spring 的自动装配可通过<beans.../>元素的default-autowire属性指定,该属性对配置文件中所有的Bean起作用;也可通过<bean.../>元素的autowire属性指定,该属性只对该Bean起作用。
自动装配可以减少配置文件的工作量,但降低了依赖关系的透明性和清晰性。
default-autowire、autowire属性可以接受如下值:
⊙ no : 不使用自动装配。Bean依赖必须通过ref元素定义。这是默认配置,在较大的部署环境中不鼓励改变这个配置,显示配置合作者能够得到跟清晰的依赖关系。
⊙ byName : 根据setter方法名进行自动装配。Spring 容器查找容器中的全部Bean,找出其id与setter方法匹配的Bean作为参数来完成注入。如果没有找到匹配的Bean实例,则Spring不会进行任何注入。
⊙ byType : 根据setter方法的形参类型来自动装配。Spring容器查找容器中的全部Bean,如果正好有一个Bean类型与setter方法的形参类型匹配,就自动注入这个Bean;如果找到多个这样的Bean,就抛出一个异常;如果没有找到这样的Bean,则什么都不会发生,setter方法不会被调用。
⊙ constructor : 与byType类似,区别是用于自动匹配构造器的参数。如果容器不能恰好找到一个与构造器类型匹配的Bean,则会抛出一个异常。
BUG ⊙ autodetect : Spring容器根据Bean内部结构,自行决定使用constructor或byType策略。如果找到一个默认的构造函数,那么就会应用byType策略。
⊙ default :
7.5.6.1 byName 规则
byName 规则是指setter方法的方法名与Bean的id进行匹配,假如Bean A的实现类包含setB()方法,而Spring的配置文件恰好包含id为b的Bean,则Spring容器会将b实例注入Bean A中。如果容器中没有名字匹配的Bean,Spring则不会做任何事情。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <!-- 指定使用byName策略,Spring会根据setter方法的方法名与Bean的id进行匹配 -->
<bean id="chinese" class="edu.pri.lime._7_5_6_1.bean.impl.Chinese" autowire="byName"/> <bean id="axe" class="edu.pri.lime._7_3_2.bean.impl.SteelAxe"/>
</beans>
7.5.6.2 byType 规则
byType 规则是根据setter方法的参数类型与Bean的类型进行匹配。假如A实例有setB(B b)方法,而Spring的配置文件中恰好有一个类型为B的Bean实例,容器为A注入类型匹配的Bean实例,如果容器中没有类型为B的实例,Spring不会调用setB()方法;但如果容器中包含多于一个的B实例,程序将会抛出异常。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <!-- 指定使用byType策略,Spring会根据setter方法的参数类型与Bean的类型进行匹配 -->
<bean id="chinese" class="edu.pri.lime._7_5_6_1.bean.impl.Chinese" autowire="byType"/> <bean id="steelAxe" class="edu.pri.lime._7_3_2.bean.impl.SteelAxe"/>
</beans>
当一个Bean既使用自动装配依赖,又使用ref显式指定依赖时,则显式指定的依赖覆盖自动装配依赖。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <!-- 即使china类中有setAxe(Axe axe) 方法,Spring也依赖以steelAxe作为调用setAxe()方法的参数,而不会以axe作为参数 -->
<bean id="china" class="edu.pri.lime._7_5_6_1.bean.impl.Chinese" autowire="byName">
<property name="axe" ref="stoneAxe"/>
</bean> <bean id="axe" class="edu.pri.lime._7_3_2.bean.impl.SteelAxe"/> <bean id="stoneAxe" class="edu.pri.lime._7_3_2.bean.impl.StoneAxe"/>
</beans>
在某些情况下,程序希望将某些Bean排除在自动装配之外,不作为Spring自动装配策略的候选者,此时可设置autowire-candidate属性,通过为<bean.../>元素设置autowire-candidate=“false”,即可将Bean排除在自动装配之外,容器在查找自动装配Bean时将不考虑该Bean。
还可通过在<beans.../>元素中指定default-autowire-candidates属性将一批Bean排除在自动装配之外。default-autowire-candidates属性的值允许使用模式字符串,例如指定default-autowire-candidates=“*abc”,则所有以“abc”结尾的Bean都将被排除在自动装配之外。不仅如此,该属性甚至可以指定多个模式字符串,这样所有匹配任意模式字符串的Bean都将被排除在自动装配之外。
7.5.7 注入嵌套Bean
如果某个Bean所依赖的Bean不想被Spring容器直接访问,则可以使用嵌套Bean。
把<bean.../>配置成<property.../>或<constructor-args.../>的子元素,那么该<bean.../>元素配置的Bean仅仅作为setter注入、构造注入的参数,这种Bean就是嵌套Bean。由于容器布恩那个获取嵌套Bean,因此它不需要指定id属性。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <bean id="chinese" class="edu.pri.lime._7_5_6_1.bean.impl.Chinese">
<!-- 驱动调用chinese的setAxe()方法,使用嵌套Bean作为参数 -->
<property name="axe">
<!-- 嵌套Bean配置的对象仅作为setter方法的参数,嵌套Bean不能被容器访问,因此无须指定id属性 -->
<bean class="edu.pri.lime._7_3_2.bean.impl.SteelAxe"/>
</property>
</bean> </beans>
使用嵌套Bean与使用ref引用ref引用容器中另一个Bean在本质上是一样的。
Spring框架的本质就是通过XML配置文件来驱动Java代码,当程序要调用setter方法或有参数的构造器时,程序总需要传入参数值,随参数类型的不同,Spring配置文件当然也要随之改变。
⊙ 形参类型是基本类型、String、日期等,直接使用value指定字面值即可。
⊙ 形参类型是复合类,那就需要传入一个Java队形作为实参,于是有两种方式:① 使用ref引用一个容器中已配置的Bean;② 使用<bean.../>元素配置一个嵌套Bean。
7.5.8 注入集合值
如果需要调用形参类型为集合的setter方法,或调用形参类型为集合的构造器,则可使用集合元素<list.../>、<set.../>、<map.../>和<props.../>分别来设置类型为List、Set、Map和Properties的集合参数值。
Class : Chinese
package edu.pri.lime._7_5_8.bean.impl; import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set; import edu.pri.lime._7_5_8.bean.Axe;
import edu.pri.lime._7_5_8.bean.Person; public class Chinese implements Person { // 集合类型的成员变量
private List<String> schools;
private Map scores;
private Map<String,Axe> phaseAxes;
private Properties health;
private Set axes;
private String[] books;
public Chinese() {
super();
System.out.println("Spring 实例化主调bean:Chinese实例...");
}
public List<String> getSchools() {
return schools;
}
public void setSchools(List<String> schools) {
this.schools = schools;
}
public Map getScores() {
return scores;
}
public void setScores(Map scores) {
this.scores = scores;
}
public Map<String, Axe> getPhaseAxes() {
return phaseAxes;
}
public void setPhaseAxes(Map<String, Axe> phaseAxes) {
this.phaseAxes = phaseAxes;
}
public Properties getHealth() {
return health;
}
public void setHealth(Properties health) {
this.health = health;
}
public Set getAxes() {
return axes;
}
public void setAxes(Set axes) {
this.axes = axes;
}
public String[] getBooks() {
return books;
}
public void setBooks(String[] books) {
this.books = books;
}
// 访问全部的集合类型的成员变量
public void test(){
System.out.println(schools);
System.out.println(scores);
System.out.println(phaseAxes);
System.out.println(health);
System.out.println(axes);
System.out.println(Arrays.toString(books));
}
}
XML :
<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <!-- 定义2个普通的Axe Bean -->
<bean id="stoneAxe" class="edu.pri.lime._7_5_8.bean.impl.StoneAxe" />
<bean id="steelAxe" class="edu.pri.lime._7_5_8.bean.impl.SteelAxe" />
<!-- 定义chinese Bean -->
<bean id="chinese" class="edu.pri.lime._7_5_8.bean.impl.Chinese">
<!-- Spring 对List集合和数组的处理是一样的,都用<list.../>元素来配置 -->
<property name="schools">
<!-- 为调用setSchools()方法配置List集合作为参数值 -->
<list>
<!-- 每个value、ref、bean...都可配置一个List元素 -->
<value>小学</value>
<value>中学</value>
<value>大学</value>
</list>
</property>
<property name="scores">
<!-- 为调用setScores()方法配置Map集合作为参数值 -->
<map>
<!-- 每个entry都配置一个key-value对 -->
<entry key="数学" value="87" />
<entry key="英语" value="89" />
<entry key="语文" value="82" />
</map>
</property>
<property name="phaseAxes">
<!-- 为调用setPhaseAxes()方法配置Map集合作为参数 -->
<map>
<!-- 每个entry都配置一个key-value对 -->
<entry key="原始社会" value-ref="stoneAxe" />
<entry key="农业社会" value-ref="steelAxe" />
</map>
</property>
<property name="health">
<!-- 为调用prop元素都匹配一个属性项,其中key指定属性名 -->
<props>
<prop key="血压">正常</prop>
<prop key="身高">175</prop>
</props>
<!--
<value>
pressure=normal
height=175
</value>
-->
</property>
<property name="axes">
<!-- 为调用setAxes()方法配置Set集合作为参数值 -->
<set>
<!-- 每个value、ref、bean...都配置一个Set元素 -->
<value>普通的字符串</value>
<bean class="edu.pri.lime._7_5_8.bean.impl.SteelAxe"/>
<ref bean="stoneAxe"/>
<!-- 为Set集合配置一个List集合作为元素 -->
<list>
<value>20</value>
<!-- 再次为List集合配置一个Set集合作为元素 -->
<set>
<value type="int">30</value>
</set>
</list>
</set>
</property>
<!-- Spring 对List集合和数组的处理是一样的,都用<list.../>元素来配置 -->
<property name="books">
<!-- 为调用setBooks()方法配置数组作为参数值 -->
<list>
<!-- 每个value、ref、bean...都可配置一个数组元素 -->
<value>li</value>
<value>me</value>
<value>Oracle</value>
</list>
</property>
</bean>
</beans>
Spring对List集合和数组的处理是一样的,都用<list.../>元素来配置。
当使用<list.../>、<set.../>、<map.../>等元素配置集合类型的参数值时,还需要配置集合元素。由于集合元素又可以是基本类型值、引用容器中的其他Bean、嵌套Bean或集合属性等,所以<list.../>、<key.../>和<set.../>元素又可接受如下子元素。
⊙ value : 指定集合元素是基本数据类型值或字符串类型值。
⊙ ref : 指定集合元素是容器中的另一个Bean实例。
⊙ bean : 指定集合元素是一个嵌套Bean。
⊙ list、set、map及props : 指定集合元素又是集合。
<props.../>元素用于配置Properties类型的参数值,Properties类型是一种特殊的类型,其key和value都只能是字符串,故Spring配置Properties类型的参数值比较简单:每个key-value对只要分别给出key和value就足够了------而且key和value都是字符串类型,所以使用如下格式的<prop.../>元素就够了。
⊙ <prop key="key">value</prop> : 其中<prop.../>元素的key属性指定key的值,<prop.../>元素的内容指定value的值。
当使用<map.../>元素配置Map参数值时,Map集合的每个元素由key、value两个部分组成,所以配置文件中的每个<entry.../>配置一组key-value对,其中<entry.../>元素支持如下4个属性:
⊙ key : 如果Map key是基本类型值或字符串,则可使用该属性来指定Map key。
⊙ key-ref : 如果Map key是容器中的另一个Bean实例,则可使用该属性指定容器中其他Bean的id。
⊙ value : 如果Map value 是节本类型值或字符串,则可使用该属性来指定Map value。
⊙ value-ref : 如果Map value 是容器中的另一个Bean实例,则可使用该属性指定容器中其他Bean的id。
Spring 还提供了一个简化语法来支持Properties形参的setter方法:但这种配置语法中属性名、属性值都只能是英文、数字,不可出现中文。
<property name="health">
<!-- 为调用prop元素都匹配一个属性项,其中key指定属性名 -->
<!-- <props>
<prop key="血压">正常</prop>
<prop key="身高">175</prop>
</props> -->
<value>
pressure=normal
height=175
</value>
</property>
从Spring 2.0 开始,Spring IoC容器支持集合的合并,子Bean中集合属性值可以从其父Bean的集合属性继承和覆盖而来。即子Bean的集合属性的最终值是父Bean、子Bean合并后的最终结果,而且子Bean集合的元素可以覆盖父Bean集合中对应的元素。
<beans>
<!-- 将父Bean定义成抽象Bean -->
<bean id="parent" abstract="true" class="edu.pri.lime._7_5_8.bean.impl.ComplexObject">
<!-- 定义Properties类型的集合属性 -->
<property name="adminEmails">
<props>
<prop key="administrator">administrator@crazyit.org</prop>
<prop key="support">support@crazyit.org</prop>
</props>
</property>
</bean>
<!-- 使用parent属性指定该Bean继承了parent Bean -->
<bean id="child" parent="parent">
<property name="adminEmails">
<!-- 指定该集合属性支持合并 -->
<props merge="true">
<prop key="sales">sales@crazyit.org</prop>
<prop key="support">master@crazyit.org</prop>
</props>
</property>
</bean>
</beans>
由于泛型的支持,Java可以使用泛型指定集合元素的类型,Spring可通过反射来获取集合元素的类型,这样Spring的类型转换器就会起作用了。
Class : Test
package edu.pri.lime._7_5_8.bean.impl; import java.util.Map; public class Test {
// 程序使用了泛型限制了Map的key是String,value是double,Spring可根据泛型信息把配置文件的集合参数值转换成响应的数据类型
private Map<String,Double> prices; public void setPrices(Map<String, Double> prices) {
this.prices = prices;
} }
XML :
<bean id="test" class="">
<property name="prices">
<map>
<!-- 程序使用了泛型限制了Map的key是String,value是double,Spring可根据泛型信息把配置文件的集合参数值转换成响应的数据类型 -->
<entry key="li" value="23.1"/>
<entry key="me" value="32.1"/>
</map>
</property>
</bean>
Spring 会自动将每个entry中的key值转换成String类型,并将value指定的值转换成Double类型。
7.5.9 组合属性
Spring 还支持组合属性的方式。例如,使用配置文件为形如foo.bar.name的属性设置参数值。为Bean的组合属性设置参数值时,除最后一个属性之外,其他属性值都不允许为null。
Class : ExampleBean
package edu.pri.lime._7_5_9.bean; public class ExampleBean { // 定义一个Person类型的成员变量。使用组合属性设置参数值时person不能为空
private Person person = new Person(); public Person getPerson() {
return person;
} public void setPerson(Person person) {
this.person = person;
} }
Class : Person
package edu.pri.lime._7_5_9.bean; public class Person { private String name; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
}
}
XML :
<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <bean id="exampleBean" class="edu.pri.lime._7_5_9.bean.ExampleBean">
<!-- 驱动Spring调用exampleBean的getPerson().setName()方法,以lime为参数 -->
<property name="person.name" value="lime" />
</bean>
</beans>
Class : TestBean
package edu.pri.lime._7_5_9.main; import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import edu.pri.lime._7_5_9.bean.ExampleBean; public class TestBean { public static void main(String[] args){
ApplicationContext ctx = new ClassPathXmlApplicationContext("app_7_5_9.xml");
ExampleBean exampleBean = ctx.getBean("exampleBean",ExampleBean.class);
System.out.println(exampleBean.getPerson().getName());
}
}
7.5.10 Spring的Bean和JavaBean
Spring容器对Bean没有特殊要求,甚至不需要该Bean像标准的JavaBean------必须为每个属性提供对应的getter和setter方法。
Spring中的Bean是Java实例、Java组件;传统Java应用中的JavaBean通常作为DTO(数据传输对象),用来封装值对象,在各层之间传递数据。
传统的JavaBean也可作为Spring的Bean,从而接受Spring管理。
eg:将数据源配置成容器中的Bean,该数据源Bean即可用于获取数据库连接。
XML :
<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <!-- 定义数据源Bean,使用C3P0数据源实现 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<!-- 指定连接数据库的驱动 -->
<property name="dirverClass" value="com.mysql.jdbc.Driver" />
<!-- 指定连接数据库的URL -->
<property name="jdbcUrl" value="jdbc:mysql://localhost/spring" />
<!-- 指定连接数据库的用户名 -->
<property name="user" value="root" />
<!-- 指定连接数据库的密码 -->
<property name="password" value="system" />
<!-- 指定连接数据库连接池的最大连接数 -->
<property name="maxPoolSize" value="200" />
<!-- 指定连接数据库连接池的最小连接数 -->
<property name="minPoolSize" value="2" />
<!-- 指定连接数据库连接池的初始连接数 -->
<property name="initialPoolSize" value="2" />
<!-- 指定连接数据库连接池的连接的最大空闲时间 -->
<property name="maxIdleTime" value="200" />
</bean>
</beans>
Class : BeanTest
package edu.pri.lime._7_5_10.main; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException; import javax.sql.DataSource; import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; public class BeanTest { public static void main(String[] args) throws SQLException{
ApplicationContext ctx = new ClassPathXmlApplicationContext("app_7_5_10.xml");
DataSource ds = ctx.getBean("dataSource",DataSource.class);
// 通过DataSource来获取数据库连接
Connection conn = ds.getConnection();
// 通过数据库连接获取PerparedStatement
PreparedStatement pstmt = conn.prepareStatement("insert into news_inf value(null,?,?)");
pstmt.setString(1, "lime Cry");
pstmt.setString(1, "lime Happy");
// 执行SQL语句
pstmt.executeUpdate();
// 清理资源,回收数据库连接资源
if(pstmt != null){
pstmt.close();
}
if(conn != null){
conn.close();
}
}
}
虽然Spring对Bean没有特殊要求,但依然建议Spring中的Bean应满足如下几个原则:
⊙ 尽量为每个Bean实现类提供无参数的构造器。
⊙ 接受构造注入的Bean,则应提供对应的、带参数的构造器。
⊙ 接受设置注入的Bean,则应提供对应的setter方法,并不要求提供对应的getter方法。
传统的JavaBean和Spring中的Bean之间的区别:
⊙ 用处不同 : 传统的JavaBean更多是作为值对象传递参数;Spring的Bean用处几乎无所不包,任何应用组件都被称为Bean。
⊙ 写法不同 : 传统的JavaBean作为值对象,要求每个属性都提供getter和setter方法;但Spring的Bean只需为接受设置注入的属性提供setter方法即可。
⊙ 生命周期不同 : 传统的javaBean作为值对象传递,不接受任何容器管理其生命周期;Spring中的Bean有Spring管理其声明周期行为。
7 -- Spring的基本用法 -- 5... Spring容器中的Bean;容器中Bean的作用域;配置依赖;的更多相关文章
-
7 -- Spring的基本用法 -- 12... Spring 3.0 提供的表达式语言(SpEL)
7.12 Spring 3.0 提供的表达式语言(SpEL) Spring表达式语言(简称SpEL)是一种与JSP 2 的EL功能类似的表达式语言,它可以在运行时查询和操作对象图.支持方法调用和基本字 ...
-
7 -- Spring的基本用法 -- 6... Spring 3.0 提供的Java配置管理
7.6 Spring 3.0 提供的Java配置管理 Spring 允许使用Java类进行配置管理,可以不使用XML来管理Bean,以及Bean之间的依赖关系. Interface :Person p ...
-
7 -- Spring的基本用法 -- 3... Spring 的核心机制 : 依赖注入
7.3 Spring 的核心机制 : 依赖注入 Spring 框架的核心功能有两个. Spring容器作为超级大工厂,负责创建.管理所有的Java对象,这些Java对象被称为Bean. Spring容 ...
-
7 -- Spring的基本用法 -- 9...容器中Bean的生命周期
7.9 容器中Bean的生命周期 Spring可以管理singleton作用域的Bean的生命周期,Spring可以精确地知道该Bean何时被创建,何时被初始化完成.容器何时准备销毁该Bean实例. ...
-
7 -- Spring的基本用法 -- 8... 抽象Bean与子Bean;Bean继承与Java继承的区别;容器中的工厂Bean;获得Bean本身的id;强制初始化Bean
7.8 深入理解容器中的Bean 7.8.1 抽象Bean与子Bean 把多个<bean.../>配置中相同的信息提取出来,集中成配置模版------这个配置模版并不是真正的Bean,因此 ...
-
7 -- Spring的基本用法 -- 4... 使用 Spring 容器:Spring 容器BeanFactory、ApplicationContext;ApplicationContext 的国际化支持;ApplicationContext 的事件机制;让Bean获取Spring容器;Spring容器中的Bean
7.4 使用 Spring 容器 Spring 有两个核心接口:BeanFactory 和 ApplicationContext,其中ApplicationContext 是 BeanFactory ...
-
spring扩展点之三:Spring 的监听事件 ApplicationListener 和 ApplicationEvent 用法,在spring启动后做些事情
<spring扩展点之三:Spring 的监听事件 ApplicationListener 和 ApplicationEvent 用法,在spring启动后做些事情> <服务网关zu ...
-
【Spring】Junit加载Spring容器作单元测试(整理)
[Spring]Junit加载Spring容器作单元测试 阅读目录 >引入相关Jar包 > 配置文件加载方式 > 原始的用法 > 常见的用法 > 引入相关Jar包 一.均 ...
-
Spring核心技术(七)——Spring容器的扩展
本文将讨论如何关于在Spring生命周期中扩展Spring中的Bean功能. 容器的扩展 通常来说,开发者不需要通过继承ApplicationContext来实现自己的子类扩展功能.但是Spring ...
随机推荐
-
如何解決 Homebrew Update 失敗?
相信許多用 MAC 系統的程式設計師.工程師們都有用 Homebrew 這個超好用的 Open Source 套件管理程式吧? 如果沒有的話,你可以透過以下的指令安裝: ruby -e "$ ...
-
推荐——Monkey《大话 app 测试——Android、iOS 应用测试指南》
<大话移动——Android与iOS应用测试指南> 京东可以预购啦!http://item.jd.com/11495028.html 当当网:http://product.dangdang ...
-
mysql 创建临时节点
use ZooKeeper; use AnyEvent; use AE; use Data::Dumper; use IO::Socket; my $zk = ZooKeeper->new(ho ...
-
java并行体系结构
并行编程 线程通信 共享内存和消息传递 线程同步 控制不同线程的运行顺序 java并发 基于共享内存模型 指令重排序 编译器重排序 处理器重排序 cpu重排序 写缓存区(cache.寄存器) 内存屏障 ...
-
shell脚本中执行另一个shell脚本
分类: 可以在一个shell脚本中执行另一个shell脚本(或非可执行文件,主要用于取得一些变量的值),方法是: . 文件名(包括路径) 或 变量=文件名(包括路径) . $变量 注意,圆点后面有 ...
-
smm框架学习------smm框架整合实现登录功能(一)
一.准备所需的jar包 1.1所需jar包 1.Spring框架jar包 2.Mybatis框架jar包 3.Spring的AOP事务jar包 4.Mybatis整合Spring中间件jar包 5.a ...
-
基于Apache axis2开发Java Web服务
1.安装配置axis2环境 1)下载axis2-1.4.1-war(发布webservice)和axis2-1.4.1-bin.zip(webservice调用使用的各种包) 下载好后把axis2-1 ...
-
A、B同时打开一个页面进行同一条数据库记录进行修改,A修改完成后提交表单,A修改的数据保存完成后;当B也修改完成后,提交数据进行数据修改。此时B修改的内容会覆盖A修改的内容,请问如何避免?
A.B同时打开一个页面进行数据中的一条数据进行修改,A修改完成后提交表单,数据修改保存完成后B开始页面也修改完成,开始提交进行修改.此时B修改的内容会覆盖A的内容,请问如何避免? 通过搜索和我个人总结 ...
-
Python并发编程之消息队列补充及如何创建线程池(六)
大家好,并发编程 进入第六篇. 在第四章,讲消息通信时,我们学到了Queue消息队列的一些基本使用.昨天我在准备如何创建线程池这一章节的时候,发现对Queue消息队列的讲解有一些遗漏的知识点,而这些知 ...
-
javaAPI实现elasticsearch5.5.2的聚合分析
https://blog.csdn.net/plei_yue/article/details/78452633