Spring二 Bean详解

Bean详解

Spring框架的本质其实是:通过XML配置来驱动Java代码,这样就可以把原本由java代码管理的耦合关系,提取到XML配置文件中管理。这样就实现了系统中各组件的解耦,有利于后期的升级和维护。
1.Bean的基本定义和Bean别名
<beans>元素是Spring配置文件的根元素,该元素可以指定如下属性:
default-lazy-init:指定<beans>元素下配置的所有bean默认的延迟初始化行为
default-merge:指定<beans>元素下配置的所有bean默认的merge行为
default-autowire:指定<beans>元素下配置的所有bean默认的自动装配行为
default-init-method:指定<beans>元素下配置的所有bean默认的初始化方法
default-destroy-method:指定<beans>元素下配置的所有bean默认的回收方法
default-autowire-candidates:指定<beans>元素下配置的所有bean默认是否作为自动装配的候选Bean
<beans>元素下所指定的属性都可以在每个<bean>子元素中指定,只要将属性名去掉default即可,这样就可以对特定bean起作用。
定义Bean时,通常要指定两个属性:
id:确定该Bean的唯一标识,Bean的id属性在Spring容器中应该是唯一的。
class:指定该Bean的具体实现类,这里不能是接口,因为在通常情况下,Spring会直接使用new关键字创建该bean的实例。
2.Bean的作用域
Spring支持5种作用域:
singleton:单例模式,在整个Spring IoC容器中,singleton作用域的Bean将只生成一个实例。
prototype:每次通过容器的getBean()方法获取prototype作用域的Bean时,都将产生一个新的Bean实例。
request、session:对于一次HTTP请求,request/session作用域的Bean将只产生一个实例,这意味着,在同一次HTTP请求内,程序每次请求该Bean,得到的总是同一个实例。只有在Web应用中使用Spring,该作用域才真正有效。
global session:每个全局的HTTP Session对应一个Bean实例,在典型的情况下,仅在使用portlet context的时候有效。只有在Web应用中使用Spring,该作用域才真正有效。
注意:如果一个Bean被设置成prototype作用域,程序每次请求该id的Bean,Spring都会新建一个Bean实例,然后返回程序。在这种情况下,Spring容器仅仅使用new关键字创建Bean实例,一旦创建成功,容器就不会再跟踪实例,也不会去维护bean实例的状态。
Java在创建Java实例时,需要进行内存申请,销毁实例时,要进行垃圾回收,这些工作会增加系统开销。因此,使用prototype作用域的Bean的创建、销毁代价比较大。而singleton作用域的Bean实例一旦创建成功,就可以重复使用。
例如:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
//创建一个singleton Bean实例
<bean id="p1" class="mySpring.Person" />
//创建一个prototype Bean实例
<bean id="p2" class="mySpring.Person" scope="prototype"/>
<bean id="date" class="java.util.Date" />
</beans>

测试:

public class myTest {
public static void main(String[] args) {
ApplicationContext ctx=new ClassPathXmlApplicationContext("a/ApplicationContext.xml");
System.out.println(ctx.getBean("p1")==ctx.getBean("p1"));
System.out.println(ctx.getBean("p2")==ctx.getBean("p2"));
System.out.println(ctx.getBean("date"));
Thread.sleep(1000);
System.out.println(ctx.getBean("date"));
}
}

