面试问烂的 Spring AOP 原理、SpringMVC 过程

   正文   

Spring AOP ,SpringMVC ,这两个应该是国内面试必问题,网上有很多答案,其实背背就可以。但今天笔者带大家一起深入浅出源码,看看他的原理。以期让印象更加深刻,面试的时候游刃有余。

Spring AOP 原理简单说说 AOP 的设计:

  1. 每个 Bean 都会被 JDK 或者 Cglib 代理。取决于是否有接口。
  2. 每个 Bean 会有多个“方法拦截器”。注意:拦截器分为两层,外层由 Spring 内核控制流程,内层拦截器是用户设置,也就是 AOP。
  3. 当代理方法被调用时,先经过外层拦截器,外层拦截器根据方法的各种信息判断该方法应该执行哪些“内层拦截器”。内层拦截器的设计就是职责连的设计。

是不是贼简单。事实上,楼主之前已经写过一个简单的例子,地址:http://thinkinjava.cn/2018/10/使用-Cglib-实现多重代理/

看完之后更简单。

可以将 AOP 分成 2 个部分来扯,哦,不,来分析。。。第一:代理的创建;第二:代理的调用。

注意:我们尽量少贴代码,尽量用文字叙述,因为面试的时候,也是文字叙述,不可能让你把代码翻出来的。。。所以,这里需要保持一定的简洁,想知道细节,看 interface 21 源码,想知道的更细,看 Spring Framework 最新的 master 分支代码。

代码位置:com.interface21.aop 包下。

开始分析(扯):

1、代理的创建(按步骤):
  1. 首先,需要创建代理工厂,代理工厂需要 3 个重要的信息:拦截器数组,目标对象接口数组,目标对象。
  2. 创建代理工厂时,默认会在拦截器数组尾部再增加一个默认拦截器 —— 用于最终的调用目标方法。
  3. 当调用 getProxy 方法的时候,会根据接口数量大余 0 条件返回一个代理对象(JDK or Cglib)。

注意:创建代理对象时,同时会创建一个外层拦截器,这个拦截器就是 Spring 内核的拦截器。用于控制整个 AOP 的流程。

2、代理的调用
  1. 当对代理对象进行调用时,就会触发外层拦截器。
  2. 外层拦截器根据代理配置信息,创建内层拦截器链。创建的过程中,会根据表达式判断当前拦截是否匹配这个拦截器。而这个拦截器链设计模式就是职责链模式。
  3. 当整个链条执行到最后时,就会触发创建代理时那个尾部的默认拦截器,从而调用目标方法。最后返回。

题外话:Spring 的事务也就是个拦截器。

来张不是很标准的 UML 图:

关于调用过程,来张流程图:

大概就是这样子,具体更多的细节,请看源码,如果还不是很明白的话,请咨询本人,本人不确定这个图是否画的很浅显易懂 —— 最起码萌新看得懂才能称之为浅显易懂。

Spring MVC 过程

先来张图:

代码位置:com.interface21.web.servlet.DispatcherServlet#doService

(没错,就是 Spring 1.0 的代码,大道至简,现在的 Spring 经过 15 年的发展,已经太过臃肿,从学习角度来说,interface 21 是最好的代码,不接受反驳)

代码如下:

1.设置属性

// 1. 设置属性
// Make web application context available
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());

// Make locale resolver available
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);

// Make theme resolver available
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);

2.根据 Request 请求的 URL 得到对应的 handler 执行链,其实就是拦截器和 Controller 代理对象。

// 2. 找 handler 返回执行链
HandlerExecutionChain mappedHandler = getHandler(request);

3.得到 handler 的适配器

// This will throw an exception if no adapter is found
// 3. 返回 handler 的适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

关于这个适配器,作用到底是啥呢?

HandlerAdapter 注释写到:This interface is not intended for application developers. It is available to handlers who want to develop their own web workflow. 译:此接口不适用于应用程序开发人员。它适用于想要开发自己的Web工作流程的处理程序。

也就说说,如果你想要在处理 handler 之前做一些操作的话,可能需要这个,即适配一下这个 handler。例如 Spring 的测试程序做的那样:

public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object delegate)
            throws IOException, ServletException {
                      // 你可能需要 doSomething.......
            ((MyHandler) delegate).doSomething(request);
            return null;
        }

4.循环执行 handler 的 pre 拦截器

