SpringBoot:Web 开发相关原理 ---- 7

静态资源配置原理

静态资源处理规则

sssSpringBoot启动自动加载 xxxAutoConfiguration 自动配置类

sssSpringMVC功能的自动配置类大都集中在 ==》 WebMvcAutoConfiguration ,生效

@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})   // 没有 WebMvcConfigurationSupport 类生效💦
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
	...	...	....	....	...	...
}

sssWebMvcAutoConfiguration 生效给容器中配置了什么?

 // WebMvcAutoConfiguration 内部类
 @Configuration(	proxyBeanMethods = false )	
 @Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
 @EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class})	//配置文件的相关属性和xxx进行了绑定
 @Order(0)
 public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
 ...	...	...
 }

sss配置文件的相关属性和xxx进行了绑定:

ssdssWebMvcProperties ==》 spring.mvc ResourceProperties ==》 spring.resources

ssdssWebProperties ==》 spring.web

ssdssWebMvcAutoConfigurationAdapter 只有一个有参构造器,有参构造器所有参数值都会从容器中确定。如下:

/**
@ ResourceProperties :获取和spring.web绑定的所有的值的对象
@ WebMvcProperties :获取和spring.mvc绑定的所有的值的对象
@ ListableBeanFactory :Spring的beanFactory
@ HttpMessageConverters :找到所有的HttpMessageConverters
@ ResourceHandlerRegistrationCustomizer :找到资源处理器的自定义器
@ DispatcherServletPath
@ ServletRegistrationBean 给应用注册Servlet、Filter....
*/
 public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider, ObjectProvider<WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider, ObjectProvider<DispatcherServletPath> dispatcherServletPath, ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {
     this.resourceProperties = resourceProperties;
     this.mvcProperties = mvcProperties;
     this.beanFactory = beanFactory;
     this.messageConvertersProvider = messageConvertersProvider;
     this.resourceHandlerRegistrationCustomizer = (WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer)resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
     this.dispatcherServletPath = dispatcherServletPath;
     this.servletRegistrations = servletRegistrations;
 }

sss资源处理的默认规则:

 public void addResourceHandlers(ResourceHandlerRegistry registry) {
 
 		// addMappings默认为true,即允许静态资源💦
 		//并且因为调用resourceProperties属性,点进去发现是属于ResourceProperties类,相对应于容器的组件,看注1💦
     if (!this.resourceProperties.isAddMappings()) {
         logger.debug("Default resource handling disabled");
     } else {
     	 //设置缓存的时间
         Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
         CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
         
         // 设置 webjars 中静态资源访问规则💦
         if (!registry.hasMappingForPattern("/webjars/**")) {
             this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{"/webjars/**"}).
             addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"}).
             setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
         }
         
		// 设置静态资源的路径 this.resourceProperties.getStaticLocations() 获得,默认为 "/**",可以修改💦
         String staticPathPattern = this.mvcProperties.getStaticPathPattern();
         if (!registry.hasMappingForPattern(staticPathPattern)) {
             this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern}).
             addResourceLocations(WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations())).
             setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
         }
     }
 }

sss【注1】:

public class ResourceProperties {
    private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", 
    "classpath:/resources/", "classpath:/static/", "classpath:/public/"};
    private String[] staticLocations;
    
    public ResourceProperties() {
        this.staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
        this.addMappings = true;
        this.chain = new ResourceProperties.Chain();
        this.cache = new ResourceProperties.Cache();
    }
    public String[] getStaticLocations() { return this.staticLocations;}
spring: 
	resources: 
		# 禁用所有静态资源 
		add-mappings: false 
		# 设置缓存 
		cache: period: 11000 
		# 修改静态资源路径 
		static-locations: classpath:/stat_resources/

