# 1 回顾web ## 阶段1:三层架构 !\[image-20250922141721473\](https://woniumd.oss-cn-hangzhou.aliyuncs.com/web/wangyongcheng/20250922141721.png) 1 浏览器发送一个请求给后端服务器,后端服务器使用Servlet来接收请求和数据 2 将后端服务器Servlet拆分成三层,分别是\`web\`、\`service\`和\`dao\` \* web层主要由servlet来处理,负责页面请求和数据的收集以及响应结果给前端 \* service层主要负责业务逻辑的处理 \* dao层主要负责数据的增删改查操作 3 后端采用了MVC设计模式,将其设计为\`controller\`、\`view\`和\`Model\` \* controller负责请求和数据的接收,接收后将其转发给service进行业务处理 \* service根据需要会调用dao对数据进行增删改查 \* dao把数据处理完后将结果交给service,service再交给controller \* controller根据需求组装成Model和View,Model和View组合起来生成页面转发给前端浏览器 ## 阶段2: ajax 阶段1的弊端: 1 整个过程为同步 2 后端是响应整个页面给前端 综上所述,当请求数据变多,业务变复杂时,整个响应速度很慢 !\[image-20250922141731022\](https://woniumd.oss-cn-hangzhou.aliyuncs.com/web/wangyongcheng/20250922141731.png) 阶段2: 1 因为是异步调用,所以后端不需要返回view视图,将其去除 2 前端如果通过异步调用的方式进行交互,后台就需要将返回的数据转换成json格式进行返回 SpringMVC==主要==负责的就是 \* controller如何接收请求和数据 \* 如何将请求和数据转发给业务层 \* 如何将响应数据转换成json发回到前端 # 2 springMVC入门 \* SpringMVC是一种基于Java实现MVC模型的轻量级Web框架 \* 优点 \* 使用简单、开发便捷(相比于Servlet) \* 灵活性强 !\[image-20260402145551865\](https://woniumd.oss-cn-hangzhou.aliyuncs.com/web/wangyongcheng/20260402145552.png) 执行流程: 1. 浏览器发出请求,直接打到DispatcherServlet 2. HandlerMapping根据URL查找对应的Handler(Controller) 3. HandlerAdapter按照既定规则执行Handler 4. Handler执行完返回ModelAndView:Model装数据、View装逻辑名 5. ViewResolver把逻辑视图名翻译成真实路径 6. DispatcherServlet把Model数据传入View,完成渲染 7. 最终视图返回给浏览器 8. 若过程中抛出异常,由HandlerExceptionResolver统一处理 ## 2.1 创建maven项目 javaEnterprise: !\[image-20250924090142766\](https://woniumd.oss-cn-hangzhou.aliyuncs.com/web/wangyongcheng/20250924090142.png) ## 2.2 依赖 \~\~\~xml 4.0.0 com.example springmvc_demo2 1.0-SNAPSHOT springmvc_demo2 war 17 17 jakarta.servlet jakarta.servlet-api 5.0.0 provided org.springframework spring-webmvc 6.2.6 org.apache.maven.plugins maven-war-plugin 3.3.2 \~\~\~ spring-webmvc 依赖 spring核心,因此,无需再在pom.xml中写spring的核心依赖 !\[image-20250924155743611\](https://woniumd.oss-cn-hangzhou.aliyuncs.com/web/wangyongcheng/20250924155743.png) \~\~\~xml springMVC:版本: 如果servlet用的是jakarta.servlet(高版本)--对应tomcat10.x--jdk17 则webmvc要用到6.x 如果servlet用的是javax.servlet(低版本),--对应tomcat8.x--jdk1.8- 则webmvc要用5.x org.springframework spring-webmvc 5.2.10.RELEASE org.springframework spring-webmvc 6.2.6 \~\~\~ 由于我们采用的是tomcat11.x版本,jdk17,因此选择高版本6.2.6 ## 2.3 结构 !\[image-20250924164338224\](https://woniumd.oss-cn-hangzhou.aliyuncs.com/web/wangyongcheng/20250924164338.png) \~\~\~ DispatcherServlet是Spring MVC框架中最重要的Servlet(前端控制器),它负责接收所有的HTTP请求并将它们分发给相应的处理器(Controller)进行处理。 \~\~\~ ## 2.4 DispatcherServletInitConfig(web容器配置类 ) \~\~\~java package com.woniu.config; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer; //web容器配置类 代替 web.xml //tomcat启动容器时会自动执行该类的方法来实现容器初始化 public class DispatcherServletInitConfig extends AbstractDispatcherServletInitializer { //加载springmvc配置类(管理controller) @Override protected WebApplicationContext createServletApplicationContext() { //初始化基于注解的WebApplicationContext对象 AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); //加载指定配置类--起始配置类 ctx.register(SpringmvcConfig.class); return ctx; } /\* 设置由springmvc控制器处理的请求映射路径 /表示DispatcherServlet将处理所有请求路径 /api/\*表示DispatcherServlet将处理/api开头的请求路径 \*/ @Override protected String\[\] getServletMappings() { return new String\[\]{"/\*"}; } //返回null代表不使用根应用上下文 //这里用于管理其他层的配置,比如管理Service和DAO @Override protected WebApplicationContext createRootApplicationContext() { return null; } } \~\~\~ ## 2.5 SpringmvcConfig (springMVC配置类) \~\~\~java package com.woniu.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.woniu.controller") public class SpringmvcConfig { } \~\~\~ ## 2.6 UserController---业务 \~\~\~java package com.woniu.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller @RequestMapping("/user") public class UserController { @RequestMapping("/") @ResponseBody public String save(){ System.out.println("user save ..."); return "{\\"username\\":\\"aaa\\"}"; } } 说明: @RequestMapping("/..") ---》请求地址 1 先匹配controller类上的@RequestMapping的地址 2 然后匹配该类中方法上的@RequestMapping的地址 @ResponseBody --返回json格式字符串 如果方法直接返回字符串,没有添加@ResponseBody,springmvc会把字符串当成页面的名称在项目中进行查找返回,因为不存在对应返回值名称的页面,所以会报404错误,找不到资源。 \~\~\~ ## 2.7 测试 !\[image-20250924202227673\](https://woniumd.oss-cn-hangzhou.aliyuncs.com/web/wangyongcheng/20250924202227.png) ## 说明 \*\*注意事项\*\* \* SpringMVC是基于Spring的,在pom.xml只导入了\`spring-webmvc\`jar包的原因是它会自动依赖spring相关坐标 \* AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化Web3.0容器的抽象类 \* AbstractDispatcherServletInitializer提供了三个接口方法供用户实现 \* createServletApplicationContext方法,创建Servlet容器时,加载SpringMVC对应的bean并放入WebApplicationContext对象范围中,而WebApplicationContext的作用范围为ServletContext范围,即整个web容器范围 \* getServletMappings方法,设定SpringMVC对应的请求映射路径,即SpringMVC拦截哪些请求 \* createRootApplicationContext方法,如果创建Servlet容器时需要加载非SpringMVC对应的bean,使用当前方法进行,使用方式和createServletApplicationContext相同。 \* createServletApplicationContext用来加载SpringMVC环境 \* createRootApplicationContext用来加载Spring环境 # 3 springmvc工作流程解析 1 启动服务器初始化过程 \~\~\~ 1. 服务器启动,执行ServletContainersInitConfig类,初始化web容器 \* 功能类似于以前的web.xml 2. 执行createServletApplicationContext方法,创建了WebApplicationContext对象 \* 该方法加载SpringMVC的配置类SpringMvcConfig来初始化SpringMVC的容器 3. 加载SpringMvcConfig配置类 4. 执行@ComponentScan加载对应的bean \* 扫描指定包及其子包下所有类上的注解,如Controller类上的@Controller注解 5. 加载UserController,每个@RequestMapping的名称对应一个具体的方法 \* 此时就建立了/save路径和save方法的对应关系 6. 执行getServletMappings方法,设定SpringMVC拦截请求的路径规则 \* /代表所拦截请求的路径规则,只有被拦截后才能交给SpringMVC来处理请求 \~\~\~ 2 请求过程 \~\~\~ 1 发送 localhost:9977/user/save 请求, 2 web容器发现该请求满足SpringMVC拦截规则,将请求交给SpringMVC处理 3. 解析请求路径/user/save 4. 由/save匹配执行对应的方法save() 5. 执行save() 6. 检测到有@ResponseBody直接将save()方法的返回值作为响应体返回给请求方 \~\~\~ # 4 设置bean加载控制 在DispatcherServletInitConfig初始化web容器类中,createServletApplicationContext方法一般只管理controller层相关的bean: !\[image-20250924175338390\](https://woniumd.oss-cn-hangzhou.aliyuncs.com/web/wangyongcheng/20250924175338.png) \`\`\` SpringmvcConfig: \`\`\` !\[image-20250924175414665\](https://woniumd.oss-cn-hangzhou.aliyuncs.com/web/wangyongcheng/20250924175414.png) 而其他层,比如service和dao层我们需要在createRootApplicationContext方法中管理。---分开管理,结构清晰 更改DispatcherServletInitConfig容器配置类的createRootApplicationContext方法: \~\~\~java //返回null代表不使用根应用上下文 //这里用于管理其他层的配置,比如管理Service和DAO @Override protected WebApplicationContext createRootApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringConfig.class); return ctx; } \~\~\~ 在config下新建SpringConfig \~\~\~java @Configuration @ComponentScan({"com.woniu.service","com.woniu.dao"}) public class SpringConfig {} \~\~\~ # 5 参数接受 ## 5.1 前端为简单 get提交(url参数) 或者 post+表单 !\[image-20250924232258469\](https://woniumd.oss-cn-hangzhou.aliyuncs.com/web/wangyongcheng/20250924232258.png) !\[image-20250924232226799\](https://woniumd.oss-cn-hangzhou.aliyuncs.com/web/wangyongcheng/20250924232226.png) 1 正常接受:使用@RequestParam注解,值需要和前端匹配 \~\~\~java @RequestMapping("/save") @ResponseBody public String save(@RequestParam("name") String name, @RequestParam("age")int age){ System.out.println("name = " + name); System.out.println("age = " + age); return "hello"; } \~\~\~ 2 使用pojo接受 \~\~\~java @Data public class UserDto { private String name; private int age; } \~\~\~ \~\~\~java @RequestMapping("/save") @ResponseBody public String save(UserDto userDto){ System.out.println("name = " + userDto.getName()); System.out.println("age = " + userDto.getAge()); return "hello"; } \~\~\~ #### 5.1.1 补充@RequestParam的其他属性 required=true:代表必须传递(默认) defaultValue="" :代表默认值 \~\~\~ //映射请求地址 @RequestMapping("/login") //为了让springmvc返回字符串而非页面 @ResponseBody public String login(@RequestParam(value="name",required = false,defaultValue = "张三") String name, @RequestParam("age") String age){ System.out.println("login.."); System.out.println("name = " + name); System.out.println("age = " + age); return "hello"; } \~\~\~ ## 5.2 前端为数组 !\[image-20250924232955860\](https://woniumd.oss-cn-hangzhou.aliyuncs.com/web/wangyongcheng/20250924232955.png) !\[image-20250924233209239\](https://woniumd.oss-cn-hangzhou.aliyuncs.com/web/wangyongcheng/20250924233209.png) 1 正常接受:使用@RequestParam注解,值需要和前端匹配 \~\~\~ @RequestMapping("/save") @ResponseBody public String save(@RequestParam("hobbys") String\[\]hobbys){ System.out.println(Arrays.toString(hobbys)); return "hello"; } @RequestMapping("/save") @ResponseBody public String save(@RequestParam("hobbys") List hobbys){ System.out.println(hobbys.toString()); return "hello"; } \~\~\~ 2 pojo接受 \~\~\~java @Data public class UserDto { //数组、集合 都行 // private String \[\] hobbys; private List hobbys; } \~\~\~ \~\~\~ @RequestMapping("/save") @ResponseBody public String save(UserDto userDto){ System.out.println(userDto.getHobbys()); return "hello"; } \~\~\~ ## 5.3 前端为json格式对象 !\[image-20250924234846084\](https://woniumd.oss-cn-hangzhou.aliyuncs.com/web/wangyongcheng/20250924234846.png) 1 SpringMVC默认使用的是jackson来处理json的转换,所以需要在pom.xml添加jackson依赖 \~\~\~xml com.fasterxml.jackson.core jackson-databind 2.15.2 com.fasterxml.jackson.core jackson-core 2.15.2 com.fasterxml.jackson.core jackson-annotations 2.15.2 \~\~\~ 2 开启json数据类型自动转换 SpringmvcConfig: \~\~\~java @Configuration @ComponentScan("com.woniu.controller") //开启json数据类型自动转换 @EnableWebMvc public class SpringmvcConfig { } \~\~\~ 3 pojo \~\~\~ @Data public class UserDto { private String name; private int age; private List hobbys; } \~\~\~ 4 使用@RequestBody注解 \`\`\`java @RequestMapping("/save") @ResponseBody public String save(@RequestBody UserDto userDto){ System.out.println(userDto); return "hello"; } \`\`\` ## 5.4 前端为json格式数组 !\[image-20250924235727375\](https://woniumd.oss-cn-hangzhou.aliyuncs.com/web/wangyongcheng/20250924235727.png) \`\`\`java @RequestMapping("/save") @ResponseBody public String save(@RequestBody List list){ System.out.println(list); return "hello"; } \`\`\` # 5.5 get和post中文乱码处理 我们上课用的是高版本的tomcat,默认两者都处理了,这里说下如果是低版本的tomcat的情况: ### 1 get:统一在tomcat服务器中配置,无需修改代码(server.xml) \~\~\~ \~\~\~ ### 2 post: 使用过滤器处理 DispatcherServletInitConfig中:新增重写方法:getServletFilters \~\~\~java //web容器配置类 代替 web.xml //tomcat启动容器时会自动执行该类的方法来实现容器初始化 public class DispatcherServletInitConfig extends AbstractDispatcherServletInitializer { @Override protected Filter\[\] getServletFilters() { CharacterEncodingFilter filter = new CharacterEncodingFilter(); filter.setEncoding("UTF-8"); return new Filter\[\]{filter}; } \~\~\~ ## 5.6 总结 @RequestBody与@RequestParam区别 \* 区别 \* @RequestParam用于接收url地址传参,表单传参【application/x-www-form-urlencoded】 \* @RequestBody用于接收json数据【application/json】 \* 应用 \* 后期开发中,发送json格式数据为主,@RequestBody应用较广 \* 如果发送非json格式数据,选用@RequestParam接收请求参数 # 6 日期类型参数接受 ## 6.1 前端为get地址栏 或 post+表单 !\[image-20250925002545310\](https://woniumd.oss-cn-hangzhou.aliyuncs.com/web/wangyongcheng/20250925002545.png) !\[image-20250925002633921\](https://woniumd.oss-cn-hangzhou.aliyuncs.com/web/wangyongcheng/20250925002633.png) 1 正常接受: \~\~\~java @RequestMapping("/save") @ResponseBody public String save(@DateTimeFormat(pattern = "yyyy-MM-dd") @RequestParam("birth") Date birth){ System.out.println(birth); return "hello"; } \~\~\~ 2 pojo接受 \~\~\~ @Data public class UserDto { @DateTimeFormat(pattern = "yyyy-MM-dd") private Date birth; \~\~\~ \~\~\~java @RequestMapping("/save") @ResponseBody public String save(UserDto userDto){ System.out.println(userDto); return "hello"; } \~\~\~ ## 6.2 前端为post+json !\[image-20250925003242309\](https://woniumd.oss-cn-hangzhou.aliyuncs.com/web/wangyongcheng/20250925003242.png) \~\~\~ @Data public class UserDto { @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8") private Date birth; \~\~\~ \~\~\~ @RequestMapping("/save") @ResponseBody public String save(@RequestBody UserDto userDto){ System.out.println(userDto); return "hello"; } \~\~\~ # 7 响应 ## 7.1 响应页面 (了解) 1 在 SpringmvcConfig配置类中 配置视图解析器 放入IOC中 \~\~\~ @Configuration @ComponentScan("com.woniu.controller") //开启json数据类型自动转换 @EnableWebMvc public class SpringmvcConfig { @Bean public ViewResolver viewResolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/"); // /代表页面放在webapp根目录下 resolver.setSuffix(".jsp"); //代表后缀为jsp return resolver; } } \~\~\~ 2 webapp根目录下新建jsp: !\[image-20250925004617435\](https://woniumd.oss-cn-hangzhou.aliyuncs.com/web/wangyongcheng/20250925004617.png) 3 controller类中 ,返回String,并且需要去掉@ResponseBody注解 \~\~\~java @RequestMapping("/save") public String save(){ System.out.println("save.."); return "home"; //不能写home.jsp,因为视图解析器已经帮我们配置了后缀为jsp } \~\~\~ ## 7.2 响应纯文本 (了解) \~\~\~java @RequestMapping("/save") @ResponseBody public String save(){ System.out.println("save.."); return "hello"; } \~\~\~ ## 7.3响应json User: \~\~\~java @Data public class User { private Long id; private String username; private String password; private Integer age; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") private Date birth; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") private Date createTime; } \~\~\~ \~\~\~java @RequestMapping("/save") @ResponseBody public User save(){ System.out.println("save.."); User user = new User(); user.setId(10000L); user.setUsername("aaa"); user.setPassword("123"); user.setBirth(new Date()); user.setAge(11); user.setCreateTime(new Date()); return user; } \~\~\~ # 8 RESTful风格 常规风格 post: 新增: /save ,修改: /update,删除 /delete... get: 查询: /find.. restful风格 增删改查: /users 根据GET查询、POST新增、PUT修改、DELETE删除对方法的请求方式进行限定 @GetMapping @PostMapping @PutMapping @DeleteMapping 路径参数风格: /user/1/张三 后端对应路径 :/user/{id} 后端对应形参 :@PathVariable("id") Long id ## 1 get提交: 前端: !\[image-20250925011554649\](https://woniumd.oss-cn-hangzhou.aliyuncs.com/web/wangyongcheng/20250925011554.png) 后端: \`\`\` @Controller @RequestMapping("/user") public class UserController { \`\`\` \~\~\~ @GetMapping("/{id}/{name}") @ResponseBody public String find( @PathVariable("id") Long id, @PathVariable("name") String name) { System.out.println(id); System.out.println(name); return "hello"; } \~\~\~ ## 2 post提交: 模拟既有路径传参,又有json !\[image-20250925012426609\](https://woniumd.oss-cn-hangzhou.aliyuncs.com/web/wangyongcheng/20250925012426.png) 后端: \~\~\~ @PostMapping("/{id}") @ResponseBody public String save(@PathVariable("id") Long id,@RequestBody UserDto userDto) { System.out.println(id); System.out.println(userDto); return "hello"; } \~\~\~ ## 3 @PutMapping 省 ## 4 @DeleteMapping 省