5、springMVC

springMVC

什么是SpringMVC?

SpringMVC是Spring框架内置的MVC的实现。SpringMVC是Spring的一个MVC框架。MVC框架,它解决了WEB开发中常见的问题,如参数的接收,文件的上传下载,表单验证,国际化等等。而且使用起来简单,与spring无缝集成。还支持restful风格的URL请求。采用了松散耦合可插拔组件结构,比其他 MVC 框架更具扩展性和灵活性。

springMVC的作用

MVC模式:(Model-View-Controller):为了解决后台代码与页面代码的分离

SpringMVC底层就是的Servlet,SpringMVC就是对Servlet进行更深层次的封装

传统的web开发模式

目前web应用中,99%的项目都用到MVC模式。

MVC的web开发

WEB开发从20世纪90+年代开始,也是使用MVC模式。在最原始的MVC上有一些改进

优秀的框架改变了这种模式,将model更广泛的使用,这样会比原始的mvc好多了.

像现在一些优秀的mvc的框架,如Struts2,springMVC

在客户端提交也使用了模型来请求参数

spring MVC 也实现的相关的功能

MVC的入门案例

  1. 导入两个支持mvc的jar

  1. springmvc的配置

    <!-- 配置包扫描 -->
    <context:component-scan base-package="cn.zj.springmvc"/>
    <!-- 开启springmvc的注解 -->
    <mvc:annotation-driven/>
  2. springmvc的前端控制器

    <!-- 配置前端控制器 -->
    <servlet>
    <servlet-name>springMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 初始化:读出springmvc.xml的配置文件 -->
    <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    </servlet>
    <servlet-mapping>
    <servlet-name>springMVC</servlet-name>
    <url-pattern>/</url-pattern>
    </servlet-mapping>
  3. 使用@RequestMapping注解映射访问路径

springmvc的执行流程

SpringMVC流程:
01、用户发送出请求到前端控制器DispatcherServlet。
02、DispatcherServlet收到请求调用HandlerMapping(处理器映射器)。
03、HandlerMapping找到具体的处理器(可查找xml配置或注解配置),生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet。
04、DispatcherServlet调用HandlerAdapter(处理器适配器)。
05、HandlerAdapter经过适配调用具体的处理器(Handler/Controller)。
06、Controller执行完成返回ModelAndView对象。
07、HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet。
08、DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)。
09、ViewReslover解析后返回具体View(视图)。
10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11、DispatcherServlet响应用户。

涉及组件分析:
1、前端控制器DispatcherServlet(不需要程序员开发),由框架提供,在web.xml中配置。
作用:接收请求,响应结果,相当于转发器,中央处理器。
2、处理器映射器HandlerMapping(不需要程序员开发),由框架提供。
作用:根据请求的url查找Handler(处理器/Controller),可以通过XML和注解方式来映射。
3、处理器适配器HandlerAdapter(不需要程序员开发),由框架提供。
作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler。
4、处理器Handler(也称之为Controller,需要工程师开发)
注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler。
作用:接受用户请求信息,调用业务方法处理请求,也称之为后端控制器。
5、视图解析器ViewResolver(不需要程序员开发),由框架提供
作用:进行视图解析,把逻辑视图名解析成真正的物理视图。
SpringMVC框架支持多种View视图技术,包括:jstlView、freemarkerView、pdfView等。
6、视图View(需要工程师开发)
作用:把数据展现给用户的页面
View是一个接口,实现类支持不同的View技术(jsp、freemarker、pdf等)

对静态资源的访问

如果在项目的web.xml中servlet的拦截路径使用/进行拦截,会对静态资源进行拦截。原因是将父类的web.xml中的路径也有一个/,那里是对静态资源进行放行,这样就冲突了,所以请求路径可以写成*.do,要想请求,以.do结尾的都可以访问。在我们的注解@RequestMapping中的url可以不用写.do,会默认加上的,好后期的维护。

@RequestMapping

@RequestMapping("")默认使用的是value的属性。@RequestMapping(value="")

两种限制

  1. 参数的限制

    @RequestMapping的params={"username","password"}可以对请求的参数限制name属性,如果与里面的参数不一致,就会报400参数的异常,格式还有params={"username=张三"}限制username只能等于张三,params={"!username"}限制没有这个属性的都可以,params={"userid!=123"}参数不是指定值。

  2. 方法的限制

    @RequestMapping的method属性,如果method="get",那么请求的方式必须是get,如果是其他的请求方式,就会报405方法不匹配的错误。

