ResponseBodyAdvice的使用原理源码解析

目录
  • 前言
  • 正文
    • 一. ResponseBodyAdvice的使用
    • 二. ResponseBodyAdvice的原理
    • 三. ResponseBodyAdvice的加载
  • 总结

前言

ResponseBodyAdvice接口可以在将handler方法的返回值写入response前对返回值进行处理,例如将返回值封装成一个与客户端约定好的对象以便于客户端处理响应数据。本篇文章将学习如何使用ResponseBodyAdvice以及其实现原理。

SpringBoot版本:2.4.1

正文

一. ResponseBodyAdvice的使用

假如已经存在一个Controller,如下所示。

@RestController
public class DemoController {
    @RequestMapping(value = "/api/v1/demo1/getdefault", method = RequestMethod.GET)
    public ResponseEntity<Demo1> getDefaultDemo1() {
        return new ResponseEntity<>(Demo1.defaultDemo1, HttpStatus.INTERNAL_SERVER_ERROR);
    }
    @RequestMapping(value = "/api/v1/demo2/getdefault", method = RequestMethod.GET)
    public Demo2 getDefaultDemo2() {
        return Demo2.defaultDemo2;
    }
}
public class Demo1 {
    private int id;
    private String name;
    public static Demo1 defaultDemo1 = new Demo1(1, "Admin");
    public Demo1() {}
    public Demo1(int id, String name) {		this.id = id;
		this.name = name;
    }
    // 省略getter和setter
}
public class Demo2 {
    private int id;
    private String desc;
    public static Demo2 defaultDemo2 = new Demo2(1, "Root");
    public Demo2() {}
    public Demo2(int id, String desc) {
		this.id = id;
        this.desc = desc;
    }
    // 省略getter和setter
}

上述Controller中有两个方法,并且返回值分别为ResponseEntity<Demo1>和Demo2。此时客户端收到响应之后,针对响应体的处理变得十分不方便,如果增加更多的方法,并且返回值都不相同,那么客户端将需要根据不同的请求来特定的处理响应体。因此为了方便客户端处理响应数据,服务器端专门创建了一个返回结果类ReturnResult,并且规定服务器端的所有handler方法执行后往response中写入的响应体都必须为ReturnResult。在这种情况下,使用ResponseBodyAdvice可以在不修改已有业务代码的情况下轻松实现上述需求。假设自定义的返回结果类ReturnResult如下所示。

public class ReturnResult<T> {
    private int statusCode;
    private T body;
    public ReturnResult() {}
    public ReturnResult(T body) {
        this.body = body;
    }
    // 省略getter和setter
}

ReturnResult的body就是原本需要写入response的响应内容,现在整个ReturnResult为需要写入response的响应内容,相当于ReturnResult对handler方法的返回值进行了一层封装。

现在创建一个ReturnResultAdvice类并实现ResponseBodyAdvice接口,如下所示。

@ControllerAdvice
public class ReturnResultAdvice implements ResponseBodyAdvice<Object> {
    @Override
    public boolean supports(@Nullable MethodParameter returnType, @Nullable Class converterType) {
        return true;
    }
    @Override
    public Object beforeBodyWrite(Object body, @Nullable MethodParameter returnType,
                                  @Nullable MediaType selectedContentType, @Nullable Class selectedConverterType,
                                  @Nullable ServerHttpRequest request, @Nullable ServerHttpResponse response) {
        if (body == null) {
            return null;
        }
        if (body instanceof ReturnResult) {
            return body;
        }
        return new ReturnResult<>(body);
    }
}

ReturnResultAdvice的beforeBodyWrite() 方法会在handler方法返回值写入response前被调用。

此时调用DemoController的接口,会发现响应数据结构统一为ReturnResult。

小节:由@ControllerAdvice注解修饰并实现ResponseBodyAdvice接口的类所实现的beforeBodyWrite()方法会在handler方法返回值写入response前被调用,并且handler方法返回值会作为入参传入beforeBodyWrite(),从而可以在返回值写入response前对返回值进行一些定制操作,例如对返回值进行一层封装。

