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 的一个参考实现,除了支持所有标准的校验注解外
还支持  一下扩展  注解 
@Email 
@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
原创粉丝点击