/**
* @RequestMapping 映射请求路径的注解
* method:方法的限制
* params:参数的限制
* @return
*/
@RequestMapping(value = "/method",method = RequestMethod.POST,params = {"username","password"})
public ModelAndView handleRequest() {
//新建一个ModelAndView对象
ModelAndView mv = new ModelAndView();
//数据传输
mv.addObject("msg", "你好呀");
//请求转发
mv.setViewName("/WEB-INF/jsp/hello.jsp");
//返回ModelAndView
return mv;
}

数据绑定

数据绑定通俗的说就是接收参数。

@Controller
@RequestMapping("/request")
public class HelloController{
/**
* 传统的接收参数
*/
@RequestMapping("/method2")
public void method2(HttpServletRequest req,HttpServletResponse resp) {
String username = req.getParameter("username");
String age = req.getParameter("age");
System.out.println("username : " +username);
System.out.println("age : " +age);
}
/**
* 直接通过形参传进来
* 这样就不用强转类型了
*/
@RequestMapping("/method3")
public ModelAndView method3(String username,Integer age) {
System.out.println("123");
System.out.println("username:"+username);
System.out.println("age:"+age);
return null;
}
/**
* 前台不同名接收参数
* 使用@RequestParam
* @param username
* @param age
* @return
*/
@RequestMapping("/method4")
public ModelAndView method4(@RequestParam("name") String username,@RequestParam("age") Integer age) {
System.out.println("username:"+username);
System.out.println("age:"+age);
return null;
}
/**
* 接收数组
*/
@RequestMapping("/method5")
public ModelAndView method5(@RequestParam("name") String username,@RequestParam("age") Integer age,String[] hobbys){
System.out.println("username:"+username);
System.out.println("age:"+age);
System.out.println("hobbys:"+Arrays.toString(hobbys));
return null;
}
/**
* 接收pojo对象
*/
@RequestMapping("/method6")
public ModelAndView method6(User user){
System.out.println(user);
return null;
}
/**
* 接受map:只能接收单个值
*/
@RequestMapping("/method7")
public ModelAndView method7(@RequestParam Map<String,Object> map){
System.out.println(map);
return null;
}
}

响应传值

/* 有 A 和 B
* 请求转发:
* 1.A请求转发到B时,地址栏还是A(地址栏的url不会改变)
* 2.A可以通过请求对象共享数据给B
* 3.可以转发访问到WEB-INF下的资源
* 4.不可以跨域
* 重定向:
* 1.A重定向到B时,地址栏的url改变成B
* 2.A不可以通过请求对象共享数据给B
* 3.不可以转发访问到WEB-INF下的资源
* 4.可以跨域
*/
/**
* 传递的方式,只发生在请求转发中
* @throws IOException
* @throws ServletException
* 1.传统的请求对象
* 2.ModelAndView中的setViewName(path):默认是请求转发,通过addObject()传值
* 3.返回一个字符串的形式:通过Model来传值,返回值就是转发路径,默认是请求转发(用得最多)
*
*/
  1. void方式:传统的方式使用request里面设置属性,通过请求转发传值给页面。

    @RequestMapping("/method1")
    public void method(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    req.setAttribute("name", "旺财");
    req.getRequestDispatcher("/WEB-INF/jsp/hello.jsp").forward(req, resp);
    }
  2. 通过ModelAndView转值

    @RequestMapping("/method2")
    public ModelAndView method2() {
    ModelAndView mv = new ModelAndView();
    mv.addObject("name", "菲菲");
    mv.setViewName("/WEB-INF/jsp/hello.jsp");
    return mv;
    }
  3. Model:通过springmvc的Model对象,可以通过addAtrribute方法,传递数据给页面

    @RequestMapping("method3")
    public String method3(Model md) {
    md.addAttribute("name", "兮兮");
    return "/WEB-INF/jsp/hello.jsp";
    }
  4. 通过JSON字符串的形式(用得最多的):spring有专门对处理JSON字符串传值的方式进行了封装

    /**
    * @ResponseBody:将响应内容设为普通字符串
    * 请求转发的路径是:/WEB-INF/jsp/response+返回首字母小写作为文件夹.jsp
    */
    @RequestMapping("/method")
    @ResponseBody
    public User method() {
    User user = new User();
    user.setEmail("www@qq.com");
    user.setPassword("aaa");
    user.setUsername("旺财");
    return user;
    }
    /**
    * 还可以返回一个list集合
    */
    @RequestMapping("/method2")
    @ResponseBody
    public List<User> method2(){
    List<User> users = new ArrayList<User>();
    for (int i = 0; i < 10; i++) {
    User user = new User();
    user.setEmail("www@qq.com");
    user.setPassword("aaa");
    user.setUsername("旺财");
    users.add(user);
    }
    return users;
    }

