Spring MVC基础
1、概念
Spring MVC是基于MVC设计的优秀WEB框架。
M:Model(模型层,包括Bean模型、Mybatis中Dao层等)
V:View(视图层,包括HTML、JSP等)
C:Controller(控制层,包括Servlet封装、请求转发等)
2、Spring MVC项目搭建
2.1、导入依赖
<!--Spring核心容器包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.5</version>
</dependency>
<!--Spring切面包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.5</version>
</dependency>
<!--AOP联盟包-->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!--Spring事务控制包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.5</version>
</dependency>
<!--Spring ORM映射依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.3.5</version>
</dependency>
<!--Apache Commons日志包-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!--log4j2 日志-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.14.0</version>
<scope>test</scope>
</dependency>
<!--lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<!--Spring Test测试支持包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.5</version>
<scope>test</scope>
</dependency>
<!--SpringMVC依赖包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.5</version>
</dependency>
<!--jsp和servlet 可选-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
2.2、web.xml配置前端控制器DispatcherServlet
web.xml在webapp包中的WEB-INF包下。
web.xml主要配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--配置前端控制器DispatcherServlet -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--
配置初始化参数,读取springMVC的核心配置文件的位置和名称
Spring MVC会默认读取配置文件名为:<servlet-name>-servlet.xml,自定义读取的配置文件名
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!--配置dispatcherServlet的映射路径为 / 包含全部的servlet, JSP除外-->
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
2.3、springmvc.xml配置Spring MVC核心配置
resources包下添加springmvc.xml。
springmvc.xml主要内容:
- <context:component-scan>标签
开启了<mvc:annotation-driven/>后,Spring才知道我们开启了注解驱动,扫描指定包下有@Component以及其三大子注解@Controller、@Service、@Repository的类加载到Spring容器中。(即将组件加载至工厂里的Map容器中)
- <mvc:annotation-driven/>标签
Spring MVC会自动帮我们注册HandlerMapping和HandlerAdapter。
HandlerMapping其实现类ReuestMappingHandlerMapping用于处理请求映射到那个RequestMapping中。
HandlerAdapter其实现类RequestMappingHandlerAdapter用于确定调用哪个Controller控制器中的哪个方法,构造方法参数传参,并将例如List、Set、Map类型数据结果(json)或ModelAndView结果返回给前端控制器。
- <bean class=“org.springframework.web.servlet.view.InternalResourceViewResolver”>
用于配置视图解析器,根据配置的前缀和后缀,跳转到相应页面
- <mvc:resources>
配置静态资源映射关系
<?xml version="1.0" encoding="UTF-8"?>
<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.cl"></context:component-scan>
<!-- 开启mvc注解扫描-->
<mvc:annotation-driven/>
<!-- 配置资源映射-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 配置静态资源访问-->
<mvc:resources mapping="/css/**" location="/css/"></mvc:resources>
<mvc:resources mapping="/js/**" location="/js/"></mvc:resources>
<mvc:resources mapping="/img/**" location="/img/"></mvc:resources>
</beans>
3、Spring MVC执行流程
- DispatcherServlet(前端控制器):调度用户请求
- HandlerMapping(处理器映射器):解析用户请求,找到对应的Handler
- HandlerAdapter(处理器适配器):应用了适配器模式,通过扩展适配器能对更多类型的处理器执行操作
- Handler(处理器):具体的Controller层(具体业务代码处理)
- ViewResolver(视图解析器):将逻辑地址解析为具体的页面地址,再生成View对象,通过View(视图)渲染后返回并展示给用户
- View(视图):SpringMVC 框架提供了很多的 View 视图类型的支持,包括:jstlView、freemarkerView、pdfView等,最常用的视图就是 jsp。 一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户。
4、@RequestMapping
4.1、@RequestMapping参数——value和path
配置具体的映射路径。
如请求登陆地址:value="/api/sys/login"
4.2、@RequestMapping参数——method
控制请求的方式。
method = {RequestMethod.POST, RequestMethod.DELETE, RequestMethod.PUT, RequestMethod.GET}
4.3、REST风格
4.3.1、访问资源可以通过mehod来标识:对资源的不同操作
- RequestMethod.POST:增加操作,增加数据
- RequestMethod.DELETE:删除操作,删除数据
- RequestMethod.PUT:更新操作,更新数据
- RequestMethod.GET:查询操作,获取数据
具体代码实例:method = {RequestMethod.POST, RequestMethod.DELETE, RequestMethod.PUT, RequestMethod.GET}
@RequestMapping(value = "/testRestful", method = {RequestMethod.PUT})
前端通过<input type=“hidden” name="_method" value=“PUT”>来使用
4.3.2、请求参数放在URL路径中,通过{}占位,具体参数通过@PathVariable获取指定参数。
例如获取请求路径中的id值:
@RequestMapping("/testPathVariable/{id}") public String testPathVariable(@PathVariable("id") Integer id){ }
4.3.3、Spring MVC需要具体在web.xml中配置
<!--配置hiddenHttpMethodFilter ,将 POST请求转换为PUT或者DELETE请求-->
<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>
4.3.4、转换原理
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest requestToUse = request;
if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) {
String paramValue = request.getParameter(this.methodParam);// "_method"
if (StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
if (ALLOWED_METHODS.contains(method)) {
requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
}
}
}
filterChain.doFilter((ServletRequest)requestToUse, response);
}
4.4、@RequestMapping参数——params
通过params控制请求参数
举个例子,请求参数name必须不等于root
@RequestMapping( value = “/***” ,params = {“name!=root”})
4.5、@RequestMapping参数——consumes
指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
示例:
@RequestMapping(value = “/testRestful”, method = {RequestMethod.PUT}, consumes = {“application/json”})
4.6、@RequestMapping参数——produces
指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回。
Header | 解释 | 示例 |
---|---|---|
Accpet | 指定客户端能够接收的内容类型 | Accept: text/plain, text/html |
示例:
@RequestMapping(value = “/testRestful”, method = {RequestMethod.PUT}, consumes = {“application/json”}, produces = {“application/json”})
5、获取请求参数
5.1、HttpServletRequest:传统方式获取请求参数
@RequestMapping("/login.do")
public String getParam(HttpServletRequest req, HttpServletResponse resp){
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println("username:" + username + ", password:" + password);
return "success";
}
5.2、通过注解@RequestParam获取参数
原理:利用Spring MVC的自动转换参数
@RequestMapping("/login.do")
public String getParam(@RequestParam("username") String username, @RequestParam("password") String password){
System.out.println("username:" + username + ", password:" + password);
return "success";
}
5.3、通过实体类获取参数
通过@RequestBody获取引用类型参数
@RequestMapping("/login.do")
public String getParam(@RequestBody User user){
System.out.println("username:" + user.getUserName()+ ", password:" + user.getPassword());
return "success";
}
5.4、获取List、Map集合参数
通过前端传入具体集合数据,Spring MVC自动映射数据到对应的参数上
示例:
@RequestMapping("/delete") public R delete(@RequestBody Long[] ids){ return R.ok(); } @RequestMapping("/getUsers") public R delete(@RequestBody List<User> users){ return R.ok(); } @RequestMapping("/getUserMaps") public R delete(@RequestBody Map<String, User> uMap){ return R.ok(); }
6、GET和POST乱码问题
web.xml配置编码过滤器
<!-- 使用Spring中提供的字符编码过滤器,解决post乱码问题-->
<filter>
<filter-name>encodingFilter</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>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
7、其他注解
7.1、@PathVariable
请求参数放在URL路径中,通过{}占位,具体参数通过@PathVariable获取路径中的指定参数。
例如获取请求路径中的id值:
@RequestMapping("/testPathVariable/{id}") public String testPathVariable(@PathVariable("id") Integer id){ }
作用:
用于绑定URL中的占位符。例如:请求URL中 /testPathVariable/{id},这个{id}就是URL占位符。
URL支持占位符是 spring3.0 之后加入的。是Spring MVC支持 REST 风格的 URL 的一个重要标志。
7.2、@RequestHeader
用于获取请求头
示例:
@RequestMapping("/getRequestHeader") public String getRequestHeader(@RequestHeader(value="Accept", required=false) String header){ return "success"; }
属性:
- value和name:请求头名称
- required:是否必须有这个请求头(默认 true)
7.3、@CookieValue
获取指定cookie名称的传入参数
示例:
@RequestMapping("/getCookie") public String getCookie(@CookieValue(value="JSESSIONID",required=false) String cookie){ return "success"; }
属性:
- value和name:指定cookie的名称
- required:是否必须有此cookie(默认 true)
7.4、@PostMapping
请求的发送方式只能是POST请求。
7.5、@GetMapping
请求的发送方式只能是GET请求。
7.6、@JsonFormat
处理响应的JSON数据
属性:
- pattern :指定响应时间日期的格式
- timezone:指定响应的时区,以GMT(格林尼治时间)为准,我们所处东八区,所以 GMT + 8
@DateTimeFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd", timezone="GMT+8")
private Date createDate;
7.7、@DateTimeFormat
处理接收的日期数据
属性:
- pattern:指定日期的格式
- iso:ISO日期时间
- ISO.DATE:“2000-10-31”
- ISO.TIME:“01:30:00.000-05:00”
- ISO.DATE_TIME:“2000-10-31T01:30:00.000-05:00”
- ISO.NONE:不应用基于ISO的日期时间格式
@DateTimeFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd", timezone="GMT+8")
private Date createDate;
7.8、@RequestBody
获取请求体的JSON格式的字符串内容
属性:
- required:是否必须有此参数(默认 true)
@RequestMapping("/login.do")
public String getParam(@RequestBody User user){
System.out.println("username:" + user.getUserName()+ ", password:" + user.getPassword());
return "success";
}
7.9、@CrossOrigin
解决跨域问题
属性:
- origins:可访问的ip列表
- maxAge:准备响应前的缓存持续的最大时间
@CrossOrigin(origins = "http://domain.com", maxAge = 3600)
@RestController
@RequestMapping("/api/sys")
public class LoginControler {
@GetMapping("/login")
public User login() { }
}
7.10、@ResponseBody
- 方法的返回值不再跳转界面,直接作为返回的数据
- 返回的数据自动由ObjectMapper转换为JSON数据
@ResponseBody
@RequestMapping("ajax")
public String ajax(User user) {
return user.getUserName();
}
8、Spring MVC响应处理
8.1、request、response请求转发和重定向
利用request请求转发,利用response重定向。
@RequestMapping("demo")
public void demo(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 请求转发
//request.getRequestDispatcher("/forward.jsp").forward(request,response);
// 响应重定向
response.sendRedirect(request.getContextPath()+"/redirect.jsp");
}
8.2、返回字符串转发和重定向
返回字符串告诉DispatcherServlet跳转的路径
- 默认返回是请求转发。
- 在路径之前放上一个redirect:关键字,就是重定向。
- /表示当前项目下。
@RequestMapping("demo")
public String demo() throws Exception {
return "redirect:/demo.jsp"; // 重定向
}
8.3、利用View视图转发和重定向
- new InternalResourceView("/forwardPage.jsp"); // 请求转发
- new RedirectView(req.getContextPath()+"/redirectPage.jsp"); // 重定向,利用RedirectView跳转后的链接中可以获取一些数据
@RequestMapping("demo")
public View demo(HttpServletRequest req) {
View view = null;
// 请求转发
//view = new InternalResourceView("/forwardPage.jsp");
// 重定向
view = new RedirectView(req.getContextPath()+"/redirectPage.jsp");
return view;
}
8.4、利用ModelAndView转发和重定向
@RequestMapping("demo")
public ModelAndView demo(HttpServletRequest req) {
ModelAndView mv = new ModelAndView();
// 请求转发
//mv.setViewName("forward:/forwardPage.jsp");
//mv.setView(new InternalResourceView("/forwardPage.jsp"));
// 重定向
//mv.setViewName("redirect:/redirectPage.jsp");
mv.setView(new RedirectView(req.getContextPath()+"/redirectPage.jsp"));
return mv;
}
8.5、利用@ResponseBody响应JSON数据
- 方法的返回值不再跳转界面,直接作为返回的数据
- 返回的数据自动由ObjectMapper转换为JSON数据
@ResponseBody
@RequestMapping("ajax")
public String ajax(User user) {
return user.getUserName();
}
8.6、类上用@RestController
相当于@Controller+@ResponseBody两个注解的结合
表示类中所有的方法都返回JSON数据
9、Spring MVC九大内置对象和四大作用域
9.1、九大内置对象
attributes:底层是Map。
名称 | 说明 | 常用方法 |
---|---|---|
page | 代表JSP本身 (很少使用) | |
pageContext | 获取JSP页面的out、request、response、session、application等对象 | pageContext.getOut()、pageContext.getRequest()、 pageContext.getResponse()、pageContext.getSession()、 pageContext.get/setAttribute() |
request | 代表客户端的请求信息,包括请求头、请求参数、请求方式、系统信息等,作用域为一次请求 | request.getParameter(String key)、 request.getParameterValues(String key)、request.getRequestDispatcher(String path) |
session | 服务器会为每一次会话生成一个session对象 | session.getAttributre(String key)、session.setAttribute(String key, Object value)、 session.incalidate()// 强制session对象失效、session.removeAttribute(String key)、session.getId()、session.get/setMaxInactiveInterval()// 获取或设置session失效的活动时间 |
application | 在整个应用都有效,直到应用服务关闭才失效 | application.getAttribute(String key)、application.setAttribute(String key, Object value)、application.getRealPath(String path)// 获取相对路径 |
response | 服务器的响应,只在JSP页面生效 | response.addCookie(Cookie cookie)、response.setContentType(String type)、response.setCharacterEncoding(String charset)、response.sendRedirect(String location) |
out | 用于在浏览器上输出信息,管理应用服务器上的输出缓冲区 | |
config | 获取服务器的配置信息,可通过pageContext.getServletConfig()获取config对象 (很少使用) | |
exception | 显示异常信息,只有包含isErrorPage="true"的页面才能被使用 (很少使用) |
9.2、四大作用域
名称 | 作用域 |
---|---|
page | 在当前页面有效 |
request | 在当前请求有效 |
session | 在当前会话有效 |
application | 在整个项目内有效 |
9.2.1、作用域传参示例:
@RequestMapping("setData")
public String setData(HttpServletRequest request, HttpSession session){
List<User> users = userService.findAllUser();
// 向三个域中放入数据
request.setAttribute("msg", "reqMessage");
request.setAttribute("users", users);
session.setAttribute("msg", "sesssionMessage");
session.setAttribute("users", users);
ServletContext application = request.getServletContext();
application.setAttribute("msg", "applictionMessage");
application.setAttribute("users", users);
// 跳转至show.jsp
return "/show.jsp";
}
10、拦截器
10.1、拦截器的实现:
- 通过实现HandlerInterceptor接口,或继承HandlerInterceptor接口的实现类(如HandlerInterceptorAdapter)来定义。
- 通过实现WebRequestInterceptor接口,或继承WebRequestInterceptor接口的实现类来定义。
10.1.1、通过实现HandlerInterceptor接口,或继承HandlerInterceptor接口的实现类(如HandlerInterceptorAdapter)来定义。
实现HandlerInterceptor接口:
ublic class MyInterceptor implements HandlerInterceptor {
/**
* 进入控制单元方法之前执行
* @param request 请求对象
* @param response 响应对象
* @param handler 目标要调用的 Handler
* @return 返回 true放行,返回 false拦截
* @throws Exception
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor preHandle");
// 可实现功能:判断登录状态
return true;
}
/**
* 在进行数据处理之后和做出响应之间进行这个方法的调用
* @param modelAndView controller响应的结果,视图和数据
*/
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor postHandle");
// 可实现功能:过滤脏话
}
/**
* 在进行页面渲染的时候执行
* 无论controller是否出现异常,都会执行的方法
* 一般来说都做一些资源释放工作
*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor afterCompletion");
}
}
继承HandlerInterceptor接口的实现类(如HandlerInterceptorAdapter ,目前是废弃过时的):
public class MyInterceptor1 extends HandlerInterceptorAdapter {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return false;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
10.1.2、通过实现WebRequestInterceptor接口,或继承WebRequestInterceptor接口的实现类来定义。
实现WebRequestInterceptor接口:
public class MyInterceptor2 implements WebRequestInterceptor {
public void preHandle(WebRequest webRequest) throws Exception {
}
public void postHandle(WebRequest webRequest, ModelMap modelMap) throws Exception {
}
public void afterCompletion(WebRequest webRequest, Exception e) throws Exception {
}
}
继承WebRequestInterceptor接口的实现类来定义(不推荐),以下是WebRequestInterceptor的实现类:
10.2、拦截器的添加:
继承WebMvcConfigurationSupport:
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
@Value("${fileupload.path}")
private String fileUploadPath;
@Resource
private RequestInterceptor requestInterceptor;
@Resource
private LocaleInterceptor localeInterceptor;
private static List<String> excludePath = Arrays.asList("/api/userLogin/login", "/js/**", "/css/**", "/images/**", "/md/**", "/plug-in/**", "/static/**");
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/images/**").addResourceLocations("file:" + fileUploadPath + "images/");
registry.addResourceHandler("/md/**").addResourceLocations("file:" + fileUploadPath + "md/");
// addResourceHandlers:指的是对外暴露的访问路径。
// addResourceLocations:指的是内部文件放置的目录。
// 设置后,在浏览器窗口输入http://localhost:8008/myimg/文件名
// 就可以访问到本地地址为E:/IdeaProjects/my-server-save-img/文件名 的文件了。
super.addResourceHandlers(registry);
}
/**
* 添加拦截器:RequestInterceptor、LocaleInterceptor
*
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// log.info("TokenConfig addInterceptors tokenInterceptor");
registry.addInterceptor(requestInterceptor)
.addPathPatterns("/**")//指定该类拦截的url
.excludePathPatterns(excludePath);//过滤静态资源
registry.addInterceptor(localeInterceptor)
.addPathPatterns("/**");//指定该类拦截的url
}
}
11、异常处理
- 使用:@ExceptionHandler ,处理局部异常(只限当前Controller层)
- 使用:@ControllerAdvice+@ExceptionHandler,处理全局异常(优先级低于局部异常,如果同时存在,访问局部处理)
- 使用:SimpleMappingExceptionResolver
- 实现HandlerExceptionResolver,自定义异常处理解析