关于springboot中静态资源管理和springmvc参数传递的方式及他们的原理

            关于静态资源的访问和实现原理

1.首先我们分析默认静态资源在哪里?
   解答 : 静态资源默认放在WebMvcConfiguration中的资源处理器中的一个内部类中WebMvcConfigurationAdapter
    在WebMvcConfigurationAdapter中的资源处理器中有很多方法,这些方法哪里来呢?哪些方法处理哪些资源呢?
        @Configuration(
        proxyBeanMethods = false
    )
    @Import({EnableWebMvcConfiguration.class})
    @EnableConfigurationProperties({WebMvcProperties.class, WebProperties.class})
    @Order(0)
    public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ServletContextAware
    故可以看到这个静态内部类里面实现了WebMvcConfigurer这个接口,里面大部分方法都来自于WebMvcConfigurer这个接口中,
    这个接口很重要,如果我们要自己实现一套配置就要实现这个接口,如果要使用里面的默认配置就不添加@EnbaleWebMvc,反之添加这个注解
    查看接口 :


  方法如下 : 
    default void configurePathMatch(PathMatchConfigurer configurer) // 路径匹配
    default void configureContentNegotiation(ContentNegotiationConfigurer configurer) // 内容协商, 
    default void configureAsyncSupport(AsyncSupportConfigurer configurer) // 异步请求
    default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) // 默认处理器 
    default void addFormatters(FormatterRegistry registry)  // 格式转换器
    default void addInterceptors(InterceptorRegistry registry) // 拦截器
    default void addResourceHandlers(ResourceHandlerRegistry registry) // 这个方法用于处理资源,是webmvc的资源处理器
    default void addCorsMappings(CorsRegistry registry) // 这个我也没了解过
    default void addViewControllers(ViewControllerRegistry registry) // 视图解析器 
    default void configureViewResolvers(ViewResolverRegistry registry) // 视图解析器的配置器
    default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) // 参数解析器 
    default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) // 返回值处理器 
    default void configureMessageConverters(List<HttpMessageConverter<?>> converters) // 信息转换
    default void extendMessageConverters(List<HttpMessageConverter<?>> converters)  // 协助内容协商的一个转换器
    default void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) // 异常处理器配置器
    default void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) 

  故看到上面的资源处理器,我们进资源处理器进行查看,
   一、     public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 用于检查是否开启了静态资源映射,可以点进去查看,默认值是打开的
            if (!this.resourceProperties.isAddMappings()) {
                logger.debug("Default resource handling disabled");
            } else {
        // 这个用于处理webjars下面的资源,WebJars 是用于管理前端库的一个工具,它的路径可以点进去看    private String webjarsPathPattern = "/webjars/**";
                this.addResourceHandler(registry, this.mvcProperties.getWebjarsPathPattern(), "classpath:/META-INF/resources/webjars/");
                // 这个就是用于处理静态资源请求的,点进去进行查看这个方法如何处理
        this.addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
                  
            registration.addResourceLocations(this.resourceProperties.getStaticLocations());
                    if (this.servletContext != null) {
            // 指定从 / 根下进行查找
                        ServletContextResource resource = new ServletContextResource(this.servletContext, "/");
                        registration.addResourceLocations(new Resource[]{resource});
                    }

                });
            }
        }


    二、如下 : 
     private void addResourceHandler(ResourceHandlerRegistry registry, String pattern, Consumer<ResourceHandlerRegistration> customizer) {
            if (!registry.hasMappingForPattern(pattern)) {
                // 添加资源到静态资源里面去
        ResourceHandlerRegistration registration = registry.addResourceHandler(new String[]{pattern});
                customizer.accept(registration);
        // 下面是处理缓存的 , 由此可以看出静态资源里面是有缓存的
        // 这个请求未修改,在多少时间内可以走缓存
                registration.setCachePeriod(this.getSeconds(this.resourceProperties.getCache().getPeriod()));
                // 设置缓存控制器
        registration.setCacheControl(this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl());
                // 最后一次修改时间
        registration.setUseLastModified(this.resourceProperties.getCache().isUseLastModified());
                this.customizeResourceHandlerRegistration(registration);
            }
        }
   三、回到第一步上面getStaticLocations这个方法中去,我们去这个方法里面去找静态资源,一直往下点
    这个类也是WebProperties的内部类
    @ConfigurationProperties("spring.web") 
     public class WebProperties {
      public static class Resources {
    // 默认的静态资源常量,如果没有指定就会是这个常量赋值给下下面的staticLocations
        private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};
        private String[] staticLocations;
    // 资源映射
        private boolean addMappings;
        private boolean customized;
        // 执行器链
    private final Chain chain;
        // 缓存,刚刚上面提到了静态资源有缓存的问题
    private final Cache cache;
    
    故找到了默认静态资源    
    


2.静态资源的该如何访问?

    关于如何访问这个问题非常的简单,大家也应该都会,在未修改的前提下,放在默认的包下面自动就会进行静态资源扫描
        private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};


