核心组件
- DispatcherServlet:作为前端控制器,整个流程控制的中心,控制其它组件执行,统一调度,降低组件之间的耦合性,提高每个组件的扩展性。
DispatcherServlet主要用作职责调度工作,本身主要用于控制流程,主要职责如下:- 文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析;
- 通过HandlerMapping,将请求映射到处理器(返回一个HandlerExecutionChain,它包括一个处理器、多个
HandlerInterceptor拦截器); - 通过HandlerAdapter支持多种类型的处理器(HandlerExecutionChain中的处理器);
- 通过ViewResolver解析逻辑视图名到具体视图实现;
- 本地化解析;
- 渲染具体的视图等;
- 如果执行过程中遇到异常将交给HandlerExceptionResolver来解析。
- HandlerMapping:通过扩展处理器映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
- HandlerAdapter:通过扩展处理器适配器,支持更多类型的处理器,调用处理器传递参数等工作。
- ViewResolver:通过扩展视图解析器,支持更多类型的视图解析,例如:jsp、freemarker、pdf、excel等。
MVC执行过程
- 前端页面发送一个请求给DispatcherServlet,DispatcherServlet负责调度
- 将这个请求发送给HandleMapping,HandlerMapping会根据这个请求地址找到对应的处理器
- 将对应的处理器封装成处理器执行链返还给DispatcherServlet
- DispatcherServlet根据这个处理器执行链找到其处理器适配器
- 处理器适配器去执行这个处理器
- 处理器调用Service方法,根据其业务逻辑调用dao层对数据库进行操作,将结果一步一步向上返还给处理器。
- 处理器将结果封装成ModelAndView交给DispatcherServlet
- DispatcherServlet将ModelAndView对象交给视图解析器ViewResolver
- 视图解析器将封装了视图的view对象返还给DispatcherServlet
- 后者再找到view对象,并进行渲染。
- 形成一个响应对象,再将此对象交付给DispatcherServlet由其响应给前端。
/和/*的区别
- /不会匹配./jsp.不会导致重定向后再次访问spring 的DispatcherServlet
- /*会匹配./jsp.会出现返回jsp视图时再次进入spring的DispatcherServlet 类,
导致找不到对应的controller所以报404错。
创建控制器类
@Controller 类注解
@RequestMapping (“请求地址”) 请求注解
RequestMapping 属性
@RequestMapping ("method=RequestMethod.GET,value=“请求名”)限定get请求
如果不填则默认为GET 和POST请求皆可
后端接收前端数据
- 通过HttpServletRequest方式
-
接收的数据类型全是String类型
@Controller public class Controller{ @RequestMapping("/test") public String test(HttpRequest request){ String username = request.getParameter("username"); String age = request.getParameter("age"); return "success"; } }
- 在Spring框架中提供的接收参数的方法
页面传值时的key=处理请求的方法的参数名@Controller public class Controller{ @RequestMapping("/test") public String test(String username ,int age){ System.out.println("username= "+username,"age="+age); return "success"; } }
- 使用控件名和对象的属性名一致的方式进行接收
@Controller public class Controller{ @RequestMapping("/test") public String test(Users user){ System.out.println("username= "+user.getUsername(),"age="+user.getAge()); return "success"; } }
- key值不一致的方式进行接收
@Controller public class Controller{ @RequestMapping("/test") public String test(@RequestParam(name="username")String name,@RequestParam(name="age",defaultValue="18")int age){//指定在页面中名为username的参数传入时对应name System.out.println("username= "+user.getUsername(),"age="+user.getAge()); return "success"; } }
日期类型处理
后端接受前端日期类型
默认接收的是String类型以斜杠为分隔,例如 “2020/5/30”
-
日期处理工具包
<dependency> <groupId>joda-time</groupId> <artifactId>joda-time</artifactId> <version>2.9.9</version> </dependency>
spring配置文件:增加mvc命名空间以及
<mvc:annotation-driven/>
public String test(@DateTimeFormat(pattern = "yyyy-MM-dd")Date birthday){}
@DateTimeFormat可以标注在Bean的日期类型的属性上
后端返还日期类型给前端
@JsonFormat(parttern=“yyyy-MM-dd”)
后端向前端返还数据
Spring的return 是默认使用转发(request)的方式
- 通过HttpServletRequest方式
@Controller public class Controller{ @RequestMapping("/test") public String test(HttpRequest request){ String username = request.getParameter("username"); String age = request.getParameter("age"); request.setAttribute("username",username); request.setAttribute("age",age);//request住阿奴发时显示,重定向时丢失 return "success"; } }
- ModelMap map ,默认作用域request
@Controller public class Controller{ @RequestMapping("/test") public String test(HttpServeletRequest request,ModelMap map){ String username = request.getParameter("username"); String age = request.getParameter("age"); request.setAttribute("username",username); request.setAttribute("age",age);//request转发时显示,重定向时丢失 map.addAttribute("modelmapkey",username); model.addAttribute(modelkey,username); return "success"; } }
- ModelAndView 对象需要new,同时作为返回值类型
@Controller public class Controller{ @RequestMapping("/test") public ModelAndView test(HttpServeletRequest request){ String username = request.getParameter("username"); String age = request.getParameter("age"); ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("success"); modelAndView.addObject("mvkey",username); return modelAndView ; } }
Session 存值
4. 使用HttpSession :request.getSession();
@Controller
public class Controller{
@RequestMapping("/test")
public String test(HttpSession session){
String username = request.getParameter("username");
String age = request.getParameter("age");
session.setAttribute("username",username);
return "success";
}
}
- 使用@SessionAttributes
@Controller
@SessionAttributes("sessionmap")
public class Controller{
@RequestMapping("/test")
public String test(HttpSession session,ModelMap map){
String username = request.getParameter("username");
String age = request.getParameter("age");
session.setAttribute("username",username);
map.addAttribute("sessionmap",age);
return "success";
}
}
页面中 指定session作用域中
${sessionScope.sessionmap}
@SessionAttributes()的小括号中如果是一个值,不要加{}
示例:
@SessionAttributes(“key”)
@SessionAttributes({“key1”,“key2”})
用注解SessionAttributes,如果想要清除.需要使用工具类SessionStatus
@RequestMapping("/logoff")
public String logoff(SessionStatus status){
status.setComplete();
return "logoff";
}
弹窗响应
例如登出账户
@RequestMapping("/logoff")
public void logoff(SessionStatus status,HttpServletResponse response){
status.setComplete();
response.setContentType("text/html;charset=utf-8");
try{
PrintWtriter writer = response.getWriter();
writer.print("<script> alert('登出成功');location.href='logoff.jsp'</scrpit>")
}catch(IOException e){
e.printStackTrace();
}
}
乱码处理
前端页面需要进行编码配置
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
在web.xml文件中加入过滤器
<filter>
<filter-name>charset</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>charset</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
转发和重定向
spring mvc中默认转发实现跳转页面
@RequestMapping("/forwardView")
public String forwardView(){
return "success";
}
重定向分为重定向到新的页面或一个请求
@RequestMapping("/forwardView")
public String forwardView(){
return "redirect:/success.jsp";//重定向时会忽略视图解析器的配置
}
重定向时地址栏会拼接modelmap中值的问题
解决方法:在重定向时避免使用modelmap的存值方式
异常处理
-
配置新的错误页面
<error-page> <error>404</error> <location>/404.html</location> </error-page>
-
局部异常
只能捕捉当前类中的异常
@EexceptionHandler(Exception.class)
public String error(){
System.out.println("异常发生");
return "error";
}
3.全局异常
创建一个异常工具类,可以捕获不同Controller中的异常
@ControllerAdvice
public class ExceptionController{
@ExceptionHandler(NullPointerException.class)
public String error(){
System.out.println("空指针异常发生");
return "error";
}
@ExceptionHandler(value=UsersException.class)
public ModelAndView exception1(Exception e){
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg",e.getMessage());
modelAndView.setViewName("userError");
return modelAndView;
}
}
Cookie操作
//@CookieValue注解可以获取请求中的cookie
//@RequestHandler获取请求头信息中的Accept-Language信息
public String testCookie(@CookieValue("JSESSIONID")String cookie,@RequestHandler("Accept-Language") String language)
{
System.out.println("cookie:"+cookie);
System.out.println("language:"+language);
session
return "result";
}
RestFul风格
REST:即Representational State Transfer ,(资源)表现层状态转化,是目前最流行的一种互联网软件架构。
它们分别代表着四种基本操作:
- GET用来获取资源 安全 幂等
- POST用来创建新资源 不安全 幂不等
- PUT用来更新资源 不安全幂等
- DELETE用来删除资源 不安全 幂等
HTTP中对于不同方法规定了不同的安全特性和幂等特性
这里的安全特性不是传统意义的“安全”,安全特性指资源的状态的改变,在经过多次调取资源的状态仍保持一致,那么则成为安全。
幂等特性指的是经过多次结果返还,结果值是否能保持一致,如果一致则幂等否则成为幂不等
HiddenHttpMethodFilter:浏览器form表单只支持GET和POST,不支持DELETE和PUT请求,Spring添加了一个过滤器,可以将这些请求转换为标准的http方法,支持GET,POST,DELETE,PUT请求!
- 添加过滤器
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>
org.springframework.web.filter.HiddenHttpMethodFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- 添加控制器
@Controller
public class RestController {
@RequestMapping(value="/testrest",method = RequestMethod.GET)
public String get(){
System.out.println("get请求");
return "getsuccess";
}
@RequestMapping(value="/testrest",method = RequestMethod.POST)
public String post(){
System.out.println("post请求");
return "postsuccess";
}
@RequestMapping(value="/testrest",method = RequestMethod.PUT)
public String put(){
System.out.println("put请求");
return "putsuccess";
}
@RequestMapping(value="/testrest",method = RequestMethod.DELETE)
public String delete(){
System.out.println("delete请求");
return "deletesuccess";
}
}
- 表单
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/testrest" method="get">
<input type ="submit" value="get">
</form>
<form action="/testrest" method="post">
<input type ="submit" value="post">
</form>
<form action="/testrest" method="post">
<input type="hidden" name = "_method" value="put">
<input type ="submit" value="put">
</form>
<form action="/testrest" method="post">
<input type="hidden" name = "_method" value="delete">
<input type ="submit" value="delete">
</form>
</body>
</html>
@PathVriable获取路径参数
@RequestMapping("/user/{name}/{id}")
public String getData(@PathVariable(value = "id") Integer id,@PathVariable(value="name")String name){
System.out.println("id = " + id,"name = " + name);
return "list" ;
}
静态资源访问
由于DispatcherServlet 设置成/会拦截静态资源,所以在需要对静态资源进行淡出处理
- 在springmvc配置文件中添加mvc命名空间
- 添加处理标签 (通过依次添加映射地址)
<mvc:annotation-driven /> <!--注解驱动-->
<mvc:resources mapping="/img/**" location="/images/" ></mvc:resources>
或通过以下注解对所有静态资源进行配置
<mvc:default-servlet-handler></mvc:default-servlet-handler>
JSON数据处理
-
添加依赖包
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.5</version> </dependency>
-
配置文件添加注解驱动
<mvc:annotation-driven/>
- 实现代码(方法返回类型前)加入注解@ResponseBody将返回值类型转化为JSON格式
SpringMVC拦截器
拦截器interceptor的作用是拦截指定的用户请求,并对其执行相应的预处理和后处理
拦截器发生在handlermapping请求映射的处理器适配器执行处理器之前
创建自定义拦截器
-
创建拦截器类:实现HandlerInterceptor接口
接口中未实现的方法:
1. postHandle 拦截器结束执行 该方法在处理器执行结束后执行,若处理器未执行,则也不执行。由于该方法在处理器执行之后执行,可获取处理器的结果ModelAndView并可以对其进行处理(修改其数据或更改跳转地址) 场景:日志
2. afterCompletion无论是否有异常都执行 在处理器返回值之后,在ModelAndView返回值之后,无法再对其值进行修改 场景:对全局资源的操作。。
3. preHandle 拦截器开始执行 返回值类型为boolean,若为true则会继续执行拦截器方法,且将afterCompletion放置到方法栈中等待执行。若为false则不执行 场景:登陆验证 -
spring配置文件中创建拦截器对象
拦截所有请求<mvc:interceptors> <bean class = "com.java.util.MyInterceptor"></bean> </mvc:interceptors>
拦截指定请求
<mvc:interceptors> <mvc:interceptor > <mvc:mapping path="/请求名" /> <mvc:mapping path="/请求名" /> <bean id="my" class="util.MyInterceptor"/> </mvc:interceptor> </mvc:interceptors>
(1)过滤器:
依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,获取我们想要获取的数据,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等
(2)拦截器:
依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。但是缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理。
多个拦截器执行顺序:
过滤器优先开始—>拦截器1开始–>拦截器2开始–>拦截器2结束---->拦截器1结束—>过滤器结束
Spring中文件上传和下载
上传
在SpringMVC上下文中默认没有装配MultipartResolver,因此默认情况下不能处理文件上传工作。
如果想使用Spring的文件上传功能,则需要先在上下文中配置MultipartResolver。
- 添加依赖包
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
- 配置MultipartResolver:
<mvc:annotation-driven/>
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"
p:defaultEncoding="UTF-8"
p:maxUploadSize="9999999"/>
- 表单提交方式必须是post形式
enctype="multipart/form-data"
- 配置java代码。
@RequestMapping("/upload")
public String saveFile(@RequestParam("name") String name , @RequestParam("file")MultipartFile
file) throws IOException {
//接收表单提交的数据,包含文件
System.out.println("name = " + name);
String path=request.getRealPath("/");
if (!file.isEmpty())
{
file.transferTo(new File(path+"upload/"+file.getOriginalFilename()));
}
return "success";
}
下载
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>uploadSuccess</h1>
<img src="uploading/${filename}">
<a href="/download?filename=${filename}" >下载</a>
</body>
</html>
@Controller
public class DownloadController {
@RequestMapping("/download")
public ResponseEntity<byte[]> download (HttpServletRequest request, String filename) throws IOException {
String realPath = request.getRealPath("/uploading");
String filePath = realPath+"/"+filename;
//创建http头信息的对象
HttpHeaders httpHeaders = new HttpHeaders();
//标记以流的方式做出响应
httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
//attachment 以附件的形式响应给客户端
httpHeaders.setContentDispositionFormData("attachment", URLEncoder.encode(filename,"UTF-8"));
File file = new File(filePath);
ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file),httpHeaders, HttpStatus.CREATED);
return responseEntity;
}
}