结果是: true false Sun May 11 14:39:03 CST 2014 Sun May Sun May 11 14:39:03 CST 2014
request和session作用域只在Web应用中有效,并且要在Web应用中增加额外配置才会生效,因此必须要将HTTP请求对象绑定到为该请求提供服务的线程上,这样才能使得具有request和session作用域的Bean实例能够在后面的调用链中被访问到。对于支持Servlet2.4及更新规范的Web容器,可以在Web应用的web.xml文件中添加如下的Listener配置:
在WEB-INF\web.xml文件中:
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
JSP脚本:
<body>
<%
WebApplicationContext ctx=WebApplicationContextUtils.getWebApplicationContext(application);
Perosn p1=(Perosn)ctx.getBean("perosn");
Perosn p2=(Perosn)ctx.getBean("perosn");
out.println((p1==p2)+"<br />");
out.println(p1);
%>
</body>
结果:刷新页面依旧输出true,但是两次的Perosn Bean不一样。
3.配置依赖
不管是设值注入,还是构造注入,都视为Bean依赖,接受Spring容器管理,依赖关系的值要么是一个确定的值,要么是Spring容器中其他Bean的引用。通常不建议使用配置文件管理Bean的基本类型的属性值,通常只使用配置文件管理容器中Bean与Bean之间的依赖关系。
创建BeanFactory时不会立即创建Bean实例,所以有可能程序可以正确创建BeanFactory实例,但当请求Bean实例时依然抛出一个异常,创建Bean实例或注入它的依赖关系时出现错误。配置错误的延迟出现,也会给系统带来不安全因素。ApplicationContext实例化过程的时间和内存开销大,但可以在容器初始化阶段就检验出配置错误。
Java类的成员变量可以是各种可以是各种数据类型,除了基本类型值、字符串类型值等,还可以是其他Java实例,也可以是容器中的其他Bean实例,甚至是Java集合、数组等,所以Spring允许通过如下元素为setter方法、构造器参数指定参数值:value,ref,bean,list,set,map及props。
(1)设置普通属性值
<value/>元素用于指定基本类型及其包装、字符串类型的参数值,Spring使用XML解析器来解析出这些数据,然后利用java.beans.PropertyEditor完成类型转换:从java.lang.String类型转换为所需的参数值类型。如果目标类型是基本类型及其包装类,通常可以正确转换。
如:
<bean id="exampleBean" class="mySpring.ExampleBean">
//指定int型的参数值
<property name="field1" value="1" />
//指定double型的参数值
<property name="field2" value="2.3" />
</bean>
(2)配置合作者Bean
如果需要为Bean设置的属性值是容器中的另一个Bean实例,则要用<ref />元素。使用<ref/>元素时可指定一个bean属性,该属性用于引用容器中其他Bean实例的id属性值。
如:
Person.java

public class Person {
private String name;
private int age;
private Car car;
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", car=" + car + "]";
}
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;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
public Person(String name, int age, Car car) {
super();
this.name = name;
this.age = age;
this.car = car;
}
public Person() {
super();
}
}

Car.java

public class Car {
private String brand;
private String corp;
private double price;
private int maxSpeed;
public Car(String brand, String corp, double price) {
super();
this.brand = brand;
this.corp = corp;
this.price = price;
}
@Override
public String toString() {
return "Car [brand=" + brand + ", corp=" + corp + ", price=" + price
+ ", maxSpeed=" + maxSpeed + "]";
}
}

ApplicationContext.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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="car" class="a.Car">
<constructor-arg value="BaoMa" type="java.lang.String"></constructor-arg>
<constructor-arg value="ShangHai" type="java.lang.String"></constructor-arg>
<constructor-arg value="240" type="int"></constructor-arg>
</bean>
<bean id="person" class="a.Person">
<property name="name" value="Tom"></property>
<property name="age" value="20"></property>
<property name="car" ref="car"></property> //也可以写为<property name="car"><ref bean="car" /></property>
</bean>
</beans>

测试:

public class myTest {
public static void main(String[] args) {
ApplicationContext ctx=new ClassPathXmlApplicationContext("a/ApplicationContext.xml");
Person person=ctx.getBean(Person.class);
System.out.println(person);
}
}