// 4. 循环执行 handler 的 pre 拦截器
for (int i = 0; i < mappedHandler.getInterceptors().length; i++) {
    HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
    // pre 拦截器
    if (!interceptor.preHandle(request, response, mappedHandler.getHandler())) {
        return;
    }
}

这个没什么好讲的吧?

5.执行真正的 handler,并返回 ModelAndView(Handler 是个代理对象,可能会执行 AOP )

// 5. 执行真正的 handler,并返回  ModelAndView(Handler 是个代理对象,可能会执行 AOP )
ModelAndView mv = ha.handle(request, response, mappedHandler.getHandler());

6.循环执行 handler 的 post 拦截器

// 6. 循环执行 handler 的 post 拦截器
for (int i = mappedHandler.getInterceptors().length - 1; i >=0 ; i--) {
    HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
    // post 拦截器
    interceptor.postHandle(request, response, mappedHandler.getHandler());
}

7.根据 ModelAndView 信息得到 View 实例

View view = null;
if (mv.isReference()) {
    // We need to resolve this view name
    // 7. 根据 ModelAndView 信息得到 View 实例
    view = this.viewResolver.resolveViewName(mv.getViewName(), locale);
}

8.渲染 View 返回

// 8. 渲染 View 返回
view.render(mv.getModel(), request, response);
Image placeholder
sumernina
未设置
  95人点赞

没有讨论,发表一下自己的看法吧

推荐文章
搭建 Spring+SpringMVC+MyBatis 框架

SSM框架整合 pom中添加依赖 添加编辑Spring配置文件 添加编辑SpringMVC配置文件 添加编辑Mybatis配置文件 配置web.xml 1、pom中添加依赖 junit ju

0103-springmvc的基本流程

背景现在的it研发,已经从管理系统时代迈入了互联网系统时代。页面开发已经从基于JSP+struts转变为为前后端分离的方式(springMVC+JS);思想MVCmvc框架不仅适用于java的开发,也