二. ResponseBodyAdvice的原理

首先说明一下为什么第一小节中DemoController的getDefaultDemo1() 方法的返回值类型为ResponseEntity<Demo1>,但是实际往response写的响应体内容为ResponseEntity中的body。首先所有ResponseBodyAdvice接口的调用是发生在AbstractMessageConverterMethodProcessor的writeWithMessageConverters() 方法中,这个方法的声明如下所示。

protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
            ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
            throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException;

其中value就是需要写入响应体的值,同时也是ResponseBodyAdvice要处理的值。然后如果handler方法的返回值是非ResponseEntity对象且handler方法由@ResponseBody注解修饰,那么writeWithMessageConverters() 方法的调用发生在RequestResponseBodyMethodProcessor#handleReturnValue方法中;

如果handler方法的返回值是ResponseEntity对象,那么writeWithMessageConverters() 方法的调用发生在HttpEntityMethodProcessor#handleReturnValue中,分别看一下在这两个方法中调用writeWithMessageConverters() 时传入的参数,就可以解释之前的疑问了。

RequestResponseBodyMethodProcessor#handleReturnValue

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
        throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    ......
    writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}

HttpEntityMethodProcessor#handleReturnValue()

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
    ......
    HttpEntity<?> responseEntity = (HttpEntity<?>) returnValue;
    ......
    writeWithMessageConverters(responseEntity.getBody(), returnType, inputMessage, outputMessage);
    ......
}

现在正式开始对ResponseBodyAdvice的原理进行分析。

已知所有ResponseBodyAdvice接口的调用是发生在AbstractMessageConverterMethodProcessor的writeWithMessageConverters() 方法中,其部分源码如下所示。

protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
        ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
        throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    ......
    if (selectedMediaType != null) {
        selectedMediaType = selectedMediaType.removeQualityValue();
        for (HttpMessageConverter<?> converter : this.messageConverters) {
            GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
                    (GenericHttpMessageConverter<?>) converter : null);
            if (genericConverter != null ?
                    ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
                    converter.canWrite(valueType, selectedMediaType)) {
                // ResponseBodyAdvice的调用发生在这里
                body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
                        (Class<? extends HttpMessageConverter<?>>) converter.getClass(),
                        inputMessage, outputMessage);
                if (body != null) {
                    Object theBody = body;
                    LogFormatUtils.traceDebug(logger, traceOn ->
                            "Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
                    addContentDispositionHeader(inputMessage, outputMessage);
                    if (genericConverter != null) {
                        genericConverter.write(body, targetType, selectedMediaType, outputMessage);
                    }
                    else {
                        ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
                    }
                }
                else {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Nothing to write: null body");
                    }
                }
                return;
            }
        }
    }
    ......
}

AbstractMessageConverterMethodProcessor的getAdvice() 方法会返回其在构造函数中加载好的RequestResponseBodyAdviceChain对象,下面看一下RequestResponseBodyAdviceChain的beforeBodyWrite() 方法。

