SpringMVC学习笔记
1:编写Helloworld程序心得:
1>首先我们要有@Controller注解的类,即表述层,该类中要含有@RequestMapping("")注解的方法,映射用户的
请求
**
* 1:使用RequestMapping("path"),用来映射请求的URL,path 表示的用户请求的地址
* 2:映射方法的返回值,会被视图解析器解析为实际的物理视图,具体过程如下
* 3:它会将该方法的返回值和视图解析器中的前缀和后缀一起组成一个新路径,并且转发到该页面下
* 前缀success后缀,即/WEB-INF/views/success.jsp
* @return
*/
@RequestMapping("/hello world")
public String hello(){
System.out.println("hello world");
return "success";
}
2>在web.xml文件中注册DispatcherServlet
其实有一种默认的加载SpringMVC的配置文件的方式,就是在WEB-INF目录下.将配置文件命名为,
3>在SpringMVC的配置文件中注册组件自动扫描器,还有视图解析器,InternalResourceViewResolver
2 详解@RequestMapping(value="")
1:SpringMVC中使用@RequestMapping()注解,来为控制器指定可以处理那些请求,
如果修饰的是类,则表示的是相对于当前web应用的根目录,
如果修饰的是方法,而类没有被修饰,对应的也是当前web应用的根目录
如果类和方法都被修饰,那么请求的路径就应该是类中的value值加方法的value值.
总结:RequestMapping修饰类,就是相对于web应用的根目录,如果类没有被修饰,也是如此
如果类和方法都被修饰,方法是相对于类的,类是相对于web应用的,所以.
Dispatcher在截获请求之后,就会通过@Controller上@RequestMapping()提供的映射信息来确定处理请求的
对应方法.
2:以上是RequestMapping通过请求的URL映射请求.
其实它还支持的有method,params,heads.即,请求方法,请求参数,请求头,他们可以连接使用,使得请求映射
更加的精确,其中params和heads还支持简单的表达式.
method=RequestMethod.POST
param1:
!param1:
param1!=value1
{"param1=value1","param2"}
method和value比较常用,其他的作为了解.
3:我们可以使用@Pathvariable("URL中参数的名字")
@RequestMapping("/test pathvariable/{id}")
public String test01(@Pathvariable("id") Integet id ){
System.out.println("id")
return "success";
}
这样我们可以把URL中携带的参数,传递给目标方法的形参,要保证二者的名称一致,不然获取会失败
这样我们就可以在目标方法中,得到并且使用URL携带的参数.
4:想要在目标方法中获得URL中携带的参数,还可以使用@RequestParam,例如:Url中携带了username和age
public String test01(@Requestparam(value="username") String un,
@RequestParam(value="age",required=false) Integer age){
System.out.println(un);
return "success";
}
这样我们就可以吧username,age映射给un,age.value的取值要和URL中携带的参数名称相对应
required属性,默认值是true,它表示请求中必须要携带对应的参数,否则会报400,取值为false
即使不携带相应的参数也不会报错,引用类型的默认值是null,但是如果是int类型的参数不携带会报500,
我们还可以增加defaultValue="0".
3 在我们日常的学习中,我们的请求方式一般是get,post,其实还有两种请求也可以,分别是put(更新),delete(删除)
,我们可以使用HiddenHttpMethodFilter,把 get,或者是post方式更改成put或者是delete.
1> 首先在web.xml文件中配置HiddenHttpMethodFilter
2> 需要发送post请求
3> 表单中要有一个name=_method的隐藏字段,value="put"或者是delete.
4: 使用POJO对象绑定请求参数值
在实际的开发环境中,我们的表单通常是一对象的形式进行数据的传递,如果我们使用普通的RequestParam()
那么成本比较高,并且比较麻烦.
SpringMVC会按照请求参数名和POJO(普通的java对象)的属性名进行自动匹配,自动为对象填充属性值,支持级联属性.
要求:表单中的字段名要和对象的属性名称,保持一致,包括级联属性.
5: 使用Servlet原生的API作为目标方法的参数,具体支持的有以下几种类型
* HttpServletRequest
* HttpServletResponse
* HttpSession
* java.security.Principal
* InputStream
* OutputStream
* Reader
* Writer
@RequestMapping("test03")
public String test03(HttpServletRequest request,HttpResponse response,Writer out){
System.out.println(request);
System.out.println(response);
out.write("hahahaha");
return "success";
}
6:SpringMVC 处理数据的方法
当一个请求发出后,DispatcherServlet拦截到请求,会根据请求的URL去寻找相应的处理器,和目标方法.
目标方法把请求中携带的模型和数据转发到目标页面显示,这是一个请求的完整过程.
我们如何处理请求数据,并且转发到目标页面呢?
SpringMVC提供了4中方法处理模型数据:
1> ModelAndView
当目标方法的返回值是ModelAndView时,我们可以在目标方法的方法体中,通过该对象,添加模型数据.
它既包含视图也包含模型数据.例如:
@RequestMapping(value="")
public ModelAndView test04(){
String ViewName = "success";
ModelAndView mv = new ModelAndView(ViewName);//通过名称创建ModelAndView
mv.addObject("time",new Date());//添加数据
return "success";
}
在目标页面中,我们可以通过ModelAndView中的属性的名称获取属性值,即
${requestScope.time},获取到存入的时间值,其内部原理就是把model中的数据进行遍历
然后把遍历的每一个元素就行request.setAttribute("",value)操作.保存到了请求域中
这样我们就可以在目标页面获取到请求域中获取到相应得属性值.(数据保存和获取必须是同一个请求)
2> Map及Model:Map或者Model中的数据会自动的添加到数据模型中.例如:
@RequestMapping(value"")
public String test05(Map
map.put("names",Array.asList("Tom","Jerry","Mike"));
return "success";
}
目标页面:names:${requestScope.names},
目标方法中map,model,或者是mapModel类型的参数,都可以在方法体中使用并且在姆比饿哦页面中红获取
3> SessionAttributes,将某个属性暂存到HttPSession中,以便多个请求之间共享该属性.
注意:该注解只能用来修饰控制器,其中value属性指通过属性名,type通过指定属性的类型.
@SessionAttributes(value={User},type={String.class})
4> ModelAttribute,该注解修饰的参数对象,会自动注入到数据模型中.使用场景如图.
首先我们从数据库中读取到的对象User,放入到Map中,键为user,SpringMVC 会从map中取出对象,
然后把表单中提交的相应的参数赋值给User的对应的属性,然后把该对象以参数的形式传递给目标方法的参数.
在被@ModelAttribute修饰的方法中,放入到 map中的对象中的键应该写成是目标方法参数第一个字母小写的类名称.
6:视图解析流程的分析:
我们如何通过目标方法的返回值就可以到我们的目标页面呢?
分析:无论我们的目标方法的返回值是什么类型(String,ModelAndView,View等对象),SpringMVC都会把它转换成
ModelAndView对象,然后通过ViewResolver解析成物理视图,转发或者重定向相应的jsp,pdf,jstl等页面.
视图对象是由视图解析器负责实例化的,由于它第无状态的(每次都是被视图解析器创建出来的),所以他们是不会
有线程安全的问题.视图的作用就是渲染模型数据,然后把数据以某一种形式呈现给客户.
常用的视图实现类:
URL资源视图:适用于InternalResourceView和JSTLView(当jsp文件中使用了国际化标签的时候使用)
文档视图:AbstractExcelView(Excel文档视图的抽象类,基于POI构造Excel文档)
7:关于自定义的视图
1>自定义视图,首先要实现的是View接口,然后重写接口中的两个方法,并且要把该接口放到Spring容器中
2>然后视图类要重写View中的两个方法代码如下:
//自定义视图.需要实现View接口
@Component
public class HelloView implements View {
@Override
//设置返回内容的类型
public String getContentType() {
return "text/html";
}
@Override
//渲染视图,我们可以在该方法中整合其他的视图,比如AbstractExcelView(我们只需要实现抽象方法
// buildExcelDocument()即可)
public void render(Map
response.getWriter().print("hello view "+ new Date());
}
}
3>仅仅这样还是不够的,我们需要在Spring容器中,声明BeanNameViewResolver,通过Bean名称解析视图,
当有多个视图解析器的时候,我们可以通过order属性指定解析器的执行顺序,数字越大优先级越低,一般常用的视图解析器的优先级设置的低
8:关于重定向的问题
一般情况下,控制器中的处理方法的返回值,会被当做逻辑视图名处理,然后和视图解析器中的前缀,后缀
一起组成一个逻辑视图,通过View的render()方法渲染视图,通过DispatcherServlet一起转发到目标页面.
那么我们如何完成重定向的操作呢?
如果返回方法返回的字符串中带有forward:或者是redirect:前缀时,SpringMVC会特殊处理,将forward:
或者是redirect:当做是指示符:后面的字符串当成URL解析处理.
If you don't leave me we'll die together.
中学:你如果不滚开,我们就同归于尽!
四级:如果你不离开我,我们就死在一起
六级:你若生死相依,我便不离不弃。
八级:问世间情为何物,只叫人生死相许。
砖家:山无棱天地合才敢与君绝
活佛:你在或不在,爱就在那里,不增不减。
骨灰:老子不好过,你龟儿子也莫想活!
9:关于数据的绑定流程的分析:
我们在实际开发的过程中,通常会遇到三种问题:
1>数据类型转换,比如字符串转换成日期类型
2>数据类型格式化,例如:我们只能输出指定类型的数据.(类型转换和格式化往往一起进行)
3>数据的校验.比如生日的输入,应该是当前系统时间之前的一个时间.
1:SpringMVC会将ServletRequest对象和目标方法的参数传递给WebDataBinderFactory实例,以创建DataBinder对象
2:DataBinder调用装配在SpringMVC上下文中的ConversionService组件进行类型转换和数据格式化工作.将Servlet中的
请求信息填充到参数中.
3:调用Validator组件对已经绑定了请求消息的参数对象进行合法性校验,并最终生成数据绑定结果BindingData对象.
4:SpringMVC抽取BindingResult中的参数和校验错误对象,将他们赋值给处理方法的参数.
SpringMVC 内置了很多的类型转换器,可以满足我们的大多数需求,但是,实际开发过程中,我们可能会根据需求
使用自定义的类型转换器,才能达到我们的需求.如何来写具有特殊功能的类型转换器呢?
10:自定义类型转换器:
ConversionService是SpringMVC类型转换体系的核心接口
可以利用ConversionServiceFactoryBean在Spring的IOC容器中定义一个ConversionService,Spring将自动识别出
ConversionService,并且在Bean的属性配置,和SpringMVC的处理方法参数的绑定等场合中使用它来完成对数据的类型的转换
可以通过ConversionServiceFactoryBean的converters属性注册自定义的类型转换器.
仅仅有这些还是不够的,我们还需要把类型转换器注册测MVC驱动中:
11:关于
我们在开发的过程当中,一般都是要加上
RequestMappingHandlerMapping
RequestMappingHandlerAdapter
ExceptionHandlerExceptionResolver这三个Bean
还将提供一下支持:
支持使用ConversionService对表单参数进行类型转换.
支持使用@NumberFormatannotation,@DateTimeFormat注解完成数据类型的格式化.
支持使用@Valid注解对javaBean实例进行JSR 303验证.
支持使用@RequestBody和@ResponseBody注解.
12:关于@InitBinder的用法
由@InitBinder修饰的方法,可以对WebDataBinder 对象进行初始化,WebDataBinder是DataBinder的子类,
用于在数据绑定流程中完成由表单字段到javabean属性的绑定
@InitBinder所修饰的方法不能含有返回值,必须是void
@InitBinder的参数类型通常是WebDataBinder.例如:
@InitBinder
public void initBinder(WebDataBinder binder){
binder.setDisallowedFields("lastName");//该方法的作用是对lastName不执行绑定的操作.
}
13:数据的格式化
一般数据的格式化是和数据类型的转换是一起执行的,但是如果我们的表单中有固定的输入格式,比如生日:
yyyy-MM-dd,工资是123,456,78.9等.我们输入的是一个字符串,但是我们却需要将其装换成其他的格式.
我们可以使用相应的注解在javabean对应的属性上.
@DataTimeFormat(patten="yyyy-MM-dd")//我们可以通过patten指定我们日期字符串的输入格式
@NumberFormat(patten="#,###,##.#")//#是数字的占位符,我们指定数字字符串的输入格式.
内部原理:
对对象属性的输入和输出进行格式化,从本质上来说依然属于类型转换的范畴,Spring在其格式化模块中
定义了一个类,FormattingConversionService,该类实现了ConversionService接口,并且拓展了
GenericConversionService接口,因此,它即具有类型转换的功能,也具有格式化的功能.
FormattingConversionService拥有一个FormattingConversionServiceFactoryBean,后者用于在
Spring上下文中创建前者.
如果我们即想使用自定义的类型转换器,还想使用Spring提供的默认的格式化的功能,我们就可以在
Spring容器中使用FormattingConversionService.
14:数据的校验:
我们使用的验证的标准就是JSR 303标准,它是java 为Bean数据合法性校验提供的标准框架,它已经包含在javaEE6.0中
JSR 303通过在Bean属性上标注@NotNull,@Max等标准的注解指定校验规则,并通过标准的Bean验证接口对Bean进行验证
常用的注解有:
注解: 功能:
@Null 被注解的元素必须为:Null
@NotNull 被注解的元素必须不为Null
@AssertTrue 被注解的元素必须为:true
@AssertFalse 被注解的元素必须为:false
@Min(value) 被注解的元素必须为一个数字,其值必须大于等于指定的值
@Max(value) 被注解的元素必须为一个数字,其值必须小于等于指定的值
@DecimalMin(value) 被注解的元素必须为一个数字,其值大于等于指定的最小值
@DecimalMax(value) 被注解的元素必须为一个数字,其值小于等于指定的最大值
@Size(max,min) 被注解的元素大小必须在指定的范围内
@past 被注解的元素必须为:一个过去的日期
@future 被注解的元素必须为:一个将来的日期
@Pattern(value) 被注解的元素必须符合指定的正则表达式
其中:Hibernate Validator是对JSR 303的一个参考实现,除了支持所有的标准校验注解外,还支持以下注解
@Email 被注释的元素必须是电子邮箱地址
@Length 被注释的字符串的大小必须在指定的长度内
@NotEmpty 被注释的字符串必须非空
@Range 被注释的元素必须在合适的范围内
对于数据的校验我们主要从以下三个方面进行学习:
1>如何校验:
1:首先我们的Bean相应的属性应该被JSR 303所修饰,例如:@Past,@Email
2:导入Hibernate Validator相应的jar包,
3:在SpringMVC中添加注解驱动
4:在目标方法的Bean类型的参数中添加@Valid注解即可.
5:如果报错500,请用hibernate中的EL的jar包替换掉Tomcat中对应的jar包.
2>验证出错时应该转向哪一个页面:
注意:对表单或者是Bean的校验的结果是保存在目标方法的参数当中,这个参数的类型必须是
BindingResult或者Errors,如果需要校验的Bean对象和其绑定结果对象或者错误对象是成对出现的,
他们必须紧挨着,之间不能出现其他的参数
我们可以通过对BindingResult的错误数量进行判断,然后可以转发到我们需要的页面,甚至完成表单的回显
3>如何把错误信息进行国际化:
错误消息的显示,比较简单,只需要使用
SpringMVC中,可以直接通过注解驱动的方式完成数据的校验,Spring的LocalValidatorFactoryBean即实现了
Validator接口,也实现了JSR 303的Validator接口,,所以我们只要在Spring容器中定义一个LocalVlidator
FactoryBean,即可将其注入到需要数据校验的bean,Spring本身并没有提供对JSR 303的实现,所以需要导入相应的jar包
15:返回json格式的数据
16:关于国际化
1:能够根据浏览器的语言设置对文本,时间,数值进行本地化处理.
2:可以在Bean中获取国际化资源文件Locale对应的消息.
3:可以通过超链接切换Locale,而不再依赖于浏览器的语言设置情况.
解决方案:
1:使用jstl的fmt标签
2:在Spring容器中配置ResourceBundleMessageSource(国际化资源文件),使用其对应的getMessage方法
3:使用LocalResolver和LocalChangeIntercepter
17:文件的上传
SpringMVC中,默认情况下是没有装配任何文件上传的组件中如果想使用文件上传的功能,需要在上下文中配置
MultiPartResolver,主要是使用它的实现类CommonsMultipartResolver,
为了让CommonsMultipartResolver正常工作,我们必须将 CommonsFileUpload和
Commons io的jar包添加到类路径下.
defaultEncoding必须要和JSP页面的PageEncoding 的值保持一致,以便正确的解析表单的内容.
具体步骤如下:
1:导入相应的依赖Commons下的Upload和IO
2:在Spring容器中配置CommonsMultipartResolver.
3:编写上传的表单:
18:自定义的拦截器
自定义的拦截器必须要实现HandlerInterceptor接口,首先应该在容器中注册,然后重写三个方法.
@Component
public class FirstInterceptor implements HandlerInterceptor {
//该方法会在目标方法执行之前进行调用,若返回true,则继续执行后续的拦截器和目标方法
//若返回false,则不会调用后续的拦截器和目标方法,一般做关于权限,事务,日志等
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
return false;
}
//调用目标方法之后,但是在渲染视图之前执行,可以对请求域中的属性或视图进行修改.
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
}
//调用目标方法之后,但是在渲染视图之后执行,一般执行释放资源的操作.
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
}
preHandle >>>> 目标方法 >>>> postHandle >>>> 渲染视图 >>>> afterHandle
我们还可以通过
}
19:异常处理器:
当数据的绑定,Handler映射和目标方法执行时发生异常,SpringMVC提供了HandlerExceptionResolver处理.
DispatcherServlet默认装配了HandlerExceptionResolver,当使用的
装配了DefaultHandlerExceptionResolver,ResponseStatusExceptionResolver,ExceptionHandlerExceptionResolver