阿里面试:看过框架源码吗?举例说明一下

2023年 11月 10日 133.6k 0

前两天有朋友面试“淘汰集团”,也就是“淘宝”+“天猫”的组合,最后被面试官问到了这道题:“你看过哪些开源框架的源码?举例说明一下”。

诚然,这是一道比较考验应聘者基本功的问题,也是很好区分“好学生”和“普通学生”的一道经典的开放性问题。

那这个问题应该怎么回答呢?

解答思路

我这给大家提供两个思路吧:

  • 可以回答比较常见的,你比较熟悉的源码,例如 Spring Boot 收到请求之后,执行流程的源码。
  • 还可以回答 Spring Cloud 微服务中,某个组件执行的流程源码,这样能很好的体现你对微服务比较熟悉,因为微服务在公司中应用比较广泛,所以回答的好,是一个极大的加分项。
  • 1.Spring Boot 源码分析

    Spring Boot 在收到请求之后,会先执行前端控制器 DispatcherServlet,并调用其父类 FrameworkServlet 中的 service 方法,其核心源码如下:

    /**
     * Override the parent class implementation in order to intercept PATCH requests.
     */
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
        HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
        if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
            processRequest(request, response);
        } else {
            super.service(request, response);
        }
    }

    继续往下看,processRequest 实现源码如下:

    protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     // 省略一堆初始化配置
       
       try {
           // 真正执行逻辑的方法
           doService(request, response);
       }
       catch (ServletException | IOException ex) {
           ...
       }
    }

    doService 实现源码如下:

    protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws Exception;

    doService 是抽象方法,由其之类 DispatcherServlet 来重写实现,其核心源码如下:

    @Override
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 省略初始化过程...
        try {
            doDispatch(request, response);
        }
        finally {
      // 省略其他...
        }
    }

    此时就进入到了 DispatcherServlet 中的 doDispatch 方法了:

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 获取原生请求
        HttpServletRequest processedRequest = request;
        // 获取Handler执行链
        HandlerExecutionChain mappedHandler = null;
        // 是否为文件上传请求, 默认为false
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        try {
            ModelAndView mv = null;
            Exception dispatchException = null;
            try {
                // 检查是否为文件上传请求
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);
                // Determine handler for the current request.
                // 获取能处理此请求的Handler
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }
                // Determine handler adapter for the current request.
                // 获取适配器
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
                // Process last-modified header, if supported by the handler.
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }
                // 执行拦截器(链)的前置处理
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }
                // 真正的执行对应方法
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
                applyDefaultViewName(processedRequest, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            // 忽略其他...
    }

    通过上述的源码我们可以看到,请求的核心代码都在 doDispatch 中,他里面包含的主要执行流程有以下这些:

  • 调用 HandlerExecutionChain 获取处理器:DispatcherServlet 首先调用 getHandler 方法,通过 HandlerMapping 获取请求对应的 HandlerExecutionChain 对象,包含了处理器方法和拦截器列表。
  • 调用 HandlerAdapter 执行处理器方法:DispatcherServlet 使用 HandlerAdapter 来执行处理器方法。根据 HandlerExecutionChain 中的处理器方法类型不同,选择对应的 HandlerAdapter 进行处理。常用的适配器有 RequestMappingHandlerAdapter 和 HttpRequestHandlerAdapter。
  • 解析请求参数:DispatcherServlet 调用 HandlerAdapter 的 handle 方法,解析请求参数,并将解析后的参数传递给处理器方法执行。
  • 调用处理器方法:DispatcherServlet 通过反射机制调用处理器方法,执行业务逻辑。
  • 处理拦截器:在调用处理器方法前后,DispatcherServlet 会调用拦截器的 preHandle 和 postHandle方法进行相应的处理。
  • 渲染视图:处理器方法执行完成后,DispatcherServlet 会通过 ViewResolver 解析视图名称,找到对应的 View 对象,并将模型数据传递给 View 进行渲染。
  • 生成响应:View 会将渲染后的视图内容生成响应数据。
  • 2.Spring Cloud 源码

    Spring Cloud 组件有很多,你可以挑一个源码实现比较简单的组件来讲,这里推荐 Spring Cloud LoadBalancer,因为其核心源码的实现比较简单。

    Spring Cloud LoadBalancer 中内置了两种负载均衡策略:

  • 轮询负载均衡策略
  • 随机负载均衡策略
  • 轮询负载均衡策略的核心实现源码如下:

    // ++i 去负数,得到一个正数值
    int pos = this.position.incrementAndGet() & Integer.MAX_VALUE;
    // 正数值和服务实例个数取余 -> 实现轮询
    ServiceInstance instance = (ServiceInstance)instances.get(pos % instances.size());
    // 将实例返回给调用者
    return new DefaultResponse(instance);

    随机负载均衡策略的核心实现源码如下:

    // 通过 ThreadLocalRandom 获取一个随机数,最大值为服务实例的个数
    int index = ThreadLocalRandom.current().nextInt(instances.size());
    // 得到实例
    ServiceInstance instance = (ServiceInstance)instances.get(index);
    // 返回
    return new DefaultResponse(instance);

    小结

    开源框架的源码在面试中经常会被问到,但只因如此,就去完整的看某个框架的源码,其实还是挺难的。第一,框架中的源码很多,很难一次性看懂。第二,即使能看懂,看完之后也会很快忘记(因为内容太多了)。此时,不如挑一些框架中的经典实现源码来看,其性价比更高,既能学到框架中的精髓,又能搞定面试,是一个不错的选择。

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论