3.有哪几种方式添加静态资源?

    目前我知道的方式 :
        第一种 :配置文件中进行修改
            
            怎么修改呢?springboot的提供了很多类与配置文件进行绑定,我们只需要找到了这些类的绑定类,查看前缀
            
            进行修改,每个类上面都有属于自己的前缀名称
            
            因为在WebProperties下面,可以查看这类的资源路径
            
            故修改方式就是 :
            
                spring.web.resources.static-locations=
             
             注意 : 这种方式会覆盖原本的静态资源路径,导致原本的静态资源路径失效


        第二种 :实现接口的方式进行修改
        import org.springframework.context.annotation.Configuration;
        import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
        import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

        @Configuration
        public class StaticResourceNotes implements WebMvcConfigurer {


            @Override
            public void addResourceHandlers(ResourceHandlerRegistry registry) {
            // 保留了父类的配置,也就是上面提到的四个常量依旧有用,而且可以进行自己添加一些额外的静态资源
            WebMvcConfigurer.super.addResourceHandlers(registry);
            registry.addResourceHandler("/**").addResourceLocations("classpath:/a/" , "classpath:/b/");
            }
        }

        第三种方式 : 采取Bean注入的方式,匿名实现类因为这个WebMvcConfiguer必须被注入到容器中才能使用web
        @Configuration
        public class StaticResourceNotes  {
            @Bean
            public WebMvcConfigurer webMvcConfigurer(){
            return new WebMvcConfigurer() {
                @Override
                public void addResourceHandlers(ResourceHandlerRegistry registry) {
                WebMvcConfigurer.super.addResourceHandlers(registry);
                registry.addResourceHandler("/**").addResourceLocations("classpath:/a/" , "classpath:/b/");
                }
            };
            }
        }
            
            

            
4.实现原理?

    实现原理如第一问分析

            
            
            
            
            
            关于各种参数接收和实现原理

