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的入门案例
- 导入两个支持mvc的jar
-
springmvc的配置
<!-- 配置包扫描 --> <context:component-scan base-package="cn.zj.springmvc"/> <!-- 开启springmvc的注解 --> <mvc:annotation-driven/> -
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> -
使用
@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="")
两种限制
-
参数的限制
@RequestMapping的
params={"username","password"}
可以对请求的参数限制name属性,如果与里面的参数不一致,就会报400
参数的异常,格式还有params={"username=张三"}
限制username只能等于张三,params={"!username"}
限制没有这个属性的都可以,params={"userid!=123"}
参数不是指定值。 -
方法的限制
@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来传值,返回值就是转发路径,默认是请求转发(用得最多) * */
-
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); } -
通过ModelAndView转值
@RequestMapping("/method2") public ModelAndView method2() { ModelAndView mv = new ModelAndView(); mv.addObject("name", "菲菲"); mv.setViewName("/WEB-INF/jsp/hello.jsp"); return mv; } -
Model:通过springmvc的Model对象,可以通过addAtrribute方法,传递数据给页面
@RequestMapping("method3") public String method3(Model md) { md.addAttribute("name", "兮兮"); return "/WEB-INF/jsp/hello.jsp"; } -
通过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使用默认的单例即可
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 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 重磅开源!