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

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
云解析 DNS,旗舰版 1个月
简介: 阿里面试:看过框架源码吗?举例说明一下

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

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

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

解答思路

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

  1. 可以回答比较常见的,你比较熟悉的源码,例如 Spring Boot 收到请求之后,执行流程的源码。
  2. 还可以回答 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 中,他里面包含的主要执行流程有以下这些:

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

    2.Spring Cloud 源码

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

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

  1. 轮询负载均衡策略
  2. 随机负载均衡策略

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

// ++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);

小结

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

本文已收录到我的面试小站 www.javacn.site,其中包含的内容有:Redis、JVM、并发、并发、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、设计模式、消息队列等模块。

相关文章
|
25天前
|
JavaScript 前端开发
【Vue面试题二十五】、你了解axios的原理吗?有看过它的源码吗?
这篇文章主要讨论了axios的使用、原理以及源码分析。 文章中首先回顾了axios的基本用法,包括发送请求、请求拦截器和响应拦截器的使用,以及如何取消请求。接着,作者实现了一个简易版的axios,包括构造函数、请求方法、拦截器的实现等。最后,文章对axios的源码进行了分析,包括目录结构、核心文件axios.js的内容,以及axios实例化过程中的配置合并、拦截器的使用等。
【Vue面试题二十五】、你了解axios的原理吗?有看过它的源码吗?
|
25天前
|
JavaScript 前端开发
【Vue面试题二十七】、你了解axios的原理吗?有看过它的源码吗?
文章讨论了Vue项目目录结构的设计原则和实践,强调了项目结构清晰的重要性,提出了包括语义一致性、单一入口/出口、就近原则、公共文件的绝对路径引用等原则,并展示了单页面和多页面Vue项目的目录结构示例。
|
1月前
|
Go API 数据库
[go 面试] 分布式事务框架选择与实践
[go 面试] 分布式事务框架选择与实践
面试官: 请你手写一份 Call()源码,看完此篇不用担心!
面试官: 请你手写一份 Call()源码,看完此篇不用担心!
|
1月前
|
存储 JavaScript 前端开发
JS浅拷贝及面试时手写源码
JS浅拷贝及面试时手写源码
|
1月前
|
JavaScript
给原始数据类型加属性和方法为什么不会报错?包装类——阿里面试题
给原始数据类型加属性和方法为什么不会报错?包装类——阿里面试题
|
2月前
|
设计模式 存储 安全
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
43 1
|
2月前
|
存储 安全 Java
Android面试题之ArrayList源码详解
ArrayList是Java中基于数组实现的列表,提供O(1)的索引访问,但插入和删除操作平均时间复杂度为O(n)。默认容量为10,当需要时会通过System.arraycopy扩容。允许存储null,非线程安全。面试常问:List是接口,ArrayList是其实现之一,推荐使用List接口编程以实现更好的灵活性。更多详情见[ArrayList源码](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/ArrayList.java#ArrayList.Node)。
23 2
|
2月前
|
SQL Java 数据库连接
Java面试题:简述ORM框架(如Hibernate、MyBatis)的工作原理及其优缺点。
Java面试题:简述ORM框架(如Hibernate、MyBatis)的工作原理及其优缺点。
43 0
|
2月前
|
存储 安全 Java
Java面试题:请解释Java中的泛型集合框架?以及泛型的经典应用案例
Java面试题:请解释Java中的泛型集合框架?以及泛型的经典应用案例
32 0