上一篇博客介绍了一些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>


如果Bean4又有自己特有的属性字段,那么继承了这个公共抽象bean之后,直接添加即可:

<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:在配置文件中给beanid取名和引用它的类中的getset方法同名,并设置即可自动加载。

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等类和接口,这里就不再一一赘述。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