一、SpringMVC
1.1 什么是SpringMVC
Java开源框架,Spring Framework的一个独立模块。
MVC框架,在项目中开辟MVC层次架构
对控制器中的功能包装简化扩展践行工厂模式,功能架构在工厂之上。
1.2 MVC架构
1.2.1 概念
名称 | 职责 |
---|---|
Model | 模型:承载数据,并对用户提交请求进行计算的模块。分为两类,一类称为数据承载Bean,一类称为业务处理Bean。所谓数据承载Bean是值实体类,专门承载业务数据的,如Student、User等。而业务处理Bean则是指Service或Dao对象,专门用于处理用户提交请求的 |
View | 视图:渲染数据,生成页面。对应项目中的Jsp , html 等 作用是与用户交互 展示数据 |
Controller | 控制器:用于将用户请求转发给相应的Model进行处理,并处理Model的计算结果向用户提供相应响应 |
1.2.2 MVC工作流程:
用户通过View页面向服务端发送请求,可以是表单请求、超链接请求、AJAX请求等。
服务端Controller控制器接收到请求后对请求进行解析,找到相应的Model对用户请求进行处理。
Model处理后,将处理结果再交给Controller。
Controller接到处理结果后,根据处理结果找到要作为向客户端发回的响应View页面。页面经渲染后,再发给客户端。
1.2.3 优点
MVC是现下软件开发中的最流行的代码结构形态;
人们根据负责的不同逻辑,将项目中的代码分成 M V C 3个层次;
层次内部职责单一,层次之间耦合度低;
符合低耦合 高内聚的设计理念。也实际有利于项目的长期维护。
二、开发流程
2.1 导入依赖
<!--——————————加入springMVC依赖—————————-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
2.2 配置核心(前端)控制器(web.xml)
作为MVC框架,首先要解决的是:如何能够收到请求!
所以MVC框架大都会设计一款前端控制器,选型在 Servlet 或 Filter两者之一,在框架最前沿率先工作,接收所有请求。
此控制器在接收到请求后,还会负责springMVC的核心的调度管理,所以既是前端又是核心。
- 补充:DispatcherServlet前端控制器,是框架提供的,作用统一处理请求和响应,整个流程的控制中心,是由它来调用其他组件处理用户的请求。
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">
<!--前端控制器-->
<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:springmvc.xml</param-value>
</init-param>
<!-- Servlet启动时刻:可选 -->
<!--DispatcherServlet前端控制器是springmvc中非常重要的一个组件,内置了很多初始化的工作,所以让他随着服务器的启动而启动
提前把初始化工作做好,避免第一次访问的时候速度很慢
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc</servlet-name>
<!--
这里设置的是前端控制器可以处理的请求路径/表示拦截处理jsp以外的所有请求
也就是说处理以.jsp结尾的请求其他请求都要经过前端控制器,这里注意不要写成/*,/*表示匹配所有路径
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 此过滤器会进行:request.setCharactorEncoding("utf-8"); -->
<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>
</web-app>
2.3 后端控制器
等价于之前定义的Servlet。
@Controller // 交由spring创建bean对象
@RequestMapping("/hello") // 访问路径
public class HelloController {
@RequestMapping("/hello1")
public String hello(){
return "index"; // 跳转到index.jsp
}
@RequestMapping("/hello2")
public String hello2(){
return "pages/main"; // 跳转到pages包下的main.jsp
}
}
2.4 springmvc.xml文件配置
<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">
<!--—— 告知springmvc哪些包中存在被注解的类 ——-->
<context:component-scan base-package="com.ymk.controller"/>
<!--—— 注册注解开发驱动 ——-->
<mvc:annotation-driven/>
<!-- 视图解析器
作用:1.捕获后端控制器的返回值="index"
2.解析: 在返回值的前后 拼接 ==> "/index.jsp"
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--—— 前缀 ——-->
<property name="prefix" value="/"/>
<!--—— 后缀 ——-->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
2.5 浏览器访问
URL地址栏输入:
- http://localhost:8080/hello/hello1
- http://localhost:8080/hello/hello2
三、接受请求参数
3.1基本类型参数
请求参数和方法的形参同名即可。
- springMVC默认可以识别的日期字符串格式为: YYYY/MM/dd HH:mm:ss
- 通过@DateTimeFormat可以修改默认日志格式
@RequestMapping("/test1")
public String test1Param(Integer id, String name, Boolean gender,@DateTimeFormat(pattern = "yyyy-MM-dd") Date birthday){
// springmvc自动接收参数,并且转换参数类型
System.out.println(id);
System.out.println(name);
System.out.println(gender);
System.out.println(birthday);
return "index";
}
浏览器访问: http://localhost:8080/.../test1?id=001&name=张三&gender=true&birthday=2002-12-11
3.2实体收参
请求参数和实体类的属性需要同名。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String name;
private Boolean gender;
/**
* 设置日期格式
*/
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
}
@RequestMapping("/test2")
public String test2Param(User user){
System.out.println(user);
return "index";
}
浏览器访问:http://localhost:8080/.../test2?id=001&name=张三&gender=true&birthday=2000-11-22
3.3 数组收参
简单类型数组
<form action="http://localhost:8080/param/test3">
<input type="checkbox" name="hobby" value="fb">足球
<input type="checkbox" name="hobby" value="bb">篮球
<input type="checkbox" name="hobby" value="vb">排球
<input type="submit" value="提交">
</form>
@RequestMapping("/test3")
public String test3Param(String[] hobby){
for (String s : hobby) {
System.out.println(s);
}
return "index";
}
浏览器访问:http://localhost:8080/.../test3?hobby=football&hobby=basketball
3.4 集合收参
@Data
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";
}
浏览器访问:
post请求:http://.../test4?userList[0].id=111&users[0].name=tom&users[0].gender=false&users[0].birthday=2000-04-02&users[1].id=2&....
3.5 路径参数
@RequestMapping("/hello/{id}")
// @PathVariable将{id}路径匹配到值赋给id参数
// 路径名和参数名相同则@PathVariable("id")可简写为 @PathVariable
public String testParam5(@PathVariable("id") Integer id){
System.out.println("id:"+id);
return "index";
}
// http://localhost:8989/.../hello/10 {id}匹配到10
注意:@PathVariable将{id}路径匹配到值赋给id参数
路径名和参数名相同则@PathVariable("id")可简写为 @PathVariable
@RequestMapping("/hello/{username}")
public String testParam6(@PathVariable("username") String name){//将{username}路径匹配到的值赋给name参数
System.out.println("username:"+name);
return "index";
}
// http://localhost:8989/.../hello/tom {username}匹配到tom
3.6中文乱码
1.页面中字符集统一
- JSP : <%@page pageEncoding="utf-8" %>
- HTML : <meta charset="UTF-8">
2.tomcat中字符集设置,对get请求中,中文参数乱码有效
3.在web.xml中设置此filter,对post请求中,中文参数乱码有效
<!-- 此过滤器会进行:request.setCharactorEncoding("utf-8"); -->
<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转发
@Controller
@RequestMapping("/forw")
public class ForwardController {
@RequestMapping("/test1")
public String test1(HttpServletRequest request){
System.out.println("test forward1");
// 这也是转发,是走视图解析器的转发
// return "index";
//请求转发传值通过request.setAttribute
request.setAttribute("name","zs");
// 这种是不走视图解析器的转发
return "forward:/page/success.jsp";
}
@RequestMapping("/test2")
public String testForward2(){
System.out.println("test forward2");
// 转发到 /forw/test1
return "forward:test1"; // 这种是相对路径(转发到本类中的test1)
// 转发到 /forw/test1
// return "forward:/forw/test1"; // 这种是绝对路径
}
}
- test1中model.addAttribute("name",name),在jsp中取值方式:${name}
- 注意:转发也可以不走视图解析器,如:forward:/page/success.jsp
4.2重定向
@Controller
@RequestMapping("/redir")
public class RedirectController {
@RequestMapping("/test1")
public String test1(HttpSession session){
System.out.println("test redirect1");
// 重定向传值通过session.setAttribute
session.setAttribute("name","ls");
// 这种是不走视图解析器的转发
return "redirect:/page/success.jsp";
}
@RequestMapping("/test2")
public String testForward2(){
System.out.println("test redirect2");
// 重定向到 /redir/test1
return "redirect:test1"; // 这种是相对路径(转发到本类中的test1)
// 重定向到 /redir/test1
// return "redirect:/redir/test1"; // 这种是绝对路径
}
}
4.3 跳转细节
- 在增删改之后,为了防止请求重复提交,重定向跳转
- 在查询之后,可以做转发跳转
五、传值
C得到数据后,跳转到V,并向V传递数据。进而V中可以渲染数据,让用户看到含有数据的页面。
- 转发跳转: Request作用域
- 重定向跳转:Session作用域
5.1 Request和Session
转发传给页面值
@RequestMapping("/test1")
public String testServlet(HttpServletRequest request, User user){
user.setName("张三");
request.setAttribute("user",user);
System.out.println("scope test1");
return "forward:/page/success.jsp";
//jsp中取值方式${user.name}
}
重定向传给页面值
@RequestMapping("test2")
public String testServlet2(HttpSession session,User user){
user.setName("李四");
session.setAttribute("user",user);
System.out.println("scope test1");
return "forward:/page/success.jsp";
//jsp中取值方式${user.name}
}
5.2 JSP中取值
//jsp中用EL表达式 取值即可
<fmt:formatDate value="${user.birth}" pattern="yyyy-MM-dd"/> <br/>
${user.birth} <br>
${age}
5.3Model
//model中的数据等价于放在了request中,会在V渲染之前,将数据复制一份给request
@RequestMapping("/test3")
public String testData(Model model){
// model中的数据等价于放在了request汇总
model.addAttribute("name", "王二1");
//转发带.jsp不走视图解析器
return "forward:/page/success.jsp";
//jsp中取值方式${name}
}
5.4 ModelAndView
//modelandview 封装了model和view
@RequestMapping("/test4")
public ModelAndView testData(){//返回值类型为ModelAndView
//新建ModelAndView对象
ModelAndView modelAndView = new ModelAndView();
// 使用model存储数据
modelAndView.addObject("age",18);
// 设置视图名,即如何跳转
modelAndView.setViewName("page/success"); //等价 return "page/success";
return modelAndView;
//jsp中取值方式${age}
}
5.5 @SessionAttributes[不用]
@SessionAttributes({"gender","name"}) :model中的 name和gender 会存入session中
SessionStatus 移除session
@Controller
@SessionAttributes({"gender","name"}) // model中的 name和gender 会存入session中
public class UserController {
@RequestMapping("/hello")
public String hello(Model m){
m.addAttribute("gender",true); // 会存入session
mv.addObject("name","zhj"); // 会存入session
return "index";
}
@RequestMapping("/hello2")
public String hello(SessionStatus status){
// 移除通过SessionAttributes存入的session
status.setComplete();
return "index";
}
}
5.6 补充
使用Map和ModelMap也可以在页面中也可以通过${user}取值。
@RequestMapping("/test5")
public String test5(Map<String,Object> map){
User user = new User();
user.setName("阿伟");
user.setBirthday(new Date());
map.put("user", user);
return "page/success";
}
@RequestMapping("/test6")
public String test6(ModelMap map){
User user = new User();
user.setName("阿伟");
user.setBirthday(new Date());
map.put("user", user);
return "page/success";
}
六、静态资源
6.1静态资源问题
静态资源:html,js文件,css文件,图片文件
静态文件没有url-pattern,所以默认是访问不到的,之所以可以访问,是因为,tomcat中有一个全局的servlet:org.apache.catalina.servlets.DefaultServlet,它的url-pattern是 "/",是全局默认的Servlet.。所以每个项目中不能匹配的静态资源的请求,有这个Servlet来处理即可。
在SpringMVC中DispatcherServlet也采用了 “/” 作为url-pattern, 则项目中不会再使用全局的Serlvet,则静态资源不能完成访问。
6.1.2 解决方案1
如果DispathcerServlet采用其他的url-pattern,web.xml中所有访问handler的路径都要以action结尾!!
<servlet>
<servlet-name>mvc9</servlet-name>
<servlet