[TOC]
一、SpringMVC  
1.1 引言 
java开源框架,Spring Framework的一个独立模块。 
MVC框架,在项目中开辟MVC层次架构     
对控制器中的功能 包装 简化 扩展践行工厂模式,功能架构在工厂之上 
 
1.2 MVC架构 1.2.1 概念 
名称 
职责 
 
 
Model 
模型:即业务模型,负责完成业务中的数据通信处理,对应项目中的 service和dao 
 
View 
视图:渲染数据,生成页面。对应项目中的Jsp 
 
Controller 
控制器:直接对接请求,控制MVC流程,调度模型,选择视图。对应项目中的Servlet 
 
1.2.2 好处 
 
二、开发流程  
2.1 导入依赖 1 2 3 4 5 <dependency >     <groupId > org.springframework</groupId >      <artifactId > spring-webmvc</artifactId >      <version > 5.1.6.RELEASE</version >  </dependency > 
 
2.2 配置核心(前端)控制器 
作为一个MVC框架,首先要解决的是:如何能够收到请求!
所以MVC框架大都会设计一款前端控制器,选型在 Servlet 或 Filter两者之一,在框架最前沿率先工作,接收所有请求。
此控制器在接收到请求后,还会负责springMVC的核心的调度管理,所以既是前端又是核心。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <servlet >     <servlet-name > mvc</servlet-name >      <servlet-class > org.springframework.web.servlet.DispatcherServlet</servlet-class >           <init-param >          <param-name > contextConfigLocation</param-name >          <param-value > classpath:mvc.xml</param-value >      </init-param >           <load-on-startup > 1</load-on-startup >  </servlet > <servlet-mapping >     <servlet-name > mvc</servlet-name >      <url-pattern > /</url-pattern >  </servlet-mapping > 
 
2.3 后端控制器 
等价于之前定义的Servlet
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Controller  @RequestMapping("/hello")   public  class  HelloController  {	@RequestMapping("/test1")    	public  String hello1 () { 		System.out.println("hello world" ); 		return  "index" ;  	} 	@RequestMapping("/test2")   	public  String hello2 () { 		System.out.println("hello c9" ); 		return  "views/users" ; 	} } 
 
2.4 配置文件 
默认名称:核心控制器名-servet.xml     默认位置:WEB-INF
随意名称:mvc.xml           随意位置:resources    但需要配置在核心控制器中
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <beans  	xmlns ="http://www.springframework.org/schema/beans"  		xmlns:context ="http://www.springframework.org/schema/context"  		xmlns:mvc ="http://www.springframework.org/schema/mvc"   		xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"   		xsi:schemaLocation ="http://www.springframework.org/schema/beans  							http://www.springframework.org/schema/beans/spring-beans.xsd 							http://www.springframework.org/schema/context 							http://www.springframework.org/schema/context/spring-context.xsd 							http://www.springframework.org/schema/mvc 							http://www.springframework.org/schema/mvc/spring-mvc.xsd" >	 	<context:component-scan  base-package ="com.qf.controller" > </context:component-scan >  	 	<mvc:annotation-driven > </mvc:annotation-driven >  	 	<bean  class ="org.springframework.web.servlet.view.InternalResourceViewResolver" >  		 		<property  name ="prefix"  value ="/" > </property >  		 		<property  name ="suffix"  value =".jsp" > </property >  	</bean >  </beans > 
 
2.5 访问 1 2 http://localhost:8989/hello/test1 http://localhost:8989/hello/test2 
 
三、接收请求参数  
3.1 基本类型参数 
请求参数和方法的形参 同名即可
 
springMVC默认可以识别的日期字符串格式为: YYYY/MM/dd HH:mm:ss 通过@DateTimeFormat可以修改默认日志格式
 