public Object beforeBodyWrite(@Nullable Object body, MethodParameter returnType, MediaType contentType,
        Class<? extends HttpMessageConverter<?>> converterType,
        ServerHttpRequest request, ServerHttpResponse response) {
    return processBody(body, returnType, contentType, converterType, request, response);
}
private <T> Object processBody(@Nullable Object body, MethodParameter returnType, MediaType contentType,
        Class<? extends HttpMessageConverter<?>> converterType,
        ServerHttpRequest request, ServerHttpResponse response) {
    // 从加载好的ResponseBodyAdvice中获取适用于当前handler的ResponseBodyAdvice
    for (ResponseBodyAdvice<?> advice : getMatchingAdvice(returnType, ResponseBodyAdvice.class)) {
        if (advice.supports(returnType, converterType)) {
            // 执行ResponseBodyAdvice的beforeBodyWrite()方法以处理handler方法返回值
            body = ((ResponseBodyAdvice<T>) advice).beforeBodyWrite((T) body, returnType,
                    contentType, converterType, request, response);
        }
    }
    return body;
}
private <A> List<A> getMatchingAdvice(MethodParameter parameter, Class<? extends A> adviceType) {
    // 获取ResponseBodyAdvice集合
    List<Object> availableAdvice = getAdvice(adviceType);
    if (CollectionUtils.isEmpty(availableAdvice)) {
        return Collections.emptyList();
    }
    List<A> result = new ArrayList<>(availableAdvice.size());
    for (Object advice : availableAdvice) {
        // 判断ResponseBodyAdvice是否由@ControllerAdvice注解修饰
        if (advice instanceof ControllerAdviceBean) {
            ControllerAdviceBean adviceBean = (ControllerAdviceBean) advice;
            // 判断ResponseBodyAdvice是否适用于当前handler
            if (!adviceBean.isApplicableToBeanType(parameter.getContainingClass())) {
                continue;
            }
            advice = adviceBean.resolveBean();
        }
        if (adviceType.isAssignableFrom(advice.getClass())) {
            result.add((A) advice);
        }
    }
    return result;
}

在RequestResponseBodyAdviceChain中,beforeBodyWrite() 方法调用了processBody() 方法,processBody() 方法会遍历所有加载好并且适用于当前handler的ResponseBodyAdvice并执行,至此,所有由@ControllerAdvice注解修饰的ResponseBodyAdvice接口会在这里执行。

小节:由@ControllerAdvice注解修饰的ResponseBodyAdvice接口会被SpringMVC框架加载到RequestResponseBodyMethodProcessorHttpEntityMethodProcessor这两个返回值处理器中,当这两个返回值处理器将返回值写入response前,适用于当前handler的ResponseBodyAdvice接口会被调用,从而可以完成对返回值的定制化改造。

三. ResponseBodyAdvice的加载

由第二小节可知,正是因为RequestResponseBodyMethodProcessor和HttpEntityMethodProcessor这两个返回值处理器会将由@ControllerAdvice注解修饰的ResponseBodyAdvice接口加载,才能够实现将返回值写入response前调用这些ResponseBodyAdvice接口对返回值进行一些操作。那么本小节将对esponseBodyAdvice接口的加载进行学习。

首先给出结论:ResponseBodyAdvice的加载发生在RequestMappingHandlerAdapterafterPropertiesSet()方法中。

已知,RequestMappingHandlerAdapter实现了InitializingBean接口,因此RequestMappingHandlerAdapter实现了afterPropertiesSet() 方法。该方法实现如下。

public void afterPropertiesSet() {
    // 加载ControllerAdviceBean相关内容(同时就会将由@ControllerAdvice注解修饰的ResponseBodyAdvice接口加载)
    initControllerAdviceCache();
    if (this.argumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
        this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.initBinderArgumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
        this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.returnValueHandlers == null) {
        // 获取返回值处理器,在这里就会完成RequestResponseBodyMethodProcessor和HttpEntityMethodProcessor的初始化,初始化的同时就会完成ResponseBodyAdvice接口的加载
        List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
        this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    }
}

上述实现中,initControllerAdviceCache() 方法会加载ControllerAdviceBean相关内容到RequestMappingHandlerAdapter中,这其中就包含由@ControllerAdvice注解修饰的ResponseBodyAdvice接口。然后在getDefaultReturnValueHandlers() 方法中会创建返回值处理器,在创建RequestResponseBodyMethodProcessor和HttpEntityMethodProcessor时会使用加载好的ResponseBodyAdvice接口完成这两个返回值处理器的初始化。上述两个方法的部分源码如下所示。

initControllerAdviceCache()