1.有哪些方式进行参数接收?
    方式一、使用原生态的Servlet进行参数接收

    提供的模拟用户表单提交的HTML,下面的所有演示方法只需要改action里面的地址栏内容。
    <form action="/getUserParameters" method="post">
          id:<input type="text" name="id"/><br/>
          name:<input type="text" name="name"/><br/>
          age:<input type="text" name="age"/><br/>
          <input type="submit" value="提交">
    </form>

    @RestController
    public class UserController {


        @RequestMapping("/getUserParameters")
        // 疑问 : 这个request怎么来的?
        public User getUserParameters(HttpServletRequest request){
        String id = request.getParameter("id");
        String name = request.getParameter("name");
        String age = request.getParameter("age");
        return new User(Integer.parseInt(id) , name , age);
        }

    }

    方式二、使用@RequestParam
        下面将讲三个一样值的注解来获取不同的参数值
            @RequestParam()
            可以通过设置形参的方式,但是如果前端参数和后端参数要求不一致,可使用里面进行设置里面的值,要求前端和注解里面的值一致
                
                属性 :
                1.value : 表示该参数的参数名 , 默认值是空字符串
                2.required : 表示value这个属性是否是必填项 , 默认值true
                3.defaultValue : 如果是空字符串 或者 参数名不匹配 , 或者不包含这个参数名 那么就会触发默认值
                
            @RequestHeader
            可以获取请求头里面的参数值 , 使用方法和上面的一致,当然如果想要获取全部请求头,可以封装在一个map中

            @CookieValue
            用来获取cookie的一个注解,每当第一次发送会话请求后,响应域就会返回一个cookieid , 通过cookieid可以获取到当前的session对象,需要注意的是不一定cookie的参数值就是JSESSIONID根据实际而定

            注意 : 以上的三个注解拥有三个一样的属性值

        演示 : 通过@RequestParam
            @RequestMapping("/getUserParametersByAnnotation")
            public User getUserParametersByAnnotation(@RequestParam("id")Integer id , @RequestParam("name")String name , @RequestParam("age")String age){
            return new User(id , name , age);
            }

        
        演示 : 通过@RequestHeader获取请求头,只要知道请求头里面的名字就可以根据名字获取对应的值

            @RequestMapping("getUserParametersByHeader")
            public String getUserParametersByHeader(@RequestHeader("Accept")String content){
            return content;
            }

    
    方式三、使用DispatcherServlet自动绑定参数的形式进行参数注入
    数据绑定是 Spring MVC 的一项重要功能,它允许将请求中的参数自动绑定到 Java 对象的属性上,从而省去了手动解析参数的步骤。Spring MVC 使用适配器(HandlerAdapter)来执行数据绑定操作。

    具体步骤如下:

    1.用户提交表单,浏览器向服务器发送 HTTP 请求。

    2Spring MVC 的 DispatcherServlet 接收到请求,根据请求的 URL 查找对应的处理器(Controller)。

    3找到匹配的处理器后,DispatcherServlet 通过适配器(HandlerAdapter)调用处理器的方法来处理请求。

    4当处理器方法被调用时,Spring MVC 会根据方法的参数列表和请求中的参数自动进行数据绑定。

    5Spring MVC 使用数据绑定的规则将请求中的参数映射到方法的参数或 Java 对象的属性上。默认情况下,Spring MVC 支持多种数据绑定方式,包括:

        表单参数到方法参数的绑定:将请求中的参数自动绑定到方法的参数上。
        表单参数到 Java 对象的绑定:将请求中的参数自动绑定到 Java 对象的属性上。
        路径变量绑定:将 URL 中的路径变量自动绑定到方法的参数上。
    处理器方法执行完成后,Spring MVC 将返回的数据(如视图名称、模型数据等)封装成 HTTP 响应,发送回给浏览器。
        
            @RequestMapping("getUserParameterByDispatcherServlet")
            public User getUserParameterByDispatcherServlet(User user){
            return user;
            }
    
    
    方式四、使用占位符的方式进行参数注入
        通过上面的的DispatcherServlet这种方式,使用参数注入,这个通过表单是无法进行直接进行占位符的方式进行参数注入的
        自己提供URL http://localhost:8080/getUserParameterByPathParameter/1/张三/男
            @RequestMapping("getUserParameterByPathParameter/{id}/{name}/{age}")
            public User getUserParameterByPathParameter(@PathVariable("id")Integer id , @PathVariable("name")String name , @PathVariable("age")String age){
            return new User(id ,name , age);
            }