sss欢迎页处理规则:

 //HandlerMapping:处理器映射。保存了每一个Handler能处理那些请求。
 @Bean
 public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
  	// 跳转欢迎页,看注【注2】💦
     WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new 
     TemplateAvailabilityProviders(applicationContext), applicationContext, 
     this.getWelcomePage(), this.mvcProperties.getStaticPathPattern());
     
     welcomePageHandlerMapping.setInterceptors(this.getInterceptors(mvcConversionService, mvcResourceUrlProvider));
     
     welcomePageHandlerMapping.setCorsConfigurations(this.getCorsConfigurations());
     
     return welcomePageHandlerMapping;
 }

sss【注2】:

 WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders, ApplicationContext applicationContext, Optional<Resource> welcomePage, String staticPathPattern) {
	 // 要使用欢迎页必须是 “ /** ”💦
     if (welcomePage.isPresent() && "/**".equals(staticPathPattern)) {
         logger.info("Adding welcome page: " + welcomePage.get());
         this.setRootViewName("forward:index.html");
     } else if (this.welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) {
     	 // 静态资源路径不是默认配置,就调用 Controller 处理 /index 请求💦
         logger.info("Adding welcome page template: index");
         this.setRootViewName("index");
     }
 }

sss【注3】:•favicon :浏览器默认会发请求访问 /favicon.ico当静态资源请求规则是 /** 时,可以访问。如果自己指定了静态资源请求路径则不能访问。

请求参数处理

请求映射

sssrest使用:

ssdssRest风格支持(使用HTTP请求方式动词表示对资源的操作)

ssdss以前: ○ 以前:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户

sdssdsdsd s ○ 现在: /user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户

sdssdsdsd s ○ 核心Filter :HiddenHttpMethodFilter

ssdsdsddsdsdss ■ 用法: 表单method=post,隐藏域 _method=put

ssdsdsddsdsdss ■ SpringBoot中手动开启

spring:
  mvc:
    hiddenmethod:
      filter:
        enabled: true	#开启页面表单的Rest功能
@RestController
public class HelloController {
    @RequestMapping("/bug.jpg")
    public String hello(){
        return "aaaa";
    }

    //    @RequestMapping(value = "/user",method = RequestMethod.GET)
    @GetMapping("/user")
    public String getUser(){

        return "GET-张三";
    }

    //    @RequestMapping(value = "/user",method = RequestMethod.POST)
    @PostMapping("/user")
    public String saveUser(){
        return "POST-张三";
    }


    //    @RequestMapping(value = "/user",method = RequestMethod.PUT)
    @PutMapping("/user")
    public String putUser(){

        return "PUT-张三";
    }

    @DeleteMapping("/user")
//    @RequestMapping(value = "/user",method = RequestMethod.DELETE)
    public String deleteUser(){
        return "DELETE-张三";
    }
}

sss rest 原理 (表单提交要使用REST的时候):

ssdss前提 :表单提交会带上_method=PUT (这里应该是_method=希望使用的提交方法)

ssdss1、请求过来被HiddenHttpMethodFilter拦截 (源码) :
在这里插入图片描述
在这里插入图片描述
ssdssHiddenHttpMehodFilter的核心方法:

protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
	//原生的servlet请求:HttpServletRequest request💦
    HttpServletRequest requestToUse = request;
    // 2、请求是否是POST请求并且正常💦
    if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) {
    	  // 4、获取到_method的值,默认methodParam = "_method"  💦
        String paramValue = request.getParameter(this.methodParam);
        if (StringUtils.hasLength(paramValue)) {
        	//或者methodParam的前缀,并将其转换成大写,比如 delete_method,所以会获取到 DELETE 
            String method = paramValue.toUpperCase(Locale.ENGLISH);
            //ALLOWED_METHODS == PUT、DELETE、PATCH💦
            if (ALLOWED_METHODS.contains(method)) {
		//就是一种包装,通过调用HiddenHttpMethodFilter的内部类HttpMethodRequestWrapper的💦
		//getMethod()方法返回输入的method而不是post💦
                requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
            }
        }
    }
    filterChain.doFilter((ServletRequest)requestToUse, response);
}

sdssd s 2、 请求是否正常,并且是POST

ssdsdsddsdsdss 3、 用法: 表单method=post,隐藏域 _method=put

ssdsdsddsdsdss 4、 获取到_method的值(无论value是大写还是小写,底层都会统一转换为大写)

ssdsdsddsdsdss 5、 兼容以下请求 :PUT.DELETE. PATCH

ssdsdsddsdsdss 6、原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值

ssdsdsddsdsdss 7、过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。

sss【注 5】:通过源码可知,methodParam = “_method”; 但是可以通过setMethodParam(String methodParam)方法改变methodParam值,因此这时候 我们可以通过把_method 换成我们自己自定义得值,比如_m:
在这里插入图片描述

@Configuration(proxyBeanMethods = false)
public class config {

    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();

        hiddenHttpMethodFilter.setMethodParam("_m");
        return hiddenHttpMethodFilter;
    }
}

sss【注 6】:ALLOWED_METHODS值的设置方式,通过静态块赋值:
在这里插入图片描述
sss【注 7】:requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);源码:
在这里插入图片描述
sss【注 8】:Rest使用客户端工具,如PostMan直接发送Put、delete等方式请求,无需Filter。因为:
在这里插入图片描述
sss【注 9】:
@ DeleteMapping实质就是 @RequestMapping ( method = { RequestMethod.DELETE} )
@ PutMapping实质就是 @RequestMapping ( method = { RequestMethod.PUT} )
@ PostMapping实质就是 @RequestMapping ( method = { RequestMethod.POST} )
@ GetMapping实质就是 @RequestMapping ( method = { RequestMethod.GET} )

请求映射原理

dasdasdas在这里插入图片描述

sssDispatcherServlet调用FrameworkServlet中的doGet和doPost

sssdoGet和doPost ==> processRequest(request, response) ==> doService(request, response)

sssdoService是一个抽象方法,并未做具体实现

sss FrameworkServlet的子类DispatcherServlet实现了doService

sssDispatcherServlet的doService ==> doDispatch(request, response)

sss请求映射流程:

sdsdsss请求进来 ==> HttpServlet的doGet和doPost

sdsdsdsdsddsdsdsss==> FrameworkServlet中的doGet和doPost

sdsdsddsdsdsddsdsdsss==> DispatcherServlet的doService

sdsdsdsddsdsdsdsdsdsdsss==> doService中调用doDispatch(request, response)

sss[注10]: SpringMVC功能分析都从 org.springframework.web.servlet.DispatcherServlet-》doDispatch()

在这里插入图片描述
sss找到当前请求使用哪个Handler : mappeHandler = this.gethHandler(processedRequest); 😒
dasdas在这里插入图片描述
ssshandlerMappings:处理器映射,有五个值

sss[注11]: 第一各处理器映射:RequestMappingHandlerMapping:保存了所有@RequestMapping 和handler的映射规则
在这里插入图片描述
所有的请求映射都在HandlerMapping中。

sss1. SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.html;

sss2. SpringBoot自动配置了默认 的 RequestMappingHandlerMapping

sss3. 请求进来,挨个尝试所有的HandlerMapping看是否有请求信息(在request)。(同样的请求信息只能有一个)

sdsss• 如果有就找到这个请求对应的handler

ssdss• 如果没有就是下一个 HandlerMapping

sss4. 我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping。自定义 HandlerMapping (映射每一个请求到对应的Handler)

sss[注12]: getHandler就是我们寻找相应处理器方法的方法:
在这里插入图片描述
sss[注13]: 点击DispatcherServlet的getHandler方法,进入到AbstractHandlerMapping的getHandler方法,
在这里插入图片描述
sss[注14]: 再进入getHandlerInternal(request):
在这里插入图片描述
sss[注15]: 再进入getHandlerInternal(request)方法:就是核心部分:
在这里插入图片描述
sss[注16]: 通过lookupHandlerMethod(lookupPath, request)获取相应的处理方法:
在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值