private void initControllerAdviceCache() {
    if (getApplicationContext() == null) {
        return;
    }
    // 获取由@ControllerAdvice注解修饰的bean
    List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
    List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();
    for (ControllerAdviceBean adviceBean : adviceBeans) {
        Class<?> beanType = adviceBean.getBeanType();
        if (beanType == null) {
            throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
        }
        Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
        if (!attrMethods.isEmpty()) {
            this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
        }
        Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
        if (!binderMethods.isEmpty()) {
            this.initBinderAdviceCache.put(adviceBean, binderMethods);
        }
        // 如果ControllerAdviceBean实现了ResponseBodyAdvice接口,那么这个ControllerAdviceBean需要加载到requestResponseBodyAdvice中
        if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
            requestResponseBodyAdviceBeans.add(adviceBean);
        }
    }
    if (!requestResponseBodyAdviceBeans.isEmpty()) {
        this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
    }
    ......
}

getDefaultReturnValueHandlers()

private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
    List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20);
    ......
    // 创建并加载HttpEntityMethodProcessor
    handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
            this.contentNegotiationManager, this.requestResponseBodyAdvice));
    ...
    // 创建并加载RequestResponseBodyMethodProcessor
    handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
            this.contentNegotiationManager, this.requestResponseBodyAdvice));
    ......
    return handlers;
}

根据getDefaultReturnValueHandlers() 方法可知,在创建HttpEntityMethodProcessor或者RequestResponseBodyMethodProcessor时,会将RequestMappingHandlerAdapter加载好的ResponseBodyAdvice传入构造函数,并且,无论是HttpEntityMethodProcessor还是RequestResponseBodyMethodProcessor,其构造函数最终都会调用到父类AbstractMessageConverterMethodArgumentResolver的构造函数,并在其中初始化一个RequestResponseBodyAdviceChain以完成ResponseBodyAdvice的加载。构造函数源码如下所示。

HttpEntityMethodProcessor#HttpEntityMethodProcessor

public HttpEntityMethodProcessor(List<HttpMessageConverter<?>> converters,
        @Nullable ContentNegotiationManager manager, List<Object> requestResponseBodyAdvice) {
    super(converters, manager, requestResponseBodyAdvice);
}

AbstractMessageConverterMethodProcessor#AbstractMessageConverterMethodProcessor

protected AbstractMessageConverterMethodProcessor(List&lt;HttpMessageConverter&lt;?&gt;&gt; converters,
        @Nullable ContentNegotiationManager manager, @Nullable List&lt;Object&gt; requestResponseBodyAdvice) {
    super(converters, requestResponseBodyAdvice);
    this.contentNegotiationManager = (manager != null ? manager : new ContentNegotiationManager());
    this.safeExtensions.addAll(this.contentNegotiationManager.getAllFileExtensions());
    this.safeExtensions.addAll(SAFE_EXTENSIONS);
}

AbstractMessageConverterMethodArgumentResolver#AbstractMessageConverterMethodArgumentResolver

public AbstractMessageConverterMethodArgumentResolver(List<HttpMessageConverter<?>> converters,
        @Nullable List<Object> requestResponseBodyAdvice) {
    Assert.notEmpty(converters, "'messageConverters' must not be empty");
    this.messageConverters = converters;
    this.allSupportedMediaTypes = getAllSupportedMediaTypes(converters);
    this.advice = new RequestResponseBodyAdviceChain(requestResponseBodyAdvice);
}

小节:RequestMappingHandlerAdapter会在其实现的afterPropertiesSet()方法中加载由@ControllerAdvice注解修饰的ResponseBodyAdvice接口,然后会创建并加载返回值处理器,在创建RequestResponseBodyMethodProcessorHttpEntityMethodProcessor这两个返回值处理器时会传入加载好的ResponseBodyAdvice,从而完成了ResponseBodyAdvice的加载。

总结

