SpringMVC
1.SpringMVC快速入门
- MVC是一种软件架构模式,分离了哑无逻辑和显示界面,包括model 模型、view视图、controller控制器,其中model负责处理业务逻辑,封装试题,view视图展现内容,controller控制器负责调度分发,如接受请求,调用模型,发出响应
- SpingMVC是一种基于java的实现MVC 设计模式的轻量级web框架,属于SpringFrameWork的后续产品,融合在Spring Web Flow中,通过一套注解让一个简单的java成为处理请求的控控制器,无须实现任何借口,同时还支持RESTful编程风格的请求
- 封装了原来的servlet中的共有行为,实现了参数封装,视图转发等功能
- 快速入门
- 创建web项目,导入SpringMVC相关坐标
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>springmvc_quick</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <!--springMVC坐标--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.5.RELEASE</version> </dependency> <!--servlet坐标--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!--jsp坐标--> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.2</version> <scope>provided</scope> </dependency> </dependencies> </project>
- 配置SpringMVC前端控制器 DispathcerServlet
<?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"> <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring_mvc.xml</param-value> </init-param> <!-- 启动时加载--> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
- 编写Controller类和视图页面,使用注解配置Controller类中业务方法的映射地址
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>成功响应</title> </head> <body> <%--视图页面--%> <h2>成功响应springmvc</h2> </body> </html>
package com.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class UserController { // 编写Controller类,跳转到/quick地址并响应 @RequestMapping("/quick") public String quick(){ System.out.println("收到请求了"); return "success.jsp"; } }
- 配置SpringMVC核心文件 spring-mvc.xml
<!-- 开启注解扫描-->
<context:component-scan base-package="controller"/> - 执行流程:Tomcat接受客户端发出的请求,解析请求资源地址,创建代表请求对象和响应对象,调用目标资源,此时通过前置DispatchServlet进行地址解析,将请求发送给对应的/quick的controller,执行响应,将响应传给前置DispatchServlet解析,组装成对应的响应发送给客户端
2.springmvc执行流程
- 执行组件(springMVC三大组件是处理器映射器,处理器适配器,和视图解析器;四大组件是前端控制器,视图解析器,处理器适配器和处理器映射器)
- 前端控制器DispatcherServlet:用户请求到达前端控制器,由它调用其他组件处理用户的请求,降低了组件之间的耦合性
- 处理器映射器HandlerMapping:通过映射器对处理器进行适配,对更多类型的处理器进行执行,采用了适配器模式
- 处理器Handler:由开发者编写,是真正执行请求的控制器,对具体业务进行处理
- 视图解析器ViewResolver:负责将处理结果生成view视图,它首先将逻辑视图名解析层物理视图名,找到对应的页面地址,再生成view视图对象,对view进行渲染并将结果通过页面展示给用户
- 视图view:有开发者编写,springMVC支持很多的view视图类型,如jstlView、freemarkerView、pdfView等,最常用的是jsp,通过页面标签或页面模板技术将页面展示给用户
-
自定义配置三大组件
- 处理器适配器和处理器映射器
<!-- 配置处理器适配器和处理器映射器 ,可以对json格式解析,进行了方法的增强--> <mvc:annotation-driven/>
- 视图解析器,一遍用于逻辑视图到物理视图地址的拼接
-
执行流程
- 用户发送请求到前端控制器DispatcherServlet,前端控制器收到请求后调用处理器映射器HandlerMapping
- 处理器映射器根据请求,通过xml配置或注解方式找到对应的处理器,生成处理器对象和处理器拦截器,并返回给前端控制器
- 前端控制器调用处理器适配器HandlerAdapter,由适配器通过适配方式找到具体的控制器即处理器(后端控制器)
- 处理器执行完返回ModelAndView,处理器适配器将后端控制器的执行结果ModelAndView返还给前端控制器
- 前端控制器将ModelAndView传给ViewReslover视图解析器,由视图解析器后返还具体的view
- 前端控制器根据view进行视图渲染,并响应客户
-
常用注解
-
Controller:SpringMVC基于Spring容器,所以在进行SpringMVC操作时,需要将Controller存储到Spring容器中,如果使用@Controller注解标注的话,就需要使用
<!--配置注解扫描--> <context:component-scan base-package="com.controller"/>
-
RequestMapping:用于建立请求url和处理请求方法之间的对应关系
- 类上:URL的一级访问目录,以/开头,方便模块化管理
-
方法上:URL的二级访问目录,和一级目录组成一个完整的 URL 路径
- value:用于指定请求的URL。它和path属性的作用是一样的
- method:用来限定请求的方式
- params:用来限定请求参数的条件,如{“accountName”} 表示请求参数中必须有accountName;{“money!100”} 表示请求参数中money不能是100
- 访问页面或跳转时,WEB-INF下默认是安全资源,在其包下的资源不可以通过url直接获取,只可以在服务器内转发
-
Controller:SpringMVC基于Spring容器,所以在进行SpringMVC操作时,需要将Controller存储到Spring容器中,如果使用@Controller注解标注的话,就需要使用
3. SpringMVC请求
- springMVC可以接收的参数类型:基本参数类型、对象参数类型、数组参数类型、集合参数类型。
<%-- Created by IntelliJ IDEA. User: Administrator Date: 2021/7/17 Time: 14:51 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>请求数据</title> </head> <body> <%--不要有空格--%> <a href="${pageContext.request.contextPath}/data?username=大明&age=19" >普通数据类型</a> <a href="${pageContext.request.contextPath}/object?name=大明&id=2" >对象类型</a> <form method="post" action="${pageContext.request.contextPath}/array"> <input type="checkbox" name="ids" value="1">1<br> <input type="checkbox" name="ids" value="2">2<br> <input type="checkbox" name="ids" value="3">3<br> <input type="checkbox" name="ids" value="4">4<br> <input type="checkbox" name="ids" value="5">5<br> <input type="submit" value="数组元素"> </form> <form method="post" action="${pageContext.request.contextPath}/listormap"> 关键词 <input type="text" name="keyword"> 用户id<input type="text" name="user.id">姓名<input type="text" name="user.name"> 第一个集合<input type="text" name="userList[0].id"><input type="text" name="userList[0].name"> 第二个集合<input type="text" name="userList[1].id"><input type="text" name="userList[1].name"> 第一个map<input type="text" name="map['u1'].id"><input type="text" name="map['u1'].name"> 第二个map<input type="text" name="map['u2'].id"><input type="text" name="map['u2'].name"> <input type="submit" value="集合数据类型"> </form> <form method="post" action="${pageContext.request.contextPath}/date"> 自定义转换器 生日<input type="text" name="date"> <input type="submit" value="自定义转换器 生日"> </form> <a href="${pageContext.request.contextPath}/page?pageNo = 2" >对象类型</a> </body> </html>
package domain; import java.util.List; import java.util.Map; public class QueryVo { private String keyword; private User user; private List<User> userList; private Map<String,User> map; @Override public String toString() { return "QueryVo{" + "keyword='" + keyword + '\'' + ", user=" + user + ", userList=" + userList + ", map=" + map + '}'; } public String getKeyword() { return keyword; } public void setKeyword(String keyword) { this.keyword = keyword; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } public List<User> getUserList() { return userList; } public void setUserList(List<User> userList) { this.userList = userList; } public Map<String, User> getMap() { return map; } public void setMap(Map<String, User> map) { this.map = map; } } package domain; public class User { private int id; private String name; @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } }
package domain;
public class User {
private int id;
private String name;
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}package controller; import domain.QueryVo; import domain.User; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.util.Arrays; import java.util.Date; @Controller public class UserController { @RequestMapping("/data") public String data(String username,Integer age){ System.out.println(username); System.out.println(age); return "success"; } @RequestMapping("/object") public String object(User user){ System.out.println(user); return "success"; } @RequestMapping(path = "/array") public String array(Integer[] ids){ System.out.println(Arrays.toString(ids)); return "success"; } @RequestMapping("/listormap") public String listormap(QueryVo queryVo){ System.out.println(queryVo); return "success"; } }
- controller中的业务方法会对参数值进行自动映射匹配和类型转换,将参数由string类型转为对应的要求类型
- 中文乱码过滤器:当post请求时数据会出现乱码,需要通过中文过滤器进行编码过滤
<!-- 中文过滤器--> <filter> <filter-name>CharacterEncodingFilter</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>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
- 自定义转换器:springMVC提供了一些常用的类型转换器,对于特有的行为让那个开发者自定义处理
package converters; import org.springframework.core.convert.converter.Converter; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class ConverterDate implements Converter<String,Date> { @Override public Date convert(String birth) { SimpleDateFormat sdf= new SimpleDateFormat("yyyy-MM-dd"); Date date =null; try { date = sdf.parse(birth); } catch (ParseException e) { e.printStackTrace(); } return date; } }
<!-- 配置处理器适配器和处理器映射器 ,可以对json格式解析,进行了方法的增强--> <mvc:annotation-driven conversion-service="conversionService"/> <!-- 配置自定义转换器--> <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <bean class="converters.ConverterDate"/> </set> </property> </bean>
package controller; import domain.QueryVo; import domain.User; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.util.Arrays; import java.util.Date; @Controller public class UserController { @RequestMapping("/date") public String date(Date date){ System.out.println(date); return "success"; } }
- 请求常用注解
- @RequestParam:当请求参数名与controller业务方法的参数名不一致时,可以显示绑定,设置默认值和数据非必须
- @RequestHeader:获取请求头数据
- @CookieValue:获取cookie中的数据
- 获取相关的Servlet相关API:SpringMVC支持使用原始ServletAPI对象作为控制器方法的参数进行注入,如request,response和session
package controller; import domain.QueryVo; import domain.User; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.util.Arrays; import java.util.Date; @Controller public class UserController { @RequestMapping("/page") // required 设置是否必须传递参数,默认值为true;如果设置了默认值,值自动改为false public String page(@RequestParam(name="pageNo", defaultValue ="1", required = false) Integer pageNum, @RequestParam(defaultValue = "5") Integer pageSize){ System.out.println(pageNum); System.out.println(pageSize); return "success"; } @RequestMapping("/header") public String header(@RequestHeader("cookie") String cookie){ System.out.println(cookie); return "success"; } @RequestMapping("/cookie") public String cookie(@CookieValue("JSESSIONID") String cookie){ System.out.println(cookie); return "success"; } @RequestMapping("/springAPI") public String springAPI(HttpServletRequest request, HttpServletResponse response, HttpSession session){ System.out.println(request); System.out.println(response); System.out.println(session); return "success"; } }
4. springMVC响应
- 页面跳转方式包括返回字符串逻辑视图、void原始ServletAPI和ModelAndView方式
- void原始ServletAPI格式
// 原始Servlet @RequestMapping("/servlet") public void servlet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); response.setContentType("text/html;utf-8"); response.getWriter().println("我收到额"); request.setAttribute("username", "原始servlet"); // request.getRequestDispatcher("success.jsp").forward(request, response); }
- 返回的数据格式可以通过直接返回字符串数据方式或将对象、集合转为json格式返回
- 使用字符串逻辑视图实现页面的跳转实际上是执行请求转发,相当于forward
- forward关键字实现转发,则路径必须是实际视图URL,相当于 request.getRequestDispatcher(“url”).forward(request,response) ,既可以转发到jsp,也可以转发到其他的控制器方法
@RequestMapping("/forward") public String forward(Model model) { model.addAttribute("username", "转发"); return "forward:success.jsp"; }
- Redirect重定向实现重定向,路径必须是实际视图URL,虚拟目录springMVC框架自动完成拼接
@RequestMapping("/redirect") public String redirect(Model model) { model.addAttribute("username", "重定向"); // 重定向的页面不会展示username的值 return "redirect:success"; }
-
ModelAndView有两种方式实现,推荐方式二
- 在Controller中方法创建并返回ModelAndView对象,并且设置视图名称
- 在Controller中方法形参上直接声明ModelAndView,无需在方法中自己创建,在方法中直接使用该对象设置视图,同样可以跳转页面
//ModelAndView方式一 @RequestMapping("/model1") public ModelAndView model1() { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("username", "modelAndView方式一"); modelAndView.setViewName("success"); return modelAndView; } //ModelAndView方式二 @RequestMapping("/model2") public ModelAndView model2(ModelAndView modelAndView) { modelAndView.addObject("username", "modelAndView方式二"); modelAndView.setViewName("success"); return modelAndView; }
-
@SessionAttributes:适用于在多个请求之间共有数据,在控制器类上标注并配置需要在session中存放的数据,Spring MVC将存放在model中对应的数据暂存到HttpSession
@RequestMapping("/forward") public String forward(Model model) { model.addAttribute("username", "转发"); return "forward:success.jsp"; } @RequestMapping("/returnString") public String returnString() { return "success"; }
5.静态资源的开启
- 问题:当有静态资源需要加载,如query文件,通过谷歌开发者工具抓包发现没有加载到jquery文件
- 原因:SpringMVC的前端控制器DispatcherServlet的url-pattern配置的是 /(缺省),代表对所有的静态资源都进行处理操作,这样就不会执行Tomcat内置的DefaultServlet处理
- 解决:配置文件放行静态资源
<!--方式一:在springmvc配置文件中指定放行资源--> <mvc:resources mapping="/js/**" location="/js/"/> <mvc:resources mapping="/css/**" location="/css/"/> <mvc:resources mapping="/img/**" location="/img/"/> <!--方式二(推荐):在springmvc配置文件中开启DefaultServlet处理静态资源--> <mvc:default-servlet-handler/>