Spring IOC容器之进阶篇
上一篇博客介绍了一些IOC容器的基础,这里总结一下,IOC容器的数据类型转化和代码复用的一个策略。一、自定义数据类型转化器: 类似Struts的数据类型的转化,诸如,java.util.Date类型的数据,如果使用Spring的容器注入,进行赋值,那么就需要自定义转换器,并集成到Spring的框架中。 1、在Bean类中设置Date类型字段的set方法:privat
上一篇博客介绍了一些IOC容器的基础,这里总结一下,IOC容器的数据类型转化和代码复用的一个策略。
一、自定义数据类型转化器:
类似Struts的数据类型的转化,诸如,java.util.Date类型的数据,如果使用Spring的容器注入,进行赋值,那么就需要自定义转换器,并集成到Spring的框架中。
1、在Bean类中设置Date类型字段的set方法:
private Date dateValue;
public Date getDateValue() {
return dateValue;
}
public void setDateValue(Date dateValue) {
this.dateValue = dateValue;
}
2、自定义Date格式转换器的java类:
UtilDatePropertyEditor需要继承PropertyEditorSupport:
public class UtilDatePropertyEditor extends PropertyEditorSupport {
//将转换格式做成可配的
private String pattern;
@SuppressWarnings("unused")
@Override
public void setAsText(String text) throws IllegalArgumentException {
System.out.println("--------UtilDatePropertyEditor.setAsText() ----"+text);
try {
//日期转化
Date date = new SimpleDateFormat(pattern).parse(text);
this.setValue(date);
} catch (ParseException e) {
e.printStackTrace();
throw new IllegalArgumentException(text);
}
}
public void setPattern(String pattern) {
this.pattern = pattern;
}
}
说明:
为了让自定义转化器支持客户自定义的格式,这里使用变量的方式,交给用户在配置文件中自定义类型。另外,这个类要继承PropertyEditorSupport,需要使用this.setValue(date);将转换后的日期交给容器。
3、将定义的转换器,配置到容器中,集成到容器中:
将自定义的配置单独放到一个配置文件(applicationContext-editor.xml)中:
<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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<bean id="customEditors" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="java.util.Date">
<bean class="com.bjpowernode.spring.UtilDatePropertyEditor">
<property name="pattern" value="yyyy-MM-dd"></property>
</bean>
</entry>
</map>
</property>
</bean>
</beans>
自定义转换器的配置和普通bean的配置类似,也使用bean标签,因为其他的类不会调用自定义的转换器,所以这里就将转换器做为一种内部bean配置到了org.springframework.beans.factory.config.CustomEditorConfigurer类的内部。
也可以为自定义的转换器提供外部访问的接口:
<bean id="customEditors" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="java.util.Date" value-ref="customDatePropertyConvertor">
</entry>
</map>
</property>
</bean>
<!-- 外部可以使用这个转换器 -->
<bean id="customDatePropertyConvertor" class="com.bjpowernode.spring.UtilDatePropertyEditor">
<property name="pattern" value="yyyy-MM-dd"></property>
</bean>
不过这样做的意义不大,通常我们都会使用第一种配置方法。
4、使用容器为Date类型字段赋值:
上面的准备工作都做好后,就可以像普通的数据类型一样,为日期类型数据赋值注入,下面是applicationContext-beans.xml
<bean id="bean1" class="com.bjpowernode.spring.Bean1">
<property name="map">
<map>
<entry key="k1" value="v1"></entry>
<entry key="k2" value="v2"></entry>
</map>
</property>
<property name="dateValue">
<value> 2013-09-28</value>
</property>
</bean>
类似Hibernate的Component映射,Spring也提供了一些代码复用的机制。如果有多个Bean有公共的属性字段,那么就可以将这些字段单独抽离出,放到一个类中,让其他类来复用这块儿代码即可。
如:Bean3和Bean4都有id、name和sex属性字段,且数据类型也都一样,那么就可以这样配置:
1、将公共属性放到一个抽象bean中,为了管理的方便,这里单独放到applicationContext-common.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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<bean abstract="true" id="abstractbean" name="beancommon">
<property name="id" value="111" />
<property name="name" value="任我行" />
<property name="sex" value="男" />
</bean>
<bean id="bean3" class="com.bjpowernode.spring.Bean3" parent="abstractbean" />
</beans>
<bean id="bean4" class="com.bjpowernode.spring.Bean4" parent="abstractbean">
<property name="age" value="25" />
</bean>
Spring的配置文件可以有多个,也可以有一个,不管有多少个,即使在不同的配置文件中,所有的bean的id都必须是唯一的,所以,Bean4的这段代码可以放到applicationContext-common.xml文件中也可以放到applicationContext-beans.xml文件中。
如果Bean2对Bean3和Bean4都有引用,那么可以像使用其他Bean一样进行配置:
<bean id="bean2" class="com.bjpowernode.spring.Bean2">
<property name="bean3" ref="bean3" />
<property name="bean4" ref="bean4" />
<property name="bean5" ref="bean5" />
</bean>
三、Ioc容器对配置文件的读取支持:
Spring支持对单个配置问价的读取,也支持对配置文件数组的读取,同时也支持若干文件的读取,但是文件命名需要有一定的规律。
//读取配置文件数组
//String[] xmlFiles= new String[]{"applicationContext-beans.xml","applicationContext-editor.xml"};
//BeanFactory factory= new ClassPathXmlApplicationContext(xmlFiles);
//读取单个配置文件
//BeanFactory factory= new ClassPathXmlApplicationContext("applicationContext.xml");
//读取以applicationContext-开头命名的配置文件
BeanFactory factory= new ClassPathXmlApplicationContext("applicationContext-*.xml");
有了上面的基础和配置,就可以对程序进行测试访问了:
public class InjectionTest extends TestCase {
//工厂是线程安全的
BeanFactory factory ;
/**
*类似servlet的初始化,只初始化一次
*在这里了放置工厂信息
*/
@Override
protected void setUp() throws Exception {
System.out.println("------Factory Init");
//读取配置文件数组
//String[] xmlFiles= new String[]{"applicationContext-beans.xml","applicationContext-editor.xml"};
//BeanFactory factory= new ClassPathXmlApplicationContext(xmlFiles);
//读取单个配置文件
//BeanFactory factory= new ClassPathXmlApplicationContext("applicationContext.xml");
//读取以applicationContext-开头命名的配置文件
factory= new ClassPathXmlApplicationContext("applicationContext-*.xml");
}
/**
* 这里销毁工厂信息
*/
@Override
protected void tearDown() throws Exception {
}
public void testInjection1() {
这里必须从容器中取出,不能使用new,类似jndi的查找,否则,不会读出相关的配置
Bean2 bean2 = (Bean2)factory.getBean("bean2");
System.out.println("bean2.bean3.id=" + bean2.getBean3().getId());
System.out.println("bean2.bean3.name=" + bean2.getBean3().getName());
System.out.println("bean2.bean3.sex=" + bean2.getBean3().getSex());
System.out.println("bean2.bean4.id=" + bean2.getBean4().getId());
System.out.println("bean2.bean4.name=" + bean2.getBean4().getName());
System.out.println("bean2.bean4.sex=" + bean2.getBean4().getSex());
System.out.println("bean2.bean4.age=" + bean2.getBean4().getAge());
System.out.println("bean2.bean5.password=" + bean2.getBean5().getPassword());
}
}
setUp()类似Servlet的初始化,只进行一次初始化,如果希望一些变量和工厂在程序推出的时候销毁可以放到tearDown()方法中进行处理。
五、关于Scope:
Scope标签可以决定不同用户的Bean是否是同一个实例,故名思议,Scope是作用域范围的意思,它的常见的赋值有singleton和prototype。如果是prototype,我们可以理解成Bean的作用域仅限于每一个用户,而singleton则是所有用户共想用一个Bean实例。
我们可以使用下面的程序进行简单的测试:
public void testInjection1(){
//这里必须从容器中取出,不能使用new,类似jndi的查找,否则,不会读出相关的配置
Bean1 bean1 = (Bean1)factory.getBean("bean1");
Bean1 bean2 = (Bean1)factory.getBean("bean1");
System.out.println(bean1==bean2);
}
六、自动装配:
Spring提供了两种自动装配的方式:byType和byName。
byName:在配置文件中给bean的id取名和引用它的类中的get和set方法同名,并设置即可自动加载。
byType:根据Bean类中,属性的数据类型,自动加载。
如,Bean2中有对Bean3、4、5的引用,并设置的相关的get、set方法:
public class Bean2 {
private Bean3 bean3;
private Bean4 bean4;
private Bean5 bean5;
public Bean3 getBean3() {
return bean3;
}
public void setBean3(Bean3 bean3) {
this.bean3 = bean3;
}
public Bean4 getBean4() {
return bean4;
}
public void setBean4(Bean4 bean4) {
this.bean4 = bean4;
}
public Bean5 getBean5() {
return bean5;
}
public void setBean5(Bean5 bean5) {
this.bean5 = bean5;
}
}
如果使用byType自动装配:
在xsi:schemaLocation约束中添加:default-autowire="byType",在对Bean2的配置中就无需手动显示添加注入:
<bean id="bean2" class="com.bjpowernode.spring.Bean2" />
<bean id="bean33" class="com.bjpowernode.spring.Bean3">
……
</bean>
<bean id="bean44" class="com.bjpowernode.spring.Bean4">
……
</bean>
<bean id="bean55" class="com.bjpowernode.spring.Bean5">
……
</bean>
如果是byName: default-autowire="byName" 要求其他的bean的id受 Bean2中的方法名的约束:
<bean id="bean2" class="com.bjpowernode.spring.Bean2" />
<bean id="bean3" class="com.bjpowernode.spring.Bean3">
……
</bean>
<bean id="bean4" class="com.bjpowernode.spring.Bean4">
……
</bean>
<bean id="bean5" class="com.bjpowernode.spring.Bean5">
……
</bean>
七、总结:
Spring的Ioc容器对对象的注入提供了强大的支持,程序的灵活性往往都是通过对配置文件做活的,控制反转的思想很重要,在设计程序的时候应该借鉴这种设计思路。
Ioc容器提供了多种读取配置文件的方式,另外,对对象的装载也提供了一些自动的默认机制,这种做法也是值得学习和借鉴的。
Ioc还有一些其他的内容,如 default-lazy-init标签,BeanFactory、ApplicationContext等类和接口,这里就不再一一赘述。
更多推荐
所有评论(0)