如果需要使用ResponseBodyAdvice来对handler方法的返回值做处理,则需要创建一个类并实现ResponseBodyAdvice接口,同时该类还需要被@ControllerAdvice注解修饰,这样的一个ResponseBodyAdvice接口会被RequestMappingHandlerAdapter加载,以及在初始化RequestResponseBodyMethodProcessor和HttpEntityMethodProcessor这两个返回值处理器时被加载。在通过这两个返回值处理器将返回值写入response前,加载好的ResponseBodyAdvice接口的beforeBodyWrite() 方法会被返回值处理器调用,完成对返回值的定制化处理。

以上就是ResponseBodyAdvice的使用原理源码解析的详细内容,更多关于ResponseBodyAdvice原理的资料请关注我们其它相关文章!

(0)

相关推荐

  • Spring中ResponseBodyAdvice的使用详解

    目录 1 ResponseBodyAdvice的简介 2 ResponseBodyAdvice的使用 1 准备一个SpringBoot项目环境 3 添加一个返回包装类 4 添加控制类 5 接口测试 ResponseBodyAdvice可以在注解@ResponseBody将返回值处理成相应格式之前操作返回值.实现这个接口即可完成相应操作.可用于对response 数据的一些统一封装或者加密等操作 1 ResponseBodyAdvice的简介 ResponseBodyAdvice接口和之前记录的R

  • Springboot FatJa原理机制源码解析

    目录 一.概述 二.标准的 jar 包结构 三.探索JarLauncher 3.1 只能拷贝出来一份儿 3.2 携带程序所依赖的jar而非仅class 四. 自定义类加载器的运行机制 4.1 指定资源 4.2 创建自定义 ClassLoader 4.3 设置线程上下文类加载器,调用程序中的 main class 一.概述 SpringBoot FatJar 的设计,打破了标准 jar 的结构,在 jar 包内携带了其所依赖的 jar 包,通过 jar 中的 main 方法创建自己的类加载器,来识

  • SpringBoot 自动配置原理及源码解析

    初始化一个Springboot项目,在主启动类会有这么一个注解:@SpringBootApplication,自动装配的秘密全在主启动类这个注解里面了 点进去一层会发现有三个子注解组成,分别是 @SpringBootConfiguration.@ComponentScan和@EnableAutoConfiguration 接下来分别解释这三个注解在整个自动装配过程中的作用 1.@SpringBootConfiguration 点进去发现它是@Configure,代表当前是一个配置类,意思就是当前

  • ResponseBodyAdvice踩坑及解决

    场景 通过ResponseBodyAdvice实现Rest接口的日志统一管理 正文 ResponseBodyAdvice原理自己百度,代码比较少但是我实践的时候发现有几个坑需要注意一下 @RestControllerAdvice(basePackages = "com.alan.api.controller") public class ApiResponseBodyAdvice implements ResponseBodyAdvice { static org.slf4j.Logg

  • Spring的Model 和 Map的原理源码解析

    Model 和 Map 为什么在Model和Map中放值传入后会出现在request的上面. 9.1.源码解析 准备测试代码 @GetMapping("/goto") public String go(HttpServletRequest request, Map<String,Object> map, Model model){ request.setAttribute("msg","传过来...."); map.put("

  • MyBatis框架底层的执行原理源码解析

    目录 1.前言 2.案例项目源码 3.MyBatis源码解析底层执行原理 3.1 读取mybatis配置文件创建出SqlSeesionFactory对象 3.2 通过SqlSeesionFactory对象进而创建出SqlSession对象 3.3 通过SqlSession的getMapper获取到接口代理对象 3.4 通过mapper接口的代理对象执行CRUD 1.前言 MyBatis框架大家肯定都用过的,废话我就不再多说了,这篇文章就给大家分享一下有关MyBatis框架底层的执行原理吧(Deb

  • React Hydrate原理源码解析

    目录 引言 Demo ReactDOM.render ReactDOM.hydrate hydrate 过程 事件绑定 hydrate 源码剖析 beginWork HostRoot Fiber HostComponent HostText Fiber tryToClaimNextHydratableInstance completeUnitOfWork popHydrationState prepareToHydrateHostInstance prepareToHydrateHostText

  • go slice 扩容实现原理源码解析

    目录 正文 扩容的示例 实际扩容倍数 growslice 实现 growslice 实现步骤 growslice 源码剖析 总结 正文 基于 Go 1.19. go 的切片我们都知道可以自动地进行扩容,具体来说就是在切片的容量容纳不下新的元素的时候, 底层会帮我们为切片的底层数组分配更大的内存空间,然后把旧的切片的底层数组指针指向新的内存中: 目前网上一些关于扩容倍数的文章都是基于相对旧版本的 Go 的,新版本中,现在切片扩容的时候并不是那种准确的小于多少容量的时候就 2 倍扩容, 大于多少容量

  • axios拦截器工作方式及原理源码解析

    目录 axios 拦截器的配置方式 use() 方法的定义 拦截器如何执行 拦截器回调方法的添加顺序 同步执行请求拦截器(顺序执行) 异步执行请求拦截器(同时执行) Q&A 拦截器是如何工作的 拦截器的执行顺序 同步&异步 axios 拦截器的配置方式 本文所用 axios 版本号为:1.3.2. axios 中有两种拦截器: axios.interceptors.request.use(onFulfilled, onRejected, options):配置请求拦截器. onFulfil

  • async-validator实现原理源码解析

    目录 async-validator 介绍 async-validator 基本使用 async-validator 源码分析 async-validator 源码-构造函数 async-validator 源码-validate方法 async-validator 源码-register方法 总结 最后 async-validator 介绍 async-validator是异步的验证数据是否合法有效的工具, 内置了不同数据类型的常见验证规则. 在需要对数据进行验证的场景中,都可以考虑使用asy

  • python装饰器原理源码示例分析

    目录 前言 一.什么是装饰器 二.为什么要用装饰器 三.简单的装饰器 四.装饰器的语法糖 五.装饰器传参 六.带参数的装饰器 七.类装饰器 八.带参数的类装饰器 九.装饰器的顺序 前言 最近有人问我装饰器是什么,我就跟他说,其实就是装饰器就是类似于女孩子的发卡.你喜欢的一个女孩子,她可以有很多个发卡,而当她戴上不同的发卡,她的头顶上就是装饰了不同的发卡.但是你喜欢的女孩子还是你喜欢的女孩子.如果还觉得不理解的话,装饰器就是咱们的手机壳,你尽管套上了手机壳,但并不影响你的手机功能,可你的手机还是该

  • spring-session简介及实现原理源码分析

    一:spring-session介绍 1.简介 session一直都是我们做集群时需要解决的一个难题,过去我们可以从serlvet容器上解决,比如开源servlet容器-tomcat提供的tomcat-redis-session-manager.memcached-session-manager. 或者通过nginx之类的负载均衡做ip_hash,路由到特定的服务器上.. 但是这两种办法都存在弊端. spring-session是spring旗下的一个项目,把servlet容器实现的httpSe

  • Vue解读之响应式原理源码剖析

    目录 初始化 initState() initProps() initData() observe() Observer defineReactive() 依赖收集 Dep Watcher 依赖收集过程 移除订阅 派发更新 notify() update() queueWatcher() flushSchedulerQueue() updated() defineProperty 缺陷及处理 Vue.set() 重写数组方法 总结 先看张图,了解一下大体流程和要做的事 初始化 在 new Vue

  • 关于feign接口动态代理源码解析

    目录 feign接口动态代理源码解析 @FeignClinet代理类注册 feign源码解析 Feign的作用 源码及流程介绍 feign接口动态代理源码解析 @FeignClinet 代理类注册 @FeignClinet 通过动态代理实现的底层http调用,既然是动态代理,必然存在创建代理类的过程.如Proxy.newProxyInstance或者 CGlib org.springframework.cloud.openfeign 的代理类注册实现如下. 首先,org.springframew

随机推荐