Day44
总结
我是最棒的!基础不牢,地动山摇!
SpringMVC
它是一款MVC型的框架,主要解决控制层这一领域的问题,是Spring的一个子项目
SpringMVC的大体流程
客户端发送的所有请求都会被SpringMVC核心控制器进行拦截,核心控制器会得到客户端请求的uri,通过与映射的配置文件(SpringMVC的核心配置文件)中进行匹配,匹配成功则执行控制器对外暴露的资源,匹配不成功返回404
SpringMVC创建控制器的三种方式
首先是核心配置文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<!-- springMvc的核心配置文件 -->
<servlet>
<!-- springMvc的核心控制器 -->
<servlet-name>springMvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 指明加载指定的springMvc的核心配置文件 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext-mvc.xml</param-value>
</init-param>
<!-- 在服务器启动的时候就将核心控制器加载 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<!-- *.do 以do后缀结尾的请求都会进行拦截 不推荐使用 -->
<!--
<servlet-name>springMvc</servlet-name>
<url-pattern>*.do</url-pattern>
-->
<!-- /* 所有的请求都会进行拦截 jsp也会被拦截 不推荐使用 -->
<!--
<servlet-name>springMvc</servlet-name>
<url-pattern>/*</url-pattern>
-->
<!-- 所有的请求都会拦截,但是不会拦截jsp,推荐使用,但是设置了url-pattern为/之后,会覆盖掉tomcat里面的/,这样导致访问不了静态资源 -->
<servlet-name>springMvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 实现Controller接口(不推荐使用,每个Controller都需要在配置文件中写)
package cn.itsource.hello;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class HelloController implements Controller{
/**
* req 请求对象 底层自动注入值
* resp 响应对象 底层自动注入值
* ModelAndView 模型和视图
* Model 保存数据
* View 控制页面跳转
*/
@Override
public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse resp) throws Exception {
System.out.println(req);
System.out.println(resp);
ModelAndView mv = new ModelAndView();
//设置要保存的数据
mv.addObject("msg", "wdnmd");
//控制页面跳转
mv.setViewName("/hello.jsp");
return mv;
}
}
- 实现HttpRequestHandler接口(不推荐,跟原生Servlet一样的方式)
package cn.itsource.hello;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.HttpRequestHandler;
public class HelloController1 implements HttpRequestHandler{
@Override
public void handleRequest(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(req);
System.out.println(resp);
req.setAttribute("msg", "wdnmd");
req.getRequestDispatcher("/hello.jsp").forward(req, resp);
}
}
- 注解实现(使用最多,推荐)
package cn.itsource.hello;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
//@Controller 这个注解表示将控制器交给spring管理
@Controller
public class HelloController2{
//@RequestMapping 这个注解表示映射的路径
@RequestMapping("/hello2")
public ModelAndView hello2(){
ModelAndView mv = new ModelAndView();
mv.addObject("msg","sb");
mv.setViewName("/hello.jsp");
return mv;
}
}
注解@Controller实现需要在核心配置文件中进行配置
<?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">
<!--
组件扫描器
主要扫描:
@Controller这个注解
扫描cn.itsource下面的包以及子子孙孙包下面所有的类,上面是否有@Controller这个注解,如果有这个注解
就证明该类交给了spring管理
-->
<context:component-scan base-package="cn.itsource"/>
<!-- 静态资源放行 当你使用了该配置之后,@RequestMapping这个注解就失效了 -->
<mvc:default-servlet-handler/>
<!-- 开启springMvc特有注解的支持 -->
<mvc:annotation-driven/>
ResetFul风格
多用于查询和删除
传统风格:传递参数的时候
http://localhost:8080/user?id=1&name=xxx
ResetFul风格(逐渐流行)
http://localhost:8080/user/1/xxx
接收参数
SpringMVC 400错误 代表类型转换异常
接收参数的各种方式
package cn.itsource.param;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
@RequestMapping("/param")
public class ParamController {
/**
* 方式1 普通属性接收参数
* @param name
*/
@RequestMapping("/01")
public void save(String name){
System.out.println(name);
}
/**
* 方式2 对象接收参数
*/
@RequestMapping("/02")
public void param2(User user){
System.out.println(user);
}
/**
* 方式3 传统方式接收参数
*/
@RequestMapping("/03")
public void param3(HttpServletRequest req,HttpServletResponse resp,HttpSession session){
System.out.println(req);
System.out.println(resp);
System.out.println(session);
String name = req.getParameter("name");
System.out.println(name);
}
/**
* 方式4 resetful风格
*/
@RequestMapping("/04/{id}/{name}")
public void param4(@PathVariable("id")Long id,@PathVariable("name")String name){
System.out.println(name + " " + id);
}
/**
* 方式5 基本不用
*/
@RequestMapping("/05")
public void param5(@RequestParam("xxx")String name){
System.out.println(name);
}
}
处理POST提交中文乱码
SpringMVC已经为我们提供了一个过滤器来解决,首先要在web.xml中进行配置。配置完毕后不会出现中文乱码问题
<!-- 解决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>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
传输数据
传输数据的三种方式
package cn.itsource.tran;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping("/tran")
public class TranController {
/**
* 传递参数 方式1 Model
* @return
*/
@RequestMapping("/01")
public String tran01(Model model){
model.addAttribute("msg", "wdnmd");
model.addAttribute("user",new User(1L,"cdd"));
return "/hello.jsp";
}
/**
* 传递参数 方式2 ModelAndView
*/
@RequestMapping("/02")
public ModelAndView tran02(){
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "wdnmd");
mv.addObject("user",new User(1L,"cdd"));
mv.setViewName("/hello.jsp");
return mv;
}
/**
* 传递参数 方式3 传统方式
*/
@RequestMapping("/03")
public String tran03(HttpServletRequest req){
req.setAttribute("msg", "wdnmd");
req.setAttribute("user", new User(2L, "dnmd"));
return "/hello.jsp";
}
}
页面跳转
跳转的两种方式:请求转发和重定向 简写的时候都是请求转发
package cn.itsource.jump;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping("/jump")
public class JumpController {
/**
* 请求转发 方式1
* 简写 "/hello.jsp"
* 全写"forward:/hello.jsp"
* @return
*/
@RequestMapping("/01")
public String forward1(){
return "/hello.jsp";
}
/**
* 请求转发 方式2
* 简写 "/hello.jsp"
* 全写"forward:/hello.jsp"
* @return ModelAndView
*/
@RequestMapping("/02")
public ModelAndView forward2(){
ModelAndView mv = new ModelAndView();
mv.setViewName("/hello.jsp");
return mv;
}
/**
* 重定向 方式1
* 只能全写"redirect:/hello.jsp"
* @return
*/
@RequestMapping("/03")
public String redirect1(){
return "redirect:/hello.jsp";
}
/**
* 重定向 方式2
* 只能全写"redirect:/hello.jsp"
* @return
*/
@RequestMapping("/04")
public ModelAndView redirect2(){
ModelAndView mv = new ModelAndView();
mv.setViewName("redirect:/hello.jsp");
return mv;
}
}
视图解析器
将重复的部分抽取成前缀和后缀
例如/hello.jsp 前缀为/,后缀为.jsp 配置了视图解析器之后就可以简写为hello。但是如果有特殊需求,例如/hello.html,这个时候就需要全写为forward:/hello.html
SpringMVC核心配置文件中
<!-- 设置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 设置视图解析器的前缀和后缀 -->
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
package cn.itsource.viewresovler;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/view")
public class ViewResolverController {
/**
* 这里访问的是/hello.jsp
* @return
*/
@RequestMapping("/02")
public String forward2(){
return "hello";
}
/**
* 想要访问html文件,无法简写
* 视图解析器设置的前缀和后缀都无法满足需求的时候,我们就需要全写来实现
* @return
*/
@RequestMapping("/01")
public String forward1(){
return "forward:/hello.html";
}
}
上传和下载
上传
上传也需要在SpringMVC核心配置文件中配置一个文件解析器
<!-- 设置文件解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置文件最大上传的字节数 -->
<property name="maxUploadSize" value="5242880"/>
</bean>
上传的核心代码就是IOUtils.copy(is,os);
package cn.itsource.upload;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.multipart.MultipartFile;
@Controller
public class UploadController {
@RequestMapping(value = "/upload",method = RequestMethod.POST)
public String upload(String username,MultipartFile headImage,HttpServletRequest req) throws IOException{
//获取根路径
String rootPath = req.getServletContext().getRealPath("/");
//随机文件名
String fileName = System.currentTimeMillis() + "." + FilenameUtils.getExtension(headImage.getOriginalFilename());
//子路径
String storePath = "/upload/" + fileName;
//创建file对象
File file = new File(rootPath, storePath);
//判断文件夹是否存在,不存在就创建
if(!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
FileOutputStream fos = new FileOutputStream(file);
InputStream is = headImage.getInputStream();
//上传的核心代码
IOUtils.copy(is, fos);
return "upload";
}
}
下载
道理与之前学过的下载相似,核心代码与上传相同
package cn.itsource.download;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class DownloadController {
@RequestMapping("/download")
public String download(HttpServletRequest req,HttpServletResponse resp) throws IOException{
//设置文件的mime类型
resp.setContentType("application/x-msdownload");
//设置下载的文件名
resp.setHeader("Content-Disposition", "attachment; filename=1570621416809.jpg");
//获取下载的路径,从upload获取
String downloadPath = req.getServletContext().getRealPath("/upload");
//创建file对象
File file = new File(downloadPath, "1570621416809.jpg");
FileInputStream fis = new FileInputStream(file);
//获取输出流
ServletOutputStream os = resp.getOutputStream();
IOUtils.copy(fis, os);
return "upload";
}
}
SpringMVC的具体执行流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WVJGxGOx-1570626558022)(E:\ITSource\homework\JavaEE\1009Day44\resources\1.PNG)]
客户端发送请求,SpringMVC将所有的请求进行拦截,根据你的请求找到相应的处理对象(判断是走的配置还是注解请求),通过处理对象在找到相应的适配器对象,在执行目标方法之前,进行数据转换,格式化,验证等操作返回一个ModelAndView对象给SpringMVC核心控制器,然后找到对应的视图解析器,拼接我们的视图,拼接完毕后携带Model存储的数据,渲染到view界面(JSP等)中,最后展示给客户端
SpringMVC的具体执行流程
客户端发送请求,SpringMVC将所有的请求进行拦截,根据你的请求找到相应的处理对象(判断是走的配置还是注解请求),通过处理对象在找到相应的适配器对象,在执行目标方法之前,进行数据转换,格式化,验证等操作,然后再执行目标方法,返回一个ModelAndView对象给SpringMVC核心控制器,然后找到对应的视图解析器,拼接我们的视图,拼接完毕后携带Model存储的数据,渲染到view界面(JSP等)中,最后展示给客户端