SpringBoot Web开发(一)

1. 静态资源访问

  1. 静态资源目录
    在这里插入图片描述

上图为部分源码

只要静态资源放在类路径下:/static,/public,/resources,/META-INF/resources
在这里插入图片描述
访问:当前项目根路径/ + 静态资源名

http://127.0.0.1:8080/photo.png

原理:静态映射 /**

请求进来,先去找Controller映射,如若不能处理则交给静态处理器处理,静态资源也找不到则报404。

  1. 静态资源访问前缀
    配置之后此后访问静态资源需要加一个前缀,默认无前缀。
    在这里插入图片描述
    使用上述配置后,访问静态资源路径为

http://127.0.0.1:8080/res/photo.png

  1. 添加默认访问路径
    在这里插入图片描述

2. 静态资源配置原理

  • SpringBoot启动,默认加载xxxAutoConfiguration(自动配置类)
  • SpringMVC功能的自动配置类WebMvcAutoConfiguration,生效
@Configuration (proxyBeanMethods = false)
@ConditionalOnWebApplication (type = Type.SERVLET)
@ConditionalOnClass ({
    
    Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean ({
    
    WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({
    
    DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
    
    }
  • 给容器中配了什么
 @Configuration(
        proxyBeanMethods = false
    )
    @Import({
    
    WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
    @EnableConfigurationProperties({
    
    WebMvcProperties.class, ResourceProperties.class})
    @Order(0)
    public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
    
    }

WebMvcProperties.class == 配置文件中前缀为spring.mvc的属性
ResourceProperties.class == 配置文件中前缀为spring.resources的属性

3. 请求参数处理

  • @xxxMapping
  • Rest风格支持(使用HTTP请求方式动词来表示对资源的操作)
    • 以前: /getUser 获取用户 /deleteUser 删除用户
    • 现在:/user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户
    • 核心Filter:HiddenHttpMethodFilter
      • 用法:表单method=post,隐藏于_method=put
      • SpringBoot中手动开启,在 *.yaml 配置文件中开启
<form action="/user" method="post">
	<input name="_method" type="hidden" value="PUT"/>
	<input type="submit" value="REST-PUT 提交"/>
</form>
//源码
@Bean
    @ConditionalOnMissingBean({
    
    HiddenHttpMethodFilter.class})
    @ConditionalOnProperty(
        prefix = "spring.mvc.hiddenmethod.filter",
        name = {
    
    "enabled"},
        matchIfMissing = false
    )
    public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
    
    
        return new OrderedHiddenHttpMethodFilter();
    }

在这里插入图片描述

  • 表单提交的REST风格原理
    1. 表单提交会带上_method=PUT
    2. 请求过来会被HiddenHttpMethodFilter拦截
      a. 判断请求是否正常,并且是POST请求
      b. 取到_method的值,并判断是否是要求的请求方式
      c. 若符合b的条件,则使用_method的值新建一个请求(HttpMethodRequestWrapper)
      d. 过滤器链放行的时候用wrapper,以后调用的getMethod()方法是wrapper的方法
public class HiddenHttpMethodFilter extends OncePerRequestFilter {
    
    
    private static final List<String> ALLOWED_METHODS;
    public static final String DEFAULT_METHOD_PARAM = "_method";
    private String methodParam = "_method";
	//过滤方法
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    
    
        HttpServletRequest requestToUse = request;
        //请求正常并且为POST
        if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) {
    
    
            String paramValue = request.getParameter(this.methodParam);
            if (StringUtils.hasLength(paramValue)) {
    
    
            	//将隐藏域中的方法改为大写
                String method = paramValue.toUpperCase(Locale.ENGLISH);
                //判断是否是集合中兼容的请求方法
                if (ALLOWED_METHODS.contains(method)) {
    
    
                	// 新建一个包装请求
                    requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
                }
            }
        }

        filterChain.doFilter((ServletRequest)requestToUse, response);
    }
    
	//可兼容的请求方式集合:PUT、DELETE、PATCH
    static {
    
    
        ALLOWED_METHODS = Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));
    }
    
  //原生request,使用包装模式
  //HttpServletRequestWrapper 实际实现的是HttpServletRequest接口
  private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
    
    
        private final String method;

        public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
    
    
            super(request);
            this.method = method;
        }
		//重写getMethod()方法
        public String getMethod() {
    
    
            return this.method;
        }
    }
}
  • 客户端工具发送Rest风格请求直接发即可,如PostMan

4. 请求映射原理

在这里插入图片描述代码分析

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    
    
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
    
    
            try {
    
    
                ModelAndView mv = null;
                Object dispatchException = null;

                try {
    
    
                	//判断是否为上传文件的请求
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
					//找到当前请求使用哪个Controller处理,通过HandlerMapping(处理器映射)寻找
                    mappedHandler = this.getHandler(processedRequest);
                    
                    //......
	@Nullable
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    
    
    	//HandlerMapping(处理器映射)什么请求对应哪个处理方法
        if (this.handlerMappings != null) {
    
    
            Iterator var2 = this.handlerMappings.iterator();

            while(var2.hasNext()) {
    
    
                HandlerMapping mapping = (HandlerMapping)var2.next();
                HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {
    
    
                    return handler;
                }
            }
        }

        return null;
    }

在这里插入图片描述

  • RequestMappingHandlerMapping:保存了所有@RequestMapping和handler的映射规则
  • 所有请求映射都在handlerMapping中
  • 请求进来挨个尝试所有的HandlerMapping
  • SpringBoot默认配置了RequestMappingHandlerMapping、WelcomePageHandlerMapping
  • 当我们需要自定义一些映射出历史,也可以自己向容器中放HandlerMapping

猜你喜欢

转载自blog.csdn.net/qq_42451178/article/details/112179670