处理乱码问题

springMVC可以对乱码进行处理,只需要设置springmvc的过滤器,对编码进行设置。

<!-- 解决中文乱码问题:springmvc的过滤器
post方式的设置
get方式:在tomcat8.x后就默认是utf-8,之前还需要到tomcat的配置文件servlet.xml中配置,
找到端口号8080的那一行,加上URIEncoding="UTF-8"
-->
<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>

支持RESTFUL风格的url请求

请求的地址栏是这样的,这种url地址称为伪静态的页面。

代码如下:可用来接收id等。

/**
* @PathVariable:路径变量
*
* 伪静态的请求
*
*/
@RequestMapping("/method1/{id}")
public ModelAndView method(@PathVariable("id") Long id) {
System.out.println(id);
return null;
}

请求转发和重定向

首先,我们返回一个url地址时,有重复的url路径,我们可以设置前后两边的url,那我们只需要返回我们的逻辑的url就行了。

<!--配置视图解析器:开启前缀后缀的命名 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 配置前缀 -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- 配置后缀 -->
<property name="suffix" value=".jsp"/>
</bean>

我们这样做有一个缺陷,就是我们想转发到其他的页面转发不了,因此我们需要在返回的时候在逻辑的url前加上forward:这样表示让配置的视图解析器无效。这样就能跳出了。

我们在使用ModelAndView和返回String类型时,默认都是请求转发,有没有重定向的呢?当然有,不过ModelAndView只能请求转发,返回String类型的只需要在前面加上redirect:就表示请求转发。

/**
* 只有返回值是String类型的才可以
*/
@RequestMapping("/method")
public String method(Model md) {
md.addAttribute("name", "兮兮");
return "hello";
}
/**
* 设置重定向,或者转发到其他的页面时
* 已forward为前缀:请求转发
* 以redirect为前缀:重定向
*
*/
@RequestMapping("/method2")
public String method2() {
return "redirect:http://www.jd.com";
}
@RequestMapping("/method3")
public String method3() {
return "forward:/response.jsp";
}

文件上传和下载

springmvc给我们集成了对文件上传下载的简化操作。

文件上传

在web开发中一般会有文件上传的操作

一般JavaWeb开发中文件上传使用的 Apache组织的Commons FileUpload组件

SpringMVC中使用 MultipartFile file对象接受上传文件,必须保证 后台参数的名称和表单提交的文件的名称一致

文件上传必须条件

\1. 表单必须post

\2. 表单必须有 file 文件域

\3. 表单的 enctype="multipart/form-data"

准备jar

配置文件的上传解析器

<!-- 创建文件上传的解析器
id:必须是multipartResolver,不然会执行错误
-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 配置文件上传时限定大小,#{1024*1024}可以计算并返回值 -->
<property name="maxUploadSize" value="#{1024*1024}"/>
</bean>

Controller

