SpringMVC的深入探讨
来源:互联网 发布:男性网络英文名字大全 编辑:程序博客网 时间:2024/05/17 07:26
### 需要格外注意的几个地方:
1.SpringMVC 严格的处理,拦截到的请求,如果没有映射的,都会报错 !!!
2.希望直接跳转到 WEB-INF/下的某个jsp 而不通过控制器,需要配置
<mvc:view-controller .........>
但是一旦配置,@RequestMapping() 无效了 ,全部访问不到
此时加上 <mvc:annotation-driven/>即可 ,两种访问方式 都正常
3. 使用REST风格 拦截所有请求 js css 等资源时候
配置 <mvc:default-servlet-handler/>即可(没有映射的请求交给web服务器处理tomcat ) ,但是同理 @RequestMapping()再次实效 ,fuck啊
此时 加上<mvc:annotation-driven/> 即可
4.增删该查操作, list,addUI editUI(回显) 请求 都使用GET方式提交
5. 转换 POST 表单时候,<input type="hidden" name="_method" value="PUT"/>
这里没有使用SpringMVC标签 <form:input path="_method" value="PUT"/> //是因为 标签是用来回显的,会默认在 "command" 找此属性,没有就报错了
6. SpringMVC 在使用修改操作,一般 要 用到 @ModelAttribute 注解
7. 蛋疼的问题, 路径问题.
SpringMVC 的 addUI.jsp 和editUI.jsp(saveUI.jsp) 也是使用同一个页面 input.jsp
<form:form action = "emp" method="POST" modelAttribute="employee">
添加页面访问 handler 操作正常, url = localhost:8080/sprinvmc/emp
修改页面访问 却变为 url = localhost:8080/sprinvmc/emp/emp
</form:form>
使用Struts2用到的是动态Action ,故 没有此问题,
故添加和修改的url 还是写为 action="${pageContext.request.contextPath}/emp" method="" 为妙
分析: 原因 请求转发 在作怪 (不是SpringMVC的问题哦,我们需要了解事物本质,而不是不明白就怪 Spring 蛋疼 )
因为我们在
访问 "修改页面" 时候 url : localhost:8080/web/emp{id} GET 跳转到 editUI.jsp( 但是url地址栏因为是请求转发:仍为localhost:8080/web/emp)
访问 editUI---> "修改" 时候 url : action="emp" ---->(相对路径,相对谁的路径,当然是当前页面的路径)即
localhost:8080/web/emp/emp
8.
打开 tomcat下 web.xml
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
调试技巧:
##############
// 如何查看执行流程 : debug ;
在某个方法上面打断点 ,刷新浏览器,
发现 Java处理 异常 包括执行打印顺序 一致
public class Test {
public static void main(String[] args) {
test();
}
public static void test(){
int a = 9/0;
}
}
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.gs.test.Test.test(Test.java:28) //-------------------->最上面的可能是引起异常的地方
at com.gs.test.Test.main(Test.java:17)
main 函数 调用 test()方法时候出现了异常,打印出来,test()自己没有处理,抛给调用者
main(), main里面也没有处理,然后又抛出
debug 追踪栈 :
// -------------------------->最上面的(栈顶) 是 1.最后正 执行的 方法体,一层一层向下 是 2 “其调用者 ”-------> 3."调用者的调用者 "
单步跳过-------> 在当前调试指针跳到下一行,但是此行还未执行
我们在看源代码使用 第三方jar 如SpringMVC的时候,一旦debug启动会打印很多的栈,可能是SpringMVC
########
Spring url-pattern
/* 和 /的区别
测试发现
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<!-- / 拦截所有以 / 结尾的请求,*.action 拦截所有以.action 结尾的请求,*.html拦截所有以.html结尾的请求 -->
<url-pattern>/*</url-pattern>
</servlet-mapping>
/* 拦截 所有请求
/ 拦截所有请求 .css .jpg .. .action ,但是不拦截 .jsp
1. 技巧之 常用的DTD,DispatcherServlet 中 xml的配置 加入到 快捷内容里面
2. a。@Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入;
b.@Autowired默认是按照类型装配注入的,如果想按照名称来转配注入,则需要结合@Qualifier一起使用;
c.@Resource注解是又J2EE提供,而@Autowired是由Spring提供,故减少系统对spring的依赖建议使用
@Resource的方式;
d. @Resource和@Autowired都可以书写标注在字段或者该字段的setter方法之上
两个注解 却没有使用 setter 方法,Spring强大的处理 ,普通注入是需要写setter方法的
3. SpringMVC 的viewResolver 特别类似于 Struts2 的 <result name=" ">/WEB-INF/jsp/list.jsp</result>
视图解析器 :
注解里面是 键值对 -->
我感觉
@Resource
public class UserService
-->等价 @Resource("userService")
-->等价 @Resource(value="userService")
@ExceptionHandler({"ArithmeticException.class"})
-->等价 @ExceptionHandler(value={"ArithmeticException.class"})
======================>
# SpringMVC 异常体系1
使用注解方式:(前提 扫描包)
/**
* 1. 在 @ExceptionHandler 方法的入参中可以加入 Exception 类型的参数, 该参数即对应发生的异常对象
* 2. @ExceptionHandler 方法的入参中不能传入 Map. 若希望把异常信息传导页面上, 需要使用 ModelAndView 作为返回值
* 3. @ExceptionHandler 方法标记的异常有优先级的问题.(如下面两个异常同时存在,抛出的是第二个,因为第二个匹配度高 )
*/
定义异常, 当发生此异常时候,自动 调用下面的方法 ;(只在本类 非全局 里面有效 ,很像Struts2 )
@ExceptionHandler({RuntimeException.class})
public ModelAndView handleArithmeticException2(Exception ex){
System.out.println("[出异常了]: " + ex);
ModelAndView mv = new ModelAndView("error");
mv.addObject("exception", ex);
return mv;
}
@ExceptionHandler({ArithmeticException.class})
public ModelAndView handleArithmeticException(Exception ex){
System.out.println("出异常了: " + ex);
ModelAndView mv = new ModelAndView("error");
mv.addObject("exception", ex);
return mv;
}
@RequestMapping("/testExceptionHandlerExceptionResolver")
public String testExceptionHandlerExceptionResolver(@RequestParam("i") int i){
System.out.println("result: " + (10 / i));
return "success";
}
======》定义全局异常 单独定义一个异常控制器类 注解是 @ControllerAdvice
* 4. @ControllerAdvice: 如果在当前 Handler 中找不到 @ExceptionHandler 方法来出来当前方法出现的异常,
* 则将去 @ControllerAdvice 标记的类中查找 @ExceptionHandler 标记的方法来处理异常.
package com.atguigu.springmvc.test;
@ControllerAdvice
public class SpringMVCTestExceptionHandler {
@ExceptionHandler({ArithmeticException.class})
public ModelAndView handleArithmeticException(Exception ex){
System.out.println("----> 出异常了: " + ex);
ModelAndView mv = new ModelAndView("error");
mv.addObject("exception", ex);
return mv;
}
}
# 异常体系2
ResponseStatusExceptionResolver 类 --> 处理 @ResponseStatus注解
@注解其实也是一个类或接口
@ResponseStatus 注解
打开 此类 ResponseStatus
public @interface ResponseStatus{
HttpStatus value(); // 状态码 如404 ,403 ...
String reason(); // 返回结果
// JavaDoc上面 Mark a method or exception class 可以标记一个方法或一个异常类
}
注意:(方法上或 异常 类)@ResponseStatus注解,就 会被SpringMVC的ResponseStatusExceptionResolver处理(总控制器调用的视图解析器 ),
且,打来ResponseStatusExceptionResolver的源代码发现,有异常时候,ResponseStatusExceptionResolver返回页面(状态码 和 信息)
无异常时候,只返回给页面信息.
例子:
@ResponseStatus(value=HttpStatus.FORBIDDEN, reason="用户名和密码不匹配!")
public class UserNameNotMatchPasswordException extends RuntimeException{
/**
*
*/
private static final long serialVersionUID = 1L;
}
@ResponseStatus(value=HttpStatus.FORBIDDEN, reason="用户名和密码不匹配!")
public class UserNameNotMatchPasswordException extends RuntimeException{
/**
*
*/
private static final long serialVersionUID = 1L;
}
# 异常体系3
DefaultHandlerExceptionResolver (视图解析器)
对一些特殊的异常进行处理, 比如:
NoSuchRequestHandlingMethodException
HttpRequestMethodNotSupportedException
HttpMediaTypeNotSupportedException
HttpMeidaTypeNotAcceptableException 等
这个异常是系统默认的;出现错误 就会执行
#异常体系4
SimpleMappingExceptionResolver
源代码分析:
打开SimpleMappingExceptionResolver ->
一步一步追踪,发现它处理异常和 #异常体系1# 中方式一样,返回ModelAndView,
把异常信息放入 域对象中, 其中一段
mv.addObject(this.exceptionAttribute,(Exception)ex);return mv;
this.exceptionAttribute 打开发现 值 ="exception";
故通过前台jsp ${exception} 可以打印出来
但是我们想改变 域对象中的 键值对 ${ex} 打印
仅需:
<!-- 配置使用 SimpleMappingExceptionResolver 来映射异常 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionAttribute" value="ex"></property> // ############################ 配置这里即可 ####################
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop> // xx.properties, 集中处理异常. key="异常全类名 "; error是视图名 error.jsp
</props>
</property>
</bean>
======================================== end =================================
## 在控制器的类定义及方法定义处都可标注
@RequestMapping
– 类定义处:提供初步的请求映射信息。相对于 WEB 应用的根目录
– 方法处:提供进一步的细分映射信息。相对于类定义处的 URL。若
类定义处未标注 @RequestMapping,则方法处标记的 URL 相对于
WEB 应用的根目录
• DispatcherServlet 截获请求后,就通过控制器上
@RequestMapping 提供的映射信息确定请求所对应的处理
方法。
## 用的不多
@RequestMapping 除了可以使用请求 URL 映射请求外,
还可以使用请求方法、请求参数及请求头映射请求
• @RequestMapping 的 value、method、params 及 heads
分别表示请求 URL、请求方法、请求参数及请求头的映射条
件,他们之间是与的关系,联合使用多个条件可让请求映射
更加精确化。
• params 和 headers支持简单的表达式:
– param1: 表示请求必须包含名为 param1 的请求参数
– !param1: 表示请求不能包含名为 param1 的请求参数
– param1 != value1: 表示请求包含名为 param1 的请求参数,但其值
不能为 value1
– {“param1=value1”, “param2”}: 请求必须包含名为 param1 和param2
的两个请求参数,且 param1 参数的值必须为 value1
##
REST 风格的增删改查
浏览器现在仅仅支持 GET POST 请求,Spring3.0提供了HiddenHttpMethodFilter
可以将这些请求转换为标准的http方法,使得支持GET Post PUT DELETE
查看源代码:
public class HiddenHttpMethodFilter extends OncePerRequestFilter {
/** Default method parameter: {@code _method} */
public static final String DEFAULT_METHOD_PARAM = "_method";
private String methodParam = DEFAULT_METHOD_PARAM;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String paramValue = request.getParameter(this.methodParam);
if ("POST".equals(request.getMethod()) && StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
HttpServletRequest wrapper = new HttpMethodRequestWrapper(request, method);
filterChain.doFilter(wrapper, response);
}
else {
filterChain.doFilter(request, response);
}
}
配置此filter : web.xml
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
例子:
GET请求
<a href="springmvc/testRest/1"/>
PUT 请求 (更新)
<form action="springmvc/testRest/1" method="post"> //删除
<input type="hidden" name="_method" value="PUT" /> // 会被 HiddenHttpMethodFilter 转换为PUT 请求
<input type="submit" value="测试"/>
</form>
POST 新增
<form action="springmvc/testRest" method="post"> //删除
<input type="submit" value="测试"/>
</form>
DELETE 请求
<form action="springmvc/testRest/1" method="post"> //删除
<input type="hidden" name="_method" value="DELETE" /> // 会被 HiddenHttpMethodFilter 转换为DELETE 请求
<input type="submit" value="测试"/>
</form>
public class SpringMVCTest{
/**
Rest 风格的URL
以crud为例:
/order POST
/order/1 PUT update?id=1
/order/1 GET get?id=1
/order/1 DELETE delete?id=1
*/
@RequestMapping(value="/testRest/{id}",method=RequestMethod.PUT)
public String testRestPut(@PathVariable Integer id ){
System.out.println("testRest Put" + id );
}
@RequestMapping(value="/testRest/{id}",method=RequestMethod.DELETE)
public String testRestDELETE(@PathVariable Integer id ){
System.out.println("testRest DELETE" + id );
}
}
/**
* Rest 风格的 URL. 以 CRUD 为例: 新增: /order POST 修改: /order/1 PUT update?id=1 获取:
* /order/1 GET get?id=1 删除: /order/1 DELETE delete?id=1
*
* 如何发送 PUT 请求和 DELETE 请求呢 ? 1. 需要配置 HiddenHttpMethodFilter 2. 需要发送 POST 请求
* 3. 需要在发送 POST 请求时携带一个 name="_method" 的隐藏域, 值为 DELETE 或 PUT
*
* 在 SpringMVC 的目标方法中如何得到 id 呢? 使用 @PathVariable 注解
*
*/
注意:
@RequestMapping(value="/testRest/{id}",method=RequestMethod.DELETE)
public String testRestDELETE(@PathVariable Integer id ){
System.out.println("testRest DELETE" + id );
}
// @PathVariable 绑定的不是请求参数哦 而是url 的占位符 -->赋值给Integer id 了
##
@RequestParam 可以绑定“请求参数”
<a href="testRequestParam?username=wangli"></a>
public String testRequestParam(@RequestParam(value="username") String uname ) {
//<a href="testRequestParam"></a> 会报错,因为前台没传username 参数
}
// @RequestParam(value="username") ---->等价 @RequestParam("username")
public String testRequestParam(@RequestParam(value="username",required=false) String uname ) {
// 改为此即可 ,那么可以不传 username 参数
}
public String testRequestParam(@RequestParam(value="age",required=false,defaultValue="0") int age ) {
//defaultValue="0" 注意,这个必须设,如果前台没有传入age 参数,那么int age是不能接受null 的,当然Integer age也可以,就不需要defaultValue了
##
@CookieValue
得到客户端的cookie (因为浏览器提交表单参数会默认带一个cookie:即JSESSIONID:xxx )
故 这里测试后台获取前台浏览器JSESSIONID的值 (也可以得到其他cookie哦 )
public String testCookie(@CookieValue("JSESSIONID") String sessionId){
System.out.println(sessionId);
return "success";
}
## 使用POJO 作为 参数
SpringMVC支持级联属性
Address 类; 省略
User user {
private String username;
private String password;
private Address address;
.. setter getter ..
}
表单
<input type="text" name="username"/>
<input type="text" name="password"/>
<input type="text" name="address.city"/>
<input type="text" name="address.provice"/>
public String handler(User user){
return "success";
}
## SpringMVc支持 Servlet原生API
HttpServletRequest
HttpServletResponse
HttpSession
java.security.Principal
InputStream
OutputStream
Reader
Writer
Locale
public void testServletAPI(Writer out,HttpServletRequest request){
out.write("hello SpringMVC ");
}
## 处理模型数据
SpringMVC
1.ModelAndView ,通过此对象添加模型数据 域对象
2.Map 及 Model org.springframework.ui.Model,org.springframework.ui.ModelMap java.util.Map
3. @SessionAttributes 将模型中某个属性暂存到HttpSession中;
4.@ModelAttribute; 方法入参标注该注解后 入参的对象就会放到数据模型中
控制器处理方法的返回值如果为 ModelAndView, 则其既
包含视图信息,也包含模型数据信息。
• 添加模型数据:
– MoelAndView addObject(String attributeName, Object
attributeValue)
– ModelAndView addAllObject(Map<String, ?> modelMap)
• 设置视图:
– void setView(View view)
– void setViewName(String viewName)
public ModelAndView testModelAndVie(){
String viewname = "success";
ModelAndView modelAndView = new ModelAndView(viewname);
modelAndView.addObject("time",new Date());
return modelAndView;
}
#######
Spring MVC 在内部使用了一个
org.springframework.ui.Model 接口存
储模型数据
• 具体步骤
– Spring MVC 在调用方法前会创建一个隐
含的模型对象作为模型数据的存储容器。
– 如果方法的入参为 Map 或 Model 类
型,Spring MVC 会将隐含模型的引用传
递给这些入参。在方法体内,开发者可以
通过这个入参对象访问到模型中的所有数
据,也可以向模型中添加新的属性数据
例子:
public String testMap(Map<String,Object> map ){
map.put("names",Arrays.asList("Tom","wangli","Mike"));
return "success";
}
### @SessionAttributes (此注解只能放在类上面 )
注解 :
public @interface SessionAttributes{
String[] value() default{};
Class<?>[] types() default{};
}
例子:
@SessionAttributes({"user"}) //2. @SessionAttributes(value={"user"},types={String.class}) // 此时
@Controller
public class SpringMVCTest{
@RequestMapping("/testSesisionAttributes")
public String testSesisionAttributes(Map<String,Object> map){
User user = new User("tom","123");
map.put("user",user); // 此时不仅放在request里面,也会放在session里 jsp: ${requestScope.user.username} ${sessionScope.user.username}
map.put("school","schoolString ");// 上面@ 注解 即使用了属性名指定,也可以通过对象类型class
return "success";
}
}
####### ModelAttribute的使用
update 操作是有两种情况的:
{
User.class id,username,birthday(Date)
1. <form>
<input type="hidden" name="id" value="1"/>
<input type="text" name="username"></input>
</form>
后台直接更新
User user = new User(); // 接收前台参数,但是第三个参数没有传入,birthday=null;
session.update(user); // 这种更新,本来不希望修改第三个字段,现在一下置 为null了
2. User user = userService.getById(model.getId());// 先从数据库取出数据
然后对此user 修改,更新
session.update(user); // ------->字段3 无影响
}
例子:
public SpringMVCTest{
// 如果在某个类里面都有此方法拦截,那么增删该查更加容易,只要前台传入id ,直接操作此对象,SpringMVC神来之笔 ----->
@ModelAttribute, 会在本类中每个方法前都调用,相当于拦截器
public void getUser(@RequestParam("id") Integer id,Map<String,Object> map){
if(id!= null){
// 模拟从数据库中取到对象
User user = userService.getById(id);
map.put("user",user);//
}
}
@RequestMapping("/testModelAtribute")
public String testModelAtribute(User user){
System.out.println("修改后:"+user);
return "success";
}
//上面的定义的一个方法上面@ModelAttribute就像一个拦截器一样,先被执行,里面已经存在参数里(数据库里面的对象,且放入域对象中.)
然后 我们访问/testModelAtribute url时候,SpringMVC 做的处理应该是
取出内存中的User对象(应该和属性名无关push(obj).),把表单数据setter进这个对象.
我们发现参数里面的(User user)没有 new 就使用了,说明Spring帮我们创建好的对象..
实验证实确实如此 ,User对象 和 引用名 没有关系 ,操作的都是同一个对象
具体分析:
//执行流程
/**
* 1. 有 @ModelAttribute 标记的方法, 会在每个目标方法执行之前被 SpringMVC 调用!
* 2. @ModelAttribute 注解也可以来修饰目标方法 POJO 类型的入参, 其 value 属性值有如下的作用:
* 1). SpringMVC 会使用 value 属性值在 implicitModel 中查找对应的对象, 若存在则会直接传入到目标方法的入参中.
* 2). SpringMVC 会以 value 为 key, POJO 类型的对象为 value, 存入到 request 中.
*/
* 注意: 在 @ModelAttribute 修饰的方法中, 放入到 Map 时的键需要和目标方法入参类型的第一个字母小写的字符串一致!
*
* SpringMVC 确定目标方法 POJO 类型入参的过程
* 1. 确定一个 key:
* 1). 若目标方法的 POJO 类型的参数木有使用 @ModelAttribute 作为修饰, 则 key 为 POJO 类名第一个字母的小写
* 2). 若使用了 @ModelAttribute 来修饰, 则 key 为 @ModelAttribute 注解的 value 属性值.
* 2. 在 implicitModel 中查找 key 对应的对象, 若存在, 则作为入参传入
* 1). 若在 @ModelAttribute 标记的方法中在 Map 中保存过, 且 key 和 1 确定的 key 一致, 则会获取到.
* 3. 若 implicitModel 中不存在 key 对应的对象, 则检查当前的 Handler 是否使用 @SessionAttributes 注解修饰,
* 若使用了该注解, 且 @SessionAttributes 注解的 value 属性值中包含了 key, 则会从 HttpSession 中来获取 key 所
* 对应的 value 值, 若存在则直接传入到目标方法的入参中. 若不存在则将抛出异常.
* 4. 若 Handler 没有标识 @SessionAttributes 注解或 @SessionAttributes 注解的 value 值中不包含 key, 则
* 会通过反射来创建 POJO 类型的参数, 传入为目标方法的参数
* 5. SpringMVC 会把 key 和 POJO 类型的对象保存到 implicitModel 中, 进而会保存到 request 中.
*
* 源代码分析的流程
* 1. 调用 @ModelAttribute 注解修饰的方法. 实际上把 @ModelAttribute 方法中 Map 中的数据放在了 implicitModel 中.
* 2. 解析请求处理器的目标参数, 实际上该目标参数来自于 WebDataBinder 对象的 target 属性
* 1). 创建 WebDataBinder 对象:
* ①. 确定 objectName 属性: 若传入的 attrName 属性值为 "", 则 objectName 为类名第一个字母小写.
* *注意: attrName. 若目标方法的 POJO 属性使用了 @ModelAttribute 来修饰, 则 attrName 值即为 @ModelAttribute
* 的 value 属性值
*
* ②. 确定 target 属性:
* > 在 implicitModel 中查找 attrName 对应的属性值. 若存在, ok
* > *若不存在: 则验证当前 Handler 是否使用了 @SessionAttributes 进行修饰, 若使用了, 则尝试从 Session 中
* 获取 attrName 所对应的属性值. 若 session 中没有对应的属性值, 则抛出了异常.
* > 若 Handler 没有使用 @SessionAttributes 进行修饰, 或 @SessionAttributes 中没有使用 value 值指定的 key
* 和 attrName 相匹配, 则通过反射创建了 POJO 对象
*
* 2). SpringMVC 把表单的请求参数赋给了 WebDataBinder 的 target 对应的属性.
* 3). *SpringMVC 会把 WebDataBinder 的 attrName 和 target 给到 implicitModel.
* 近而传到 request 域对象中.
* 4). 把 WebDataBinder 的 target 作为参数传递给目标方法的入参.
*/
}
注意##########注意: 如果实在不太清楚源代码的流程 ,且同时使用
容易 出现异常:是因为在访问url @RequestMapping("/hello")时候,取出域对象找不到.)))) 没有指定放入域对象中 键-值 (1.没有指定 键名,且map.put("写的不是User类的小写",user)),默认根据"小写user"找的,没找到。2.根据指定的名称找,又没找到 3. 最后 直接去@SessionAttributes 中查找出现错误的,明确指定就不会了
@SessionAttributes("形参属性名") // 修饰目标方法形参 或类型
和@ModelAttribute 同时使用时,那么 map("user(User类的小写字母)",user); 即可
Demo1:
@Controller
@SessionAttributes("u")
public class UserAction {
@ModelAttribute
public void getUser(@RequestParam(value="id",required=false) Integer id , Map<String,Object> map){
System.out.println("-------->拦截请求<------------");
// 模拟从数据库中取出User对象
if(id!=null){
User user = new User(3,"王立","3352@qq.com");
map.put("user", user);
}
}
@RequestMapping("/hello")
public String hello(User u ){ // 或写为:-------> public String hello(@ModelAttribute("user") User u )
System.out.println("----------->hello");
System.out.println(u);
return "success";
}
}
Demo2 :
@Controller
@SessionAttributes("u")
public class UserAction {
@ModelAttribute
public void getUser(@RequestParam(value="id",required=false) Integer id , Map<String,Object> map){
System.out.println("-------->拦截请求<------------");
// 模拟从数据库中取出User对象
if(id!=null){
User user = new User(3,"王立","3352@qq.com");
map.put("abc", user);
}
}
@RequestMapping("/hello")
public String hello(@ModelAttribute("abc") User u ){
System.out.println("----------->hello");
System.out.println(u);
return "success";
}
}
Demo3 :
找不到 abc ,
@Controller
@SessionAttributes("u") //作用 ,把请求request域对象中 参数放入同时放入session中 .
public class UserAction {
@ModelAttribute
public void getUser(@RequestParam(value="id",required=false) Integer id , Map<String,Object> map){
System.out.println("-------->拦截请求<------------");
// 模拟从数据库中取出User对象
if(id!=null){
User user = new User(3,"王立","3352@qq.com");
map.put("abc", user);
}
}
@RequestMapping("/hello")
public String hello(User uuuu ){ (由于@SessionAttributes("u")原因 ) 抛出异常 --------> // 1.没有指定,按照User类小写"user" 没有找到域对象,2. 没有指定根据"键" 找 3.到 @SessionAttributes("u")根据 “u”找,也没有找到对应的对象,抛出异常
System.out.println(u);
return "success";
}
}
Demo4 :
@Controller
@SessionAttributes("u")
public class UserAction {
@ModelAttribute
public void getUser(@RequestParam(value="id",required=false) Integer id , Map<String,Object> map){
System.out.println("-------->拦截请求<------------");
// 模拟从数据库中取出User对象
if(id!=null){
User user = new User(3,"王立","3352@qq.com");
map.put("user", user);
map.put("love","I love you ");
}
}
@RequestMapping("/hello")
public String hello(User u ){ // --------->等价于 public String hello(@ModelAttribute("user") User u ); //且 @ModelAttribute的作用之一就是默认把"user"放入request域对象中,故前台可以直接${user.name},无需再次map.put("user",user);
System.out.println(u); // ---------------->发现2 ,前台不仅可以取出${user} , 且可以取出 @ModelAttribute 栈里面所有 属性 ${love },猜测: SpringMVC 把ModelAttribute栈里所有键值都放到了一个新 栈 里;
return "success";
}
}
重点:: Session --------->异常再 分析:
1. 知道了Model 数据Model 绑定的流程, 即使不写
public String hello(User u )
默认 --> public String hello (@ModelAttribute("user") User user ) // 控制器就是根据 这个字符串 "user" 去域对象中找的, 【横插一刀 @ModelAttribute】 request.getAttribute("user")没找到对象 ---->session.getAttribute("user")没有此对象,异常
如果没有配置
/*
@ModelAttribute
public void getTxxx(){
.........
map.put("user",user);
}
*/
但配置了 SessionAttributes("user") ,那么就会去找,没找到就异常了 .
2. 一般没有@SessionAttributes()注解,或者@SessionAttributes("名称") 和 "user" 不一致。 就不会出现那种异常
最终 解决方案:
1.配置 @ModelAttribute方法 即可 (拦截 请求,令 域对象中 存在 "user":" 值 ") 存在
2. 去掉 @SessionAttributes()注解
3. @SessionAttributes("notuserString")注解 ,名称和 public String hello (@ModelAttribute("user") User user ) 不一样即可 .........
其实 形参(String id ,String name,User user)--->都是有默认注解的.
就像Struts2 值栈 接收参数一样.;
SpringMVC形参没有写注解时候前台可以不传参数
写了注解后 ,没有指定defaultValue="",required=false; 则必须传入值 ;
################
25 #### 国际化
只要把jstl(jstl.jar,standard.jar)放入到 lib 类路径下 下面,那么Spring自动把
View 转换为 JstlView,
国际化
放在src下面
i18n.properties
内容:
i18n.username=Username
i18n.password=Password
i18n_zh_CN.properties
内容:
i18n.username=用户名
i18n.password=密码
i18n_en_US.properties
i18n.username=Username
i18n.password=Password
jstl国际化使用的是
<fmt:message key="i18n.username"></fmt:message>标签
<fmt:message key="i18n.password"></fmt:message>标签
Spring里面配置国际化;
springmvc.xml 中
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="i18n"></property>
</bean>
26.#####
SpringMVC 确实强大
在 Web-Inf 下面的 资源jsp文件。我们希望通过index.jsp --->success.jsp 希望直接连过去,而不想通过Controller
springmvc.xml 中配置 直接转发的页面
<mvc:view-controller path="/success" view-name="success"/>
实际开发中 通常都需要配置 mvc:annotation-driven标签
(当然没有配置<mvc:view-controller> 的情况下可以不配置<mvc:annotation-driven/>)
问题是,一旦配置<mvc:view-controller,那么 @RequestMapping()注解就不起作用了.
27.####
自定义视图:(有点像jtsl的 自定义标签:jstl标签只针对某个标签,而自定义视图针对整个返回客户端的 页面)
excel,word........pdf.等视图
此时使用
<!-- 配置视图解析器: 如何把 handler 方法返回值解析为实际的物理视图 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
// 此视图解析器 查看源代码: 有一个属性是 private int order = Integer.MAX_VALUE; 此值是指定各个视图解析器的优先级问题..
</bean>
解析器不行 ------>
步骤1: 配置
<!--使用视图的名字解析视图 -->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order">100</property> // 优先级是100, 值越小,优先级越大(先执行),所以根据上面InternalResourceViewResolver的优先级,知道只要配置自定义view解析器的order属性,那么就会先执行 这个视图解析器
</bean>
翻看源码:
BeanNameViewResolver.class
public View resolveViewName(String viewName,Locale locale){
ApplicationContext context=getApplicationContext();
if(!context.containBean(viewName)){
return null;
}
return context.getBean(viewName,view.class);// 从这里看出这个视图解析器 是根据Spring容器中的视图的<bean id="viewName"> 找到并返回的view
}
故 仅仅需要
步骤2:
定义view ,随便起个包名.
@Component
public class HelloView implements View{
@Override
public String getContextType(){
return "text/html";
}
@Override
public void render(Map<String,?) model,HttpServletRequest request,HttpServletResponse response ){
response.getWriter().print("hello wangli ,time"+new Date());
}
}
步骤3: 使用
@RequestMapping("/testMyView")
public String testMyView(){
return "helloView"; // -->返回 "helloView"字符串后,先经过BeanNameViewResolver解析器解析,去Spring容器中找 "helloView",找到即返回视图.
}
====================>
那么 我们自定义的HelloView.class 里面可以写任何想要返回的视图,特别简单 .
如 jfreeChart ,excel,word ,pdf .........
如想要实现Excel,使用SpringMVC特别简单 ..
@Component("excelView")
public class ExcelView implements AbstractExcelView{
// 实现 ...........
}
• 若希望使用 Excel 展示数据列表,仅需要扩展
SpringMVC 提供的 AbstractExcelView 或
AbstractJExcel View 即可。实现 buildExcelDocument()
方法,在方法中使用模型数据对象构建 Excel 文档就可以
了。
• AbstractExcelView 基于 POI API,而
AbstractJExcelView 是基于 JExcelAPI 的。
• 视图对象需要配置 IOC 容器中的一个 Bean,使用
BeanNameViewResolver 作为视图解析器即可
• 若希望直接在浏览器中直接下载 Excel 文档,则可以设置
响应头 Content-Disposition 的值为
attachment;filename=xxx.xls
28.. ##
• 一般情况下,控制器方法返回字符串类型的值会被当成逻
辑视图名处理
• 如果返回的字符串中带 forward: 或 redirect: 前缀
时,SpringMVC 会对他们进行特殊处理:将 forward: 和
########## redirect: 当成指示符,其后的字符串作为 "URL" 来处理
– redirect:success.jsp:会完成一个到 success.jsp 的重定向的操作
– forward:success.jsp:会完成一个到 success.jsp 的转发操
@RequestMapping("testRedirect")
public String testRedirect(){
return "redirect:/index.jsp"; // 重定向到 /index.jsp ,注意,这里的字符串被被当做 url 处理,不会加前后缀 了
}
@RequestMapping("testForward")
public String testRedirect(){
return "forward:/index.jsp"; // 请求 转发 到 /index.jsp ,注意,这里的字符串被被当做 url 处理,不会加前后缀 了
}
上面执行的视图解析器是什么呢:
UrlBasedViewResolver.class
if(viewName.startWith(REDIRECT_URL_PREFIX)){
String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length);
..........
}
if(viewName.startWith(FORWARD_URL_PREFIX)){
String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length);
..........
}
29 ###############################################
REST 风格 的增删该查
crud 操作
SpringMVC 的标签库 也可以回显
如表单标签 ,同struts一样 如 :
<form:form action="emp" method="POST" modelAttribute="employee"> // 这个必须指定,否则默认值是 "command"
<form:input path="lastname"/> // 相当于 <input type="text" name="lastname" value="${employee.lastname}"/> jstl 没有此属性,不会报错,顶多不显示
<form:input path="password"/>
</form:form>
// 这些标签会回显 ,但是注意了,和struts2一样,这些属性 -->存在才行
// 最好使用jstl 标签
Action 中 !!
ActionContext.getContext().put("employee",employee);
Controller
map.put("employee",employee);
#############
问题1:
• 优雅的 REST 风格的资源URL 不希望带 .html 或 .do 等后缀
• 若将 DispatcherServlet 请求映射配置为 /,则 Spring MVC 将捕获
WEB 容器的所有请求,包括静态资源的请求, SpringMVC 会将他
们当成一个普通请求处理,因找不到对应处理器将导致错误。
• 可以在 SpringMVC 的配置文件中配置 <mvc:default-servlethandler/> 的方式解决静态资源的问题:
– <mvc:default-servlet-handler/> 将在 SpringMVC 上下文中定义一个
DefaultServletHttpRequestHandler,它会对进入 DispatcherServlet 的
请求进行筛查,如果发现是没有经过映射的请求,就将该请求交由 WEB
应用服务器默认的 Servlet 处理,如果不是静态资源的请求,才由
DispatcherServlet 继续处理
– 一般 WEB 应用服务器默认的 Servlet 的名称都是 default。若所使用的
WEB 服务器的默认 Servlet 名称不是 default,则需要通过 defaultservlet-name 属性显式指定
问题2:
使用REST风格 即 POST,GET,PUT,DELETE 四种 请求,其他两种请求需要通过把 POST 请求进行转换 (隐藏表单域)
web.xml 进行转换
<!-- 配置 HiddenHttpMethodFilter: 把 POST 请求转为 DELETE、PUT 请求 -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
但是 普通 <a href="emp_delete?id=2">删除</a>
SpringMVC <a class="delete" href="emp/${emp.id}">Delete</a>
<a />仅仅支持GET, 如果删除都写一个表单,太蛋疼了 !!
方案: 只能通过jQuery 改变 了
(1).在本页面加一个表单
<a class="delete" href="emp/${emp.id}">Delete</a>
<form action="" method="POST">
<input type="hidden" name="_method" value="DELETE"/>
</form>
(2).
<script type="text/javascript">
$(function(){
$(".delete").click(function(){
var href = $(this).attr("href");
$("form").attr("action", href).submit();
return false;
});
})
</script>
问题再次出现:
dispacherServlet 拦截器配置的是 / ,此设置拦截所有请求 ,连资源文件都不放过 .
<script language="text/javascript" src="js/jquery.js"></script> 实际上也是一个请求 http://localhost:8080/sprinvmc/js/jquery.js
• 优雅的 REST 风格的资源URL 不希望带 .html 或 .do 等后缀
• 若将 DispatcherServlet 请求映射配置为 /,则 Spring MVC 将捕获
WEB 容器的所有请求,包括静态资源的请求, SpringMVC 会将他
们当成一个普通请求处理,因找不到对应处理器将导致错误。
• 可以在 SpringMVC 的配置文件中配置 <mvc:default-servlethandler/> 的方式解决静态资源的问题:
– <mvc:default-servlet-handler/> 将在 SpringMVC 上下文中定义一个
DefaultServletHttpRequestHandler,它会对进入 DispatcherServlet 的
请求进行筛查,如果发现是没有经过映射的请求,就将该请求交由 WEB
应用服务器默认的 Servlet 处理,如果不是静态资源的请求,才由
DispatcherServlet 继续处理
– 一般 WEB 应用服务器默认的 Servlet 的名称都是 default。若所使用的
WEB 服务器的默认 Servlet 名称不是 default,则需要通过 defaultservlet-name 属性显式指定
SpringMVC 严格的处理,拦截到的请求,如果没有映射的,都会报错 !!!
####### 修改操作 :
需要回显, editUI , SpringMVC使用@ModelAttribute注解
例子:
SpringMVC 修改和添加共用一个页面 :
<form:form action="${pageContext.request.contextPath }/emp" method="POST" modelAttribute="employee">
<c:if test="${employee.id == null }">//---------->如果 域对象中 employee.id 为空,默认执行添加 操作
LastName: <form:input path="lastName"/>
</c:if>
<c:if test="${employee.id != null }">
<form:hidden path="id"/>
<input type="hidden" name="_method" value="PUT"/> //---------->如果 域对象中 employee.id 不为空,执行修改操作
</c:if>
Email: <form:input path="email"/>
</form:form>
Action :
//添加 add.action
@RequestMapping(value="/emp", method=RequestMethod.POST)
public String save(@Valid Employee employee, Errors result,
Map<String, Object> map){
System.out.println("save: " + employee);
if(result.getErrorCount() > 0){
System.out.println("出错了!");
for(FieldError error:result.getFieldErrors()){
System.out.println(error.getField() + ":" + error.getDefaultMessage());
}
//若验证出错, 则转向定制的页面
map.put("departments", departmentDao.getDepartments());
return "input";
}
employeeDao.save(employee);
return "redirect:/emps";
}
//修改 edit.action
@ModelAttribute
public void getEmployee(@RequestParam(value="id",required=false) Integer id,
Map<String, Object> map){
if(id != null){
map.put("employee", employeeDao.get(id)); // 如果id不为 null ,才到数据库中获取 需要update的对象
}
}
@RequestMapping(value="/emp", method=RequestMethod.PUT)
public String update(Employee employee){
employeeDao.save(employee);
return "redirect:/emps";
}
30 .##########################
数据绑定流程:
开发中经常遇到的问题:
1.数据类型转换 (后台是 Date date类型,前台 传入的确实String )
2. 数据类型格式化 (Date yyyy-MM-dd hh:mm:ss)
3. 数据校验 (如,目前时间是2014,你的生日却写为 2015 )
1. Spring MVC 主框架将 ServletRequest 对象及目标方
法的入参实例传递给 WebDataBinderFactory 实例,以创
建 DataBinder 实例对象
• 2. DataBinder 调用装配在 Spring MVC 上下文中的
ConversionService 组件进行数据类型转换、数据格式
化工作。将 Servlet 中的请求信息填充到入参对象中
• 3. 调用 Validator 组件对已经绑定了请求消息的入参对象
进行数据合法性校验,并最终生成数据绑定结果
BindingData 对象
• 4. Spring MVC 抽取 BindingResult 中的入参对象和校验
错误对象,将它们赋给处理方法的响应入参
• Spring MVC 上下文中内建了很多转换器,可完成大多数 Java 类型的转换工作。
• ConversionService converters =
– java.lang.Boolean -> java.lang.String :
org.springframework.core.convert.support.ObjectToStringConverter@f874ca
– java.lang.Character -> java.lang.Number : CharacterToNumberFactory@f004c9
– java.lang.Character -> java.lang.String : ObjectToStringConverter@68a961
– java.lang.Enum -> java.lang.String : EnumToStringConverter@12f060a
– java.lang.Number -> java.lang.Character : NumberToCharacterConverter@1482ac5
– java.lang.Number -> java.lang.Number : NumberToNumberConverterFactory@126c6f
– java.lang.Number -> java.lang.String : ObjectToStringConverter@14888e8
– java.lang.String -> java.lang.Boolean : StringToBooleanConverter@1ca6626
– java.lang.String -> java.lang.Character : StringToCharacterConverter@1143800
– java.lang.String -> java.lang.Enum : StringToEnumConverterFactory@1bba86e
– java.lang.String -> java.lang.Number : StringToNumberConverterFactory@18d2c12
– java.lang.String -> java.util.Locale : StringToLocaleConverter@3598e1
– java.lang.String -> java.util.Properties : StringToPropertiesConverter@c90828
– java.lang.String -> java.util.UUID : StringToUUIDConverter@a42f23
– java.util.Locale -> java.lang.String : ObjectToStringConverter@c7e20a
– java.util.Properties -> java.lang.String : PropertiesToStringConverter@367a7f
– java.util.UUID -> java.lang.String : ObjectToStringConverter@112b07f ……
上面是SpringMVC内建的转换器 ,不需要我们实现 ,SpringMVC自动转换
自定义转换器:
步骤1:
表单:
<form action="testConversionServiceConverer" method="POST">
<!-- lastname-email-gender-department.id 例如: GG-gg@gmail.com-0-12 -->
<input type="text" name="employee"/>
</form>
@Controller
public class SpringMVCTest{
@Autowired
private EmployeeDao employeeDao;
@RequestMapping("/testConversionServiceConverer")
public String testConverter(@RequestParam("employee") Employee employee){
System.out.println("save"+employee);
employeeDao.save(employee);
}
}
步骤2 :
@Component
public class EmployeeConverter implements Converter<String,Employee>{
@Override
public Employee convert(String source){
if(source !=null){
String [] vals = source.split("-");
//GG-gg@gmail-0-12
if(vals !=null&& vals.length==4){
String lastName =vals[0];
String email = vals[1];
Integer gender = Integer.parseInt(vals[2]);
Department department = new Department();
department.setId(Integer.parseInt(val[3]));
Employee employee = new Employee(null,lastName,email,gender,department);
return employee;
}
}
}
}
ConversionService 是 Spring 类型转换体系的核心接口。
• 可以利用 ConversionServiceFactoryBean 在 Spring 的 IOC
容器中定义一个 ConversionService. Spring 将自动识别出
IOC 容器中的 ConversionService,并在 Bean 属性配置及
Spring MVC 处理方法入参绑定等场合 使用它进行数据的转换
• 可通过 ConversionServiceFactoryBean 的 converters 属性
注册自定义的类型转换器
步骤3 :
<mvc:annotation-driver conversion-service="conversionService"></mvc:annotation-driven>
<!--配置 ConversionService -->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"
<property name="converters">
<set>
<ref bean="employeeConveter"/>
</set>
</property>
</bean>
31.#######
<mvc:annotation-driven ..属性值 .../>
配置说明
• <mvc:annotation-driven /> 会自动注
册RequestMappingHandlerMapping
、RequestMappingHandlerAdapter 与
ExceptionHandlerExceptionResolver 三个bean。
• 还将提供以下支持:
– 支持使用 ConversionService 实例对表单参数进行类型转换
– 支持使用 @NumberFormat annotation、@DateTimeFormat
注解完成数据类型的格式化
– 支持使用 @Valid 注解对 JavaBean 实例进行 JSR 303 验证
– 支持使用 @RequestBody 和 @ResponseBody 注解
是否配置 了 <mvc:annotation-driven />
dispacherServlet 中 handlerAdapters的属性 elementData(ArrayList)的元素是不同的
[0] HttpRequestHandlerAdapter
[1] SimpleContrllerHandlerAdapter
[2] RequestMappingHandlerAdapter
32.#########
@InitBinder
前面已经讲过 DataBinder(数据绑定 ) 可以通过
SpringMVC会自动调用 conversionService(conversionService又会调用employeeConveter 转换器 )(在xml配置过的 )
来完成数据绑定
• 由 @InitBinder 标识的方法,可以对 WebDataBinder 对
象进行初始化。WebDataBinder 是 DataBinder 的子类,用
于完成由表单字段到 JavaBean 属性的绑定
• @InitBinder方法不能有返回值,它必须声明为void。
• @InitBinder方法的参数通常是是 WebDataBinder
故 我们同样可以使用 @InitBinder 初始化的形式完成数据绑定
步骤: 1.定义 :
@InitBinder
public void intBinder(WebDataBinder binder){ //必须void返回值
binder.setDisallowedFields("lastName"); // 不对 lastName自动赋值
}
########## 数据格式化
// 得到类型转换出错的消息
@RequestMapping("/emp")
public String save(Employee employee,BindingResult result){
if(result.getErrorCount() > 0 ){
System.out.println("出错了 ");
for(FieldError error:result.getFieldErrors() ){
System.out.println(error.getField()+":"+error.getDefaultMessage() );
}
return "redirect:/emps";
}
}
## 重点 :##
• 对属性对象的输入/输出进行 “格式化”,从其本质上讲依然
属于 “类型转换” 的范畴。
• Spring 在格式化模块中定义了一个实现
ConversionService 接口的
FormattingConversionService 实现类,该实现类扩展
了 GenericConversionService,因此它既具有类型转换的
功能,又具有格式化的功能
• FormattingConversionService 拥有一个
FormattingConversionServiceFactroyBean 工厂类,
后者用于在 Spring 上下文中构造前者
• FormattingConversionServiceFactroyBean 内部已经注册了 :
#– NumberFormatAnnotationFormatterFactroy:支持对数字类型的属性
使用 @NumberFormat 注解
#– JodaDateTimeFormatAnnotationFormatterFactroy:支持对日期类型
的属性使用 @DateTimeFormat 注解
// 说明: SpringMVC默认的就是使用这个转换器 (只要<mvc:annotation-driven/> 就默认创建这个,无需我们配置此bean )
• 装配了 FormattingConversionServiceFactroyBean 后,就可
以在 Spring MVC 入参绑定及模型数据输出时使用注解驱动
了。<mvc:annotation-driven/> 默认创建的
ConversionService 实例即为
FormattingConversionServiceFactroyBean
(接口)
使用注解后 :DispatcherServlet 会 调用 FormattingConversionService
<mvc:annotation-driven/> 默认创建的就是 ConversionService 实例 即为 FormattingConversionServiceBean
interface ConversionService =(DefaultFormattingConversionService) 1.类型转换 2.数据格式化
private Set<Conveter> converters =
@DateTimeFormatAnnotationFormatterFactory
@NumberFormatAnnotationFormatterFactory
@.....
@.....
@.....
....
.......
######### 开发过程中: 如果要使用 自定义 + 注解 类型 格式化可以直接使用
<mvc:annotation-driven/> 标签默认创建了FormattingConversionServiceFactoryBean
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="xxxx"/> // 自定义的 类
</list>
</property>
</bean>
#######
1.数据类型转换
2.数据类型格式化
3. 数据校验 {
(1).如何校验 ?注解
(2).验证出错转向那个页面
(3).错误消息? 如何显示,如何把错误消息进行国际化
}
JSR 303
• JSR 303 是 Java 为 Bean 数据合法性校验提供的标准框架,(是个规范 )
它已经包含在 JavaEE 6.0 中 .
• JSR 303 通过在 Bean 属性上标注类似于 @NotNull、@Max
等标准的注解指定校验规则,并通过标准的验证接口对 Bean
进行验证
注解 :
@Null
@NotNull
@AssertTrue 被注释的元素必须为true
@AssertFalse 被注释的元素必须为false
@Min(value)
@Max(value)
@DecimalMin
@Past 目标必须是一个过去的日期
@Future 必须是将来日期
........
## Hibernate Validator 是 JSR 303 的一个参考实现,除了支持所有标准的校验注解外
还支持 一下扩展 注解
@Length
@NotEmpty
@Range 被注解的元素必须在合适的范围内
## • Spring 4.0 拥有自己独立的数据校验框架,同时支持 JSR
303 标准的校验框架。
• Spring 在进行数据绑定时,可同时调用校验框架完成数据校
验工作。在 Spring MVC 中,可直接通过注解驱动的方式
进行数据校验
• Spring 的 LocalValidatorFactroyBean 既实现了 Spring 的
Validator 接口,也实现了 JSR 303 的 Validator 接口。只要
在 Spring 容器中定义了一个
LocalValidatorFactoryBean,即可将其注入到需要数据校
验的 Bean 中。
##• Spring 本身并没有提供 JSR303 的实现,所以必须将
JSR303 的实现者的 jar 包放到类路径下。
• <mvc:annotation-driven/> 会默认装配好一个
LocalValidatorFactoryBean,通过在处理方法的入参上标
注 @valid 注解即可让 Spring MVC 在完成数据绑定后执行
数据校验的工作
• 在已经标注了 JSR303 注解的表单/命令对象前标注一个
@Valid,Spring MVC 框架在将请求参数绑定到该入参对象
后,就会调用校验框架根据注解声明的校验规则实施校验
• Spring MVC 是通过对 “处理方法” 签名的规约来保存校验结果
的:前一个 表单/命令对象 的校验结果保存到随后的 入参
中,这个保存校验结果的入参必须是 BindingResult 或
Errors 类型,这两个类都位于
org.springframework.validation 包中
################
hibernate-validator-5.0.0.CR2
1. hibernate-validator-5.0.0.CR2.jar
2. hibernate-validator-annotation-proce..jar
required:lib
classmate-0.8.0.jar
jboss-logging-3.1.1.GA.jar
validation-api.1.1.0.CR1.jar
(el-api-2.2.jar ,javax.el-2.2.4.jar ,javax.el-api-2.2.4.jar)不需要
注意: 删除tomcat下面的el-api-2.2.jar),把上面()中jar copy到 tomcat下面
使用步骤 :
1. 加入 hibernate-validator-5 jar
2. SpringMVC中 添加 <mvc:annotation-driven />注解
3. JavaBean 上面加入对应 注解
4. 在目标方法 bean 类型前面 添加@ Valid注解
注意:
• 需校验的 Bean 对象和其绑定结果对象或错误对象时成对出现的,它们
之间不允许声明其他的入参
• Errors 接口提供了获取错误信息的方法,如 getErrorCount() 或 getFieldErrors(String field)
• BindingResult 扩展了 Errors 接口 Interface BindingResult extends Errors
public String handle 91(@Valid User user, BindingResult userBindingResult,String sessionId,ModelMap mm,@Valid Dept dept, Errors deptErrors){
User和其绑定结果的对象
Dept和其校验的结果对象
错误的回显: 简单:
回显全部错误
<form:errors path="*" />
显示单个错误
<form:errors path="email"></form:errors>
############
定制 错误消息:
消息的 国际化
• 每个属性在数据绑定和数据校验发生错误时,都会生成一
个对应的 FieldError 对象。
• 当一个属性校验失败后,校验框架会为该属性生成 4 个消
息代码,这些代码以校验注解类名为前缀,结合
modleAttribute、属性名及属性类型名生成多个对应的消
息代码:例如 User 类中的 password 属性标准了一个 @Pattern 注
解,当该属性值不满足 @Pattern 所定义的规则时, 就会产生以下 4
个错误代码:
– Pattern.user.password
– Pattern.password
– Pattern.java.lang.String
– Pattern
• 当使用 Spring MVC 标签显示错误消息时, Spring MVC 会查看
WEB 上下文是否装配了对应的国际化消息,如果没有,则显示默认
的错误消息,否则使用国际化消息。
SpringMVC jar 包下面一定存在 默认的 i18n.properties 文件, 默认加载的是这个
定义 我们自己的进行覆盖
步骤:
1.
i18n.properties文件
NotEmpty.employee.lastName=lastName不能为空
Email.employee.email=Email 格式不合法
Past.employee.birth = 不能是一个过去的日期
• 若数据类型转换或数据格式转换时发生错误,或该有的参
数不存在,或调用处理方法时发生错误,都会在隐含模型
中创建错误消息。其错误代码前缀说明如下:
– required:必要的参数不存在。如 @RequiredParam(“param1”)
标注了一个入参,但是该参数不存在
– typeMismatch:在数据绑定时,发生数据类型不匹配的问题
– methodInvocation:Spring MVC 在调用处理方法时发生了错误
i18n.properties文件
required.employee.email = email 不能为null
typeMismatch.employee.birth=Birth 不是一个日期
methodInvocation.employee.email = 方法调用出现异常了 哈哈
2. springmvc.xml 配置国际化资源
<bean id="messageSource" class="org.springframework.support.ResourceBundleMessageSource">
<property name="basename" value="i18n"></property>
</bean>
41. #############
SpringMVC显示Json
$(function(){
$().click(function(){
var url = this.href;
var args = {};
$.post(url,args,function(data){
for(var i =0;i<data.length;i++){
var id=data[i].id;
var lastName = data[i].lastName;
alert(id +" :" +lastName);
}
});
});
});
后台: 需要 jackson
{
1.jackson-annotation-2.1.5.jar
2. jackson-core-2.1.5.jar
3. jackson-databind-2.1.5.jar
}
@ResponseBody
public Connection<Employee> testJson(){
return employeeDao.getAll(); // 直接返回一个对象或 集合即可 ,强大的工具啊
}
原理: 使用 HttpMessageConverter (特殊的类型转换器 -->前面还有其他的内建 的类型转换器 )
• HttpMessageConverter<T> 是 Spring3.0 新添加的一个接
口,负责将请求信息转换为一个对象(类型为 T),将对象(
类型为 T)输出为响应信息
• HttpMessageConverter<T>接口定义的方法:
– Boolean canRead(Class<?> clazz,MediaType mediaType): 指定转换器
可以读取的对象类型,即转换器是否可将请求信息转换为 clazz 类型的对
象,同时指定支持 MIME 类型(text/html,applaiction/json等)
– Boolean canWrite(Class<?> clazz,MediaType mediaType):指定转换器
是否可将 clazz 类型的对象写到响应流中,响应流支持的媒体类型
在MediaType 中定义。
– LIst<MediaType> getSupportMediaTypes():该转换器支持的媒体类
型。
– T read(Class<? extends T> clazz,HttpInputMessage inputMessage):
将请求信息流转换为 T 类型的对象。
– void write(T t,MediaType contnetType,HttpOutputMessgae
outputMessage):将T类型的对象写到响应流中,同时指定相应的媒体类
型为 contentType。
public interface HttpInputMessage extends HttpMessage {
OutputStream getBody() throws IOException;
}
public interface HttpInputMessage extends HttpMessage {
InputStream getBody() throws IOException;
}
#############
44 . 国际化
关于国际化:
1. 在页面上能够 根据浏览器语言设置的情况对文本(而不是内容),时间,数值进行本地化处理
2.可以在bean 中获取国际化 资源文件 Locale对应的消息
3.可以通过超链接 切换Locale,而不再依赖于浏览器的语言设置 情况
解决:
1.使用 jstl 的fmt标签
<fmt:message key="i18n.user"></fmt:message>
2. 在bean 中注入ResourceBundleMessageSource 的示例.使用其对应的getMessage 方法即可
@Autowired
private ResourceBundleMessageSource resourceBundleMessageSource;
@RequestMapping
public String testI18n(Locale locale){
String val = resourceBundleMessageSource.getMessage("i18n.user",null,locale);
System.out.println(val);
return "i18n";
}
3. 配置LocalResolver 和 LocaleChangeInterceptor
Struts2 : 方案: 配置请求参数的拦截器,拦截请求参数所对应的Locale,然后Locale放入到session里面 ,下次用时候直接从session中 使用这个Locale即可
SpringMVC的运行原理:
----->获取 name=locale 的请求参数------->把第一步的locale请求参数解析为Locale 对象----->获取 LocaleResolver 对象
(由LocaleChangeInterceptor完成 )
----->把Locale对象设置为Session属性-----> 从Session中获取Locale对象
(由 SessionLocaleResolver完成 )
0 0
- SpringMVC的深入探讨
- final的深入探讨
- 引用的深入探讨
- Java线程的深入探讨
- SQL Injection的深入探讨
- Java线程的深入探讨
- Java线程的深入探讨
- 有孔就入SQLInjection的深入探讨
- SQL Injection的深入探讨
- SQL Injection的深入探讨
- 指针数组的深入探讨
- Java线程的深入探讨
- 深入探讨ViewPager的FragmentPagerAdapter
- position:absolute 的深入探讨
- 关于freelist的一些深入的探讨
- 【Windows7的设备驱动的深入探讨】
- 让文本框“聪明”一点的深入探讨!
- 深入探讨JavaMail API的使用
- C++的一些感想
- hdu 5265 pog loves szh II
- 2_opencv2计算机视觉学习_操作像素
- java
- 程序员网址推荐
- SpringMVC的深入探讨
- 特征选择常用算法综述
- C++三目运算符的增强
- 并行计算复习————第一篇 并行计算硬件平台:并行计算机
- Cisco AnyConnect 与 Internet Connection Sharing (ICS) 服务不兼容
- 光流法的介绍
- 用户行为的收集
- Java(Android) Http通信
- wpa_supplicant软件架构分析