1 2 3 4 5 6 7 8 9 10 @RequestMapping("/test1") public  String testParam1 (Integer id,                          String name,                          Boolean gender,                          @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") Date birth) {    System.out.println("test param1" );     return  "index" ; } 
 
3.2 实体收参【重点】 
请求参数和实体的属性 同名即可
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public  class  User  {	private  Integer id; 	private  String name; 	@DateTimeFormat(pattern="yyyy-MM-dd")  	private  Date birth; 	private  Boolean gender; 	 } @RequestMapping("/test2") public  String testParam2 (User user) {    System.out.println("test param2" );     System.out.println("user:" +user);     return  "index" ; } 
 
3.3 数组收参 
简单类型的 数组
 
1 2 3 4 5 6 7 <form >     ......     <input  type ="checkbox"  name ="hobby"  value ="fb" /> 足球      <input  type ="checkbox"  name ="hobby"  value ="bb" /> 篮球      <input  type ="checkbox"  name ="hobby"  value ="vb" /> 排球      </form > 
 
1 2 3 4 5 6 7 8 @RequestMapping("/test3") public  String testParam3 (String[] hobby) {    for (String h:hobby){         System.out.print(h+" " );     }     return  "index" ; } 
 
3.4 集合收参 【了解】 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public  class  UserList  {	 	private  List<User> users; 	 } @RequestMapping("/test4") public  String testParam4 (UserList userList) {    for (User user:userList.getUsers()){         System.out.println(user);     }     return  "index" ; } 
 
3.5 路径参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @RequestMapping("/hello/{id}") public  String testParam5 (@PathVariable("id")  Integer id) {    System.out.println("id:" +id);                 return  "index" ; } @RequestMapping("/hello/{username}") public  String testParam6 (@PathVariable("username")  String name) {    System.out.println("username:" +name);     return  "index" ; } 
 
3.6 中文乱码 
首先,页面中字符集统一
 
1 2 JSP : <%@page   pageEncoding="utf-8"  %> HTML : <meta charset="UTF-8" > 
 
其次,tomcat中字符集设置,对get请求中,中文参数乱码有效
 
1 Tomcat配置:URIEncoding=utf-8 
 
最后,设置此filter,对post请求中,中文参数乱码有效
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <filter >     <filter-name > encoding</filter-name >      <filter-class > org.springframework.web.filter.CharacterEncodingFilter</filter-class >      <init-param >          <param-name > encoding</param-name >          <param-value > utf-8</param-value >      </init-param >  </filter > <filter-mapping >     <filter-name > encoding</filter-name >      <url-pattern > /*</url-pattern >  </filter-mapping > 
 
四、跳转  
4.1 转发 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @RequestMapping("/forw") class  ForwardController {    @RequestMapping("/test1")      public  String testForward () {         System.out.println("test forward1" );                           return  "forward:/views/users.jsp" ;     }     @RequestMapping("/test2")      public  String testForward2 () {         System.out.println("test forward2" );                                    return  "forward:/forw/test1" ;      } } 
 
4.2 重定向 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @RequestMapping("/redir") class  RedirectController {    @RequestMapping("/test1")      public  String testRedirect1 () {         System.out.println("test redirect1" );                           return  "redirect:/redir/test1" ;     }     @RequestMapping("/test2")      public  String testRedirect2 () {         System.out.println("test redirect2" );                  return  "redirect:/view/user.jsp" ;     } } 
 
4.3 跳转细节 
在增删改之后,为了防止请求重复提交,重定向跳转
 
在查询之后,可以做转发跳转
 
 
 
五、传值  
C得到数据后,跳转到V,并向V传递数据。进而V中可以渲染数据,让用户看到含有数据的页面
转发跳转:Request作用域
重定向跳转:Session作用域
 
5.1 Request和Session 1 2 3 4 5 6 7 8 9 @RequestMapping("/test1") public  String testData (HttpSession session,HttpServletRequest req,Integer id) {    session.setAttribute("user" ,new  User ());     req.setAttribute("age" , 18 );     req.setAttribute("users" ,Arrays.asList(new  User (),new  User ()));          return  "forward:/WEB-INF/test2.jsp" ; } 
 
5.2 JSP中取值 
建议:重点复习 EL  JSTL
 
1 2 3 4 <fmt:formatDate value="${sessionScope.user.birth}"  pattern="yyyy-MM-dd" /> <br/> ${sessionScope.user.birth} <br> ${requestScope.age} 
 
5.3 Model 1 2 3 4 5 6 7 8 9 @RequestMapping("/test") public  String testData (Model model) {    model.addAttribute("name" , "张三" );     return  "index" ; } ${requestScope.name} 
 
5.4 ModelAndView 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @RequestMapping("/test") public  ModelAndView testData () {         ModelAndView  mv  =  new  ModelAndView ();          mv.setViewName("forward:/index.jsp" );          mv.addObject("age" ,18 );     return  mv; } ${requestScope.age} 
 
5.5 @SessionAttributes 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Controller @SessionAttributes({"gender","name"})  public  class  UserController  {    @RequestMapping("/hello")      public  String hello (Model m) {         m.addAttribute("gender" ,true );          mv.addObject("name" ,"zhj" );          return  "index" ;     }          @RequestMapping("/hello2")      public  String hello (SessionStatus status) {                  status.setComplete();         return  "index" ;     } } 
 
六、静态资源  
6.1 静态资源问题 
静态资源:html,js文件,css文件,图片文件
 
静态文件没有url-pattern,所以默认是访问不到的,之所以可以访问,是因为,tomcat中有一个全局的servlet:org.apache.catalina.servlets.DefaultServlet,它的url-pattern是 “/“,是全局默认的Servlet.  所以每个项目中不能匹配的静态资源的请求,有这个Servlet来处理即可。
 
但,在SpringMVC中DispatcherServlet也采用了 “/” 作为url-pattern, 则项目中不会再使用全局的Serlvet,则静态资源不能完成访问。
 
6.2 解决方案1 
DispathcerServlet采用其他的url-pattern
此时,所有访问handler的路径都要以 action结尾!!
 
1 2 3 4 5 6 7 8 <servlet >   	<servlet-name > mvc9</servlet-name >    	<servlet-class > org.springframework.web.servlet.DispatcherServlet</servlet-class >  </servlet > <servlet-mapping >     <servlet-name > mvc9</servlet-name >      <url-pattern > *.action</url-pattern >  </servlet-mapping > 
 
6.3 解决方案2 
DispathcerServlet的url-pattern依然采用 “/“,但追加配置
 
1 2 3 4 5 6 <mvc:default-servlet-handler /> 
 
6.4 解决方案3 
mapping是访问路径,location是静态资源存放的路径 
将/html/** 中 /**匹配到的内容,拼接到 /hhh/后 http://…./html/a.html  访问 /hhh/a.html 
 
 
1 <mvc:resources  mapping ="/html/**"  location ="/hhh/" /> 
 
七、Json处理  
7.1 导入依赖 1 2 3 4 5 6 <dependency >     <groupId > com.fasterxml.jackson.core</groupId >      <artifactId > jackson-databind</artifactId >      <version > 2.9.8</version >  </dependency > 
 
7.2 使用@ResponseBody 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Controller public  class  JsonController {    	@RequestMapping("/test1")      @ResponseBody       public  User hello1 () {         System.out.println("hello world" );         User  user  =  new  User ();         return  user;     }          @RequestMapping("/test2")      public  @ResponseBody  List<User> hello2 () {         System.out.println("hello world" );         List<User> users = Arrays.asList(new  User (),new  User ());         return  users;     }          @RequestMapping(value="/test3",produces = "text/html;charset=utf-8")       @ResponseBody       public  String hello2 () {         System.out.println("hello world" );         return  "你好" ;     } } 
 
7.3 使用@RestController 
Controller类上加了@RestController注解,等价于在类中的每个方法上都加了@ResponseBody
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Controller @RestController public  class  JsonController {    @RequestMapping("/test1")      public  User hello1 () {         System.out.println("hello world" );         User  user  =  new  User ();         return  user;     }          @RequestMapping("/test2")      public  List<User> hello2 () {         System.out.println("hello world" );         List<User> users = Arrays.asList(new  User (),new  User ());         return  users;     } } 
 
7.4 使用@RequestBody 
@RequestBody , 接收Json参数 
7.4.1 定义Handler 1 2 3 4 5 6 class  User {    private  Integer id;     private  String name;     private  Boolean gender;      } 
 
1 2 3 4 5 6 @RequestMapping("/users") public  String addUser (@RequestBody  User user) {    System.out.println("cap2" );     System.out.println("Post user :" +user);     return  "index" ; } 
 
7.4.2 Ajax发送json 1 2 3 4 var  xhr = new  XMLHttpRequest ();xhr.open ("post" ,"${pageContext.request.contextPath}/users?" +new  Date ().getTime ()); xhr.setRequestHeader ("content-type" ,"application/json" ); xhr.send ('{"id":1,"name":"shine","gender":"true"}' ); 
 
1 2 3 4 5 6 7 8 9 10 11 var  user = {id :1 ,name :"shine" };$.ajax ({     url :'${pageContext.request.contextPath}/json2/test4' ,     type :'post' ,     contentType :"application/json" ,     data :JSON .stringify (user),     success :function (ret ){         console .log (ret);     } }); 
 
7.5 Jackson常用注解 7.5.1 日期格式化 
@JsonFormat(pattern=”yyyy-MM-dd HH:mm:ss”,timezone = “GMT+8”)
 
1 2 3 4 5 6 7 8 public  class  User {	private  Integer id; 	private  String name; 	@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")  	private  Date birth;     ....     get/set } 
 
7.5.2 属性名修改 
@JsonProperty(“new_name”)
 
1 2 3 4 5 6 7 8 public  class  User {	@JsonProperty("new_id")       private  Integer id; 	private  String name;     ....     get/set } 输出的json:{“new_id”:xx,"name" :"xx" } 
 
7.5.3 属性忽略 
@JsonIgnore
 
1 2 3 4 5 6 7 8 public  class  User {    private  Integer id;     @JsonIgnore   	private  String name;     ....     get/set } 输出json时: {"id" :xx} 
 
7.5.4 null和empty属性排除 
Jackson 默认会输出null值的属性,如果不需要,可以排除。
@JsonInclude(JsonInclude.Include.NON_NULL) //null值 属性不输出 @JsonInclude(value= JsonInclude.Include.NON_EMPTY) // empty属性不输出( 空串,长度为0的集合,null值)
 
1 2 3 4 5 6 7 8 9 10 public  class  User {    private  Integer id;     @JsonInclude(JsonInclude.Include.NON_NULL)   	private  String name;     @JsonInclude(value= JsonInclude.Include.NON_EMPTY)        private  List<String> hobby;     ....     get/set } 如果name=null ,且 hobby长度为0 ,则输出json时:{"id" :xx} 
 
7.5.5 自定义序列化 
@JsonSerialize(using = MySerializer.class) // 使用MySerializer输出某属性
 
1 2 3 4 5 6 7 8 9 public  class  User  {    private  Integer id;     private  String name;     @JsonSerialize(using = MySerializer.class)      private  Double  salary  =  10000.126 ;     ....     get/set } 则输出json时:{"id" :xx,"name" :"xxx" ,"salary" :10000.13 } 
 
1 2 3 4 5 6 7 8 9 10 11 public  class  MySerializer  extends  JsonSerializer <Double> {         @Override       public  void  serialize (Double value, JsonGenerator gen, SerializerProvider serializers)  throws  IOException {                  String  number  =  BigDecimal.valueOf(value).setScale(2 , BigDecimal.ROUND_HALF_UP).toString();                  gen.writeNumber(number);     } } 
 
7.6 FastJson 7.6.1 导入依赖 1 2 3 4 5 6 <dependency >     <groupId > com.alibaba</groupId >      <artifactId > fastjson</artifactId >      <version > 1.2.54</version >  </dependency > 
 
7.6.2 安装FastJson 1 2 3 4 5 6 7 8 9 10 11 12 13 <mvc:annotation-driven >          <mvc:message-converters >          <bean  class ="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter" >                           <property  name ="supportedMediaTypes" >                  <list >                      <value > application/json</value >                  </list >              </property >          </bean >      </mvc:message-converters >  </mvc:annotation-driven > 
 
7.6.3 使用 
@ResponseBody  @RequestBody @RestController 使用方法不变
 
7.6.4 常用注解 
日期格式化:@JSONField(format=”yyyy/MM/dd”) 
属性名修改:@JSONField(name=”birth”) 
忽略属性:@JSONField(serialize = false) 
包含null值:@JSONField(serialzeFeatures = SerializerFeature.WriteMapNullValue)  默认会忽略所有null值,有此注解会输出null
@JSONField(serialzeFeatures = SerializerFeature.WriteNullStringAsEmpty)  null的String输出为”” 
 
 
自定义序列化:@JSONField(serializeUsing = MySerializer2.class) 
 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 public  class  User  implements  Serializable {	@JSONField(serialize = false)      private  Integer id;     @JSONField(name="NAME",serialzeFeatures = SerializerFeature.WriteNullStringAsEmpty)  	private  String name;     @JSONField(serialzeFeatures = SerializerFeature.WriteMapNullValue)       private  String city; 	@JSONField(format="yyyy/MM/dd")  	private  Date birth;     @JSONField(serializeUsing = MySerializer2.class)      private  Double salary; 	... } 
 
1 2 3 4 5 6 7 8 9 public  class  MySerializer2  implements  ObjectSerializer  {    @Override      public  void  write (JSONSerializer serializer, Object object, Object fieldName, Type fieldType,                        int  features)  throws  IOException {        Double  value  =  (Double) object;          String  text  =  value + "元" ;         serializer.write(text);      } } 
 
1 2 3 4 new  User (1 ,null ,null ,new  Date (),100.5 );{NAME:"" ,city:null ,"birth" :"2020/12/12" ,"salary" :"100.5元" } 
 
八、异常解析器  
8.1 现有方案,分散处理 
Controller中的每个Handler自己处理异常
此种处理方案,异常处理逻辑,分散在各个handler中,不利于集中管理
 
1 2 3 4 5 6 7 8 9 10 11 public  String xxx () {    try {     	...     }catch (Exception1 e){     	e.printStackTrace();         return  "redirect:/xx/error1" ;     }catch (Exception2 e){     	e.printStackTrace();         return  "redirect:/xx/error2" ;     } } 
 
8.2 异常解析器,统一处理 
Controller中的每个Handler不再自己处理异常,而是直接throws所有异常。
定义一个“异常解析器” 集中捕获处理 所有异常
此种方案,在集中管理异常方面,更有优势!
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public  class  MyExResolver  implements  HandlerExceptionResolver {	 	@Override  	public  ModelAndView resolveException (HttpServletRequest request,  			HttpServletResponse response, Object handler, Exception ex)  {		ex.printStackTrace(); 		 		ModelAndView  mv  =  new  ModelAndView (); 		 		if  (ex instanceof  Exception1) { 			mv.setViewName("redirect:/xxx/error1" ); 		}else  if (ex instanceof  Exception2){ 			mv.setViewName("redirect:/xxx/error2" ); 		}else { 			mv.setViewName("redirect:/xxx/error" ); 		} 		return  mv; 	} } 
 
1 2 	 <bean  class ="com.baizhi.exception.resolver.MyExResolver" > </bean > 
 
九、拦截器  
9.1 作用 
作用:抽取handler中的冗余功能
 
9.2 定义拦截器 
执行顺序: preHandle–postHandle–afterCompletion
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public  class  MyInter1  implements  HandlerInterceptor {	 	@Override  	public  boolean  preHandle (HttpServletRequest request,  			HttpServletResponse response, Object handler)  throws  Exception {		System.out.println("pre~~~" );          		return  true ; 	} 	 	@Override  	public  void  postHandle (HttpServletRequest request,  			HttpServletResponse response, Object handler, 			ModelAndView modelAndView)  throws  Exception {		System.out.println("post~~" ); 	} 	 	@Override  	public  void  afterCompletion (HttpServletRequest request,  			HttpServletResponse response, Object handler, Exception ex) 			throws  Exception { 		System.out.println("after~~" ); 	} } 
 
9.3 配置拦截路径 1 2 3 4 5 6 7 8 9 10 <mvc:interceptors >     <mvc:interceptor >          <mvc:mapping  path ="/inter/test1" />          <mvc:mapping  path ="/inter/test2" />          <mvc:mapping  path ="/inter/test*" />           <mvc:mapping  path ="/inter/**" />           <mvc:exclude-mapping  path ="/inter/a/**" />             <bean  class ="com.baizhi.interceptor.MyInter1" > </bean >         </mvc:interceptor >  </mvc:interceptors > 
 
十、上传  
10.1 导入jar 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <dependency >     <groupId > commons-io</groupId >      <artifactId > commons-io</artifactId >      <version > 2.4</version >  </dependency > <dependency >     <groupId > commons-fileupload</groupId >      <artifactId > commons-fileupload</artifactId >      <version > 1.3.3</version >      <exclusions >          <exclusion >              <groupId > javax.servlet</groupId >              <artifactId > servlet-api</artifactId >          </exclusion >      </exclusions >  </dependency > 
 
10.2 表单 1 2 3 4 5 <form  action ="${pageContext.request.contextPath }/upload/test1"  method ="post"         enctype ="multipart/form-data" >   file: <input  type ="file"  name ="source" />  <br >    <input  type ="submit"  value ="提交" />  </form > 
 
10.3 上传解析器 1 2 3 4 5 6 7 8 <bean  id ="multipartResolver"         class ="org.springframework.web.multipart.commons.CommonsMultipartResolver" >          <property  name ="maxUploadSize"  value ="1048576" > </property >  </bean > 
 
10.4 Handler 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @RequestMapping("/test1") public  String hello1 (String username,MultipartFile source,HttpSession session)  {         String  filename  =  source.getOriginalFilename();          String  unique  =  UUID.randomUUID().toString();          String  ext  =  FilenameUtils.getExtension(filename);          String  uniqueFileName  =  unique+"." +ext;     System.out.println("唯一的文件名:" +uniqueFileName);          String  type  =  source.getContentType();     System.out.println("filename:" +filename+" type:" +type);          String  real_path  =  session.getServletContext().getRealPath("/upload_file" );     System.out.println("real_path:" +real_path);               source.transferTo(new  File (real_path+"\\" +uniqueFileName));     return  "index" ; } 
 
十一、下载  
11.1 超链 1 <a  href ="${pageContext.request.contextPath}/download/test1?name=Koala.jpg" > 下载</a > 
 
11.2 Handler 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @RequestMapping("/test1") public  void  hello1 (String name,HttpSession session,HttpServletResponse response) {    System.out.println("name:" +name);          String  path  =  session.getServletContext().getRealPath("/upload_file" );          String  real_path  =  path+"\\" +name;          response.setHeader("content-disposition" ,"attachment;filename=" +name);          IOUtils.copy(new  FileInputStream (real_path), response.getOutputStream());      } 
 
十二、验证码  
12.1 作用 
防止暴力攻击,前端安全保障
 
12.2 导入jar 1 2 3 4 5 6 7 8 9 10 11 12 <dependency >     <groupId > com.github.penggle</groupId >      <artifactId > kaptcha</artifactId >      <version > 2.3.2</version >      <exclusions >          <exclusion >              <groupId > javax.servlet</groupId >              <artifactId > javax.servlet-api</artifactId >          </exclusion >      </exclusions >  </dependency > 
 
12.3 声明验证码组件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <servlet >     <servlet-name > cap</servlet-name >      <servlet-class > com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class >      <init-param >        <param-name > kaptcha.border</param-name >        <param-value > no</param-value >      </init-param >      <init-param >        <param-name > kaptcha.textproducer.char.length</param-name >        <param-value > 4</param-value >      </init-param >      <init-param >        <param-name > kaptcha.textproducer.char.string</param-name >        <param-value > abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789</param-value >      </init-param >      <init-param >        <param-name > kaptcha.background.clear.to</param-name >        <param-value > 211,229,237</param-value >      </init-param >      <init-param >               <param-name > kaptcha.session.key</param-name >        <param-value > captcha</param-value >      </init-param >    </servlet >    <servlet-mapping >      <servlet-name > cap</servlet-name >      <url-pattern > /captcha</url-pattern >    </servlet-mapping >  
 
12.4 Page 1 2 3 4 5 6 7 8 9 10 <img  src ="${pageContext.request.contextPath}/captcha"  style ="width:85px"  id ="cap" /> <script >     $(function ( ){         $("#cap" ).click (function ( ){                          path = $(this ).attr ("src" )+"?" +new  Date ().getTime ();             $(this ).attr ("src" ,path);         });     }); </script > 
 
十三、REST  
13.1 开发风格 
是一种开发风格,遵从此风格开发软件,符合REST风格,则RESTFUL。
 
两个核心要求:
每个资源都有唯一的标识(URL) 
不同的行为,使用对应的http-method 
 
 
13.2 优点 
 
13.3 使用 13.3.1 定义Rest风格的 Controller 
@RequestMapping(value=”/users”,method = RequestMethod.GET)
等价
@GetMapping(“/users”)
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 @RestController public  class  RestController  {    @GetMapping("/users")      public  List<User> queryAllUsers () {         System.out.println("get" );         List<User> users = ....         return  users;     }     @PostMapping("/users")      public  String addUser (@RequestBody  User user) {         System.out.println("Post user :" +user);         return  "{status:1}" ;     }          @PutMapping("/users")      public  String updateUser (@RequestBody  User user) {         System.out.println("Put user"  user:"+user);          return " {status:1 }";     }     @GetMapping(" /users/{id}")     public String queryOneUser(@PathVariable Integer id){//@PathVariable 接收路径中的值         System.out.println(" Get user id:"+id);         return " {status:1 }";     }     @DeleteMapping(" /users/{id}")     public String deleteOneUser(@PathVariable Integer id){//@PathVariable 接收路径中的值         System.out.println(" delete user id:"+id);         return " {status:1 }";     } } 
 
13.3.2 Ajax请求 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <script >      	function  putUser ( ){          var  xhr = new  XMLHttpRequest ();     	         xhr.open ("put" ,"${pageContext.request.contextPath}/rest04/users" );     	         xhr.setRequestHeader ("content-type" ,"application/json" );                  var  user = {id :1 ,NAME :"shine" ,city :"bj" ,"birth" :"2020/12/12" ,"salary" :100.5 };         xhr.send (JSON .stringify (user));         xhr.onreadystatechange =function ( ){             if (xhr.readyState ==4  && xhr.status ==200 ){                 var  ret = xhr.responseText ;                                  console .log (JSON .parse (ret));             }         }     	     } 	function  delUser ( ){           var  xhr = new  XMLHttpRequest ();                  xhr.open ("delete" ,"${pageContext.request.contextPath}/rest04/users/1" );         xhr.send ();         xhr.onreadystatechange =function ( ){             if (xhr.readyState ==4  && xhr.status ==200 ){                 var  ret = xhr.responseText ;                 console .log (JSON .parse (ret));             }         }     } </script > 
 
十四、跨域请求  
14.1 域 
域:协议+IP+端口
 
14.2 Ajax跨域问题 
Ajax发送请求时,不允许跨域,以防用户信息泄露。
 
当Ajax跨域请求时,响应会被浏览器拦截(同源策略),并报错。即浏览器默认不允许ajax跨域得到响应内容。
 
互相信任的域之间如果需要ajax访问,(比如前后端分离项目中,前端项目和后端项目之间),则需要额外的设置才可正常请求。
 
 
 
14.3 解决方案 
允许其他域访问
 
在被访问方的Controller类上,添加注解
 
 
 
1 2 3 4 @CrossOrigin("http://localhost:8080")  public  class  SysUserController  {	.... } 
 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 $.ajax ({      type : "POST" ,      url : "http://localhost:8989/web/sys/login" ,      ...,      xhrFields : {                withCredentials : true       } }); 或 var  xhr = new  XMLHttpRequest ();xhr.withCredentials =true ; 
 
十五、SpringMVC执行流程  
十六、Spring整合  
16.1 整合思路 
此时项目中有两个工厂
DispatcherServlet 启动的springMVC工厂==负责生产C及springMVC自己的系统组件 
ContextLoaderListener 启动的spring工厂==负责生产其他所有组件 
springMVC的工厂会被设置为spring工厂的子工厂,可以随意获取spring工厂中的组件 
整合过程,就是累加:代码+依赖+配置。然后将service注入给controller即可 
 
 
16.2 整合技巧 
两个工厂不能有彼此侵入,即,生产的组件不能有重合。
 
1 2 3 4 5 6 7 8 <context:component-scan  base-package ="com.zhj"  use-default-filters ="false" >  	<context:include-filter  type ="annotation"  expression ="org.springframework.stereotype.Controller" />  </context:component-scan > 
 
1 2 3 4 5 <context:component-scan  base-package ="com.zhj"  use-default-filters ="true" > 	<context:exclude-filter  type ="annotation"  expression ="org.springframework.stereotype.Controller" />  </context:component-scan >