`
jinnianshilongnian
  • 浏览: 21450708 次
  • 性别: Icon_minigender_1
博客专栏
5c8dac6a-21dc-3466-8abb-057664ab39c7
跟我学spring3
浏览量:2408450
D659df3e-4ad7-3b12-8b9a-1e94abd75ac3
Spring杂谈
浏览量:3000231
43989fe4-8b6b-3109-aaec-379d27dd4090
跟开涛学SpringMVC...
浏览量:5633497
1df97887-a9e1-3328-b6da-091f51f886a1
Servlet3.1规范翻...
浏览量:258247
4f347843-a078-36c1-977f-797c7fc123fc
springmvc杂谈
浏览量:1594165
22722232-95c1-34f2-b8e1-d059493d3d98
hibernate杂谈
浏览量:249305
45b32b6f-7468-3077-be40-00a5853c9a48
跟我学Shiro
浏览量:5850025
Group-logo
跟我学Nginx+Lua开...
浏览量:699055
5041f67a-12b2-30ba-814d-b55f466529d5
亿级流量网站架构核心技术
浏览量:781537
社区版块
存档分类
最新评论

Spring3 Web MVC下的数据类型转换(第一篇)——《跟我学Spring3 Web MVC》抢先看

阅读更多

 

基于spring-framework-3.1.1.RELEASE

7.1、简介

在编写可视化界面项目时,我们通常需要对数据进行类型转换、验证及格式化。

 

一、在Spring3之前,我们使用如下架构进行类型转换、验证及格式化:


 

流程:

①:类型转换:首先调用PropertyEditor的setAsText(String),内部根据需要调用setValue(Object)方法进行设置转换后的值;

②:数据验证:需要显示调用Spring的Validator接口实现进行数据验证;

③:格式化显示:需要调用PropertyEditor的getText进行格式化显示。

 

使用如上架构的缺点是:

1、PropertyEditor被设计为只能String<——>Object之间转换,不能任意对象类型<——>任意类型,如我们常见的Long时间戳到Date类型的转换是办不到的;

2、PropertyEditor是线程不安全的,也就是有状态的,因此每次使用时都需要创建一个,不可重用;

3、PropertyEditor不是强类型的,setValue(Object)可以接受任意类型,因此需要我们自己判断类型是否兼容;

4、需要自己编程实现验证,Spring3支持更棒的注解验证支持;

5、在使用SpEL表达式语言或DataBinder时,只能进行String<--->Object之间的类型转换;

6不支持细粒度的类型转换/格式化,如UserModel的registerDate需要转换/格式化类似“2012-05-01”的数据,而OrderModel的orderDate需要转换/格式化类似“2012-05-01 15:11:13”的数据,因为大家都为java.util.Date类型,因此不太容易进行细粒度转换/格式化。

 

Spring Web MVC环境中,数据类型转换、验证及格式化通常是这样使用的:

流程:

①、类型转换:首先表单数据(全部是字符串)通过WebDataBinder进行绑定到命令对象,内部通过PropertyEditor实现;

②:数据验证:在控制器中的功能处理方法中,需要显示的调用Spring的Validator实现并将错误信息添加到BindingResult对象中;

③:格式化显示:在表单页面可以通过如下方式展示通过PropertyEditor格式化的数据和错误信息:

 

<%@taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

 首先需要通过如上taglib指令引入spring的两个标签库。

 

//1、格式化单个命令/表单对象的值(好像比较麻烦,真心没有好办法)
<spring:bind path="dataBinderTest.phoneNumber">${status.value}</spring:bind>

 

//2、<spring:eval>标签,自动调用ConversionService并选择相应的Converter SPI进行格式化展示
<spring:eval expression="dataBinderTest.phoneNumber"></spring:eval>

 如上代码能工作的前提是在RequestMappingHandlerMapping配置了ConversionServiceExposingInterceptor,它的作用是暴露conversionService到请求中以便如<spring:eval>标签使用。

 

//3、通过form标签,内部的表单标签会自动调用命令/表单对象属性对应的PropertyEditor进行格式化显示
<form:form commandName="dataBinderTest">
    <form:input path="phoneNumber"/><!-- 如果出错会显示错误之前的数据而不是空 -->
</form:form>

 

//4、显示验证失败后的错误信息
<form:errors></form:errors>

 接下来我们就详细学习一下这些知识吧。

 

 

7.2、数据类型转换

7.2.1、Spring3之前的PropertyEditor

PropertyEditor介绍请参考【4.16.1、数据类型转换】。

 

一、测试之前我们需要准备好测试环境:

1、模型对象,和【4.16.1、数据类型转换】使用的一样,需要将DataBinderTestModel模型类及相关类拷贝过来放入cn.javass.chapter7.model包

2、控制器定义:

package cn.javass.chapter7.web.controller;
//省略import
@Controller
public class DataBinderTestController {
    @RequestMapping(value = "/dataBind")
    public String test(DataBinderTestModel command) {
        	//输出command对象看看是否绑定正确
		System.out.println(command);
		model.addAttribute("dataBinderTest", command);
		return "bind/success";
	}
}

 

 (3、Spring配置文件定义,请参考chapter7-servlet.xml,并注册DataBinderTestController

<bean class="cn.javass.chapter7.web.controller.DataBinderTestController"/>
    

 4、测试的URL:

 

http://localhost:9080/springmvc-chapter7/dataBind?username=zhang&bool=yes&schooInfo.specialty=computer&hobbyList[0]=program&hobbyList[1]=music&map[key1]=value1&map[key2]=value2&phoneNumber=010-12345678&date=2012-3-18 16:48:48&state=blocked

 

 

二、注解式控制器注册PropertyEditor:

1、使用WebDataBinder进行控制器级别注册PropertyEditor(控制器独享)

@InitBinder
//此处的参数也可以是ServletRequestDataBinder类型
public void initBinder(WebDataBinder binder) throws Exception {
	//注册自定义的属性编辑器
	//1、日期
	DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	CustomDateEditor dateEditor = new CustomDateEditor(df, true);
	//表示如果命令对象有Date类型的属性,将使用该属性编辑器进行类型转换
	binder.registerCustomEditor(Date.class, dateEditor);	
	//自定义的电话号码编辑器(和【4.16.1、数据类型转换】一样)
	binder.registerCustomEditor(PhoneNumberModel.class, new PhoneNumberEditor());
}

 和【4.16.1、数据类型转换】一节类似,只是此处需要通过@InitBinder来注册自定义的PropertyEditor。

 

2、使用WebBindingInitializer批量注册PropertyEditor

4.16.1、数据类型转换】不太一样,因为我们的注解式控制器是POJO,没有实现任何东西,因此无法注入WebBindingInitializer,此时我们需要把WebBindingInitializer注入到我们的RequestMappingHandlerAdapter或AnnotationMethodHandlerAdapter,这样对于所有的注解式控制器都是共享的。

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
    <property name="webBindingInitializer">
        <bean class="cn.javass.chapter7.web.controller.support.initializer.MyWebBindingInitializer"/>
    </property>
</bean>

 

 此时我们注释掉控制器级别通过@InitBinder注册PropertyEditor的方法。

 

3、全局级别注册PropertyEditor(全局共享)

和【4.16.1、数据类型转换】一节一样,此处不再重复。请参考【4.16.1、数据类型转换】的【全局级别注册PropertyEditor(全局共享)】。

 

接下来我们看一下Spring3提供的更强大的类型转换支持。

7.2.2、Spring3开始的类型转换系统

Spring3引入了更加通用的类型转换系统,其定义了SPI接口(Converter等)和相应的运行时执行类型转换的API(ConversionService等),在Spring中它和PropertyEditor功能类似,可以替代PropertyEditor来转换外部Bean属性的值到Bean属性需要的类型。

 

该类型转换系统是Spring通用的,其定义在org.springframework.core.convert包中,不仅仅在Spring Web MVC场景下。目标是完全替换PropertyEditor,提供无状态、强类型且可以在任意类型之间转换的类型转换系统,可以用于任何需要的地方,如SpEL、数据绑定。

 

Converter SPI完成通用的类型转换逻辑,如java.util.Date<---->java.lang.Long或java.lang.String---->PhoneNumberModel等。

7.2.2.1、架构

1、类型转换器:提供类型转换的实现支持。

 

一个有如下三种接口:

1、Converter:类型转换器,用于转换S类型到T类型,此接口的实现必须是线程安全的且可以被共享。

package org.springframework.core.convert.converter;
public interface Converter<S, T> { //① S是源类型 T是目标类型
	T convert(S source); //② 转换S类型的source到T目标类型的转换方法
}

 示例:请参考cn.javass.chapter7.converter.support.StringToPhoneNumberConverter转换器,用于将String--->PhoneNumberModel。

 

此处我们可以看到Converter接口实现只能转换一种类型到另一种类型,不能进行多类型转换,如将一个数组转换成集合,如(String[] ----> List<String>String[]----->List<PhoneNumberModel>等)。

 

2、GenericConverter和ConditionalGenericConverter:GenericConverter接口实现能在多种类型之间进行转换,ConditionalGenericConverter是有条件的在多种类型之间进行转换。

package org.springframework.core.convert.converter;
public interface GenericConverter {
	Set<ConvertiblePair> getConvertibleTypes();
	Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}

getConvertibleTypes:指定了可以转换的目标类型对;

convert:在sourceType和targetType类型之间进行转换。

package org.springframework.core.convert.converter;
public interface ConditionalGenericConverter extends GenericConverter {
	boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}

 matches:用于判断sourceType和targetType类型之间能否进行类型转换。

 

示例:如org.springframework.core.convert.support.ArrayToCollectionConverter和CollectionToArrayConverter用于在数组和集合间进行转换的ConditionalGenericConverter实现,如在String[]<---->List<String>、String[]<---->List<PhoneNumberModel>等之间进行类型转换。

 

对于我们大部分用户来说一般不需要自定义GenericConverter, 如果需要可以参考内置的GenericConverter来实现自己的。

 

3、ConverterFactory:工厂模式的实现,用于选择将一种S源类型转换为R类型的子类型T的转换器的工厂接口。

package org.springframework.core.convert.converter;
public interface ConverterFactory<S, R> {
	<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}

 S:源类型;R目标类型的父类型;T:目标类型,且是R类型的子类型;

getConverter:得到目标类型的对应的转换器。

 

示例:如org.springframework.core.convert.support.NumberToNumberConverterFactory用于在Number类型子类型之间进行转换,如Integer--->Double, Byte---->Integer, Float--->Double等。

 

对于我们大部分用户来说一般不需要自定义ConverterFactory,如果需要可以参考内置的ConverterFactory来实现自己的。

 

 

2、类型转换器注册器、类型转换服务:提供类型转换器注册支持,运行时类型转换API支持。


 一共有如下两种接口:

1、ConverterRegistry:类型转换器注册支持,可以注册/删除相应的类型转换器。

package org.springframework.core.convert.converter;
public interface ConverterRegistry {
	void addConverter(Converter<?, ?> converter);
	void addConverter(Class<?> sourceType, Class<?> targetType, Converter<?, ?> converter);
	void addConverter(GenericConverter converter);
	void addConverterFactory(ConverterFactory<?, ?> converterFactory);
	void removeConvertible(Class<?> sourceType, Class<?> targetType);
}

 可以注册:Converter实现,GenericConverter实现,ConverterFactory实现。

 

2、ConversionService:运行时类型转换服务接口,提供运行期类型转换的支持。

package org.springframework.core.convert;
public interface ConversionService {
	boolean canConvert(Class<?> sourceType, Class<?> targetType);
	boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);
	<T> T convert(Object source, Class<T> targetType);
	Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}

 convert:将源对象转换为目标类型的目标对象。

 

Spring提供了两个默认实现(其都实现了ConverterRegistryConversionService接口):

DefaultConversionService:默认的类型转换服务实现;

DefaultFormattingConversionService:带数据格式化支持的类型转换服务实现,一般使用该服务实现即可。

 

7.2.2.2Spring内建的类型转换器如下所示:   

类名

说明

第一组:标量转换器

StringToBooleanConverter

String----->Boolean

true:true/on/yes/1; false:false/off/no/0

ObjectToStringConverter

Object----->String

调用toString方法转换

StringToNumberConverterFactory

String----->Number(如Integer、Long等)

NumberToNumberConverterFactory

Number子类型(Integer、Long、Double等)<——> Number子类型(Integer、Long、Double等)

StringToCharacterConverter

String----->java.lang.Character

取字符串第一个字符

NumberToCharacterConverter

Number子类型(Integer、Long、Double等)——> java.lang.Character

CharacterToNumberFactory

java.lang.Character ——>Number子类型(Integer、Long、Double等)

StringToEnumConverterFactory

String----->enum类型

通过Enum.valueOf将字符串转换为需要的enum类型

EnumToStringConverter

enum类型----->String

返回enum对象的name()值

StringToLocaleConverter

String----->java.util.Local

PropertiesToStringConverter

java.util.Properties----->String

默认通过ISO-8859-1解码

StringToPropertiesConverter

String----->java.util.Properties

默认使用ISO-8859-1编码

第二组:集合、数组相关转换器

ArrayToCollectionConverter

任意S数组---->任意T集合(List、Set)

CollectionToArrayConverter

任意T集合(List、Set)---->任意S数组

ArrayToArrayConverter

任意S数组<---->任意T数组

CollectionToCollectionConverter

任意T集合(List、Set)<---->任意T集合(List、Set)

即集合之间的类型转换

MapToMapConverter

Map<---->Map之间的转换

ArrayToStringConverter

任意S数组---->String类型

StringToArrayConverter

String----->数组

默认通过“,”分割,且去除字符串的两边空格(trim)

ArrayToObjectConverter

任意S数组---->任意Object的转换

(如果目标类型和源类型兼容,直接返回源对象;否则返回S数组的第一个元素并进行类型转换)

ObjectToArrayConverter

Object----->单元素数组

CollectionToStringConverter

任意T集合(List、Set)---->String类型

StringToCollectionConverter

String----->集合(List、Set)

默认通过“,”分割,且去除字符串的两边空格(trim)

CollectionToObjectConverter

任意T集合---->任意Object的转换

(如果目标类型和源类型兼容,直接返回源对象;否则返回S数组的第一个元素并进行类型转换)

ObjectToCollectionConverter

Object----->单元素集合

第三组:默认(fallback)转换器:之前的转换器不能转换时调用

ObjectToObjectConverter

Object(S)----->Object(T)

首先尝试valueOf进行转换、没有则尝试new 构造器(S)

IdToEntityConverter

Id(S)----->Entity(T)

查找并调用public static T find[EntityName](S)获取目标对象,EntityName是T类型的简单类型

FallbackObjectToStringConverter

Object----->String

ConversionService作为恢复使用,即其他转换器不能转换时调用(执行对象的toString()方法)

S:代表源类型,T:代表目标类型

如上的转换器在使用转换服务实现DefaultConversionService和DefaultFormattingConversionService时会自动注册。

 

7.2.2.3、示例

1、自定义String----->PhoneNumberModel的转换器

package cn.javass.chapter7.web.controller.support.converter;
//省略import
public class StringToPhoneNumberConverter implements Converter<String, PhoneNumberModel> {
	Pattern pattern = Pattern.compile("^(\\d{3,4})-(\\d{7,8})$");
	@Override
	public PhoneNumberModel convert(String source) {		
		if(!StringUtils.hasLength(source)) {
			//①如果source为空 返回null
			return null;
		}
		Matcher matcher = pattern.matcher(source);
		if(matcher.matches()) {
			//②如果匹配 进行转换
			PhoneNumberModel phoneNumber = new PhoneNumberModel();
			phoneNumber.setAreaCode(matcher.group(1));
			phoneNumber.setPhoneNumber(matcher.group(2));
			return phoneNumber;
		} else {
			//③如果不匹配 转换失败
			throw new IllegalArgumentException(String.format("类型转换失败,需要格式[010-12345678],但格式是[%s]", source));
		}
	}
}

 

 

String转换为Date的类型转换器,请参考cn.javass.chapter7.web.controller.support.converter.StringToDateConverter。

 

(2、测试用例(cn.javass.chapter7.web.controller.support.converter.ConverterTest)

@Test
public void testStringToPhoneNumberConvert() {
	DefaultConversionService conversionService = new DefaultConversionService();
	conversionService.addConverter(new StringToPhoneNumberConverter());
	
	String phoneNumberStr = "010-12345678";
	PhoneNumberModel phoneNumber = conversionService.convert(phoneNumberStr, PhoneNumberModel.class);
		
	Assert.assertEquals("010", phoneNumber.getAreaCode());
}

 类似于PhoneNumberEditor将字符串“010-12345678”转换为PhoneNumberModel。

@Test
public void testOtherConvert() {
	DefaultConversionService conversionService = new DefaultConversionService();
	
	//"1"--->true(字符串“1”可以转换为布尔值true)
	Assert.assertEquals(Boolean.valueOf(true), conversionService.convert("1", Boolean.class));
	
	//"1,2,3,4"--->List(转换完毕的集合大小为4)
	Assert.assertEquals(4, conversionService.convert("1,2,3,4", List.class).size());
}

 其他类型转换器使用也是类似的,此处不再重复。

7.2.2.4、集成到Spring Web MVC环境

1、注册ConversionService实现和自定义的类型转换器

<!-- ①注册ConversionService -->
<bean id="conversionService" class="org.springframework.format.support.
	 	 	 	 	 	 	 	 	 	 	 FormattingConversionServiceFactoryBean">
    <property name="converters">
       <list>
	        <bean class="cn.javass.chapter7.web.controller.support.
	 	 	 	 	 	 	 converter.StringToPhoneNumberConverter"/>
	        <bean class="cn.javass.chapter7.web.controller.support.
	 	 	 	 	 	 	 converter.StringToDateConverter">
	            <constructor-arg value="yyyy-MM-dd"/>
	        </bean>
        </list>
    </property>
</bean>

 FormattingConversionServiceFactoryBean:是FactoryBean实现,默认使用DefaultFormattingConversionService转换器服务实现;

converters:注册我们自定义的类型转换器,此处注册了String--->PhoneNumberModelString--->Date的类型转换器。

 

2、通过ConfigurableWebBindingInitializer注册ConversionService

<!-- ②使用ConfigurableWebBindingInitializer注册conversionService -->
<bean id="webBindingInitializer" class="org.springframework.web.bind.support.
	        	        	        	        	        	        ConfigurableWebBindingInitializer">
	<property name="conversionService" ref="conversionService"/>
</bean>

 此处我们通过ConfigurableWebBindingInitializer绑定初始化器进行ConversionService的注册;

 

3、注册ConfigurableWebBindingInitializer到RequestMappingHandlerAdapter

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

 通过如上配置,我们就完成了Spring3.0的类型转换系统与Spring Web MVC的集成。此时可以启动服务器输入之前的URL测试了。

 

此时可能有人会问,如果我同时使用PropertyEditor和ConversionService,执行顺序是什么呢?内部首先查找PropertyEditor进行类型转换,如果没有找到相应的PropertyEditor再通过ConversionService进行转换。

 

如上集成过程看起来比较麻烦,后边我们会介绍<mvc:annotation-driven>和@EnableWebMvc,ConversionService会自动注册,后续章节再详细介绍。

31
9
分享到:
评论
6 楼 jinnianshilongnian 2012-05-03  
偶尔玩玩 写道
  楼主辛苦了,继续加油

谢谢
5 楼 偶尔玩玩 2012-05-03  
  楼主辛苦了,继续加油
4 楼 jinnianshilongnian 2012-05-03  
yingzhor 写道
jinnianshilongnian 写道
yingzhor 写道
矮油~~~
哥,你太牛逼了。 搞完Spring又搞SpringMVC啊。

没有啦, ,估计我得叫你姐


。。。
其实我是男的,头像那个是我老婆。

………………  哈哈哈哈,那得喊你哥
3 楼 yingzhor 2012-05-03  
jinnianshilongnian 写道
yingzhor 写道
矮油~~~
哥,你太牛逼了。 搞完Spring又搞SpringMVC啊。

没有啦, ,估计我得叫你姐


。。。
其实我是男的,头像那个是我老婆。
2 楼 jinnianshilongnian 2012-05-03  
yingzhor 写道
矮油~~~
哥,你太牛逼了。 搞完Spring又搞SpringMVC啊。

没有啦, ,估计我得叫你姐
1 楼 yingzhor 2012-05-03  
矮油~~~
哥,你太牛逼了。 搞完Spring又搞SpringMVC啊。

相关推荐

    spring杂谈 作者zhang KaiTao

    1. spring杂谈[原创] 1.1 Spring事务处理时自我调用的解决方案及一些实现方式的风险 ...1.32 Spring3 Web MVC下的数据类型转换(第一篇)——《跟我学Spring3 Web MVC》抢先看 1.33 Spring 注入集合类型

    跟我学Spring MVC

    跟我学SpringMVC高清电子版

    跟我学spring3 .pdf

    详细讲解了 IOC DI AOP JDBC MVC 等等spring知识,有很高的学习价值

    跟我学spring3

    跟我学spring3,包括spring 对象管理,aop,mvc,resource等,非常详细!

    Spring3 MVC +HibernateJPA Eclipse Demo

    2、平台环境:JAVA1.6以上、Apache Tomcat6.0以上、MS SQL2005(可以自己更换数据库,在persistence.xml更改参数就行,具体配置参考第一天给出的链接) 3、调试环境:eclipse(File-import-existing Eclipse projects...

    Spring3 MVC Hibernate-JPA Eclipse Demo

    2、平台环境:JAVA1.6以上、Apache Tomcat6.0以上、MS SQL2005(可以自己更换数据库,在persistence.xml更改参数就行,具体配置参考第一天给出的链接) 3、调试环境:eclipse(File-import-existing Eclipse projects...

    跟我学SpringMVC 教程

    第一章 Web MVC简介 第二章 Spring MVC入门 第三章 DispatcherServlet详解 第四章 Controller接口控制器详解(1) 第四章 Controller接口控制器详解(2) 第四章 Controller接口控制器详解(3) 第四章 ...

    跟我学SpringMVC

    第一章 Web MVC简介 第二章 Spring MVC入门 第三章 DispatcherServlet详解 第四章 Controller接口控制器详解(1) 第四章 Controller接口控制器详解(2) 第四章 Controller接口控制器详解(3) 第四章 Controller...

    亿级流量网站架构核心技术 跟开涛学搭建高可用高并发系统_PDF电子书下载 高清 带索引书签

    通过梳理大型网站技术发展历程,剖析大型网站技术架构模式,深入讲述大型互联网架构设计的...写过《跟我学Spring》《跟我学Spring MVC》《跟我学Shiro》《跟我学Nginx Lua开发》等系列教程,博客现有1000多万访问量。

    跟我StepByStep学FLEX教程-王一松.pdf

    1 8 跟我StepByStep学FLEX教程------Demo3之数据类型 1 9 跟我StepByStep学FLEX教程------Demo4之进度条数据绑定 1 10 跟我StepByStep学FLEX教程------Demo5之事件Event 1 11 跟我StepByStep学FLEX教程------...

    spring开发文档

    跟开涛学SpringMVC.pdf Spring-mvc开发文档.pdf Spring_MVC_3.0实战指南.pdf 跟我学SpringMVC.pdf

    asp.net知识库

    Web标准和ASP.NET - 第一部分 XHTML介绍 在ASP.NET页面中推荐使用覆写(Override)而不是事件处理(Event Handler) 常用编码工具类,支持base64,md5,des,crc32 也谈谈技术面试 在C#里把ArrayList转换为Array 或 把...

    跟我学Shiro教程.zip

    本教程主要是针对Java Web开发中用到的Shiro的详细讲解,希望可以帮到喜欢的朋友们!

    基于J2EE框架的个人博客系统项目毕业设计论文(源码和论文)

    第一章 绪论 1.1. 选题背景 这几年,许多新名词涌入我们的视野:博客、圈子、播客、WAP等。这些都预示着我们进入了一个新的互联网阶段web 2.0,它是相对web 1.0的新的一类互联网应用的总称,是一次从核心内容到外部...

    单点登录源码

    SpringMVC | MVC框架 | [http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc](http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc) ...

    Servlet规范 中文版(3.1)

    很多人在学习Spring+struts2+hibernate或者现在比较流行的Spring+Spring MVC + Mybatis的时候跟我一样忽略的web的基础,基础未打实,就开始冒进,导致后面的学习很多地方模模糊糊。下层基础决定上层建筑、磨刀不误砍...

Global site tag (gtag.js) - Google Analytics