结果:Person [name=Tom, age=20, car=Car [brand=BaoMa, corp=ShangHai, price=0.0, maxSpeed=240]]
(3)使用自动装配注入合作者Bean
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方法名去掉set前缀,并小写首字母后同名的Bean来完成注入。如果没有找到匹配的Bean,则不注入。
byType:根据setter方法的形参类型来自动装配。Spring容器查找容器中的全部Bean,如果正好有一个Bean类型与setter方法的形参类型匹配,就自动注入这个Bean;如果找到多个,就抛出一个异常;如果没找到,就什么也不会发生。
具体使用:
byName规则
<bean id="chinese" class="mySpring.Chinese" autowire="byName" />
<bean id="gunDog" class="mySpring.GunDog">
<property name="name" value="wangwang" />
</bean>
在Chinese类中一定会有如下setter方法:
public void setGunDog(Dog dog){
this.dog=dog;
}
Spring容器会寻找id为gunDog的Bean,如果能找到,该Bean就会作为调用setGunDog()方法的参数。
byType规则
<bean id="chinese" class="mySpring.Chinese" autowire="byType" />
<bean id="gunDog" class="mySpring.GunDog">
<property name="name" value="wangwang" />
</bean>
在Chinese类中一定会有如下setter方法:
public void setGunDog(Dog dog){
this.dog=dog;
}
上面setter方法的形参类型是Dog,而mySpring.GunDog Bean类实现了Dog接口,但是如果还有一个类同时也实现了Dog接口的话,如下:
<bean id="gunDog" class="mySpring.PetDog">
<property name="name" value="ohoh" />
</bean>
由于容器中有两个类型为Dog的Bean,Spring无法确定应为chinese Bean注入哪个Bean,则程序会抛出异常。
当一个Bean既使用自动装配依赖,又使用ref显式指定依赖时,则显示指定的依赖会覆盖掉自动装配依赖。
(4)注入嵌套Bean
如果某个Bean所依赖的Bean不想被Spring容器直接访问,则可以使用嵌套Bean。这样的Bean称为内部Bean,不能被外部Bean引用,只能在内部使用,这里的bean不用id。
<bean id="person" class="a.Person">
<property name="name" value="Tom"></property>
<property name="age" value="20"></property>
<property name="car">
<bean class="a.Car">
<constructor-arg value="Ford"></constructor-arg>
<constructor-arg value="Changan"></constructor-arg>
<constructor-arg value="200000"></constructor-arg>
</bean>
</property>
</bean>
当形参类型是基本类型、String、日期等,直接使用value指定字面值即可;但形参类型是复合类(如Person、DataSource等),那就需要传入一个Java对象作为实参,主要有两种方法:使用ref引用一个容器中已经配置的Bean,或者使用<bean>元素配置一个嵌套Bean;形参类型是Set、List、Map等集合或者是数组类型时,要进行如下配置。
(5)注入集合值
集合元素<list>,<set>,<map>,<props>的具体设置
Address.java

public class Address{
private String city;
private String street;
...//此处省略getter/setter方法及其他方法
}

Car.java

public class Car {
private String brand;
private String corp;
private double price;
...//此处省略getter/setter方法及其他方法
}

Person.java

public class Person {
private List<Car> cars;
private List<String> schools;
private Map scores;
private Map<String,Car> cars1;
private Properties health;
private String[] books;
...//此处省略getter/setter方法及其他方法
}

ApplicationContext.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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="car1" class="SpringTest.Car">
<property name="brand" value="Ford"></property>
<property name="corp" value="Changan"></property>
<property name="price" value="200000"></property>
</bean> <bean id="car2" class="SpringTest.Car">
<property name="brand" value="BaoMa"></property>
<property name="corp" value="ShangHai"></property>
<property name="price" value="240000"></property>
</bean> <bean id="person" class="SpringTest.Person">
<property name="cars">
<list>
<ref bean="car1"></ref>
<ref bean="car2"></ref>
</list>
</property> <property name="schools">
<list>
<value>小学</value>
<value>中学</value>
<value>大学</value>
</list>
</property> <property name="scores">
<map>
<entry key="数学" value="90" />
<entry key="语文" value="90" />
<entry key="英语" value="90" />
</map>
</property> <property name="cars1">
<map>
<entry key="AA" value-ref="car1"></entry>
<entry key="BB" value-ref="car2"></entry>
</map>
</property> <property name="health">
<props>
<prop key="血压">正常</prop>
<prop key="身高">175</prop>
</props>
</property> <property name="books">
<list>
<value>java学习</value>
<value>javascript学习</value>
<value>javaEE学习</value>
</list>
</property>
</bean>
</beans>

测试:

public class MainTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("SpringTest/ApplicationContext.xml");
Person p=(Person)ctx.getBean("person");
System.out.println(p);
ctx.close();
}
}

结果:
Person [cars=[Car [brand=Ford, corp=Changan, price=200000.0], Car [brand=BaoMa, corp=ShangHai, price=240000.0]], schools=[小学, 中学, 大学], scores={数学=90, 语文=90, 英语=90}, cars1={AA=Car [brand=Ford, corp=Changan, price=200000.0], BB=Car [brand=BaoMa, corp=ShangHai, price=240000.0]}, health={血压=正常, 身高=175}, books=[java学习, javascript学习, javaEE学习]]

上一篇:TimedSupervisorTask


下一篇:Android学习笔记之Json的使用....