第一章:SpringMVC简介与执行过程
- SpringMVC简介
1) Spring Web MVC框架提供了MVC(模型 - 视图 - 控制器)架构和用于开发灵活和松散耦合的Web应用程序的组件。
(1) 模型(Model)封装了应用程序数据,通常它们将由POJO类组成。
(2) 视图(View)负责展示数据,一般来说它生成客户端浏览器可以解释HTML输出。
(3) 控制器(Controller)负责处理用户请求并构建适当的模型,并将其传递给视图进行解析展示。
2) 在SpringMVC 中,控制器Controller 负责处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个Model ,然后再把该Model 返回给对应的View 进行展示。
- SpringMVC处理请求的流程
- SpringMVC的核心组件
1) DispatcherServlet:
DispatcherServlet就是所谓的SpringMVC前端控制器,作为整个SpringMVC的控制中心;
2) HandlerMapping:
HandlerMapping主要用来解析请求url,解析出控制器,从而映射控制器;
3) HandlerAdapter:
HandlerAdapter主要是调度Controller来处理业务逻辑等;
4) ViewResolver:
ViewResolver接口主要作用是解析DispatcherServlet传递的逻辑视图名,并将解析结果传回给DispatcherServlet;
第二章:SpringMVC配置
- SpringMVC的配置过程
1) 新建一个Maven项目,然后选择maven-webapp模板
2) 等待项目创建完毕,如果项目没有构建完毕,使用手动创建文件夹的话,系统构建完毕后,会覆盖pom.xml和web.xml文件,所以需要重新配置这两个文件。
3) 项目完毕后,创建了resources文件夹和webapp文件夹,之后需要手动创建src下的java文件夹,并将不需要的配置文件删除,webapp下的WEB-INF下只留web.xml文件即可。
4) Module中,添加web单元,选择src下的webapp文件夹
5) 配置web.xml文件,springmvc-config.xml文件和pom.xml文件
6) Pom.xml文件设置依赖项,并import changes
7) 在src下新建包,并在springmvc-config.xml中,配置自动扫描的包。
8) 配置tomcat运行项,
在发布页面上配置
9) 在webapp下新建index.html文件,并运行tomcat,测试是否能显示页面
10) 部署时容易出的问题
(1) 没有在项目结构的module中,设置web模块
(2) 在运行配置tomcat时,没有在发布选项卡设置发布的artificts
(3) 如果项目没有构建完毕,已经配置好的pom文件和web.xml文件可能被覆盖,所有需要再次配置这两个文件。
第三章:SpringMVC乱码
乱码的解决,在web.xml文件中添加
|
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app> <!--过滤器设置--> <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>
<display-name>Archetype Created Web Application</display-name> <servlet> <servlet-name>springmvc</servlet-name> <!-- 从Servlet容器跳转springMVC的框架容器 --> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- contextConfigLocation配置springmvc加载的配置文件(配置处理器映射器、适配器等等) 如果不配置contextConfigLocation,默认加载的是/WEB-INF/servlet-name名称-servlet.xml --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc-config.xml</param-value> </init-param> </servlet> <!-- DispatcherServlet处理所有请求 --> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
|
第四章:SpringMVC常用注解
- 注意事项:
1) 采用return字符串进行重定向时,有可能会影响System.out.println的输出。
- @Controller:用于定义控制器类
- @RequestMapping:提供路由信息,负责URL到Controller中的具体函数的映射,主要属性为value属性和method属性,value属性为默认属性,表示映射的前端请求地址,method为请求方式,可以为get,post或其他,比如:
@RequestMapping(value = "/",method = RequestMethod.GET)
value:指定请求的实际地址;
1) method: 指定请求的类型—— GET、POST、PUT、DELETE等。
2) 值为:RequestMethod.XXX
l @ RequestMapping注解在类上
|
@Controller @RequestMapping("books") public class BookController { @RequestMapping("/book01")
|
- @GetMapping:是@RequestMapping(method = RequestMethod.GET)的缩写。
- @PostMapping:是@RequestMapping(method = RequestMethod.POST)的缩写。
- @PathVariable:映射URL绑定的占位符,用户获取路径参数,用在方法参数前面。
|
前端页面请求:
<a href="delete/5">URL中的占位符</a>
|
|
Controller中:
@RequestMapping("delete/{id}") public String delete(@PathVariable("id") String u_id) { System.out.println("id=" + u_id); return "delete"; }
|
- @RequestParam:用于将请求参数区数据映射到功能处理方法的参数上,用在方法的参数前面。
1) 如果前端的上传数据和Controller的参数名称相同,能自动赋值,并能自动根据Controller参数类型自动转成相应的数据类型
|
前端页面:
<li><a href="books/book01?bName=三国演义&price=56.9&pageNum=100">传递和Controller参数相同名称的数据</a></li>
|
|
Controller:
@Controller @RequestMapping("books") public class BookController { @RequestMapping("/book01") public String book01(String bName,Double price,int pageNum){ System.out.println(bName+","+price+","+pageNum); return "welcome"; } }
|
2) 上传的参数名和Controller中的接收的参数名称不一致时。
|
前端页面:
<li><a href="books/book01?bName=三国演义&price=56.9&pageNum=100">传递和Controller参数相同名称的数据</a></li>
|
|
Controller:
@RequestMapping("/book01") public void book01(@RequestParam("bName") String name, Double price, int pageNum, boolean isNew){ System.out.println(name+","+price+","+pageNum+","+isNew); // 注意从定向会导致控制台不能输出信息 // return "welcome"; }
|
3) 如果前端传递的文本框的value值,和后端的实体类属性名称一致时,可以自动映射到对象中。
|
前端请求:
<form action="doLogin" method="post"> <input type="text" name="uName"><br> <input type="text" name="uPwd"><br> <input type="submit" value="login"> </form>
|
|
实体类:
public class User implements Serializable { private String uId; private String uName; private String uPwd;
public User() { }
|
|
Controller:
@RequestMapping("doLogin") public String login(User user){ //如果前端传递的文本框的value值,和后端的实体类属性名称一致时,可以自动映射到对象中 System.out.println("user="+user); return "welcome"; }
|
- @DateTimeFormat使用,注意前端的数据和后端的格式要对应
|
前端:
<li><a href="books/book02?addDate=2020-03-21 19:00:00">时间类型的传递</a></li>
|
|
后端:
@RequestMapping("/book02") public String book01(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date addDate){ System.out.println(addDate); return "login"; }
|
- @JsonFormat:设置Json序列化格式
当参数为Date类型,或默认返回为时间戳(毫秒数)
|
@RequestMapping("doLogin") @ResponseBody public Date login(User user){ //如果前端传递的文本框的value值,和后端的实体类属性名称一致时,可以自动映射到对象中 System.out.println("user="+user); return user.getUaddDate(); }
显示结果:
|
如果在实体类有日期,并且需要通过参数构造实体类,并自动注入的情况下,需要处理Date类型的数据,这时,我们需要在实体类的属性上添加如下注解
|
@DateTimeFormat(pattern = "yyyy-MM-dd") @JsonFormat(pattern = "yyyy年-MM月dd日") private Date uaddDate;
|
|
前端:
|
- @RequestBody:允许request的参数在request体中,而不是在直接链接在地址后面。此注解放在参数前。
a) 常用来处理Content-Type: 不是application/x-www-form-urlencoded编码的内容,而是如application/json, application/xml等;如果Controller方法的参数需要映射为集合,该注解非常有用!
b) 注意:使用该注解,提交json格式数据,一定是json格式字符串,而不是js对象或者数组!
- @ResponseBody:用于将Controller的方法返回的对象转换为指定格式后,写入到Response对象的body数据区。
使用:返回的数据不是html标签的页面,而是其他某种格式的数据时(如json、xml等)使用。
|
public Map<String,Object> book03(@RequestParam("bName") String name, Double price, int pageNum, boolean isNew){ System.out.println(name+","+price+","+pageNum+","+isNew); // 注意从定向会导致控制台不能输出信息 Map<String,Object> map = new HashMap<String, Object>(); map.put("errorCode",1); map.put("message","登录成功"); return map; }
|
表单登录:
|
前端:
login: <form action="doLogin" method="post"> <input type="text" name="uName"><br> <input type="text" name="uPwd"><br> <input type="text" value="2000-01-10" name="uaddDate"><br> <input type="radio" checked="checked" name="gender" value="male">男<input type="radio" name="gender" value="female">女<br> <select name="city" > <option value="天津">天津</option> <option value="北京">北京</option> <option value="河北">河北</option> </select> <input type="checkbox" name="favorites" value="篮球">篮球 <input type="checkbox" name="favorites" value="足球">足球 <input type="checkbox" name="favorites" value="排球">排球 <input type="submit" value="login"> </form>
|
|
后端:
public User login(User user){ //如果前端传递的文本框的value值,和后端的实体类属性名称一致时,可以自动映射到对象中 System.out.println("user="+user); return user; }
|
- @RestController:是@Controller和@ResponseBody的合集
- @ExceptionHandler:用在方法上表示遇到这个异常就执行以下方法。Controller层的全局异常统一处理。
- @ControllerAdvice:控制器增强,用于定义@ExceptionHandler,@InitBinder和@ModelAttribute方法,适用于所有使用@RequestMapping方法。
- @CrossOrigin :允许跨域请求
- @SessionAtrriBute
- ModelAndView类用于html页,只能设置setViewName,不能设置model数据
第五章:ModelAndView
|
@RequestMapping("testModel") public ModelAndView testModel(){ ModelAndView mv = new ModelAndView(); mv.setViewName("calc"); return mv; }
|
- 在jsp页中,可以测试model数据的传递
|
@RequestMapping("testModel") public ModelAndView testModel(){ ModelAndView mv = new ModelAndView(); mv.addObject("test","abc"); mv.setViewName("testmodelandview"); return mv; }
|
|
JSP页面:
<body> <h3>testmodelandview</h3> <%=request.getAttribute("test")%>
</body>
|
第六章:拦截器
- 拦截器定义
|
@Component
public class AuthorityInterceptor implements HandlerInterceptor {
@Override//在一个请求进入Controller层方法执行前执行这个方法
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//在这里可以对参数做一些预处理和做一些验证
return true;//方法给予执行,就是允许controller的方法进行执行
//false 不允许,可以在这之前在reponse中编写返回的结果
}
//返回model前
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
//Controller执行完毕后,返回之前,可以对request和reponse进行处理
//如果是前后端没有分离,在进入View层中前执行
}
//返回model后
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//在一个请求处理完毕,即将销毁的时候,执行,可以做一些资源释放之类的工作
}
}
|
- 拦截器和Filter的区别
拦截器和过滤器(Filter)的区别
1) 拦截器是基于反射机器,Filter是基于函数回调
2) 拦截器不依赖servlet容器,Filter依赖
3) 拦截器只对action其作用,Filter对几乎所有请求都起作用
4) 拦截器可以访问action的上下文,或者值栈里的对象,Filter不行
5) 在action的生命周期中,拦截器可以多次被调用,Filter只在容器初期化时被调用一次
6) 拦截器可以获取IOC容器中的各个bean,但是Filter不行
- 拦截器的顺序:在xml文件中的顺序就是拦截器执行的顺序
|
<mvc:interceptors> <!-- 该拦截器拦截所有请求--> <mvc:interceptor> <mvc:mapping path="/login/**"/> <bean class="com.iss.interceptor.SecondInterceptor"></bean> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/books/**"/> <bean class="com.iss.interceptor.FirstInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
|
- 拦截器的方法的返回值:如果preHandle方法返回为false,那么会拦截该拦截器的postHandle和afterCompletion方法,以及其后的拦截器。
- 拦截器设置mapping过滤要拦截的路径
|
<mvc:interceptors> <!-- 该拦截器拦截所有请求--> <mvc:interceptor> <mvc:mapping path="/books/**"/> <bean class="com.iss.interceptor.FirstInterceptor"></bean> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/login/**"/> <bean class="com.iss.interceptor.SecondInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
|
- 通过拦截器实现权限校验
|
登录的session操作:
@RequestMapping("/doLogin") @ResponseBody public User login(User user, HttpSession session){ //如果前端传递的文本框的value值,和后端的实体类属性名称一致时,可以自动映射到对象中 System.out.println("user="+user); System.out.println("model1="+session); session.setAttribute("user",user); return user; }
|
|
拦截器类:
如果没有HTTPSession类,可以手动键入import javax.servlet.http.HttpSession,然后报错地方帮助,选择javaEE6导入:
public class AuthInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); System.out.println("preHandle"); if (session.getAttribute("user") == null) { System.out.println("user"); response.sendRedirect(request.getContextPath()+"/pages/login.html"); return false; } return true;
}
@Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle");
super.postHandle(request, response, handler, modelAndView); } }
|
|
拦截器配置:
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/books/**"/>--路径必须加斜杠,表示从主路径开始 <mvc:exclude-mapping path="/login/**"/> <bean class="com.iss.interceptor.AuthInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
|
|
操作步骤:先测试没有登录是能不能访问books下的链接,然在在登录状态上,访问books下的链接。
|
静态文件不拦截,配置文件中添加:
|
<!-- 将静态资源的请求,转给Web应用服务器默认的Servlet处理 -->
<mvc:default-servlet-handler />
|
第七章:文件上传与下载
第一节:文件上传
- 文件上传
|
Pom添加上传需要的依赖:
<!-- 文件上传 --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.5</version> </dependency>
|
|
前端:
单个文件上传 <form action="/books/douploadone" method="post" enctype="multipart/form-data"> <input type="text" name="bookname"> <input type="file" name="bookimage"> <input type="submit" value="确定"> </form>
|
|
Springmvc-config中配置CommMultipartReslover
<!-- multipartResolver名字不能改,它和后台DispatcherServlet中的常量保持一致,自动注入 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaultEncoding" value="utf-8"></property> <property name="maxUploadSize" value="1024000"></property> <property name="maxInMemorySize" value="4096"></property> </bean>
|
|
Controller:
@RequestMapping("/douploadone") @ResponseBody public Map<String ,Object> doUploadOne(@RequestParam("bookimage")CommonsMultipartFile file, @RequestParam("bookname") String name, HttpServletRequest request){
Map<String,Object> map = new HashMap<String, Object>(); System.out.println("name="+name); System.out.println(file.getOriginalFilename()); //设置路径 String spath = request.getServletContext().getRealPath("uploads"); System.out.println("path="+spath); File path = new File(spath); if(!path.exists()){ path.mkdirs(); } File newFile = new File(path,System.currentTimeMillis()+"_"+file.getOriginalFilename()); //保存文件 try { FileOutputStream fos = new FileOutputStream(newFile); InputStream is = file.getInputStream(); byte[] buffer = new byte[1024]; int len = 0; while((len = is.read(buffer))!=-1){ fos.write(buffer,0,len); } fos.close(); is.close(); map.put("error",0); map.put("status","ok"); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
return map; }
|
|
结果:存储在tomcat路径下:
|
- 多文件上传,配置和单文件一样
|
前端页面:
<form action="douploadmore" method="post" enctype="multipart/form-data"> <input type="text" name="text"><br>
<input type="file" name="files"><br> <input type="file" name="files"><br> <input type="submit" value="确定"><br> </form>
|
|
Controller:
@RequestMapping("douploadmore") @ResponseBody public Map<String,Object> doUploadMore(@RequestParam("text") String text, @RequestParam("files") CommonsMultipartFile[] files, HttpServletRequest request){ Map<String,Object> map = new HashMap<String, Object>(); if(files == null ||files.length == 0){ return map; } String spath = request.getServletContext().getRealPath("uploadsmore"); System.out.println("path="+spath); File path = new File(spath); if(!path.exists()){ path.mkdirs(); } //循环处理文件 for(int i=0;i<files.length;i++){ File newFile = new File(path,System.currentTimeMillis()+"_"+files[i].getOriginalFilename()); //保存文件 byte[] buffer = new byte[1024]; int len = 0; try { FileOutputStream fos = new FileOutputStream(newFile); InputStream is = files[i].getInputStream();
while((len = is.read(buffer))!=-1){ fos.write(buffer,0,len); } fos.close(); is.close(); map.put("error",0); map.put("status","ok"); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } return map; }
|
第二节:文件下载
- 文件下载
|
首先,在tomcat路径下添加文件夹及文件:
|
|
前端页面:
<h3>文件下载</h3> <form action="dodownload"> <input type="submit" value="下载文件"> </form>
|
|
Controller:
@RequestMapping("dodownload") @ResponseBody public Map<String, Object> doDownlaod(HttpServletRequest request, HttpServletResponse response) { Map<String, Object> map = new HashMap<String, Object>(); String downLoadPath = "file"; /* 如果文件名是从客户端获取,则需要对文件名进行处理,避免乱码 fileName = new String(fileName.getBytes("iso-8859-1"),"utf-8");//设置文件名编码格式,否则资源请求中有中文会乱码 */ // 根据id获取文件名,测试假设下载文件 String fileName = "测试封面.jpg"; //获取下载绝对路径, String filePath = request.getServletContext().getRealPath("file"); System.out.println("filepath = " + filePath); File file = new File(filePath, fileName);
//查看请求头"User-Agent"以获取浏览器类型,并设置链接的编码方式 String header = request.getHeader("User-Agent"); byte[] buffer = new byte[1024]; int len = 0; try { if (header.contains("firefox")) { BASE64Encoder base64Encoder = new BASE64Encoder(); fileName = "=?utf-8" + base64Encoder.encode(fileName.getBytes("utf-8")) + "?="; } else { fileName = URLEncoder.encode(fileName,"utf-8"); }
//通知浏览器以下载的方式响应请求资源 //a.Content-Type 设置文件媒体格式 response.setContentType(request.getServletContext().getMimeType(fileName)); //b.Content-Disposition 设置要被下载的文件名 response.setHeader("Content-Disposition","file;filename="+fileName); //获取输入流 FileInputStream fis = new FileInputStream(file); // 获取输出流 ServletOutputStream fos = response.getOutputStream();
while((len=fis.read(buffer))!=-1){ fos.write(buffer,0,len); } fis.close(); fos.close(); map.put("error", 0); map.put("status", "ok"); } catch (UnsupportedEncodingException e ) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return map; }
|
|
测试结果:
|
第八章:异常处理
- 异常处理的目的是,不在前端页面显示后端服务器的500错误信息,通过异常处理可以实现不同的异常产生时,跳转相应的错误提示页面去。
- 使用自带的简单的异常处理器
SimpleMappingExceptionResolver是HandlerExceptionResolver的子类
|
SpringMVC-config文件中配置异常处理器:
<!-- SrpingMVC提供简单异常处理器--> <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <!-- 定义默认的异常处理页面--> <property name="defaultErrorView" value="error/error"></property> <!-- 定义异常处理页面用来获取异常信息的变量名,也可以不定义,默认名为Exception--> <property name="exceptionAttribute" value="ex"></property> <!-- 定义需要特殊处理的异常,重点--> <property name="exceptionMappings"> <props> <prop key="com.iss.exception.DivZeroException">error/error_div</prop> </props> </property> </bean>
|
|
自定义异常类:
public class DivZeroException extends Exception{ private String message;
public DivZeroException(String message) { this.message = message; }
@Override public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; } }
|
|
前端页面:
<form action="calc/div"> <input type="text" name="num1">/ <input type="text" name="num2"> <input type="submit" value="ok"> </form>
|
|
设置异常后要跳转的页面
<body> 不能进行除0操作,请重新输入数据 </body>
|
|
Controller:
@RestController @RequestMapping("calc") public class CalcController { @RequestMapping("div") public Map<String ,Double> div(Double num1, Double num2) throws DivZeroException { Map<String ,Double> map = new HashMap<String, Double>(); if(num2 == 0){ throw new DivZeroException("不能进行除0操作!"); } map.put("result",num1/num2); return map; } }
|
- 定义全局异常处理器,控制器里必须要把异常throws,不能用try-catch,如果try-catch的话,就在Controller中处理了,就不能抛给异常处理器了。
|
自定义一个除0的异常类:
public class DivZeroException extends Exception{ private String message;
public DivZeroException(String message) { this.message = message; }
@Override public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; } }
|
|
自定义另一个异常类:
public class DefaultException extends Exception{ private String message;
public DefaultException(String message) { this.message = message; }
@Override public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; } }
|
|
自定义异常处理类:继承HandlerExceptionResolver
public class MyHanderExceptionResolver implements HandlerExceptionResolver { public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { ModelAndView modelAndView = new ModelAndView(); Exception exception = null; if(ex instanceof DivZeroException){ System.out.println("if"); exception = (DivZeroException) ex; modelAndView.setViewName("/error/error_div"); }else{ System.out.println("else"); exception = new DefaultException("默认错误"); modelAndView.setViewName("/error/error"); }
return modelAndView; } }
|
|
SpringMVC-config文件中配置自定义异常处理器:
<bean class="com.iss.exception.MyHanderExceptionResolver"></bean>
|
|
Controller:
@RestController @RequestMapping("calc") public class CalcController { @RequestMapping("div") public Map<String ,Double> div(String num1, String num2) throws Exception { double n1 = Integer.parseInt(num1); double n2 = Integer.parseInt(num2); Map<String ,Double> map = new HashMap<String, Double>(); if(n2 == 0){ throw new DivZeroException("不能进行除0操作!"); } map.put("result",n1/n2); return map; } }
|
|
前端页面和跳转的错误页面同上。
|
|
执行结果
- 除0时:
结果:
- 输入为非数字时:
结果:
|