@Controller
public class UploadController {
@RequestMapping("/upload")
public ModelAndView upload(MultipartFile headImg, String username) {
/*
* 使用springmvc集成的上传组件,MultipartFile接口 1.创建上传的解析器,
* org.springframework.web.multipart.commons.CommonsMultipartResolver
* id必须是:multipartResolver
*
* 2.调用MultipartFile的transferTo(dest)来上传文件到指定位置
*/
System.out.println("username:" + username);
System.out.println(headImg.getContentType());// 获取上传文件的MEMI类型,比如text/html
System.out.println(headImg.getName());// 获取文件上传时的文件名的name属性
System.out.println(headImg.getOriginalFilename());// 获取文件上传时的文件名
System.out.println(headImg.getSize());// 获取文件上传的大小
File path = new File("F:/upload");
// 判断文件夹是否存在
if (!path.exists()) {
// 不存在就创建
path.mkdirs();
}
File dest = new File(path, headImg.getOriginalFilename());
// 开始接收文件的,dest所要存储的位置
try {
headImg.transferTo(dest);
} catch (IllegalStateException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
/*
* 多文件上传,同名的name使用一个MultipartFile[]来接收,循环遍历即可 不同名只能一个一个的去接收。
*/
@RequestMapping("/uploads")
public ModelAndView uploads(MultipartFile[] headImgs) {
// 先指定存放的位置,只需要创建一个即可
File path = new File("f:/upload");
if (!path.exists()) {
path.mkdirs();
}
for (int i = 0; i < headImgs.length; i++) {
// 使用UUID解决文件上传重名问题
String fileName = UUID.randomUUID().toString().replaceAll("-", "");
String extension = FilenameUtils.getExtension(headImgs[i].getOriginalFilename());
//还需要夹后缀
File dest = new File(path,fileName+"."+extension);
try {
headImgs[i].transferTo(dest);
} catch (IllegalStateException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
public static void main(String[] args) {
// 使用UUID解决文件上传重名问题
String fileName = UUID.randomUUID().toString().replaceAll("-", "");
System.out.println(fileName);
String path = "c:/aa/aa/tom.jpg";
String extension = FilenameUtils.getExtension(path);
System.out.println(FilenameUtils.getBaseName(path));
System.out.println(extension);
System.out.println(FilenameUtils.getName(path));
}
}

文件下载

使用到Common-io的一个工具类IOUtils.copy(input,output)

@RequestMapping("/download")
public void download(String fileName, HttpServletResponse resp,HttpServletRequest req) throws IOException {
/*
* 需要用到输入输出流
*/
// 从磁盘上读到内存中
InputStream input = new FileInputStream("F:/music/" + fileName);
// 通过响应流给浏览器
ServletOutputStream output = resp.getOutputStream();
//处理浏览器编码的问题
//获取响应头
String userAgent = req.getHeader("User-Agent");
if(!userAgent.contains("MSIE")) {
//根据编码格式转为字节数组
byte[] bytes = fileName.getBytes("utf-8");
//根据字节和编码转为字符串;ISO-8859-1是国际标准的编码,W3C标准
fileName = new String(bytes, "ISO-8859-1");
}
//响应的内容应该是以附件的形式响应给浏览器(设置响应头)
// 响应下载时的名称
resp.setHeader("Content-Disposition", "attachment;filename=" + fileName);
// 通过IOUtils将输入流拷贝给输出流
IOUtils.copy(input, output);
}

springmvc的拦截器

跟web的三大组件之一的Filter差不了的功能,只是springmvc对拦截的规则更加简洁。

<!-- 配置拦截器 -->
<mvc:interceptors>
<!-- 可以配置多个拦截器 -->
<mvc:interceptor>
<!-- 配置拦截规则
/*:一级拦截,只对最后的一级目录拦截,比如delete.do,list.do
/**:多级拦截,比如/user/delete.do,/user/list.do,/a/ab/a.do
-->
<mvc:mapping path="/**"/>
<!-- 对不需要拦截的放行
多个之间用逗号隔开
-->
<mvc:exclude-mapping path="/user/login.do"/>
<!-- 拦截器的类型 -->
<bean class="cn.zj.ssm.interceptor.CheckLoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>

SpringMVC 控制器 Controller的生命周期

Spring 容器创建的对象默认 都是单例 对象

SpringMVC对象 Controller的对象的创建有三种情况

Request : 在用户的一次请求中生效(用户每次请求都会创建Controller对象)多例

Session : Controller对象在一次会话中创建一个对象

如果控制器中有成员变量 设置或者赋值操作,必须使用 request 返回

问题:Web表现层控制器到底使用单例singleton还是request,还是session?

答 :

首先看控制器有没有成员变量要并发修改,如果有,必须使用request/prototype

早期的Struts2框架使用的就是成员变量接受请求参数,对应的控制器对象必须是多例

SpringMVC接受请求参数,作为方法形式参数,每次请求都会重新没参数开辟新的内存存储这个参数,不会产生线程安全问题

所以SpringMVC使用默认的单例即可

posted @   站着说话不腰疼  阅读(37)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· Open-Sora 2.0 重磅开源!
点击右上角即可分享
微信分享提示