Spring MVC中數據綁定之日期類型

數據綁定應該算是Spring MVC的特點之一吧~簡單易用且功能強大,極大地簡化了我們編程人員對於用戶輸入數據的接收及轉換。

早先版本的Spring中的數據綁定完全都是基於PropertyEditor的。而Spring3中引入了Converter,用來替代PropertyEditor完成類型轉換。

那麼我們也依照這個順序,先來講講基於傳統的PropertyEditor來實現日期類型的數據綁定。


在Spring MVC中,我們可以通過WebDataBinder來註冊自定義的PropertyEditor,從而添加對應的請求參數綁定。有兩種方式:

1、使用@InitBinder註解@Controller中的方法 2、自定義WebBindingInitializer來提供一個全局的數據綁定規則。


1、使用@InitBinder註解

	@InitBinder
	public void initBinder(WebDataBinder binder){
		binder.registerCustomEditor(Date.class, new DateEditor());
	}

public class DateEditor extends PropertyEditorSupport {
	@Override
	public void setAsText(String text) throws IllegalArgumentException {
		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		Date date = null;
		try {
			date = format.parse(text);
		} catch (ParseException e) {
			format = new SimpleDateFormat("yyyy-MM-dd");
			try {
				date = format.parse(text);
			} catch (ParseException e1) {
				e1.printStackTrace();
			}
		}
		setValue(date);
	}
}

這裏我們將DateEditor提出來封裝成一個類方便重用。

另外這裏有個try...catch的小妙用,就是首先以"yyyy-MM-dd HH:mm:ss"的形式來解析用戶輸入的參數,若解析失敗則轉以"yyyy-MM-dd"的形式來解析。這樣的邏輯就可以同時處理"yyyy-MM-dd HH:mm:ss"和"yyyy-MM-dd"形式的日期數據,我想在一般的中文系統中,這兩種形式應該是最常用的了吧。

添加如上代碼之後,@InitBinder所在的Controller就可以自動綁定日期類型的數據了,不過這僅僅是在該Controller中生效,若想在全局範圍內生效的話,可以將@InitBinder註解所在的Controller定義爲一個BaseController,其餘Controller都繼承這個Controller。當然還有另外的方法,若你有興趣的話,請看2。


2、自定義WebBindingInitializer

public class MyWebBindingInitializer implements WebBindingInitializer {
	
	@Override
	public void initBinder(WebDataBinder binder, WebRequest request) {
		binder.registerCustomEditor(Date.class, new DateEditor());
	}
}

還是前面寫的DateEditor,這麼快又見面了,只不過註冊的位置改變了,在WebBindingInitializer中註冊的PropertyEditor是在全局範圍內共享的。

不過光這樣還不夠,還要將WebBindingInitializer注入到AnnotationMethodHandlerAdapter中。

	<bean
		class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
		<property name="webBindingInitializer">
			<bean
				class="org.springframework.samples.petclinic.web.ClinicBindingInitializer" />
		</property>
	</bean>


如果是用<mvc:annotation-driven />的童鞋,上面的配置就沒效果了,而mvc命名空間也沒提供如此細粒度的配置,怎麼辦呢?

別怕,方法還是有的,我們可以通過一個自定義PostBeanProcessor來處理:

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		if (bean instanceof RequestMappingHandlerAdapter) {
			RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter) bean;
			adapter.setWebBindingInitializer(new MyWebBindingInitializer());
		}
		return bean;
	}

不過實際上<mvc:annotation-driven />默認就爲我們提供了一個WebBindingInitializer——ConfigurableWebBindingInitializer

而上面的方法則會覆蓋默認的ConfigurableWebBindingInitializer,其實我們可以直接使用這個Bean來註冊我們的PropertyEditor:

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		if(bean instanceof ConfigurableWebBindingInitializer){
			ConfigurableWebBindingInitializer initializer = (ConfigurableWebBindingInitializer) bean;
			initializer.setPropertyEditorRegistrar(new PropertyEditorRegistrar() {
				@Override
				public void registerCustomEditors(PropertyEditorRegistry registry) {
					registry.registerCustomEditor(Date.class, new DateEditor());
				}
			});
		}
		return bean;
	}

這裏需要說明一下,WebBindingInitializer中不僅僅能註冊PropertyEditor,還能註冊Converter,也就是下面的3


3、使用ConverstionService

Spring3新引入了Converter系統,而ConversionService則是一個Facade類,用來封裝底層實現而對外提供便捷的類型轉換。所以這裏不能重用之間的DateEditor了,不過大致邏輯還是一樣的。另外補充說明一下,Converter是處理任意兩類型間的轉換,而Formatter是處理字符串和另一類型之間的轉換的。可以看出來,Formatter是一類特殊的Converter,並且在處理數據綁定時,Formatter比Converter更加合適。所以我們這裏就用Formatter來做:

public class DateFormatter implements Formatter<Date> {

	@Override
	public String print(Date object, Locale locale) {
		return null;
	}

	@Override
	public Date parse(String text, Locale locale) throws ParseException {
		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		Date date = null;
		try {
			date = format.parse(text);
		} catch (Exception e) {
			format = new SimpleDateFormat("yyyy-MM-dd");
			date = format.parse(text);
		}
		return date;
	}

}

這裏我們只寫String到Date的邏輯。然後需要將DateFormatter註冊到一個ConversionService中,最後再將ConversionService註冊到Spring MVC中。

	<bean id="conversionService"
		class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
		<property name="formatters">
			<set>
				<bean class="com.test.common.core.DateFormatter"></bean>
			</set>
		</property>
	</bean>


如果是用<mvc:annotation-driven />的童鞋,那麼很簡單,只需要:

<mvc:annotation-driven conversion-service="conversionService"/>


而未使用<mvc:annotation-driven />的童鞋,需要定義一個WebBindingInitializer(或者使用ConfigurableWebBindingInitializer),然後注入到RequestMappingHandlerAdapter中去:

	<bean
		class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
		<property name="webBindingInitializer" ref="webBindingInitializer">
		</property>
	</bean>
	
	<bean id="webBindingInitializer" class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
		<property name="conversionService" ref="conversionService"></property>
	</bean>

此時可能有人會問,如果同時使用PropertyEditor和ConversionService,執行順序是什麼呢?內部首先查找PropertyEditor進行類型轉換,如果沒有找到相應的PropertyEditor再通過ConversionService進行轉換。


4、PropertyEditor的自動註冊

對了,這裏再稍帶提一下自動註冊PropertyEditor,只需要將JavaBean和JavaBean名稱+Editor這兩個類放在同一包下,那麼JavaBeans的基礎結構會自動發現PropertyEditor的類,而無需你手動註冊~



發佈了211 篇原創文章 · 獲贊 85 · 訪問量 77萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章