目录
一、SpringMVC自动配置概览
二、简单功能分析
2.1静态资源访问
1、静态资源路径
2、webjar
2.2、欢迎页支持
2.3、自定义 Favicon
2.4、静态资源配置原理
1、配置类中只有一个构造器
2、资源处理的默认规则
3、欢迎页的处理规则
三、请求参数处理
3.1 请求映射原理
3.2 参数处理原理
1、 HandlerAdapter
2、执行目标方法
3、参数解析器-HandlerMethodArgumentResolver
4、结果处理器
5、如何确定目标方法每一个参数的值
6、 挨个判断所有参数解析器那个支持解析这个参数
7、 解析这个参数的值编辑
3.3、Servlet API:
3.4、复杂参数:
1、原理:
2、目标方法执行完毕后
3.处理派发结果
Spring Boot provides auto-configuration for Spring MVC that works well with most applications.(大多场景我们都无需自定义配置)
SprongBoot通过xxxAutoConfiguration的类,对我们一些我们常常用到的场景进行了默认设置。
例如在SpringMvc XML版我们要设置的:视图解析器、全局异常解析器、静态资源访问、前端控制器等等都无序我们进行配置。只需要将我们想要设置的在配置文件中设置即可
当然我们想要自定义,也是可以的自己定义这些类,SpringBoot就会使用自定义的类
只要静态资源放在类路径下(resources下的):
/static 、 /public、 /resources 、 /META-INF/resources
访问: 当前项目的根路径 /静态资源名称
static-path-pattern 默认是/**
(当前项目 + static-path-pattern + 静态资源名 = 静态资源文件夹下找 )
原理 静态映射/**
请求进来,先去找Controller看能不能处理。不能处理的所以请求又都交给静态资源处理器、静态资源也找不到则响应404页面
改变默认的静态资源前缀和路径
spring:
mvc:
#设置静态资源的模式 默认是/**
static-path-pattern: /res/**
web:
resources:
static-locations: [classpath:/haha/]
自动映射 /webjars/**
WebJars - Web Libraries in Jars
org.webjars jquery 3.5.1
访问地址:http://localhost:8080/webjars/jquery/3.5.1/jquery.js 后面地址要按照依赖里面的包路径
静态资源路径下 index.html
可以配置静态资源路径
但是不可以配置静态资源的访问前缀。否则回导致index.html不能被默认访问(后面源码中可以看见)
Favicon
favicon.ico 放在静态资源目录下即可。
是小图标的设置
SpringBoot启动默认加载XXXAutoConfiguration类(自动配置类)
而静态资源配置的原理是因为自动加载了WebMVCAutoConfiguration自动配置类,才生效
@AutoConfiguration(after = { DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
public class WebMvcAutoConfiguration {
主要配置了什么
配置文件的相关属性 WebMvcPorperties ===> spring.mvc
WebProperties ===> spring.web
主要就是进行一些初始化工作
webProperties、mvcProperties 是获取配置文件中的相关信息 beanFactory bean工厂 等
spring:
web:
addMappings: false 禁用所有静态资源规则
WelcomePageHandlerMapping这个类是欢迎页的规则
这个源码也体现了静态资源的前缀必须要是/**才行
3.1 请求映射原理
主要是通过DispatcherServlet这个前端控制器来实现的
而DispatcherServlet其实它的父类是HttpServerlet,HttpServlet是通过doGet()、doPost()、等等请求方法进行实现映射的
然后我们发现是FrameworkServlet中有doGet()、doPost()的方法,并且每个方法调用了
processRequest(request, response);
processRequest(request, response);方法中的核心方法是doService(request,response)
doService()方法是一个抽象类,它的实现是在DispatcherServlet中
每一个doService中都调用doDispatch(request,response)
RequestMappingHandlerMapping:保存了所有@RequestMapping 和handler的映射规则。
所有的请求映射都在HandlerMapping中。
HandlerMapping中能找到处理请求的Handler(即能找到请求到底是访问的那个方法)
然后通过为当前的Handler找一个适配器HandlerAdapter 进行参数的处理,适配器的主要作用执行目标方法并确定方法参数的每个值
进入到方法中我们可以看到 handlerAdpaters一共有四种
第一种支持方法书上标注@RequestMapping的 第二种是支持函数式变成的 。这两种是我们经常要使用的
这里我们是通过@RequestMapping注解标注的方法,因此第一次就成功得到了对应的处理器适配器
进入这个方法,我们方法最终是执行的这个方法
进入这个方法后我们发现了两个关键的参数
this.argumentResolvers 参数解析器 this.returnValueHandlers 结果处理器
这里是将这些方法先设置到 invocableMethod
确定将要执行的目标方法的每一个参数的值是什么;
SpringMvc目标方法能写多少种参数类型,取决于参数解析器
一共有27种 我们常见的如@RequestParam @PathVariable等
通过这个结果我们看到了有两个方法
supportsParameter() 这个是先判断这个参数是否有参数解析器能解析
如果能解析就执行下面的方法
resolveArgument()
这个显示的SpringMvc目标方法能有多少中返回结果
介绍完上面两个参数,继续执行找到要执行目标方法的代码
进入后 我们在117和118个打一个端点 然后在Controller代码中打一个端点
我们发现117执行完毕后执行执行的是 Controller的代码 也就是15行的
这说明了117这个代码执行完毕后就将方法的参数已经解析完毕了。
下面我们进入到117行代码中,看一下到底是如何解析的
我们发现这个args执行完毕后就是参数的值,然后通过doInvoke(args)反射进行执行
args得到的值和我输入参数的值一样
因此解析方法就是在146行代码中,我们进入一探究竟
进入后这句是获取方法参数的类型、参数使用说明注解的
@RequestParam是直接判断是否通过这个注解检查
通过检查后进行参数的获取
通过名称获取值
这样第一个参数的值就获取到了
WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId
ServletRequestMethodArgumentResolver 以上的部分参数
Map、Model(map、model里面的数据会被放在request的请求域 request.setAttribute)、Errors/BindingResult、RedirectAttributes( 重定向携带数据)、ServletResponse(response)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder
Mapmap, Model model, HttpServletRequest request 这三个都是可以给request域中放数据
进入看看参数解析器是如何解析的
看第一个参数map
然后调用的是getModel 给我们返回的ModelMap
map类型的参数,会返回
mavContainer的 getModel() ;
getModel() 最终返回的是 private final ModelMap defaultModel = new BindingAwareModelMap();
这个继承的是
这样第一个参数args[0]就是
第二个参数同理,model我们得到的是
最终完毕后转发到success
这个时候,我们看一下mavContainer 发现model和map是在同一个BindingAwareModelMap
接着往下执行,看一下处理返回结果的方法中 这个mvaContainer执行了什么操作
继续
执行完毕后,我们会将所有的数据都放在ModelAndViewContainer,包含有页面地址号view ,还包含有model数据
最终执行完毕后
mv就是modelAndVie render是渲染 进入这方法
@Override
protected void renderMergedOutputModel(
Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Expose the model object as request attributes.
exposeModelAsRequestAttributes(model, request);
// Expose helpers as request attributes, if any.
exposeHelpers(request);
// Determine the path for the request dispatcher.
String dispatcherPath = prepareForRendering(request, response);
// Obtain a RequestDispatcher for the target resource (typically a JSP).
RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
if (rd == null) {
throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
"]: Check that the corresponding file exists within your web application archive!");
}
// If already included or response already committed, perform include, else forward.
if (useInclude(request, response)) {
response.setContentType(getContentType());
if (logger.isDebugEnabled()) {
logger.debug("Including [" + getUrl() + "]");
}
rd.include(request, response);
}
else {
// Note: The forwarded resource is supposed to determine the content type itself.
if (logger.isDebugEnabled()) {
logger.debug("Forwarding to [" + getUrl() + "]");
}
rd.forward(request, response);
}
}
关键:暴露模型作为请求属性
exposeModelAsRequestAttributes(model, request);
所以这把就解释了,为什么是Map 和Model是在请求域中
这个代码就解释了为什么