2.关于参数处理的实现原理    
    
      第一步 : protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
            // 省略
            .
            .
            .
            
            // 上文也提到过DispatcherServlet 通过适配器(HandlerAdapter)调用处理器的方法来处理请求。    
                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
            // 省略
            .
            .
            .
                    // 这里用处理器进行处理
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            // 省略
            .
            .
            .                    
    }

    第二步:点进上文提到过的getHandlerAdapter这个方法进行查看
        protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (this.handlerAdapters != null) {
            Iterator var2 = this.handlerAdapters.iterator();

            while(var2.hasNext()) {
            HandlerAdapter adapter = (HandlerAdapter)var2.next();
            if (adapter.supports(handler)) {
                return adapter;
            }
            }
        }

        throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
        }

        在这里进行查找合适的处理器适配器来进行方法处理,这里有四个处理器适配器
        this.handlerAdapters = {ArrayList@6214}  size = 4
         0 = {RequestMappingHandlerAdapter@6958} 
         1 = {HandlerFunctionAdapter@6959} 
         2 = {HttpRequestHandlerAdapter@6960} 
         3 = {SimpleControllerHandlerAdapter@6961} 

         返回合适的处理器适配器

    第三步:通过上文选择合适的处理器适配器来进行处理mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            @Nullable
            public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            // 在这里选择的方法选择合适的模型和视图,关于这里在springmvc中已经提到了
            return this.handleInternal(request, response, (HandlerMethod)handler);
            }


    第四步:点进这个方法this.handleInternal(request, response, (HandlerMethod)handler);
            protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
            this.checkRequest(request);
            ModelAndView mav;
            // 不管怎么样都会调用方法处理器来对当前的请求和响应进行处理
            if (this.synchronizeOnSession) {
                HttpSession session = request.getSession(false);
                // 这是为了怕session中有数据而不能合理的处理
                if (session != null) {
                Object mutex = WebUtils.getSessionMutex(session);
                synchronized(mutex) {
                    mav = this.invokeHandlerMethod(request, response, handlerMethod);
                }
                } else {
                mav = this.invokeHandlerMethod(request, response, handlerMethod);
                }
            } else {
                mav = this.invokeHandlerMethod(request, response, handlerMethod);
            }

            if (!response.containsHeader("Cache-Control")) {
                if (this.getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
                this.applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
                } else {
                this.prepareResponse(response);
                }
            }

            return mav;
        }    
    第五步: mav = this.invokeHandlerMethod(request, response, handlerMethod);
        @Nullable
        protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        WebDataBinderFactory binderFactory = this.getDataBinderFactory(handlerMethod);
        ModelFactory modelFactory = this.getModelFactory(handlerMethod, binderFactory);
        ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);
        if (this.argumentResolvers != null) {
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        }

        if (this.returnValueHandlers != null) {
            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        }
        // 这一块是在进行参数设置
        invocableMethod.setDataBinderFactory(binderFactory);
        invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
        ModelAndViewContainer mavContainer = new ModelAndViewContainer();
        mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
        modelFactory.initModel(webRequest, mavContainer, invocableMethod);
        mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
        AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
        asyncWebRequest.setTimeout(this.asyncRequestTimeout);
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.setTaskExecutor(this.taskExecutor);
        asyncManager.setAsyncWebRequest(asyncWebRequest);
        asyncManager.registerCallableInterceptors(this.callableInterceptors);
        asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
        if (asyncManager.hasConcurrentResult()) {
            Object result = asyncManager.getConcurrentResult();
            mavContainer = (ModelAndViewContainer)asyncManager.getConcurrentResultContext()[0];
            asyncManager.clearConcurrentResult();
            LogFormatUtils.traceDebug(this.logger, (traceOn) -> {
            String formatted = LogFormatUtils.formatValue(result, !traceOn);
            return "Resume with async result [" + formatted + "]";
            });
            invocableMethod = invocableMethod.wrapConcurrentResult(result);
        }
        // 方法处理器
        invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);
        return asyncManager.isConcurrentHandlingStarted() ? null : this.getModelAndView(mavContainer, modelFactory, webRequest);
        }
    
    第六步:点进去看这个方法处理器怎么执行的
        public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        // 这个用于请求的参数按照处理器方法的参数列表进行数据绑定,即将请求参数自动映射到处理器方法的参数上
        Object returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs);
        省略
         .
         .
         .
        }

    第七步:点进这个方法
        public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        // 获取参数值
        Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
        if (logger.isTraceEnabled()) {
            logger.trace("Arguments: " + Arrays.toString(args));
        }
        // 执行这个方法调度器,将参数放进去
        return this.doInvoke(args);
        }

    第八步:点进这个doInvoke()
        protected Object doInvoke(Object... args) throws Exception {
            // 获取方法
            Method method = this.getBridgedMethod();

            try {
            // 调用method.invoke(this.getBean(), args)进行参数处理
                return KotlinDetector.isSuspendingFunction(method) ? this.invokeSuspendingFunction(method, this.getBean(), args) : method.invoke(this.getBean(), args);
            } catch (IllegalArgumentException var8) {
                this.assertTargetBean(method, this.getBean(), args);
                String text = var8.getMessage() != null && !(var8.getCause() instanceof NullPointerException) ? var8.getMessage() : "Illegal argument";
                throw new IllegalStateException(this.formatInvokeError(text, args), var8);
            } catch (InvocationTargetException var9) {
                Throwable targetException = var9.getCause();
                if (targetException instanceof RuntimeException runtimeException) {
                throw runtimeException;
                } else if (targetException instanceof Error error) {
                throw error;
                } else if (targetException instanceof Exception exception) {
                throw exception;
                } else {
                throw new IllegalStateException(this.formatInvokeError("Invocation failure", args), targetException);
                }
            }
            }

    第九步:点金这个method.invoke(this.getBean() ,args);
        public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
        {
        if (!override) {
            Class<?> caller = Reflection.getCallerClass();
            checkAccess(caller, clazz,
                Modifier.isStatic(modifiers) ? null : obj.getClass(),
                modifiers);
        }
        MethodAccessor ma = methodAccessor;             // read volatile
        if (ma == null) {
            ma = acquireMethodAccessor();
        }
        // 方法执行,方法执行器,类似于jdk动态代理,这地下就用的代理模式,完全交给DelegatingMethodAccessorImpl这个类来进行的,点进去可以看到这个类里面就是类似于动态代理
        return ma.invoke(obj, args);
        }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值