为了使Spring可插入MVC架构,SpringFrameWork在Spring基础上开发SpringMVC框架,从而使用Spring进行WEB开发时可以选择使用Spring的SpringMVC框架作为web开发的控制器框架。SpringMVC是轻量级,典型的MVC框架,在整个MVC框架中充当控制器框架。
一、编程步骤
1、引入依赖
spring、spring-mvc(核心依赖)、servlet-api规范
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.3.5</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.4</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency>
2、配置web.xml
DispatchServlet 使用servlet标签 url-pattern /
<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、引入springmvc.xml配置文件
位置随便、名字随意。推荐使用springmvc.xml并放置在resources目录中
开启注解扫描、处理器映射器、处理器适配器、视图解析器
<!--开启注解扫描--> <context:component-scan base-package="com.icucoder.controller"/> <!--配置处理器映射器--> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/> <!--配置处理器适配器--> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/> <!--配置视图解析器--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--注入前缀和后缀--> <property name="prefix" value="/"/> <property name="suffix" value=".jsp"/> </bean>
现在使用下面的:
<!--开启注解扫描--> <context:component-scan base-package="com.icucoder.controller"/> <!--配置处理器映射器、适配器 参数类型转换等--> <mvc:annotation-driven/> <!--配置视图解析器--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--注入前缀和后缀--> <property name="prefix" value="/"/> <property name="suffix" value=".jsp"/> </bean>
4、开发controller 加入相关注解
@Controller public class HellorController { @RequestMapping("/hello") public String hello() { System.out.println("hello springmvc"); return "index"; } }
二、springmvc跳转方式
1、原始servlet技术中的跳转方式
1)forward跳转:请求转发,特点是服务器内部跳转,跳转后地址栏不变,一次跳转,跳转时可以使用request作用域传递数据。
2)redirect跳转:请求重定向,特点是客户端跳转,跳转之后地址栏改变,多次跳转,跳转过程不能使用request作用域传递数据。
2、SpringMVC中跳转方式
1)controller到JSP页面跳转(默认是forward跳转)
(1)forward方式:
具体语法:return "视图逻辑名"
@Controller public class ForwardAndRedirectController { //forward跳转到页面 @RequestMapping("/forward") public String forward(){ return "index"; } }
(2)redirect方式:
具体语法(使用redirect跳转不会经过视图解析器):return "redirect:视图全名"
@Controller public class ForwardAndRedirectController { //redirect跳转到页面 @RequestMapping("/redirect") public String redirect(){ return "redirect:/index.jsp"; } }
2)controller到controller直接跳转(相同控制器或者不同控制器)
(1)forward方式:
具体语法:return "forward:跳转Controller类上的@RequestMapping路径/跳转类中指定方法上@RequestMapping路径"
@Controller @RequestMapping("/forwardAndRedirect") public class ForwardAndRedirectController { //forward跳转到页面 @RequestMapping("/test1") public String test1(){ return "forward:/forwardAndRedirect/test2"; } @RequestMapping("/test2") public String test2(){ return "index"; } }
(2)redirect方式:
具体语法:return "redirect:跳转Controller类上的@RequestMapping路径/跳转类中指定方法上@RequestMapping路径"
@Controller @RequestMapping("/forwardAndRedirect") public class ForwardAndRedirectController { //forward跳转到页面 @RequestMapping("/test1") public String test1(){ return "redirect:/forwardAndRedirect/test2"; } @RequestMapping("/test2") public String test2(){ return "index"; } }
三、springmvc中参数接收
1、概述
1)语法:使用控制器中方法形参列表接收客户端的请求参数
2)要求:要求传递参数key要与对应方法的形参变量名一致才能完成自动赋值
2、不同类型参数接收
1)零散类型参数接收
方法的参数与路径上的参数保持一致即可。
@Controller @RequestMapping("/param") public class ParamController { //路径:/param/test?name=mayun @RequestMapping("/test") public String test(String name) { System.out.println("接收的参数:" + name); return "index"; } }
2)对象类型参数接收
直接将要接收对象作为控制器方法参数声明。springmvc封装对象时直接根据传递参数key与对象中属性名一致自动封装对象。
@Controller @RequestMapping("/param") public class ParamController { //路径:/param/test?id=1&name=mayun&sex=false @RequestMapping("/test") public String test(User user) { System.out.println("接收的参数:" + user); return "index"; } }
3)数组或集合类型参数接收
(1)数组:将要接收数组类型直接声明为方法的形参即可
要保证请求参数的多个参数key与声明数组变量名一致,springmvc会自动放入同一个数组中。
@Controller @RequestMapping("/param") public class ParamController { //路径:/param/test?names=mahuateng&names=mayun&names=haubin @RequestMapping("/test") public String test(String[] names) { for (String name : names) { System.out.println("接收的参数:" + name); } return "index"; } }
(2)集合(list set map):
springmvc不能直接通过形参列表方式接收结合类型参数。如果要接收集合类型的参数必须将集合放入对象中接收。
@Controller @RequestMapping("/param") public class ParamController { //路径:/param/test?lists=mahuateng&lists=mayun&lists=haubin //lists为User的一个属性 private List<String> lists; @RequestMapping("/test") public String test(User user) { user.getLists().forEach(str -> System.out.println(str)); return "index"; } }
推荐放入vo对象中接收集合类型。 vo= value object 值对象
四、数据传递机制
1、概述
1)数据怎么存
request(forward)、session(redirect)、application(redirect)、地址栏(redirect)
2)数据在页面如何获取
EL表达式。直接将request/response对象作为控制器方法参数声明即可获取。
3)在页面中获取的数据该如何展示
EL+JSTL标签展示
2、forward传递对象示例
1)使用HttpServletRequest传递对象
controller:
@Controller @RequestMapping("/attr") public class AttrController { @RequestMapping("/test") public String test(HttpServletRequest request, HttpServletResponse response) { String name = "小城"; request.setAttribute("username", name); return "index"; } }
jsp(可以引入JSTL标签):
<%@page contentType="text/html; utf-8" pageEncoding="UTF-8" isELIgnored="false" %> <html> <body> <h2>Hello World Index!</h2> <h3> ${requestScope.username} <hr> ${username} </h3> </body> </html>
2)使用Model传递对象
Model底层封装的是request,只能适用于forward跳转。
controller:
@Controller @RequestMapping("/attr") public class AttrController { @RequestMapping("/test") public String test(Model model) { String name = "小城"; model.addAttribute("username",name); return "index"; } }
jsp同上。
3、redirect传递对象示例
1)使用地址栏?拼接(适用于数据较少情况)
controller:
@Controller @RequestMapping("/attr") public class AttrController { @RequestMapping("/test") public String test() throws UnsupportedEncodingException { String name = "小城"; return "redirect:/index.jsp?name="+ URLEncoder.encode(name,"UTF-8"); } }
jsp:
<%@page contentType="text/html; utf-8" pageEncoding="UTF-8" isELIgnored="false" %> <html> <body> <h2>Hello World Index!</h2> <h3> ${param.name} </h3> </body> </html>
2)使用session对象
controller:
@Controller @RequestMapping("/attr") public class AttrController { @RequestMapping("/test") public String test(HttpServletRequest request) throws UnsupportedEncodingException { String name = "小城"; request.getSession().setAttribute("username",name); return "redirect:/index.jsp?name="+ URLEncoder.encode(name,"UTF-8"); } }
jsp:
<%@page contentType="text/html; utf-8" pageEncoding="UTF-8" isELIgnored="false" %> <html> <body> <h2>Hello World Index!</h2> <h3> ${sessionScope.username} </h3> </body> </html>
五、静态资源拦截问题
1、出现静态资源拦截问题的原因
由于在web.xml中配置springmvc的核心servlet DispatcherServlet时,url-pattern配置为“/”,因此会导致项目中所有“/”开头请求,均被作为控制器请求处理,这样会导致项目中的静态资源被拦截。
解决方案:
1)url-pattern配置成“/*.do”或者“/*.action”,使用这种指定后缀方式,后面访问路径的结尾必须加入指定后缀,如:http://127.0.0.1:8080/springmvc/user/findAll.do
2)url-pattern依然配置成“/”,但是在springmvc配置文件中加入如下配置:
<!--默认servlet处理机制--> <mvc:default-servlet-handler/>
默认servlet处理机制:先找静态资源,找到直接响应,找不到当做控制器处理,还是找不到再报错。
六、文件上传
文件上传指的就是将用户本地计算机中文件通过网络的形式上传到服务器上的过程。
1、springmvc中开发文件上传功能
1)开发一个可以进行文件上传的页面
其中form表单提交方式必须为post,form的enctype属性等于application/x-www-form-urlencoded表示文本,等于multipart/form-data表示二进制。
<h3>文件上传</h3> <form action="${pageContext.request.contextPath}/file/upload" method="post" enctype="multipart/form-data"> <input type="file" name="file"/> <input type="submit" value="开始上传"> </form>
2)开发Controller,其方法的使用MultipartFile来接收文件
@Controller @RequestMapping("/file") public class FileController { @RequestMapping("/") public String index() { return "file"; } @RequestMapping("/upload") public String upload(MultipartFile file, HttpServletRequest request) throws IOException { System.out.println(file.getOriginalFilename()); //根据相对路径获取绝对路径 String realPath = request.getSession().getServletContext().getRealPath("/file"); file.transferTo(new File(realPath, file.getOriginalFilename())); return "file"; } }
3)在springmvc配置文件中加入文件上传解析器配置
要求:文件上传解析器必须存在id,并且必须等于multipartResolver。
<!--文件上传解析器--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
4)引入文件上传依赖
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency>
2、文件上传细节处理
1)修改文件上传时文件的原始名称
//获取文件后缀 String extension= FilenameUtils.getExtension(file.getOriginalFilename()); //生成新的文件名 String newFileName=UUID.randomUUID().toString()+"."+extension;
2)便于文件管理,按照上传日期对进行上传文件管理
//生成当天日期目录 LocalDate now=LocalDate.now(); File dateDir=new File(realPath,now.toString()); if (!dateDir.exists()){ dateDir.mkdirs(); } file.transferTo(new File(dateDir, newFileName));
3)解决文件上传文件大小限制
在springmvc中默认上传文件没有大小限制。
<!--文件上传解析器--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!--设置文件大小限制 单位B 2M=2097152B--> <property name="maxUploadSize" value="2097152"/> </bean>
七、文件下载
用户将服务器中文件下载到自己本地计算机中过程称之为文件下载。
1、springmvc中开发文件下载功能
1)定位哪些文件可以被文件下载
2)将需要下载文件放入指定下载目录中
3)开发一个页面提供文件下载链接(一个请求只能对应一个响应,处理文件下载时方法不能有返回(即跳转页面,因为跳转页面也是一次响应),因为下载是文件响应流)
<h3>文件下载</h3> <a href="${pageContext.request.contextPath}/file/download?fileName=init">下载</a>
4)开发下载的Controller
@Controller @RequestMapping("/file") public class FileController { @RequestMapping("/") public String index() { return "file"; } //请求对应响应输出流 @RequestMapping("/download") public void download(String fileName, HttpServletRequest request, HttpServletResponse response) throws IOException { //根据下载相对目录获取下载文件的绝对目录 String realPath = request.getSession().getServletContext().getRealPath("/file"); //通过文件输入流读取文件 FileInputStream is = new FileInputStream(new File(realPath, fileName));
//文件内容字符编码处理 response.setContentType("text/plain;charset=UTF-8"); //获取响应输出流 ServletOutputStream os = response.getOutputStream(); //附件形式下载 attachment 附件,inline 在线打开 response.setHeader("content-disposition", "attachment;fileName=" + fileName); //处理下载流复制-传统写法 int len; byte[] b = new byte[1024]; while (true) { len = is.read(b); if (len == -1) break; os.write(b, 0, len); } //释放资源 is.close(); os.close(); } }
2、文件下载细节处理
1)使用工具类
//处理下载流复制-使用工具类
IOUtils.copy(is,os);
IOUtils.closeQuietly(is);
IOUtils.closeQuietly(os);
注意:操作流的用IOUtils类,操作文件的用FileUtils类。需要引入commons-fileupload依赖。
2)文件名称为中文处理
response.setHeader("content-disposition", "attachment;fileName=" + URLEncoder.encode(fileName, "UTF-8"));
八、springmvc与ajax集成
1、 案例实现思路
1)给页面上的按钮绑定一个单击事件,在单击事件触发时发送异步事件($.ajax、$.get()、$.post()、$.getJSON())
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>ajax</title> <script src="${pageContext.request.contextPath}/js/jquery-3.6.0.js"></script> </head> <body> <h3>all users info</h3> <button id="btn">获取所有用户信息</button> <script> $(function () { $("#btn").click(function () { $.get("${pageContext.request.contextPath}/user/all", function (res) { console.log(res); }, "JSON"); }); }) </script> </body> </html>
2)在控制器中将对应的数据转为json格式
(1)使用fastjson
@Controller @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @RequestMapping("/all") public void getUser(HttpServletResponse response) throws IOException { List<User> users = userService.findAll(); String json_users = JSONObject.toJSONString(users); response.setContentType("application/json;charset=utf-8"); response.getWriter().write(json_users); } }
需要引入fastjson相关依赖:
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.75</version> </dependency>
(2)使用springmvc提供的注解@ResponseBody(用在方法或者方法的返回值上,自动转换为json格式字符串并响应到前台)
@Controller @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @RequestMapping("/all") //@ResponseBody public @ResponseBody List<User> getUser(HttpServletResponse response) throws IOException { List<User> users = userService.findAll(); return users; } }
底层使用的是jackson,所以需要引入相关依赖:
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.11.3</version> </dependency>
可以使用jackson提供的注解来解决日期格式问题,加在实体类的日期字段上:@JsonFormat(pattern="yyyy-MM-dd")
九、springmvc中拦截器使用
类似于javaweb中的Filter,用来对请求进行拦截,可以将多个Controller中执行的共同代码放入拦截器中执行,减少Controller类中代码的冗余。
特点:拦截器只能拦截Controller的请求,不能拦截jsp、静态资源相关请求;拦截器可中断用户的请求轨迹;请求先经过拦截器,之后还会经过拦截器。
1、开发拦截器步骤
1)实现HandlerInterceptor接口,并实现接口中的方法
请求经过拦截器会优先进入拦截器中的preHandle方法,执行preHandle方法中的内容;如果preHandle返回为true,代码放行请求(会执行当前请求对应的控制器中的方法,当控制器中的方法执行结束之后,会返回拦截器中,并执行拦截器中postHandle方法,postHandle执行完成之后,会响应请求,同时在响应请求完成后会执行afterCompletion方法),如果返回false,则中断请求。
public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//可以在这里面判断用户是否登录 return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
2)配置拦截器(在springmvc.xml中)
(1)注册拦截器对象
<!--注册拦截器--> <bean id="myInterceptor" class="com.icucoder.interceptors.MyInterceptor"/>
(2)配置拦截器拦截的请求路径
<!--配置拦截器--> <mvc:interceptors> <!--配置一个拦截器 mvc:mapping代表拦截哪个请求路径 ref代表使用哪个拦截器--> <mvc:interceptor> <mvc:mapping path="/user/all"/> <ref bean="myInterceptor"/> </mvc:interceptor> </mvc:interceptors>
十、springmvc中全局异常处理
当控制器中某个方法在运行过程中突然发生运行时异常时,为了增加用户体验对于用户不能出现500错误代码,应该给用户良好展示错误界面,全局异常处理就能更好解决这个问题。
1、全局异常处理开发步骤
1)编写一个类实现HandlerExceptionResolver接口
public class GlobalExceptionResolver implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { ModelAndView mv = new ModelAndView(); mv.setViewName("error"); return mv; } }
2)在springmvc.xml中配置bean
<bean class="com.icucoder.exception.GlobalExceptionResolver"/>
3)编写全局异常处理jsp:error.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h3>server error.</h3> </body> </html>
4)测试抛出一个运行时异常
@Controller @RequestMapping("/error") public class ErrorController { @RequestMapping("500") public String error500() { //测试抛出一个运行时异常 throw new RuntimeException("500"); } }