Spring总结

作者: 爱看书的独角兽 | 来源:发表于2022-06-22 11:42 被阅读0次
    Spring知识框架.png

    1.Spring核心

    今天给大家讲讲Spring吧,谈到Spring,学过的人估计第一反应就是IOC和AOP,的确,这两个就是Spring的核心知识,也可以说是一种思想理念。下面我们就来具体讲讲这两个东西到底是什么吧~
    1.IOC(控制反转):顾名思义,意思就是将我们控制Bean对象的权利反转交给Spring来控制和管理。那么Spring是怎么控制Bean的呢?众所周知,通常我们创建一个对象一般都是要用的时候new一个出来,在还没有出现IOC之前,这种方式习以为常,但是当IOC出现之后,再去使用new这种方式写代码,大家肯定有一种非常难受的感觉。Spring用一个注解能解决的事情使得大家就不用再去大费周章new一个对象了。
    如何使用IOC?
    这个问题其实就是再问我们如何去注册那些我们所需要的Bean对象,Spring 提供了四种方式供大家使用,分别是XML/注解/Java Config/Groovy DSL;前三种是我们经常使用的方法,我就不过多解释了。后面那种是基于DSL语言的,可以把它通俗理解为一个伪class文件,如果有接触过protocol buff格式数据或者是Grpc框架的同学应该知道,我们在进行java与python系统交互的时候,会将java对象转换生成一个proto文件,Groovy DSL方式大概就是这个意思了——自定义一个对象文件。
    优势:
    1.提供了一套管理bean的生命周期模式
    2.单例模式
    3.方便测试
    4.屏蔽对象创建的细节
    2.AOP(面向切口编程):在Spring中,我们还是会经常看到AOP的身影的,比如在我们实现一些非业务代码时——日志记录、接口记录、接口使用情况等。它的底层原理是动态代理,在spring中依赖的是beanPostProcessor。一般都是配合注解使用。在我们的项目中,为了响应网监的监管,我就是使用AOP+自定义注解的方式实现了一套用户ip地址记录和详细用户数据日志记录。

    2.Bean

    下面就来详细介绍一下在Spring中Bean对象创建到消亡的整个过程,话不多少,直接上图!

    bean生命周期.png
    具体流程:spring在启动的时候,首先加载配置文件、注解、config中需要创建的bean,把这些元数据和相关信息封装成DefineBean对象,然后放入一个DefineBeanMap中,紧接着遍历这个DefineBeanMap,根据工厂模式思想,执行BeanFactoryPostProcesor前置处理器的方法,创建相关bean对象的工厂类,这里可以对对象的信息做一些修改操作。然后工厂类开始创建bean对象,下一步属性注入,当bean对象创建完成之后,接下来就要考虑实例化了;这时,首先检查该对象是否实现了Aware相关的接口,如果实现了,那么就要去填充相关资源。其次,再考虑执行BeanPostProcessor后置处理器里面的扩展方法,这里我们可以实现自己的方法,具体看业务逻辑是否需要!最后就是执行初始化相关的方法了,init相关方法——如PostConstrut、实现了 InitializingBean接口的类、init-method方法等。创建完成!(顺便提一下资源的销毁,实现disposable的desory()方法,或者自定义销毁方法)

    这里需要注意一点,在做依赖注入时,可能会遇上循环依赖的问题,Spring又是如何解决循环依赖问题的呢?(补充:循环依赖可以理解为创建对象A需要依赖对象B,而对象B又需要依赖对象A)
    答案是三级缓存!看图

    三级缓存.png
    从上面的流程可以知道,Spring在创建对象的时候,是和属性注入分开的,而且在创建之前是先创建该对象对应的工厂类,所以,我们从这里就可以将bean创建分为三个缓存阶段,也就是三个Map,在对象A创建的时候,首先在三级缓存中生成一个对象map,key为对象名,值为对象。发现需要依赖对象B,那么这时会先去创建对象B,在对象B创建的时候,会去get对象A,A已经存在,这时我们可以把对象B从三级缓存中移除,放入二级缓存,而对象A自然而然就可以生成了,再去做属性注入,放入一级缓存。

    提一下,这里为什么要考虑三级缓存,而不是二级缓存!
    从三级缓存中,我们能够拿到的代理对象,这样方便管理对象
    从二级缓存中,我们不用每次获取对象都去工厂类中生成,提高性能

    3.Spring MVC

    如果说spring是让我们和系统对象打交道,那么MVC就是在和用户打交道!我们可以理解为一个用户发起一个请求,MVC封装了中间所有的通讯细节,找到这个请求想要的数据,并封装成一个ModelAndView对象返回给用户。
    1.交互流程

    MVC运行流程.png

    总结一下:
    ①用户发起url请求
    ②DispatcherServlet处理并分发请求到handleMapping
    ②handleMapping根据url找到相应的handle
    ③handle查找适配器
    ④适配器调用对应的handle方法处理请求
    ⑤将响应结果封装成ModleAndViewdu对象返回
    ⑥通过dispatcherservlet将处理好的视图对象返回给用户

    2.核心源码解析
    根据上面讲解的运行流程,大家带着这个思路逻辑看一下核心代码

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
            // 定义一个已处理请求,指向参数的request
            HttpServletRequest processedRequest = request;
            // 定义处理器执行连,内部封装拦截器列表和处理器
            HandlerExecutionChain mappedHandler = null;
            // 是否有文件上传的请求标志
            boolean multipartRequestParsed = false;
    
            // 获取异步管理器,执行异步操作
            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    
            try {
                // 保存处理器执行的返回结果
                ModelAndView mv = null;
                // 保存处理过程中的异常
                Exception dispatchException = null;
    
                try {
                    // 判断当前请求是否有上传需求,并返回保存到processedRequest中
                    processedRequest = checkMultipart(request);
                    // 判断当前请求是否是文件上传的请求,如果是则说明是上传请求已经处理
                    multipartRequestParsed = (processedRequest != request);
    
                    // Determine handler for the current request.
                    // 获取可处理当前请求的请求处理器,通过HandlerMapping进行查找
                    mappedHandler = getHandler(processedRequest);
                    // 如果没有,就执行没有处理器的逻辑
                    if (mappedHandler == null) {
                        // 在内部处理中抛出异常或者返回404
                        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.
                    // 处理last-modified请求头,用于判断请求内容是否发生修改
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    // 只有get请求和head请求执行此判断
                    if (isGet || "HEAD".equals(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }
    
                    // 通过mappedHandler这个HandlerExecutionChain执行链的封装,链式执行所有连接器的前置拦截方法
                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        // 任意一个拦截器的前置拦截方法返回false,提前结束请求的处理
                        return;
                    }
    
                    // Actually invoke the handler.
                    // 执行处理适配器的处理方法,传入请求,对请求进行处理,此方法的返回值是ModelAndView对象,封装了模型和视图
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    // 如果是异步处理,则直接返回,后续处理通过异步执行
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }
                    // 返回的mv对象中如果没有视图名称,则根据请求设置默认视图名
                    applyDefaultViewName(processedRequest, mv);
                    // 请求处理正常完成,链式执行所有拦截器的后置方法
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                }
                catch (Exception ex) {
                    // 保存异常信息
                    dispatchException = ex;
                }
                catch (Throwable err) {
                    // As of 4.3, we're processing Errors thrown from handler methods as well,
                    // making them available for @ExceptionHandler methods and other scenarios.
                    // 4.3版本之后提供了error类型异常的处理
                    dispatchException = new NestedServletException("Handler dispatch failed", err);
                }
                // 对下执行结果进行处理,包括视图的处理和异常的处理
                processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
            }
            catch (Exception ex) {
                // 链式执行拦截器链的afterCompletion方法
                triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
            }
            catch (Throwable err) {
                // 拦截error类型异常,拦截后链式执行拦截器链的afterCompletion方法
                triggerAfterCompletion(processedRequest, response, mappedHandler,
                        new NestedServletException("Handler processing failed", err));
            }
            // 做资源清理
            finally {
                if (asyncManager.isConcurrentHandlingStarted()) {
                    // Instead of postHandle and afterCompletion
                    if (mappedHandler != null) {
                        mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                    }
                }
                else {
                    // Clean up any resources used by a multipart request.
                    if (multipartRequestParsed) {
                        cleanupMultipart(processedRequest);
                    }
                }
            }
        }
    

    看完之后,是不是或多或少都有一些想法了呢?不着急,我们先把一些前置知识给大家梳理一下。
    在Spring运行开始,首先会初始化一些handleMapping,HandlerAdapter等核心组件,这些组件的作用,说白了就是一点——将用户发起的URL请求映射到我们写好的controller方法,执行业务逻辑;mvc将其封装好供我们直接使用;

    总结:通过handleMapping,我们可以顺利找到相关的适配器,再通过适配器调用handle()方法(我们的业务逻辑),得到result之后封装成ModelAndView对象返回,从代码中我们可以看到,getHandler()方法会返回一个HandlerExecutionChain对象,(这个执行链对象包含两个拦截器和一个处理器,拦截器分别做前置和后置处理)所以在代码中有mappedHandler.getHandler()方法先拿到这个处理器,然后再通过getHandlerAdapter()获得对应的适配器紧接着执行handle()方法处理业务逻辑,最后封装ModelAndView对象返回。

    大致步骤是说完了,其中还是有一些细节我们没有说的到
    比如说初始化时是如何加载这些核心组件的,(可以思考一下url与处理逻辑如何映射?使用什么数据结构?)
    或者是为什么采用执行链和适配器这种模式?
    再或者说ModelAndView对象是如何封装的,以及mvc是如何处理异常的?
    这些问题就留给读者在源码中寻找答案了!此时只提供一个总体的思路框架......

    面试总结系列第三面——欢迎留言讨论,共同进步!

    相关文章

      网友评论

        本文标题:Spring总结

        本文链接:https://www.haomeiwen.com/subject/aqkrvrtx.html