SpringMVC加载流程

 这节介绍SpringMVC,SpringMVC是一种基于Java的实现MVC设计模式的请求驱动类型的轻量级Web框架。本章会介绍相关概念,流程,再从源码进行讲解。1.MVC MVC(ModelVie

Spring-SpringAOP原理,手写Spring事务框架

一、Spring核心知识Spring是一个开源框架,Spring是于2003年兴起的一个轻量级的Java开发框架,由RodJohnson在其著作ExpertOne-On-OneJ2EEDevelopm

MVVM原理(Object.defineProperty和订阅者模式)

想着去了解vue的mvvm数据驱动是怎么实现的,百度中看了这篇文章,demo很好。其他文章只是讲到defineProperty的set,get。彻底理解Vue中的Watcher、Observer、De

万字详解Oracle架构、原理、进程,学会世间再无复杂架构

学习是一个循序渐进的过程,从面到点、从宏观到微观,逐步渗透,各个击破,对于Oracle, 怎么样从宏观上来理解呢?先来看一个图,这个图取自于教材,这个图对于从整体上理解ORACLE 的体系结构组件,非

阿里提出针对多目标优化的全新算法框架,同时提升电商推荐场景 GMV 和 CTR

在推荐系统中,多目标优化一直是热门话题,阿里巴巴的XiaoLin、HongjieChen等人针对推荐中的多目标优化问题提出了一种基于帕累托效率的优化算法框架,并应用在电商推荐场景中,对GMV和CTR

使用 Vue.js 和 Iris 共建一个简单的 Todo MVC 应用

本文用Golang的Iris框架作为后端服务,vuejs渲染前端UI,用websocket通信。基于监听hash变化director.js库实现简单路由,axios库与后方沟通,netoffos.j

使用 Vue.js 和 Iris 共建一个简单的 Todo MVC 应用

数据服务 packagetodo import"sync" //Item条目数据 typeItemstruct{ SessionIDstring`json:"-"` IDint64`json:"i

我的天!这是史上最烂的项目:苦撑12年,600多万行代码…

编译:欧剃来源:projectfailures.wordpress.com转载自:Java技术栈你见过最烂的项目,撑了多长时间才完蛋?六个月?一年?今天介绍的这个奇葩项目,不但一开始就烂得透透的,还硬

你真的了解 React 吗?这里有 50 个 React 面试问题

如果你是一位有理想的前端开发人员,并且正在准备面试,那么这篇文章就是为你准备的。本文收集了React面试中最常见的50大问题,这是一份理想的指南,让你为React相关的面试做好充分的准备工作。首先我

2020年前必须掌握的数据库面试问题~

一、为什么用自增列作为主键1、如果我们定义了主键(PRIMARYKEY),那么InnoDB会选择主键作为聚集索引。如果没有显式定义主键,则InnoDB会选择第一个不包含有NULL值的唯一索引作为主键索

面试问题总结

作为一个一年半Java开发的技术小白面试的心得。在原先公司工作接触nginx、redis等工具。由于工作上对这些工具只做到会用的程度没了解深入。在面试中出现很多笑话(一问三不知的情况都出来)。今天做个

【搞定 Java 并发面试】面试最常问的 Java 并发基础常见面试题总结!

Java并发基础常见面试题总结 1.什么是线程和进程? 1.1.何为进程? 进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。

HTML与CSS的dic、span和格式化标签

课程推荐:前端开发工程师--学习猿地精品课程 div和spandiv? 是一个块级元素,通常与css配合使用,用于布局。 ?标签可以把文档分割为独立的、不同的部分。它可以用作严格的组织工具,并且不使

gMIS吉密斯十年执念:Lower Costs较低成本Better Productivity较高效率

Hello2020!元旦快乐!今起揭开21世纪20年代的篇章.1.gMIS吉密斯十周年2010-2020,十年转眼已成历史,gMIS吉密斯——通用管理信息系统(generalManagementInf

Spring WebFlux 的设计及工作原理剖析

前言 Spring5发布有两年了,随Spring5一起发布了一个和SpringWebMvc同级的SpringWebFlux。这是一个支持反应式编程模型的新框架体系。反应式模型区别于传统的MVC最大的不

理解 cookie、session、token、jwt

发展史 1、很久以前,Web基本上就是文档的浏览而已,既然是浏览,作为服务器,不需要记录谁在某一段时间里都浏览了什么文档,每次请求都是一个新的HTTP协议,就是请求加响应,尤其是我不用记住是谁刚刚发了

List、Tuple、Set、Dict数据类型

List声明:[]访问列表数据:变量[n]n表示列表中数据的位置编号。修改列表数据:变量[n]=数据则可以替换原本位于n-1位置的数据。 Tuple声明:()元组与列表不同之处在于其中数据不可修改。访

Spring Boot 面试,一个问题就干趴下了!

随着SpringBoot使用越来越广泛,SpringBoot已经成为Java程序员面试的知识点,很多同学对SpringBoot理解不是那么深刻,经常就会被几个连环跑给干趴下了!比如下面这一段的Spri

gMIS吉密斯i18n多语言国际化更新

gMIS吉密斯部署和使用范围日益扩大,跨国多语言版本成为迫切需要的功能,早在2018年年中就考虑要增加这一功能——在gMIS吉密斯中实现多语言版本的支持。以期实现gMIS吉密斯的跨行业、跨地区和国际化

SpringBoot个人应用开发框架(SpringBoot版本2.1)+IDEA

前言: 此笔记为本人首个SpringBoot项目框架学习实践记录,期间参考了许多大神的笔记和心得。 参考文档如下: 项目git地址: 一、创建SpringBoot工程 1.1创建父POM工程结

再见 Spring Boot 1.X ,Spring Boot 2.X 走向舞台中心

2019年8月6日,Spring官方在其博客宣布,SpringBoot1.x停止维护,SpringBoot1.x生命周期正式结束。其实早在2018年7月30号,Spring官方就已经在博客进行过预告,

BAT大牛推荐开发人员必备Spring源码剖析文档,深度剖析Spring

为什么学习读源码我们每天都和代码打交道。经过数年的基础教育和职业培训,大部分程序员都会「写」代码,或者至少会抄代码和改代码。但是,会读代码的并不在多数,会读代码又真正读懂一些大项目的源码的,少之又少。

笨办法学 Linux 学习处理文件,`pwd`,`ls`,`cp`,`mv`,`rm`,`touch`

Bash:处理文件,pwd,ls,cp,mv,rm,touch 在Linux中,一切都是文件。但是什么是文件?现在完全可以说,它是一个包含一些信息的对象。它通常定义如下: 计算机文件是用于存储信息的