springmvc
-
- 经典三层架构
- 入门springmvc
- 九大组件
- springmvc 处理流程
- 数据输出机制
- 过滤器,监听器,拦截器
- 处理multipart形式的数据
- 全局异常处理
- 请求转发和重定向
经典三层架构
表现层(view 视图+ controller):负责接收请求,转发请求,它转发给业务层
也就是我们常说的web 层。它负责接收客户端请求,向客户端响应结果,通常客户端使用http 协 议请求web 层,web 需要接收 http 请求,完成 http 响应。
业务层:执行业务逻辑,需要操作数据源则使用持久层的功能
持久层:操作数据库
springmvc是一个应用于表现层的框架,是对servlet的封装,简化了servlet的开发
M:(数据模型(各种vo,po,dto)+业务模型(业务逻辑代码))
V:view视图(jsp,html)
C:controller控制器(servlet)
配置<servlet-mappin>的时候注意/ 和/*的区别
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
使用/*会连.jsp请求(跳转jsp)一块拦截(拦截后会在controller中找对应的handler,显然handler中是没有这种xxxx/xx.jsp的url的),/* 的优先级最高。
而使用/不会拦截jsp请求(Tomcat中的servlet会拦截.jsp,并处理)。注意:它不拦截jsp但是它会拦截html,js,css等静态资源
静态资源配置方案
<!--会在springmvc中定义一个DefaultServletHttpRequestHandler对象,
它会对进入dispatcherServlet的url请求进行过滤,如果发现这是静态资源请求,
会把它还给web应用服务器(Tomcat)默认的defaultservlet来处理-->
<mvc:default-servlet-handler>
<!-- 他会把所有满足mapping的请求拦截到 classpath中去-->
<mvc:resources location = "classpath:/" mapping="/resources/**">
入门springmvc
新建一个maven项目,配置pom文件
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.12.RELEASE</version>
</dependency>
</dependencies>
<!-- 需要Tomcat插件可以加入-->
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8080</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
配置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>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置文件的地址,这里让springmvc去加载配置文件-->
<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>
</web-app>
新建配置类springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 开启扫描-->
<context:component-scan base-package="com.test.springmvc.controller"/>
<!-- 配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 配置视图前缀-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- 配置视图后缀-->
<property name="suffix" value=".jsp"/>
</bean>
<!-- 自动注册处理器映射器,处理器适配器-->
<mvc:annotation-driven/>
</beans>
新建一个controller
package com.test.springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.Date;
@Controller
@RequestMapping("/testController")
public class MyController {
@RequestMapping("/test1")
public ModelAndView test1() {
ModelAndView modelAndView = new ModelAndView();
//向请求域中中添加属性,request.setAttribtue("Date", new Date())
modelAndView.addObject("date", new Date());
//加上springmvc.xml设置的视图解析器的前缀和后缀这里相当于
// modelAndView.setViewName("/WEB-INF/jsp/test.jsp ")
modelAndView.setViewName("test");
return modelAndView;
}
}
根据上面controller返回的视图test.jsp新建视图
<%@ page isELIgnored="false" contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
test my mvc ${date}
</body>
</html>
最后的项目结构是这样的
浏览器输入http://localhost:8080/testController/test1,页面返回
九大组件
HandleMapping:处理器映射器,存储了url请求的路径和handle处理器的映射关系,请求到达后它会去找到对应的handler和interceptor。标注了@RequestMapping的每个方法都可以看成是一个Handler。
HandlerAdapter: 处理器适配器,handler的形式不一样(可能是方法上加注解@requestmapping,或者是类实现了接口,这些都是handler),需要使用不同的适配器来执行handler。
HandlerExceptionResolver:处理handle产生的异常,根据异常设置modelandview。
ViewResolver:视图解析器,拼接视图的前缀和后缀。
RequestToViewNameTranslator:从请求路径中获取viewname,handler没有设置view的时候它从请求中查找视图名。
LocalResolver:国际化处理。用于从请求中解析出 Locale,比如中国 Locale 是 zh-CN,用来表示一个区域。这 个组件也是 i18n 的基础。
ThemeResolver:解析主题,现在很少使用,一般由前端来写页面不使用主题。
MultipartResolver:处理文件上传请求。
FlashMapMavager:重定向的时候进行参数的传递。
springmvc 处理流程
第一步:用户发送请求至前端控制器DispatcherServlet
第二步:DispatcherServlet收到请求调用HandlerMapping处理器映射器
第三步:处理器映射器根据请求Url找到具体的Handler(后端控制器),生成处理器对象及处理器拦截 器(如果 有则生成)一并返回DispatcherServlet
第四步:DispatcherServlet调用HandlerAdapter处理器适配器去调用Handler
第五步:处理器适配器执行Handler
第六步:Handler执行完成给处理器适配器返回ModelAndView
第七步:处理器适配器向前端控制器返回 ModelAndView,ModelAndView 是SpringMVC 框架的一个 底层对 象,包括 Model 和 View
第八步:前端控制器请求视图解析器去进行视图解析,根据逻辑视图名来解析真正的视图。
第九步:视图解析器向前端控制器返回View
第十步:前端控制器进行视图渲染,就是将模型数据(在 ModelAndView 对象中)填充到 request 域
第十一步:前端控制器向用户响应结果
数据输出机制
ModelAndView类中包括ModelMap 和view
直接在controller的方法中声明一个形参ModelMap或者Model,然后使用Model.addObject(“date”, new Date());或者ModelMap.addObject(“date”, new Date());也是可以的。== 甚至直接用Map都可以 ==
===》 运行时ModelMap,Model,Map用的都是BindingAwareModelMap,
BindingAwareMode继承了ExtendedModelMap, 然后ExtendedModelMap继承了ModelMap, 实现了Model接口。
过滤器,监听器,拦截器
过滤器:对请求过滤,作用在servlet之前,对Request请求起到过滤的作用,作用在Servlet之前,如果配置为/*可以对所 有的资源访问(servlet、js/css静态资源等)进行过滤处理
监听器:实现了ServletContextListener接口的服务器端组件,生命周期和web应用一致,只会初始化一次,可以在某些动作前后增加处理。
作用一:做一些初始化工作,web应用中spring容器启动ContextLoaderListener
作用二:监听web中的特定事件,比如HttpSession,ServletRequest的创建和销毁;变量的创建、 销毁和修改等。可以在某些动作前后增加处理,实现监控,比如统计在线人数,利用 HttpSessionLisener等。
拦截器:是SpringMVC、Struts等表现层框架自己的,只会拦截hangder不会拦截jsp、html等。(拦截3次,hander前拦截,hander执行完未跳转页面拦截,跳转后拦截)==》比如实现HanlerInterceptor接口
servlet、filter、listener是配置在web.xml中的(javaee的组件),而intercepor是配置在自己的配置文件中(springmvc等表现层框架的组件)。
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
单个拦截器的执行流程:
1)程序先执行preHandle()方法,如果该方法的返回值为true,则程序会继续向下执行处理器中的方 法,否则将不再向下执行。
2)在业务处理器(即控制器Controller类)处理完请求后,会执行postHandle()方法,然后会通过 DispatcherServlet向客户端返回响应。
3)在DispatcherServlet处理完请求后,才会执行afterCompletion()方法。
自定义拦截器
public class MyIntercepter01 implements HandlerInterceptor {
/**
* 会在handler方法业务逻辑执行之前执行
* 往往在这里完成权限校验工作 * @param request
* @param response
* @param handler
* @return 返回值boolean代表是否放行,true代表放行,false代表中止
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse
response, Object handler) throws Exception {
System.out.println("MyIntercepter01 preHandle......");
return true;
}
/**
* 会在handler方法业务逻辑执行之后尚未跳转⻚面时执行
* @param request
* @param response
* @param handler
* @param modelAndView
数据和视图信息进行修改
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse
response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyIntercepter01 postHandle......"); }
/**
* ⻚面已经跳转渲染完毕之后执行
* @param request
* @param response
* @param handler
* @param ex 可以在这里捕获异常 * @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyIntercepter01 afterCompletion......"); }
}
在springmvc.xml注册拦截器
<mvc:interceptors>
<!--拦截所有handler-->
<!--<bean class="com.test.interceptor.MyIntercepter01"/>-->
<mvc:interceptor>
<!--配置当前拦截器的url拦截规则,**代表当前目录下及其子目录下的所有url-->
<mvc:mapping path="/**"/>
<!--exclude-mapping可以在mapping的基础上排除一些url拦截-->
<!--<mvc:exclude-mapping path="/demo/**"/>-->
<bean class="com.test.interceptor.MyIntercepter01"/>
</mvc:interceptor>
</mvc:interceptors>
处理multipart形式的数据
上传文件的处理
加入上传文件所需要的jar包
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
配置上传解析器
<!--配置文件上传解析器,id是固定的multipartResolver-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--设置上传大小,单位字节-->
<property name="maxUploadSize" value="1000000000"/>
</bean>
前端
<%--
1 method="post"
2 enctype="multipart/form-data" 3 type="file"
--%>
<form method="post" enctype="multipart/form-data" action="/demo/upload">
<input type="file" name="uploadFile"/>
<input type="submit" value="上传"/> </form>
后台
@RequestMapping("upload")
public String upload(MultipartFile uploadFile, HttpServletRequest request) throws IOException {
// 文件原名,如xxx.jpg
String originalFilename = uploadFile.getOriginalFilename(); // 获取文件的扩展名,如jpg
String extendName =
originalFilename.substring(originalFilename.lastIndexOf(".") + 1, originalFilename.length());
String uuid = UUID.randomUUID().toString(); // 新的文件名字
String newName = uuid + "." + extendName; String realPath =
request.getSession().getServletContext().getRealPath("/uploads");
// 解决文件夹存放文件数量限制,按日期存放
String datePath = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
File floder = new File(realPath + "/" + datePath);
if(!floder.exists()) {
floder.mkdirs();
}
uploadFile.transferTo(new File(floder,newName));
return "success";
}
全局异常处理
// 可以让我们优雅的捕获所有Controller对象handler方法抛出的异常
@ControllerAdvice
public class GlobalExceptionResolver {
@ExceptionHandler(ArithmeticException.class)
public ModelAndView handleException(ArithmeticException exception,HttpServletResponse response) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg",exception.getMessage());
modelAndView.setViewName("error");
return modelAndView;
}
}
请求转发和重定向
转发:A 找 B 借钱400,B没有钱但是悄悄的找到C借了400块钱给A
url不会变,参数也不会丢失,一个请求
重定向:A 找 B 借钱400,B 说我没有钱,你找别人借去,那么A 又带着400块的借钱需求找到C url会变,参数会丢失需要重新携带参数,两个请求
用flash解决重定向参数丢失,向上下文中添加flash属性,框架会在session中记录该属性值,当 跳转到⻚面之后框架会自动删除flash属性,不需要我们手动删除
@RequestMapping("/handleRedirect")
public String handleRedirect(String name,RedirectAttributes redirectAttributes) {
//return "redirect:handle01?name=" + name; // 拼接参数安全性、参数⻓度都有局限
// addFlashAttribute方法设置了一个flash类型属性,该属性会被暂存到session中,在跳转到⻚面之后该属性销毁
redirectAttributes.addFlashAttribute("name",name);
return "redirect:handle01";
}
九大组件初始化
// 多部件解析器
@Nullable
private MultipartResolver multipartResolver;
// 区域化 国际化解析器
@Nullable
private LocaleResolver localeResolver;
// 主题解析器
@Nullable
private ThemeResolver themeResolver;
// 处理器映射器组件
@Nullable
private List<HandlerMapping> handlerMappings;
// 处理器适配器组件
@Nullable
private List<HandlerAdapter> handlerAdapters;
// 异常解析器组件
@Nullable
private List<HandlerExceptionResolver> handlerExceptionResolvers;
// 默认视图名转换器组件
@Nullable
private RequestToViewNameTranslator viewNameTranslator;
// flash属性管理组件
@Nullable
private FlashMapManager flashMapManager;
// 视图解析器
@Nullable
private List<ViewResolver> viewResolvers;
在org.springframework.web.servlet.DispatcherServlet#onRefresh方法中初始化
@Override
protected void onRefresh(ApplicationContext context) {
// 初始化策略
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
// 多文件上传的组件
initMultipartResolver(context);
// 初始化本地语言环境
initLocaleResolver(context);
// 初始化模板处理器
initThemeResolver(context);
// 初始化HandlerMapping
initHandlerMappings(context);
// 初始化参数适配器
initHandlerAdapters(context);
// 初始化异常拦截器
initHandlerExceptionResolvers(context);
// 初始化视图预处理器
initRequestToViewNameTranslator(context);
// 初始化视图转换器
initViewResolvers(context);
// 初始化 FlashMap 管理器
initFlashMapManager(context);
}