SpringMVC
Spring MVC是目前主流的实现MVC设计模式的企业级开发框架,Spring框架的一个子模块,无需整合,开发更加便捷。
复习总结: 首次编辑2020.7.2(后续继续完善)
什么是MVC设计模式
应用程序分为:Controller
、View
、Model
三层。controller接收客户端请求,调用model生成业务数据,传递给view.SpringMVC就是对这套流程的封装,屏蔽了许多底层代码。
SpringMVC 的核心组件
DispatherServlet
:前置控制器,核心,控制其他组件执行,进行统一调度,降低组件之间耦合性。Handler
:处理器,完成具体业务逻辑,相当于Servlet
或者action
HandlerMapping
:接受来自DispatherServlet
的请求,映射请求到不同的HandlerHandlerIntercrptor
:handler拦截器,是一个接口,需要拦截业务,则实现该接口HandlerExecutionChain
:处理器执行链,包括Handler
和HandlerIntercrptor
(系统会有一个默认的HandlerIntercrptor
)HandlerAdaptor
:处理器适配器,处理器适配器,Handler 执⾏业务⽅法之前,需要进⾏⼀系列的操作,包括表单数据的验证、数据类型的转换、将表单数据封装到 JavaBean 等,这些操作都是由HandlerApater 来完成,开发者只需将注意⼒集中业务逻辑的处理上,DispatcherServlet 通过HandlerAdapter 执⾏不同的 Handler。ModelAndView
:包含视图和模型数据,作为handler
的处理结果返回给dispatherServlet
ViewReslover
:视图解析器,将逻辑视图转化为物理视图,渲染结果响应给客户端
SpringMVC的工作流程
- 客户端请求被
DispatherServlet
接受 - 根据
HandlerMapping
映射到handler
- 生成
handler
和handlerInterceptor
Handler
和handlerIntercept
以handlerExecutionChain
的形式返回给DispatherServlet
DispatherServlet
通过handlerAdaptor
调用handler的方法完成业务逻辑handler
返回一个modelandview
给DispatherServlet
DispatherServle
t将返回的modelandview
对象传给viewResolver
视图解析器,将逻辑视图变成物理视图view
viewResolver
返回一个view给DispatherServlet
dispaServlet
根据view进行视图渲染,响应给客户端
SpringMVC流程复杂,实际开发中很简单,因为大部分的组件不需要开发者创建、管理,只需要通过配置文件的方式完成配置即可。真正需要开发者进行处理的只有handler、view。
SpringMVC的特点
- 清晰的角色划分
- 灵活的配置功能
- 提供了大量的控制器接口和实现类
- 分离view层的实现
- 国际化支持
- 面向接口编程
如何搭建springmvc项目
-
创建maven工程
- 配置
archetypeCAtalog
==internal
加快项目的构建
-
最初结构
-
在
main
下新建两个文件夹java
和resources
,右键Mark Directory as
-
pom.xml
中添加依赖
<!--springMVC依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.0.11.RELEASE</version> </dependency>
- 配置
-
在
web.xml
中配置DispatherServlet
配置<web-app> <display-name>Archetype Created Web Application</display-name> <servlet> <servlet-name>dispatherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--读取spring.xml配置文件--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> </servlet> <!--根目录下所有都交由DispatherServlet处理--> <servlet-mapping> <servlet-name>dispatherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
-
springmvc.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" 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"> <!--自动扫描--> <context:component-scan base-package="com.kingwan"></context:component-scan> <!--配置视图解析器--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/"></property> <property name="suffix" value=".jsp"></property> </bean> </beans>
-
创建
Handler
package com.kingwan.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class MyHandler { @RequestMapping("/index") public String index(){ System.out.println("执行了index..."); return "index"; } }
-
流程说明:
客户端输入
http://localhost:8888/index
其中index
被DispatcherServlet
捕获,交给对应的HandlerMapping
处理(Handler已经通过ioc实例化,通过@RequestMapping
注解映射),返回一个逻辑视图(return "index"
)交给DispatcherServlet
,DispatcherServlet
再交给视图解析器ViewResolver
解析,根据定义的解析规则(配置视图解析器的那个bean
),拼凑之后(/index.jsp
)就去target
根目录里找对应jsp
文件。
SpringMVC注解
@RequestMapping
将URL请求与业务方法进行映射,在Handler
的类定义处以及方法定义处都可以添加@RequestMapping
,在类定义处添加,相当于多了一层访问路径,在方法处添加,即与方法绑定。
@Controller
在类定义处添加,将该类交给IOC容器管理(结合springmvc.xml
的自动扫描),同时使其成为一个控制器,可以接受客户端请求。
package com.kingwan.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class MyHandler {
@RequestMapping("/index")
public String index(){
System.out.println("执行了index...");
return "index";
}
}
@RequestMapping
的相关参数
-
value:指定URL请求的实际地址,是
@RequestMapping
的默认值@RequestMapping("/index") public String index(){ System.out.println("执行了index..."); return "index"; } /*这两个效果相当*/ @RequestMapping(value = "/index") public String index(){ System.out.println("执行了index..."); return "index"; }
-
method:指定请求的method类型,GET、POST、PUT、DELET
//只允许接受get请求 @RequestMapping(value = "/index",method = RequestMethod.GET) public String index(){ System.out.println("执行了index..."); return "index"; }
-
params:指定请求必须包含某些参数,否则无法调用该方法。
@RequestMapping(value = "/index",method = RequestMethod.GET,params = {"name","id=10"}) public String index(){ System.out.println("执行了index..."); return "index"; }
上述代码表示只能接受get请求,请求中必须包含两个参数name和id,其中id的值必须为10。
关于参数绑定,在形参列表中,通过添加
@RequestParm
注解完成HTTP请求参数与业务方法形参映射.@RequestMapping(value = "/index",method = RequestMethod.GET,params = {"name","id=10"}) public String index(String name,@RequestParam("id") int id){ System.out.println("执行了index..."); System.out.println(name); System.out.println(id); return "index"; }
上述代码表示请求的参数name和id分别赋给了形参name和id,同时完成了数据类型的转化,这些工作是由
HandlerAdapter
来完成的
-
支持
Restful
风格@Controller @RequestMapping("/rest") public class RestfulHandler { @RequestMapping("/restful/{id}/{name}") public ModelAndView restful(@PathVariable("id") Integer id, @PathVariable("name") String name){ ModelAndView mv = new ModelAndView(); mv.addObject("id",id); mv.addObject("name",name); mv.setViewName("/view/hello"); return mv; } }
<a href="/rest/restful/1/张三">RESTful风格</a>
使用
@PathVariable
注解完成请求参数和形参的映射
SpringMVC数据绑定
-
传递请求参数
<h4>地址栏参数绑定</h4> <a href="/paramTest01?name=张三&id=12">参数绑定1</a> <a href="/paramTest02?name=张三&id=12">参数绑定2</a> <a href="/paramTest03?name=张三&id=12">参数绑定3</a> <a href="/paramTest04?name=张三&id=12">参数绑定4</a> <h4>表单参数绑定</h4> <form method="post" action="/paramTest05"> 编号<input type="text" name="id"> 姓名<input type="text" name="name"> <input type="submit" value="提交"> </form> <h4>表单参数绑定(支持级联)</h4> <form method="post" action="/paramTest06"> 学生姓名<input type="text" name="name"> 学生编号<input type="text" name="user.id"> <input type="submit" value="提交"> </form>
/** * 通过传统request.getParameter获取参数 * @param request * @return */ @RequestMapping("/paramTest01") public ModelAndView getParam(HttpServletRequest request) { ModelAndView mv = new ModelAndView(); String name = request.getParameter("name"); String id = request.getParameter("id"); mv.addObject("name",name); mv.addObject("id",id); System.out.println(name+id); mv.setViewName("/view/hello"); return mv; } /** * 通过形参列表,形参名需要对应,顺序可以不一致 * @param id * @param name * @return */ @RequestMapping("/paramTest02") public ModelAndView getParam02(String id,String name){ ModelAndView mv = new ModelAndView(); mv.addObject("name",name); mv.addObject("id",id); System.out.println(name+id); mv.setViewName("/view/hello"); return mv; } /** * 使用@RequestParam注解方式 * @param myid * @param myname * @return */ @RequestMapping("/paramTest03") public ModelAndView getParam03(@RequestParam("id") String myid, @RequestParam("name") String myname){ ModelAndView mv = new ModelAndView(); mv.addObject("name",myname); mv.addObject("id",myid); System.out.println(myname+myid); mv.setViewName("/view/hello"); return mv; } /** * 传入一个封装对象 * @param user * @return */ @RequestMapping("/paramTest04") public ModelAndView getParam03(User user){ ModelAndView mv = new ModelAndView(); mv.addObject("name",user.getName()); mv.addObject("id",user.getId()); mv.setViewName("/view/hello"); return mv; } /** * 表单提交方式 * @param user * @return */ @RequestMapping("/paramTest05") public ModelAndView getParam05(User user){ ModelAndView mv = new ModelAndView(); mv.addObject("name",user.getName()); mv.addObject("id",user.getId()); mv.setViewName("/view/hello"); return mv; } /** * 表单提交方式(支持级联) * @param student * @return */ @RequestMapping("/paramTest06") public ModelAndView getParam06(Student student){ ModelAndView mv = new ModelAndView(); System.out.println(student); mv.addObject("name",student.getName()); mv.addObject("id",student.getUser().getId()); mv.setViewName("/view/hello"); return mv; }
-
基本数据类型绑定
<a href="/data/basetype?id=10">直接返回数据(不返回视图)</a>
/** * 绑定基本数据类型 * @param id * @return */ @RequestMapping("/basetype") @ResponseBody public String baseType(String id){ return id; }
@ResponseBody
表示 Spring MVC 会直接将业务⽅法的返回值响应给客户端,如果不加@ResponseBody
注解,Spring MVC 会将业务⽅法的放回值传递给DispatcherServlet
,再由DisptacherServlet
调⽤ViewResolver
对返回值进⾏解析,映射到⼀个 JSP 资源。 -
List类型
Spring MVC 不⽀持 List 类型的直接转换,需要对 List 集合进⾏包装
form action="/data/list" method="post"> 编号1<input type="text" name="userList[0].id"> 姓名1<input type="text" name="userList[0].name"><br> 编号2<input type="text" name="userList[1].id"> 姓名2<input type="text" name="userList[1].name"><br> 编号3<input type="text" name="userList[2].id"> 姓名3<input type="text" name="userList[2].name"> <input type="submit" value="提交"> </form>
/** * list类型 * @param userList * @return */ @RequestMapping("/list") @ResponseBody public String userList(UserList userList){ StringBuffer buffer = new StringBuffer(); for (User user : userList.getUserList()) { buffer.append(user); } String string = buffer.toString(); return string; }
处理
@ResponseBody
中文乱码,springmvc.xml中配置消息转换器<!--配置编码--> <mvc:annotation-driven> <!--消息转化器--> <mvc:message-converters> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes" value="text/html;charset=utf-8"></property> </bean> </mvc:message-converters> </mvc:annotation-driven>
-
JSON数据
<script type="text/javascript"> $(function () { var obj = JSON.stringify({'id':'123','name':'张三'}); //后台使用对象的形式接受 $.ajax({ url: "/data/jsonAfter", type: "post", data: obj, contentType: 'application/json;charset=utf-8', dataType:"json", success:function (msg) { alert(msg.id+"=="+msg.name) } }) }) </script>
/** * 跳转到json.jsp页面 * @return */ @RequestMapping(value = "/jsonBefore") public String jsonBefore(){ return "/view/json"; } /** * 接受json数据,修改后返回 * @param user * @return */ @RequestMapping(value = "/jsonAfter",method = RequestMethod.POST) @ResponseBody public User jsonAfter(@RequestBody(required=false) User user){ System.out.println(user); user.setName("李四"); return user; }
-
静态js资源无法访问:
web.xml
设置不拦截静态资源<!--不拦截静态资源--> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.js</url-pattern> </servlet-mapping>
-
fastjson解析
<!--json数据解析--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.32</version> </dependency>
<!--配置编码--> <mvc:annotation-driven> <!--消息转化器--> <mvc:message-converters> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes" value="text/html;charset=utf-8"></property> </bean> <!--配置fastjson--> <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter4"></bean> </mvc:message-converters> </mvc:annotation-driven>
-
SpringMVC自定义数据转换器
数据转换器是指将客户端 HTTP 请求中的参数转换为业务⽅法中定义的形参,⾃定义表示开发者可以⾃主设计转换的⽅式,HandlerApdter 已经提供了通⽤的转换,String 转 int,String 转 double,表单数据的封装等,但是在特殊的业务场景下,HandlerAdapter ⽆法进⾏转换,就需要开发者⾃定义转换器。
1.日期转换器
-
创建
DateConverter
实现Converter
接口package com.kingwan.converter; import org.springframework.core.convert.converter.Converter; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /** * Created by kingwan on 2020/6/29. * 说明:日期转换器String-->日期类型 */ public class DateConverter implements Converter<String,Date> { private String pattern; public DateConverter(String pattern) { this.pattern = pattern; } @Override public Date convert(String s) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat(this.pattern); Date date = null; try { date = simpleDateFormat.parse(s); } catch (ParseException e) { e.printStackTrace(); } return date; } }
-
springMVC.xml
配置日期转换器<!--配置自定义转换器--> <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <bean class="com.kingwan.converter.DateConverter"> <constructor-arg type="java.lang.String" value="yyyy-MM-dd"> </constructor-arg> </bean> </property> </bean>
-
加入驱动
<mvc:annotation-driven conversion-service="conversionService"> <!--消息转化器--> <mvc:message-converters> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes" value="text/html;charset=utf-8"></property> </bean> </mvc:message-converters> </mvc:annotation-driven>
-
前端及业务代码
h4>日期类型转换器</h4> <form action="/converter/date" method="post"> 日期<input type="date" name="date"> <input type="submit" value="提交"> </form>
package com.kingwan.handler; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.util.Date; /** * Created by kingwan on 2020/6/29. * 说明: */ @Controller @RequestMapping("/converter") public class ConverterHandler { @RequestMapping("/date") @ResponseBody public String data(Date date){ return date.toString(); } }
SpringMVC自定义异常
-
编写自定义异常类
package com.kingwan.exception; /** * Created by kingwan on 2020/7/1. * 说明:自定义异常类 */ public class MyException extends Exception{ public MyException() { } public MyException(String message) { super(message); } public MyException(String message, Throwable cause) { super(message, cause); } public MyException(Throwable cause) { super(cause); } public MyException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } }
-
编写
MyExceptionResolver
类实现HandlerExceptionResolver
接口package com.kingwan.exception; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Created by kingwan on 2020/7/1. * 说明: */ public class MyExceptionResolver implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) { ModelAndView mv = new ModelAndView(); if(e instanceof MyException){ System.out.println("属于自定义异常"); mv.addObject("error_message",e.getMessage()); mv.setViewName("/404"); }else { System.out.println("错误日志信息"); mv.addObject("error_message",e.getMessage()); mv.setViewName("/test.jsp"); } return mv; } }
-
测试手动抛出异常
/** * 测试抛异常 * @param request * @return * @throws Exception */ @RequestMapping("/throw") public ModelAndView throwException(HttpServletRequest request) throws Exception{ ModelAndView mv = new ModelAndView(); mv.setViewName("/view/hello"); if(true){ throw new MyException("手动抛Myexception异常"); } return mv; }
SpringMVC拦截器
SpringMVC文件上传及下载
1.单文件上传
2.多文件上传
3.文件下载
4.配置Tomcat虚拟路径
复习总结
- 导入pom.xml中mvc依赖
- 配置dispatcherServlet