弯月刀 發表於 2025-10-10 09:00:00

Spring AOP、MVC高频面试题解析

<h2 id="什么是aop">什么是AOP?</h2>
<p><strong>面向切面</strong>编程,作为面向对象的一种补充,将公共逻辑(事务管理、日志、缓存等)封装成切面,跟业务代码进行分离,可以减少系统的重复代码和降低模块之间的耦合度。切面就是那些与业务无关,但所有业务模块都会调用的公共逻辑。</p>
<h2 id="面向切面编程和面向对象编程的区别两者有冲突吗">面向切面编程和面向对象编程的区别,两者有冲突吗?</h2>
<p>面向切面编程(AOP)和面向对象编程(OOP)是两种不同的编程范式,它们各自解决不同类型的问题,并且通常可以互补使用而不是冲突。</p>
<p>OOP 关注的是对象和它们的交互,强调的是数据和行为的封装。<br>
AOP关注的是横切关注点,强调的是将与业务无关的代码(如日志、事务)从业务逻辑中分离出来。</p>
<p>AOP 和 OOP 是互补的。OOP 关注的是业务逻辑的实现和数据的封装,而 AOP 关注的是横切关注点的管理。你可以在 OOP 代码中使用 AOP 来简化和管理横切关注点。<br>
并且AOP 通常在 OOP 代码的基础上实现。例如 AOP 框架(如 AspectJ、Spring AOP)都是基于 OOP 语言(如Java)实现的。</p>
<h2 id="aop有哪些实现方式">AOP有哪些实现方式?</h2>
<p>AOP有两种实现方式:静态代理和动态代理。</p>
<ul>
<li><strong>静态代理</strong></li>
</ul>
<p>静态代理:代理类在编译阶段生成,在编译阶段将通知织入Java字节码中,也称编译时增强。AspectJ使用的是静态代理。</p>
<p>缺点:代理对象需要与目标对象实现一样的接口,并且实现接口的方法,会有冗余代码。同时,一旦接口增加方法,目标对象与代理对象都要维护。</p>
<ul>
<li><strong>动态代理</strong></li>
</ul>
<p>动态代理:代理类在程序运行时创建,AOP框架不会去修改字节码,而是在内存中临时生成一个代理对象,在运行期间对业务方法进行增强,不会生成新类。</p>
<h2 id="spring-aop的实现原理">Spring AOP的实现原理</h2>
<p><code>Spring</code>的<code>AOP</code>实现原理其实很简单,就是通过<strong>动态代理</strong>实现的。如果我们为<code>Spring</code>的某个<code>bean</code>配置了切面,那么<code>Spring</code>在创建这个<code>bean</code>的时候,实际上创建的是这个<code>bean</code>的一个代理对象,我们后续对<code>bean</code>中方法的调用,实际上调用的是代理类重写的代理方法。而<code>Spring</code>的<code>AOP</code>使用了两种动态代理,分别是<strong>JDK的动态代理</strong>,以及<strong>CGLib的动态代理</strong>。</p>
<p>底层实现主要分两部分:创建AOP动态代理和调用代理</p>
<ol>
<li>在启动Spring会创建AOP动态代理:
<ol>
<li>首先通过AspectJ解析切点表达式:在创建代理对象时,Spring AOP使用AspectJ来解析切点表达式。它会根据定义的条件匹配目标Bean的方法。如果Bean不符合切点的条件,将跳过,否则将会通动态代理包装Bean对象:具体会根据目标对象是否实现接口来选择使用JDK动态代理或CGLIB代理。这使得AOP可以适用于各种类型的目标对象。</li>
</ol>
</li>
<li>在调用阶段:
<ol>
<li>Spring AOP使用责任链模式来管理通知的执行顺序。通知拦截链包括前置通知、后置通知、异常通知、最终通知和环绕通知,它们按照配置的顺序形成链式结构。</li>
<li>通知的有序执行: 责任链确保通知按照预期顺序执行。前置通知在目标方法执行前执行,后置通知在目标方法成功执行后执行,异常通知在方法抛出异常时执行,最终通知无论如何都会执行,而环绕通知包裹目标方法,允许在方法执行前后添加额外的行为。<br>
综上所述,Spring AOP在创建启动阶段使用AspectJ解析切点表达式如果匹配使用动态代理,而在调用阶段使用责任链模式确保通知的有序执行。这些机制共同构成了Spring AOP的底层实现。</li>
</ol>
</li>
</ol>
<h2 id="spring-aop-和-aspectj-aop-有什么区别">Spring AOP 和 AspectJ AOP 有什么区别?</h2>
<p><strong>Spring AOP 属于运行时增强,而 AspectJ 是编译时增强。</strong> Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation)。</p>
<p>Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。AspectJ 相比于 Spring AOP 功能更加强大,但是 Spring AOP 相对来说更简单,</p>
<p>如果我们的切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择 AspectJ ,它比 Spring AOP 快很多。</p>
<p>Spring AOP:是 Spring 框架提供的一种 AOP 实现,主要用于运行时的代理机制。</p>
<ul>
<li>特点:Spring AOP 是基于动态代理实现的,适用于Spring 容器管理的 Bean,较轻量级,使用方便。</li>
<li>使用场景:适合大部分业务场景,尤其是需要简单 AOP 功能的 Spring 应用。</li>
</ul>
<p>AspectJ:AspectJ 是功能更强大的 AOP 框架,支持编译时、类加载时和运行时的 AOP 功能。</p>
<ul>
<li>特点:AspectJ 支持更加灵活的切点和增强操作,提供编译期和加载期的织入方式,性能较高。</li>
<li>使用场景:适合对性能要求较高或需要复杂切点匹配的场景,如日志、监控等</li>
</ul>
<h2 id="jdk动态代理和cglib动态代理的区别">JDK动态代理和CGLIB动态代理的区别?</h2>
<p>Spring AOP中的动态代理主要有两种方式:JDK动态代理和CGLIB动态代理。SpringFramework 默认使用的动态代理是JDK动态代理 (由于后续版本已经整会了CGLB,所以如果这个代理类没有实现接口,会用 CGLIB),SpringBoot 2.x版本的默认动态代理是 CGLIB</p>
<ul>
<li><strong>JDK动态代理</strong></li>
</ul>
<p>如果目标类实现了接口,Spring AOP会选择使用JDK动态代理目标类。代理类根据目标类实现的接口动态生成,不需要自己编写,生成的动态代理类和目标类都实现相同的接口。JDK动态代理的核心是<code>InvocationHandler</code>接口和<code>Proxy</code>类。</p>
<p>缺点:<strong>目标类必须有实现的接口</strong>。如果某个类没有实现接口,那么这个类就不能用JDK动态代理。</p>
<ul>
<li><strong>CGLIB动态代理</strong></li>
</ul>
<p><strong>通过继承实现</strong>。如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library)可以在运行时动态生成类的字节码,动态创建目标类的子类对象,在子类对象中增强目标类。</p>
<p>CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为<code>final</code>,那么它是无法使用CGLIB做动态代理的。</p>
<p>优点:目标类不需要实现特定的接口,更加灵活。</p>
<p>什么时候采用哪种动态代理?</p>
<ol>
<li>如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP</li>
<li>如果目标对象实现了接口,可以强制使用CGLIB实现AOP</li>
<li>如果目标对象没有实现了接口,必须采用CGLIB库</li>
</ol>
<ul>
<li><strong>两者的区别</strong>:</li>
</ul>
<ol>
<li>jdk动态代理使用jdk中的类Proxy来创建代理对象,它使用反射技术来实现,不需要导入其他依赖。cglib需要引入相关依赖:<code>asm.jar</code>,它使用字节码增强技术来实现。</li>
<li>当目标类实现了接口的时候Spring Aop默认使用jdk动态代理方式来增强方法,没有实现接口的时候使用cglib动态代理方式增强方法。</li>
</ol>
<h2 id="aspectj-定义的通知类型有哪些">AspectJ 定义的通知类型有哪些?</h2>
<ul>
<li><strong>Before</strong>(前置通知):目标对象的方法调用之前触发</li>
<li><strong>After</strong> (后置通知):目标对象的方法调用之后触发</li>
<li><strong>AfterReturning</strong>(返回通知):目标对象的方法调用完成,在返回结果值之后触发</li>
<li><strong>AfterThrowing</strong>(异常通知):目标对象的方法运行中抛出 / 触发异常后触发。AfterReturning 和 AfterThrowing 两者互斥。如果方法调用成功无异常,则会有返回值;如果方法抛出了异常,则不会有返回值。</li>
<li><strong>Around</strong> (环绕通知):编程式控制目标对象的方法调用。环绕通知是所有通知类型中可操作范围最大的一种,因为它可以直接拿到目标对象,以及要执行的方法,所以环绕通知可以任意的在目标对象的方法调用前后搞事,甚至不调用目标对象的方法</li>
</ul>
<h2 id="spring-aop相关术语">Spring AOP相关术语</h2>
<ul>
<li><strong>切面</strong>(Aspect):切面是通知和切点的结合。通知和切点共同定义了切面的全部内容。</li>
<li><strong>连接点</strong>(Join point):指方法,在Spring AOP中,一个连接点总是代表一个方法的执行。连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。</li>
<li><strong>通知</strong>(Advice):在AOP术语中,切面的工作被称为通知。</li>
<li><strong>切入点</strong>(Pointcut):切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。</li>
<li><strong>引入</strong>(Introduction):引入允许我们向现有类添加新方法或属性。</li>
<li><strong>目标对象</strong>(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。它通常是一个代理对象。</li>
<li><strong>织入</strong>(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。在目标对象的生命周期里有以下时间点可以进行织入:</li>
</ul>
<h2 id="spring通知有哪些类型">Spring通知有哪些类型?</h2>
<p>在AOP术语中,切面的工作被称为通知。通知实际上是程序运行时要通过Spring AOP框架来触发的代码段。</p>
<p>Spring切面可以应用5种类型的通知:</p>
<ol>
<li><strong>前置通知</strong>(Before):在目标方法被调用之前调用通知功能;</li>
<li><strong>后置通知</strong>(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;</li>
<li><strong>返回通知</strong>(After-returning ):在目标方法成功执行之后调用通知;</li>
<li><strong>异常通知</strong>(After-throwing):在目标方法抛出异常后调用通知;</li>
<li><strong>环绕通知</strong>(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的逻辑。</li>
</ol>
<h2 id="多个切面的执行顺序如何控制">多个切面的执行顺序如何控制?</h2>
<p>5种类型的通知执行顺序:</p>
<ol>
<li>正常执行:前置---&gt;方法----&gt;返回---&gt;后置</li>
<li>异常执行:前置---&gt;方法----&gt;异常---&gt;后置</li>
</ol>
<p>同类型切面执行顺序:</p>
<ol>
<li>通常使用<code>@Order</code> 注解直接定义切面顺序</li>
<li>. 实现<code>Ordered</code> 接口重写 <code>getOrder</code> 方法。</li>
</ol>
<h2 id="什么情况下aop会失效怎么解决">什么情况下AOP会失效,怎么解决?</h2>
<p>大部分失效是由于:</p>
<ul>
<li>内部方法调用: 如果在同一个类中的一个方法调用另一个方法,AOP通知可能不会触发,因为AOP通常是通过代理对象拦截外部方法调用的。<br>
解决方式是注入本类对象进行调用, 或者设置暴露当前代理对象到本地线程, 可以通过AopContext.currentProxy() 拿到当前正在调用的动态代理对象。</li>
<li>静态方法: AOP通常无法拦截静态方法的调用,因为静态方法不是通过对象调用的。<br>
解决方法是将静态方法调用替换为实例方法调用,或者考虑其他技术来实现横切关注点。</li>
<li>AOP配置问题: 错误的AOP配置可能导致通知不正确地应用于目标方法,或者在不希望的情况下应用。<br>
解决方法是仔细检查AOP配置,确保切点表达式和通知类型正确配置。</li>
<li>代理问题: 如果代理对象不正确地创建或配置,AOP通知可能无法生效。<br>
解决方法是调试底层源码确保代理对象正确创建,并且AOP通知能够拦截代理对象的方法调用。</li>
</ul>
<h2 id="说说你对-springmvc-的理解">说说你对 SpringMVC 的理解</h2>
<p>SpringMVC是一种基于 Java 的实现MVC设计模型的请求驱动类型的轻量级Web框架,属于Spring框架的一个模块。</p>
<p>它通过一套注解,让一个简单的Java类成为处理请求的控制器,而无须实现任何接口。同时它还支持RESTful编程风格的请求。</p>
<p>Spring MVC 基于 servlet API 构建的,可以说核心就是 DispatcherServlet,即一个前端控制器。它通过注解、配置等方式,将 HTTP 请求映射到控制器方法,然后由控制器处理请求逻辑并将数据返回给视图层进行渲染。它的主要功能包括请求映射、数据绑定、视图解析、表单处理、异常处理等,帮助我们快速构建 Web 应用。</p>
<h2 id="spring-和-spring-mvc-的关系是什么">Spring 和 Spring MVC 的关系是什么?</h2>
<p>Spring 是基础,Spring MVC 构建于 Spring 核心之上,利用其提供的容器管理、依赖注入、AOP 等功能来实现 Web 层的处理</p>
<h2 id="什么是mvc模式">什么是MVC模式?</h2>
<p>MVC的全名是Model View Controller ,是模型(model)-视图(view)-控制器(controller)的缩写,是<br>
一种软件设计典范。它是用一种业务逻辑、数据与界面显示分离的方法来组织代码,将众多的业务逻辑聚集到一个部件里面,在需要改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑,达到减少编码的时间。</p>
<p>View,视图是指用户看到并与之交互的界面。比如由html元素组成的网页界面,或者软件的客户端界面。MVC的好处之一在于它能为应用程序处理很多不同的视图。在视图中其实没有真正的处理发生,它只是作为一种输出数据并允许用户操纵的方式。</p>
<p>model,模型是指模型表示业务规则。在MVC的三个部件中,模型拥有最多的处理任务。被模型返回的数据是中立的,模型与数据格式无关,这样一个模型能为多个视图提供数据,由于应用于模型的代码只需写一次就可以被多个视图重用,所以减少了代码的重复性。</p>
<p>controller,控制器是指控制器接受用户的输入并调用模型和视图去完成用户的需求,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定哪个视图来显示返回的数据。</p>
<h2 id="什么是-restful-风格的接口">什么是 Restful 风格的接口?</h2>
<p>Restful风格的接口是一种基于资源的设计风格,用于构建面向 Web的 API。</p>
<p>REST(Representational State Transfer)是一种无状态的架构风格,它以 HTTP 协议为基础,通过定义资源和标准的操作方法来组织接口,使得客户端和服务器之间的交互更加简单、清晰和高效。</p>
<p>示例:假设有一个用户资源,URI为/users,RESTful API设计如下</p>
<ul>
<li>GET /users:获取所有用户。</li>
<li>GET /users/{id}:获取指定 ID 的用户。</li>
<li>POST /users :创建新用户。</li>
<li>PUT /users/{id}:更新指定 ID 的用户。</li>
<li>DELETE /users/{id}:删除指定 ID 的用户</li>
</ul>
<h2 id="spring-mvc-的核心组件有哪些">Spring MVC 的核心组件有哪些?</h2>
<p>记住了下面这些组件,也就记住了 SpringMVC 的工作原理。</p>
<ul>
<li><strong><code>DispatcherServlet</code></strong>:<strong>核心的中央处理器</strong>,负责接收请求、分发,并给予客户端响应。</li>
<li><strong><code>HandlerMapping</code></strong>:<strong>处理器映射器</strong>,根据 URL 去匹配查找能处理的 <code>Handler</code> ,并会将请求涉及到的拦截器和 <code>Handler</code> 一起封装。</li>
<li><code>Controller</code>:<strong>控制器</strong>,负责处理用户请求的核心组件。Controller 接收请求后,调用业务逻辑处理,返回数据给前端</li>
<li><code>ModelAndView</code>:Controller 返回的对象,包含模型数据和视图信息,表示返回给用户的页面或API 响应。</li>
<li><strong><code>HandlerAdapter</code></strong>:<strong>处理器适配器</strong>,根据 <code>HandlerMapping</code> 找到的 <code>Handler</code> ,适配执行对应的 <code>Handler</code>;</li>
<li><strong><code>Handler</code></strong>:<strong>请求处理器</strong>,处理实际请求的处理器。</li>
<li><strong><code>ViewResolver</code></strong>:<strong>视图解析器</strong>,根据 <code>Handler</code> 返回的逻辑视图 / 视图,解析并渲染真正的视图,并传递给 <code>DispatcherServlet</code> 响应客户端</li>
</ul>
<h2 id="mvc-中的视图解析器有什么作用">MVC 中的视图解析器有什么作用?</h2>
<p>视图解析器 (ViewResolver)是 Spring MVC框架中的一个接口,负责将逻辑视图名称解析为实际的视图对象(如 JSP、Thymeleaf、FreeMarker 模板等)。根据控制器返回的视图名称,找到对应的视图文件,并将模型数据传递给视图,生成最终的 HTML 响应。来看下工作流程:</p>
<ol>
<li>接收视图名称:控制器返回一个逻辑视图名称</li>
<li>视图解析:视图解析器根据逻辑视图名称和配置,解析并找到实际的视图文件</li>
<li>渲染视图:将模型数据传递给视图对象,由视图对象生成最终的 HTML响应。</li>
</ol>
<h2 id="springmvc-工作原理了解吗">SpringMVC 工作原理了解吗?</h2>
<p>Spring MVC 的工作流程可以分为以下几个关键步骤:</p>
<ol>
<li>客户端请求:浏览器向服务器发送 HTTP 请求。</li>
<li>DispatcherServlet:所有的请求首先出 Spring MVC 的核心前端控制器 Dispatcherservlet 接收,它充当整个流程的调度中心</li>
<li>处理器映射(Handler Mapping): DispatcherServlet 根据请求的 URL 使用处理器映射器找到对应的控制器(Controller)</li>
<li>控制器(Controller):控制器接收请求并处理业务逻辑,通常通过注解 @Controller 和 @RequestMapping 定义请求的映射方法</li>
<li>模型和视图(ModelAndView):控制器处理完业务逻辑后,将数据封装到模型对象中,并指定返回的视图名称。</li>
<li>视图解析器 (ViewResolver): DispatcherServlet 调用视图解析器,将逻辑视图名称解析为实际的视图,如 JSP、Thymeleaf 等模板引擎</li>
<li>视图渲染:视图渲染引擎根据模型中的数据生成 HTML 页面并返回给客户端。</li>
</ol>
<p><img src="https://seven97-blog.oss-cn-hangzhou.aliyuncs.com/imgs/202404281540071.png" alt="" loading="lazy"></p>
<ol>
<li>用户发送请求——&gt;DispatcherServlet(用户发送请求):接收用户的请求:前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行 处理,作为统一访问点,进行全局的流程控制;</li>
<li>DispatcherServlet——&gt;HandlerMapping:HandlerMapping 将会把请求映射为 HandlerExecutionChain 对象(包含一 个Handler 处理器(页面控制器)对象、多个HandlerInterceptor 拦截器)对象,通过这种策略模式,很容易添加新的映射策略;</li>
<li>DispatcherServlet——&gt;HandlerAdapter:HandlerAdapter 将会把处理器包装为适配器,从而支持多种类型的处理器, 即适配器设计模式的应用,从而很容易支持很多类型的处理器;</li>
<li>HandlerAdapter——&gt;处理器功能处理方法的调用:HandlerAdapter 将会根据适配的结果调用真正的处理器的功能处理方法(也就是执行所有注册拦截器的preHandler方法),完成功能处理;并返回一个ModelAndView 对象(包含模型数据、逻辑视图名);</li>
<li>倒序执行所有注册拦截器的postHandler方法</li>
<li>ModelAndView 的逻辑视图名——&gt; ViewResolver:ViewResolver 将把逻辑视图名解析为具体的View,通过这种策 略模式,很容易更换其他视图技术;</li>
<li>View——&gt;渲染:View 会根据传进来的Model 模型数据进行渲染,此处的Model 实际是一个Map 数据结构,因此 很容易支持其他视图技术;</li>
<li>返回控制权给DispatcherServlet:由DispatcherServlet 返回响应给用户,到此一个流程结束</li>
</ol>
<h2 id="spring-mvc的常用注解有哪些">Spring MVC的常用注解有哪些?</h2>
<ul>
<li>@Controller:用于标识此类的实例是一个控制器。</li>
<li>@RequestMapping:映射Web请求(访问路径和参数)。</li>
<li>@ResponseBody:注解返回数据而不是返回页面</li>
<li>@RequestBody:注解实现接收 http 请求的 json 数据,将 json 数据转换为 java 对象。</li>
<li>@PathVariable:获得URL中路径变量中的值</li>
<li>@RestController:@Controller+@ResponseBody</li>
</ul>
<h2 id="spring-mvc中的controller是什么如何定义一个controller">Spring MVC中的Controller是什么?如何定义一个Controller?</h2>
<p>Controller 是 Spring MVC 框架中的核心组件之一。负责处理客户端的请求,并返回相应的视图或数据。Controller通过接收用户请求,调用业务层逻辑处理,最后将数据返回给视图层进行渲染,或者直接返回JSON、XML等格式的数据给客户端。</p>
<p>定义 Controller 的步骤:</p>
<ol>
<li>使用 @Controller或 @RestController注解:类上标记 @Controller 注解,表示这是一个控制器,Spring会将其注册为一个Spring MVC的 Bean,如果是用于RESTful API的开发,可以使用 @RestController,它是@controller 和 @ResponseBody 的组合注解,默认返回 JSON 数据。</li>
<li>使用 @Requestapping 注解映射请求路径:在方法上使用@Requestapping或其行生注解(如@Getmapping、@postmapping 等)来映射 HTTP 请求,</li>
</ol>
<h2 id="spring-mvc-中如何处理表单提交">Spring MVC 中如何处理表单提交?</h2>
<p>在 Spring MVC 中,表单提交是通过 @ModelAttribute、@RequestParam、@RequestBody 等注解来处理的。</p>
<ul>
<li>@ModelAttribute :可以自动将表单中的字段与 Java 对象的属性进行绑定,通常用于处理复杂的表单提交。</li>
<li>@RequestParam :用于处理表单中单个字段的数据。对于简单的表单,可以使用该注解从请求中获取参数值</li>
<li>@Requesteody:如果表单数据是以JSON 格式提交的,可以使用 @RequestBody 注解将请求体中的 JSON 数据映射为 Java 对象。</li>
</ul>
<h2 id="spring-mvc的异常如何处理">Spring MVC的异常如何处理?</h2>
<p>可以将异常抛给Spring框架,由Spring框架来处理;只需要配置简单的异常处理器,在异常处理器中添视图页面即可。</p>
<ul>
<li>使用系统定义好的异常处理器 SimpleMappingExceptionResolver</li>
<li>使用自定义异常处理器</li>
<li>使用异常处理注解
<ul>
<li>局部异常处理@ExceptionHandler 注解:用于局部的异常处理,通常定义在控制器类中。它可以捕获特定的异常,并返回自定义的错误信息或视图。</li>
<li>全局异常处理@ControllerAdvice:应用于所有的控制器。通过这个注解,可以定义全局的异常处理逻辑,避免在每个控制器中重复编写相同的异常处理代码。</li>
</ul>
</li>
</ul>
<h2 id="springmvc-父子容器是什么知道吗">SpringMVC 父子容器是什么知道吗?</h2>
<p>父容器:父容器指的是 Spring 的根容器,通常是 Spring 应用上下文(AplitcationContext),如 ContextLoaderListener加载的根容器。它主要用于管理应用程序的全局Bean,如服务层(Service)、数据访问层(DAO)等。<br>
子容器:是Web 容器,每个 DispatcherServlet 实例都会创建一个子容器,用于管理 Web 层(如控制器和拦截器)中的 Bean</p>
<p>父子容器的关系:</p>
<ul>
<li>子容器可以访问父容器的 Bean:如果一个 Bean在父容器中定义,子容器也可以直接访问它,这种机制有助于 Web层(子容器)使用服务层或 DAO 层(父容器)中的 Bean</li>
<li>父容器不能访问子容器的 Bean:父容器无法访问子容器中的 Bean,这是 Sping MVC的设计之一。父容器中的 Bean 和子容器中的 Bean 被分开管理,避免了不必要的耦合。</li>
</ul>
<h2 id="requestbody和requestparam的区别">RequestBody和RequestParam的区别</h2>
<ul>
<li>@RequestBody一般处理的是在ajax请求中声明contentType: "application/json; charset=utf-8"时候。也就是json数据或者xml数据。</li>
<li>@RequestParam一般就是在ajax里面没有声明contentType的时候,为默认的x-www-form-urlencoded格式时。</li>
</ul>
<h2 id="能说说-spring-拦截链的实现吗">能说说 Spring 拦截链的实现吗?</h2>
<p>在 Spring 中,拦截链通常指的是一系列拦截器(如 AOP切面、过滤器、拦截器等)依次作用于请求或方法调用,实现横切关注点的处理,比如日志记录、权限控制、事务管理等。</p>
<p>Spring 拦截链的核心实现包括以下几个方面:</p>
<ul>
<li>HandlerInterceptor (MVC 拦载器):用于拦截 HTTP 请求并进行预处理和后处理,通过实现 HandlerInterceptor 接口的 pretHandle、postHandle 和 afterCompletion 方法,可以在请求到达控制器之前、控制器方法执行之后以及请求完成后进行处理。</li>
<li>Fiter(过滤器):基于 Servlet API的过滤器,可对请求进行初步筛选,应用于安全验证、编码过滤、跨域处理等场景。过滤器通过 Fiter 接口的 dofilter 方法拦截请求。</li>
<li>AOP 拦截链(切面):SpingAOP提供的方法级别的拦截,通过定义切面(Aspect)可以实现方法的前后处理,切面中的 @Before、@After、@Around 等注解用于控制拦截的执行顺序。</li>
</ul>
<h2 id="过滤器和拦截器有什么区别">过滤器和拦截器有什么区别?</h2>
<p>过滤器(Filter)和拦截器(Interceptor)都是用于解决项目中与请求处理、响应管理和业务逻辑控制相关问题的工具,但它们之间存在明显的区别。</p>
<ul>
<li>来源不同:拦截器是基于java反射机制的,是Servlet 相关的包,而过滤器是基于函数回调,是Spring的包。</li>
<li>触发时机不同:请求的执行顺序是:请求进入容器 &gt; 进入过滤器 &gt; 进入 Servlet &gt; 进入拦截器 &gt; 执行控制器(Controller);所以过滤器和拦截器的执行时机也是不同的,过滤器会先执行,然后才会执行拦截器,最后才会进入真正的要调用的方法。</li>
<li>拦截器不依赖于Servlet容器,而过滤器依赖于servlet容器。</li>
<li>拦截器只能对action请求起作用,而过滤器可以对几乎所以的请求起作用。</li>
<li>拦截器可以访问action上下文,值栈里的对象,而过滤器不能。</li>
<li>在Action的生命周期周,拦截器可以被多次调用,而过滤器只能在容器初始化的时候被调用一次。</li>
<li>使用场景不同:
<ul>
<li>拦截器更接近业务系统,所以拦截器主要用来实现项目中的业务判断的,比如:登录判断、权限判断、日志记录等业务。</li>
<li>过滤器通常是用来实现通用功能过滤的,比如:敏感词过滤、字符集编码设置、响应数据压缩等功能。</li>
</ul>
</li>
</ul>
<h2 id="spring-webflux-是什么它与-spring-mvc-有何不同">Spring WebFlux 是什么?它与 Spring MVC 有何不同?</h2>
<p>Spring WebFlux:</p>
<ul>
<li>异步非阻塞框架:Spring WebFlux是Spring5 引入的响应式 Web 框架,旨在支持异步非阻塞编程模型</li>
<li>基于 Reactor: WebFlux基于 Reactor 库,支持响应式流(Reactive Streams)规范,使用 Mono 和 Flux来表示单个和多个异步序列。</li>
<li>适用于高并发:WebFlux 适用于需要处理大量并发请求的场景,如实时数据流和高负载应用。</li>
</ul>
<p>Spring MVC:</p>
<ul>
<li>同步阻塞框架:Spring MVC是一个基于 Servlet 的传统 Web 框架,使用同步阻塞模型处理请求</li>
<li>基于 Servlet APl:Spring MVC 使用标准的 Servlet API,通常每个请求对应一个线程。</li>
<li>广泛应用:Spring MVC适用于大多数 Web 应用,特别是传统的 CRUD 操作和企业应用</li>
</ul>
<p>适用场景:</p>
<ul>
<li>Spring MVC:适用于 IO 操作较少、请求数相对较少的应用。。</li>
<li>Spring WebFlux:适用于 IO 操作频繁、高并发、低延迟的应用。</li>
</ul>
<h2 id="mvc-中的国际化支持是如何实现的">MVC 中的国际化支持是如何实现的?</h2>
<p>主要通过 LocaleResolver 和 ResourceBundleMessagesource实现,它可以根据用户的语言环境(Locale)来动态选择和显示对应语言的文本或内容,从而支持多语言的 Web 应用程序</p>
<p>实现国际化的核心步骤:</p>
<ol>
<li>定义国际化资源文件:使用 messages.properties 等资源文件来存储不同语言的文本内容。Spring MVC 会根据当前用户的 Locale(区域设置)加载对应的资源文件。</li>
<li>配置 LocaleResolver:LocaleResolver 用于确定用户的语言环境(Locale),可以基于请求参数、会话、Cookie 等来解析用户的 Locale</li>
<li>配置 ResourceBundlemesagesource:Spring 使用ResourceBundleMessagesource 来加载国际化资源文件,并根据用户的 Locale 返回相应的语言内容</li>
<li>使用 @Requestmapping 或 Thymeleaf 等模板引擎的国际化标签来实现动态内容切换。</li>
</ol>


</div>
<div id="MySignature" role="contentinfo">
    <p>本文来自在线网站:seven的菜鸟成长之路,作者:seven,转载请注明原文链接:www.seven97.top</p><br><br>
来源:https://www.cnblogs.com/sevencoding/p/19130165
頁: [1]
查看完整版本: Spring AOP、MVC高频面试题解析