目录结构
配置web.xml
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
/*会拦截.jsp /不会
<url-pattern>/</url-pattern>
</servlet-mapping>
配置视图解析器
<context:component-scan base-package="com"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/page/"/>
<property name="suffix" value=".jsp"/>
</bean>
使用
直接返回字符串会自动拼接前后缀转发到目标页面
@GetMapping("/hello")
public String hello(){
System.out.println("请求收到");
return "success";
}
- SpringMVC的前端控制器收到请求
- 来看请求地址和@RequestMapping标注的哪个注解,来找到使用哪个类的哪个方法
- 前端控制器找到了目标处理器类和目标方法,直接利用反射执行目标方法来处理
- 方法执行完成后会有一个返回值,SpringMVC认为这个返回值就是要去的页面地址
- 拿到返回值后拿到视图解析器进行拼串得到完整的页面地址
- 拿到页面地址,前端控制器帮我们转发到页面
如果不指定配置文件位置会默认去/WEB-INF/下找前端控制器名-servlet.xml配置文件 /WEB-INF/springDispatcherServlet-servlet.xml
DefaultServlet是tomcat来处理静态资源的,除过.jsp和serrvlet外剩下的都是静态资源,
index.html:静态资源,tomcat就会在服务器下找到设个资源并返回,
我们自己配置前端控制器的url-parent=/ 禁用了(重写)tomcat服务器中的DefaultServet
服务器的web.xml默认配置中有一个DefaultServlet是url-parent=/
我们配置中前端控制器url-pattern=/
静态资源就会来到DispatcherServlet(前端控制器)看那个方法的RequestMapping是处理这个静态资源
包括所有的图片也都需要通过前端控制器去查找对应的处理方法
因为服务器中也默认配置了Jspservlet我们没有进行覆盖,所以可以访问.jsp页面
/* 直接拦截所有请求
@RequestMapping注解的参数
value="/hello"
params= {}
1.{"username"} 参数必须携带带username的参数 /hello?username="zhangsan" {"username=123"} {"username!=123"}
2.{"!username"} 发送请求时不能包含名为username的参数 带了会404
3.{"username=123","!age"} 多规则
headers={}
headers={"User-Agent=Google"}
headers = "content-type=text/*"
规定请求头 的任意字段
consumes={} 只接受内容类型是那种的请求
规定请求头中的Content-Type
consumes = "text/plain"
consumes = {"text/plain", "application/*"}
consumes = MediaType.TEXT_PLAIN_VALUE
produces{} 告诉浏览器返回的内容是什么
produces = "text/plain"
produces = {"text/plain", "application/*"}
produces = MediaType.TEXT_PLAIN_VALUE
produces = "text/plain;charset=UTF-8"
produces = "application/json"
给响应头加上Content-Type:text/html:charset=utf-8
ant风格的资源地址
? :匹配文件名中的一个字符
* :匹配文件名中的任意字符
** :匹配多层路径
精确优先
/user/*/createUser /user/aaa/createUser /user/bbb/createUser
/use*/createUser /user/createUser
/user/**/createUser /user/aaa/bbb/createUser /user/c/d/v/createUser
/user**/createUser
/user/createUs?? /user/createUser /user/createUsaa
@RequestParam() 获取请求参数
value="user"
required=false
defaultValue="默认值"
@RequestHender():获取请求头中的值
value="user"
required=false
defaultValue="默认值"
@CookieValue 获取某个cookie值
value="JESSIONID"
required=false
defaultValue="默认值"
请求参数是POJO SpringMVC会自动根据请求参数进行封装
还可以级联属性赋值
User{
addreaa=new Address{
ciry=""
}
}
name=address.city
参数 写原生API 只能支持一下原生参数
Httpsession
HttpServletRequest
HttpServletResponse
request.InputStream
response.OutputStream
request.Reader
response.Writer
Locale
Principal
乱码解决
配置过滤器
<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>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
SpringMvc 除过在方法上传入原生的request和session外还能怎么样把数据带给页面
可以在方法处传入Map ,Model ,ModelMap
给这些参数里面保存的所有数据都会放在请求域中。可以在页面获取
这三个最终都是BindingAwareModelMap在工作 都会保存到请求域中
Map(interface:jdk)
Model(interface:)
ModelMap(继承自 LinkedHashMap)
BindingAwareModelMap()实现了Model接口并且继承了ModelMap
方法的返回值可以变为ModelAndView类型
@RequestMapping("handle")
public ModelAndView handle(){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("success");
modelAndView.addObject("mag","hello");
return modelAndView;
}
既包含视图信息(页面地址)也包含模型信息(给页面携带的数据)而且数据也是放在request域中
SpringMvc提供了一种可以临时给Session域中保存数据的方式
给BindingAwareModelMap中保存数据,或者ModelAndView中的数据,同时给session中放一份
value指定保存数据时要给session中放的数据的key
@SessionAttributes(只能标注在类上)
@SessionAttributes(
value={"msg","haha"}, 只要保存的是这种key的数据 给session中放入一份
types={String.class} 只要是String类型都放入session中 ()
)
推荐用原生API
@ModelAttribute
参数
方法位置:这个方法就会提前与目标方法运行
@ModelAttribute标在方法上的另外一个作用;
可以把方法运行后的返回值按照方法上@ModelAttribute("abc")指定的key放到隐含模型中;
如果没有指定这个key;就用返回值类型的首字母小写
@RequestMapping("/hello")
public String hello(@ModelAttribute("name")String name){
System.out.println("请求收到"+name);
return "success";
}
@ModelAttribute
public void md(Map<String,Object> map){
map.put("name":"zhangsan")
System.out.println("pre............");
}
pre............
请求收到zhangsan
大致流程
doDispatch()
根据当前请求地址找到哪个类能来处理
mappedHandler = getHandler(processedRequest);
noHandlerFound(processedRequest, response); 没有找到处理器就抛异常
确定当前请求的处理程序适配器 能执行当前方法的适配器(反射工具)
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
!mappedHandler.applyPreHandle(processedRequest, response) 处理之前
实际调用处理程序。 适配器执行目标方法,将目标方法执行完成后的返回值作为视图名,设置保存到ModelAndView
目标方法无论怎么写,最终适配器执行完成以后都会将执行后的信息封装成
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
如果没有视图名 使用默认视图名 地址名
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv); 后置处理器
处理转发资源 根据方法最终执行完成后封装的ModelAndView转发到对应的页面 而且ModelAndView中的数据可以在请求域中获取
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
具体方法
根据当前请求地址找到哪个类能来处理 HandlerExecutionChain 封装了拦截器
DispatcherServlet
getHandler()
HandlerExecutionChain getHandler(HttpServletRequest request)
IOC启动创建controller时 就会扫描每个requestMapping能处理什么请求 ,
就保存在 HandlerMappings <Map> 中
如果请求过来直接看哪个handlerMapping中有这个请求的映射信息
for in this.handlerMappings 比所有 看那个HandlerMapping能处理这个请求
handlerMapping.getHandler(request)
Object handler = getHandlerInternal(request); 能处理的目标对象 HandlerMethod
super.getHandlerInternal(request);
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); /hello
最终会调用executionChain .setHandler(handler)
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
封装处理器....
return executionChain
确定当前请求的处理程序适配器 能执行当前方法的适配器(反射工具)
mappedHandler.getHandler() 目标处理器 com.controller.MyFirstController#hello() HandlerMethod
如何找到目标处理器类的适配器。要拿适配器才去执行目标方法
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
for (HandlerAdapter adapter : this.handlerAdapters)
if (adapter.supports(handler)
supports(handler)
AbstractHandlerMethodAdapter
supports(handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
return adapter
ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
handleInternal(request, response, (HandlerMethod) handler);
ModelAndView handleInternal()
mav = invokeHandlerMethod(request, response, handlerMethod);
- SpringMvc九大组件
- 全部都是接口 接口就是规范
处理文件
/** MultipartResolver used by this servlet. */
@Nullable
private MultipartResolver multipartResolver;
处理国际化
/** LocaleResolver used by this servlet. */
@Nullable
private LocaleResolver localeResolver;
处理主题
/** ThemeResolver used by this servlet. */
@Nullable
private ThemeResolver themeResolver;
handler映射信息
/** List of HandlerMappings used by this servlet. */
@Nullable
private List<HandlerMapping> handlerMappings;
handler适配器
/** List of HandlerAdapters used by this servlet. */
@Nullable
private List<HandlerAdapter> handlerAdapters;
springMvc的强大异常解析功能 异常解析器
/** List of HandlerExceptionResolvers used by this servlet. */
@Nullable
private List<HandlerExceptionResolver> handlerExceptionResolvers;
没有返回地址就把请求地址当成返回地址
/** RequestToViewNameTranslator used by this servlet. */
@Nullable
private RequestToViewNameTranslator viewNameTranslator;
springmvc中允许重定向携带数据功能
/** FlashMapManager used by this servlet. */
@Nullable
private FlashMapManager flashMapManager;
视图解析器
/** List of ViewResolvers used by this servlet. */
@Nullable
private List<ViewResolver> viewResolvers;
- 组件初始化
- 去容器中找这个组件,如果没有就用默认配置
protected void onRefresh(ApplicationContext context)
initStrategies(context);
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
else
HandlerMapping hm = context.getBean(handleMapping, HandlerMapping.class);
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
ClassPathResource resource = new ClassPathResource(DispatcherServlet.properties, DispatcherServlet.class);
创建对象放入 this.handlerMappings
List<T> strategies = new ArrayList<>(classNames.length);
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
Object strategy = createDefaultStrategy(context, clazz);
strategies.add((T) strategy);
return strategies
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
handleInternal(request, response, (HandlerMethod) handler);
设置参数解析器
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
设置返回值处理器
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
容器
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
执行标注@ModelAttribute的方法【提前运行】
mav = invokeHandlerMethod(request, response, handlerMethod);
modelFactory.initModel(webRequest, mavContainer, invocableMethod); 执行标注@ModelAttribute的方法【提前运行】
invokeModelAttributeMethods(request, container); 执行标注@ModelAttribute的方法
Object returnValue = modelMethod.invokeForRequest(request, container);
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
MethodParameter[] parameters = getMethodParameters(); 获取方法参数
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory); 解析参数
执行目标方法 调用方法并处理返回值
invocableMethod.invokeAndHandle(webRequest, mavContainer); 【执行目标方法】
invokeModelAttributeMethods(request, container);
Object returnValue = modelMethod.invokeForRequest(request, container);
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
MethodParameter[] parameters = getMethodParameters();
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
doInvoke(args);
method.invoke(getBean(), args);
先解析方法参数再执行目标方法
invocableMethod.invokeAndHandle(webRequest, mavContainer); 【执行目标方法】
调用方法并处理返回值
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
MethodParameter[] parameters = getMethodParameters(); 获取所有参数
Object[] args = new Object[parameters.length];
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter); 获取参数解析器
先从缓存中获取
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
如果没有遍历所有参数解析器 查看是否支持
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers)
resolver.supportsParameter(parameter)
resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
springmvc4.x的运行流程
视图解析
有前缀的转发和重定向操作,视图解析器就不会进行拼窜
- forward 转发不会有视图解析器进行拼串
@RequestMapping("/handle3")
public String handle3(){
return "forward:/handle2";
}
@RequestMapping("/handle2")
public String handle2(){
return "forward:/page/success.jsp";
}
- /是从当前项目下开始;SpringMVC会为路径自动的拼接上项目名
@RequestMapping("/handle2")
public String handle2(){
return "redirect:/page/success.jsp";
}
任何方法的返回值都会被包装成ModelAndView对象
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
render(mv, request, response);
String viewName = mv.getViewName();
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
遍历所有视图解析器
for (ViewResolver viewResolver : this.viewResolvers)
View view = viewResolver.resolveViewName(viewName, locale);
view = createView(viewName, locale);
// Check for special "redirect:" prefix.
if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
RedirectView view = new RedirectView(redirectUrl,isRedirectContextRelative(), isRedirectHttp10Compatible());
// Check for special "forward:" prefix.
if (viewName.startsWith(FORWARD_URL_PREFIX))
String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
InternalResourceView view = new InternalResourceView(forwardUrl);
return applyLifecycleMethods(FORWARD_URL_PREFIX, view);
view.render(mv.getModelInternal(), request, response);
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
sendRedirect(request, response, targetUrl, this.http10Compatible);
response.sendRedirect(encodedURL);
视图解析器只是为了得到视图对象;视图对象才能真正的转发(将模型数据全部放在请求域中)或者重定向到页面
视图对象才能真正的渲染视图;
流程图
自定义视图解析器
public class MyViewResolver implements ViewResolver, Ordered {
private Integer order;
/**
* 根据视图名返回视图对象
* @param viewName
* @param locale
* @return
* @throws Exception
*/
@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
if(viewName.startsWith("h:") ) {
return new MyView();
}
return null;
}
@Override
public int getOrder() {
return this.order;
}
public void setOrder(Integer order){
this.order=order;
}
}
public class MyView implements View {
@Override
public String getContentType() {
return "text/html";
}
@Override
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
response.getWriter().write("<h1>ahhahhah</h1>");
}
}
<bean class="com.config.MyViewResolver" id="myViewResolver">
<property name="order" value="1"/>
</bean>
数据绑定
WebDataBinder
conversionService 类型转换器
validators 数据校验器
bindingResult 负责保存以及解析数据绑定期间数据校验产生的错误
bindingErrorProcessor 绑定异常处理
conversionService 负责数据类型的转换以及格式化功能;
不同类型的转换和格式化用它自己的converter
conversionService 中有非常多的Converter
resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
bindRequestParameters(binder, webRequest);
servletBinder.bind(servletRequest);
addBindValues(mpvs, request);
doBind(mpvs);
super.doBind(mpvs);
applyPropertyValues(mpvs);
绑定请求参数到目标对象
getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields());
for (PropertyValue pv : propertyValues)
setPropertyValue(pv);
GenericConversionService
private final Converters converters = new Converters();
static class Converters
add(GenericConverter converter)
Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType)
Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
return converter.convert(source, sourceType, targetType);
自定义类型转换器
conversionService:interface
它里面有 public interface Converter<S, T> 实际工作
- 实现Converter接口 写一个自定义的类型转换器
- Converter是ConverterService中的组件
Converter需要放入ConverterService中
将WebDataBinder中的ConversionServicer设置成我们这个加了自定义类型转换器的ConversionService
public class MyStringToEmployeeConverter implements Converter<String, User> {
@Override
public User convert(String source) {
User user = new User();
return user;
}
}
<!-- 使用自己设置的conversionService组件-->
<mvc:annotation-driven conversion-service="conversionService"/>
<!-- 告诉SpringMvc用我们自定义的类型转换器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<!-- converters中添加自定义类型转换器 -->
<property name="converters">
<set>
<bean class="com.config.MyStringToEmployeeConverter"/>
</set>
</property>
</bean>
BeanDefinionParser bean定义信息解析器 解析xml成beanDefinion
- 动态资源能访问 web.xml中 url-parent= / 覆盖了 tom中的配置 便由SpringMvc来处理,mvc中的HandleMapping的handleMap中保存了每一个资源的映射信息,静态不能访问是因为handleMap中没有保存静态资源的映射请求信息,HandleAdapter来帮我们执行目标方法
- HandleMapping实现类发生变化
所有请求过来直接交给tomcat进行处理
(SimpleUrlHandlerMapping) /**=DefaultServletHandleMapping - 都加 RequestMappingHandleMapping Simpxxxx 能处理的就处理,不能处理的就交给tomcat
数据格式化
ConversionserviceFactoryBearn 之前自定义的转换器不带格式化功能
FormattingConversionserviceFactoryBearn 带格式化功能的转换器 默认的也带
以后写自定义类型转化器时写FormattingConversionServiceFactoryBean
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"/>
@DateTimeFormat(pattern="yyyy-MM-dd")
private Date birth;
@NumberFormat(pattern="#,###,###.##")
private Double salary
数据校验
JSR303 规范
Hibernate Validator 第三方校验框架
导入校验框架包
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.2.Final</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>3.2.1.Final</version>
</dependency>
<dependency>
<groupId>com.fasterxml</groupId>
<artifactId>classmate</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>org.hibernate.common</groupId>
<artifactId>hibernate-commons-annotations</artifactId>
<version>5.1.0.Final</version>
</dependency>
@NotBlank 至少有一个字符
加入校验注解
@NotEmpty
private String name;
在SpringMVc封装对象的时候,告诉SpringMVC这个javaBean需要校验
public String addEmp(@Valid Employee employee)
校验结果
给需要校验的javaBean后面紧跟一个BindingResult, BindingResult就是封装前一个bean的校验结果
public String addEmp(@Valid Employee employee,BindingResult result)
根据校验结果判断
if(result.hasError){
List<FieldError> errors=result.getFieldErrors()
for(FieldError fielderror :errors){
sout("消息"+rielderror.getDefaultMessage)
sout("错误字段"+getField)
}
}
忽略json数据返回
@JsonIgnore
json格式化
@JsonFormat(pattern="yyyy-MM-dd")
HttpEntity 获取请求头数据 全部
@RequestHeader 只能获取某个请求头
public String test(HttpEntity<String> str){
}
响应体中国内容的类型
public ResponseEntity<String> test(){
HttpStatus statusCode
MultiValueMap<String,String> headers=new HttpHeaders()
String body
HttpStatus.OK
headers.add("Set-Cookies","username=hhhhh")
new ResponseEntity<String>(body,headers,statusCode)
}
文件下载
@RequestMapping("/download")
public ResponseEntity<byte[]> download(HttpServletRequest request) throws IOException {
ServletContext servletContext = request.getServletContext();
String realPath = servletContext.getRealPath("/static");
FileInputStream fileInputStream = new FileInputStream(realPath);
byte[] bytes = new byte[fileInputStream.available()];
fileInputStream.read(bytes);
fileInputStream.close();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set("Content-Disposition","attachment:filename=aaa.txt");
ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(bytes, httpHeaders, HttpStatus.OK);
return responseEntity;
}
文件上传
表单准备: enctype= “multipart/form-data”
导包
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
配置文件上传解析器
<!-- id必须是 multipartResolver-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="#{1024*1024*20}"/>
<property name="defaultEncoding" value="utf-8"/>
</bean>
文件上传表单准备;enctype= "multipart/form-data"
@RequestMapping("/upload")
public String upload(@RequestParam("img")MultipartFile file) throws IOException {
file.transferTo(new File("h:\\hah|"+file.getOriginalFilename()));
return "";
}
多文件
乱码
拦截器
HandlerInterceptor:interface
preHandle 在目标方法运行之前 返回Boolean true放行 反之
postHandle 在目标方法运行之后调用
afterCompletion 在请求整个完成之后,来到目标页面之后 chain.doFilter()放行 资源响应之后
实现HandlerInterceptor
public class MyFirstInterceptor 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 {
}
}
注册拦截器,配置拦截那些方法
<mvc:interceptors>
<!-- 默认拦截所有-->
<!-- <bean class="com.config.MyFirstInterceptor"/>-->
<!-- 配置某个拦截器更详细的信息-->
<mvc:interceptor>
<!-- 只去拦截test01请求-->
<mvc:mapping path="/test01"/>
<bean class="com.config.MyFirstInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
拦截器的preHandle------目标方法-----拦截器postHandle-----页面-------拦截器的afterCompletion;
其他流程︰
1、只要preHandle不放行就没有以后的流程;
2、只要放行了 ,afterCompletion都会执行;
多拦截器
谁先配置谁优先
哪一块不放行以后都没有,已放行的拦截器的afterComplrtion还是会执行
doDispatch()
拿到执行链,包含拦截器
HandlerExecutionChain mappedHandler= getHandler(processedRequest);
拿到所有执行拦截器,并执行preHandle方法 有记录最后一个放行拦截器的索引,从他开始把之前所有放行的拦截器的afterCompletion都执行
if (!mappedHandler(HandlerExecutionChain ).applyPreHandle(processedRequest, response))return;
执行目标方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
拿到所有执行拦截器,并逆序执行postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
render(mv, request, response);
页面渲染完成后
mappedHandler.triggerAfterCompletion(request, response, null);
只要有任何异常
cache{triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
异常处理
AnnotationDrivenBeanDefinitionParser
开启注解驱动类
线从IOC容器中查找
HandlerExceptionResolver her =
context.getBean("handlerExceptionResolver", HandlerExceptionResolver.class);
找不到就获取默认的
this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
org.springframework.web.servlet.HandlerExceptionResolver=
org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
try{
try{
//执行目标方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}catch (Exception ex) {
执行后出现异常
dispatchException = ex;
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
if (exception != null) {
拿到目标方法
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
处理异常
mv = processHandlerException(request, response, handler, ex);
遍历所有异常解析器
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers)
查看哪个解析器能够解析这个异常 3个异常解析器
exMv = resolver.resolveException(request, response, handler, ex);
如果都不能处理直接抛出去 给tomcat处理
throw ex;
}
if (mv != null && !mv.wasCleared())
render(mv, request, response);
}catch(){
triggerAfterCompletion(processedRequest, response, mappedHandler, ex)
}
ExceptionHandlerExceptionResolver 处理 @ExceptionHandler
ResponseStatusExceptionResolver @ResponseStatus
DefaultHandlerExceptionResolver 判断是否SpringMvc自带的异常
/**
* 告诉SpringMVC这个方法专门处理这个类发生的异常
* 给方法上写一个Exception用来接收异常
* 要携带异常信息不能给参数上写Model
* 可以直接返回ModelAndView
*/
@ExceptionHandler(value = {ArithmeticException.class,NullPointerException.class})
public String handleException(){
return "error";
}
全局异常处理
/**
* 集中处理所有异常
* 要加入到IOC容器中 @ControllerAdvice 专门来处理异常的类
*/
@ControllerAdvice
public class MyProcessException {
@ExceptionHandler(value = {ArithmeticException.class,NullPointerException.class})
public String handleException(Exception e){
return "error";
}
// 精确优先
@ExceptionHandler(value = {Exception.class})
public String basicHandleException(Exception e){
return "error";
}
}
自定义异常 抛出 throw MyException()
@ResponseStatus(reason = "错误",value = HttpStatus.NOT_FOUND)
public class MyException extends RuntimeException {
}
SpringMVC运行流程:
1、所有请求,前端控制器(DispatcherServlet)收到请求,调用doDispatch进行处理
2、根据HandlerMapping中保存的请求映射信息找到,处理当前请求的,处理器执行链(包含拦截器)
3、根据当前处理器找到他的HandlerAdapter(适配器)
4、拦截器的preHandle先执行
5、适配器执行目标方法,并返回ModelAndView
1)、ModelAttribute注解标注的方法提前运行
2)、执行目标方法的时候(确定目标方法用的参数)
1)、有注解
2)、没注解:
1)、 看是否Model、Map以及其他的
2)、如果是自定义类型
1)、从隐含模型中看有没有,如果有就从隐含模型中拿
2)、如果没有,再看是否SessionAttributes标注的属性,如果是从Session中拿,如果拿不到会抛异常
3)、都不是,就利用反射创建对象
6、拦截器的postHandle执行
7、处理结果;(页面渲染流程)
1)、如果有异常使用异常解析器处理异常;处理完后还会返回ModelAndView
2)、调用render进行页面渲染
1)、视图解析器根据视图名得到视图对象
2)、视图对象调用render方法;
3)、执行拦截器的afterCompletion;
整合
SpringMVC和Spring整合的目的;分工明确;
SpringMVC的配置文件就来配置和网站转发逻辑以及网站功能有关的(视图解析器,文件上传解析器,支持ajax,xxx);
Spring的配置文件来配置和业务有关的(事务控制,数据源,xxx);
Spring管理业务逻辑组件
<context:component-scan base-package="com.atguigu">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
SpringMVC管理控制器组件;
<context:component-scan base-package="com.atguigu" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>