Spring面试题合集
Spring
1.谈谈你对Spring的理解
首先Spring是一个轻量级的开源框架,为Java程序的开发提供了基础架构支持,简化了应用开发,让开发者专注于开发逻辑;
同时Spring是一个容器,它通过管理Bean的生命周期和依赖注入,处理对象之间的依赖关系,从而简化了开发者的工作,让代码更加模块化、灵活、易维护;
最后我认为Spring是一个生态,无论后面使用的springMVC,springboot,springcloud等等,都是基于Spring来开发的,为整个Spring全家桶提供了底层支持。
他最主要的两个核心设计就是IOC控制反转和AOP面向切面编程。
2.谈谈你对IOC的理解
控制反转,将创建对象进行反转,就是由容器来负责控制对象的生命周期和对象间的关系。以前是我们想要什么,就自己创建什么,现在是我们需要什么,容器就给我们送来什么。
依赖注入(DI)是IOC的具体实现:指的是Spring容器会根据配置(XML、注解或Java配置类),自动将对象的依赖关系注入到对应的类中
常见的依赖注入方式有两种:
- 构造函数注入:通过类的构造函数来注入依赖。
- Setter方法注入:通过类的setter方法注入依赖。
IOC的实现原理
实现原理是工厂模式加反射机制。
3.Spring框架中都用到了哪些设计模式?
- 工厂模式: BeanFactory就是简单工厂模式的体现,用来创建对象的实例;
- 单例模式: Bean默认为单例模式。
- 代理模式: Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
- 模板方法: 用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
- 观察者模式: 定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现–ApplicationListener。
- 适配器模式 : Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller。
- 装饰器模式: Spring 中配置 DataSource 的时候,DataSource 可能是不同的数据库和数据源,项目需要连接多个数据库,这种模式让我们可以根据客户需求切换不同的数据源。
- 策略模式: Spring中资源访问接口Resource的设计是一种典型的策略模式。Resource接口是所有资源访问类所实现的接口,客户端程序只和 Resource 接口耦合,并不知道底层采用何种资源访问策略,这样客户端程序可以在不同的资源访问策略之间自由切换
4.谈谈你对AOP的理解
面向切面编程,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),添加通用的代码逻辑,减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理等。
AOP通过一种动态插入的方法使我们能够在不修改核心业务逻辑的情况下增强功能,帮助保持代码的清洁与模块化。
AOP的实现原理
AOP的实现原理是动态代理。
动态代理分为 JDK 动态代理 和 CGLIB(Code Generation Library)动态代理 两种方式。
-
JDK 动态代理: JDK的动态代理机制只适用于实现了接口的类。它通过
java.lang.reflect.Proxy
和InvocationHandler
这两个类动态创建代理对象。主要工作原理如下:这种方式比较轻量,效率较高,但只能代理接口方法。
- 创建一个代理对象,代理对象与目标对象具有相同的接口。
- 通过
InvocationHandler
拦截目标对象的方法调用。 - 在
InvocationHandler.invoke()
方法中插入切面逻辑,比如日志记录、权限校验等。
-
CGLIB 动态代理: CGLIB 是一种基于 字节码 的动态代理机制,能够代理没有实现接口的普通类。它通过 继承 目标类并重写其方法来实现代理。主要工作原理如下:
CGLIB 更加通用,但由于是基于继承的,所以不能代理
final
修饰的方法。- 生成目标类的子类,并通过字节码技术对目标类的方法进行重写。
- 在重写的方法中插入切面逻辑,调用父类的原方法以保持业务逻辑不变。
AOP有哪些核心概念
- 切面(Aspect):封装横切关注点的模块。可以理解为包含与业务无关的某些功能的类
- 连接点(Join Point):插入切面逻辑的地方。
- 通知(Advie):在特定连接点执行的操作,定义了具体行为。
- 切入点(Pointcut):定义在什么地方应用切面。用来选择哪些连接点应该插入切面通知
- 织入(Weaving):将切面代码插入到目标代码中的过程。可在编译时、加载时、或运行时进行。
Advice通知的五种类型
- 前置通知(Before Advice):在目标方法执行前前执行
- 后置通知(After Advice):在目标方法执行后执行
- 环绕通知(Around Advice):在目标方法调用前后均可执行自定义逻辑
- 返回通知(After returning Advice):在目标方法返回结果后执行
- 异常通知(After throwing Advice):异常通知,在方法抛出异常后执行
AOP的应用场景
1. 日志记录:通常,在每个方法调用时,我们希望记录日志来跟踪系统的行为,包括方法的开始、结束、耗时等信息。
2.事务管理:通过 AOP,可以在数据库操作方法前后插入事务的开启、提交和回滚逻辑,从而避免在业务逻辑中直接处理事务控制。
3.数据缓存:通过 AOP,可以在数据访问方法前检查缓存中是否已经存在数据,如果存在则直接返回缓存数据,否则执行目标方法并将结果缓存。
4.权限校验:某些方法只允许特定角色的用户访问。通过 AOP,可以在方法执行之前进行权限检查,如果用户没有权限,则直接抛出异常。
5.Spring的事务管理
编程式: beginTransaction()、commit()、rollback()等事务管理相关的方法,灵活度高,但是维护性差。
声明式: 利用注解或者xml配置,将业务和事务分离出来,是依靠AOP实现的。通过@Transactional
注解来定义事务边界,避免了使用复杂的编程式事务管理。
事物的实现方式和实现原理
spring事务就是对数据库事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。
通过AOP生成动态代理帮开发接管事物的开启回滚和提交
隔离级别
- DEFAULT:使用数据库默认的隔离级别。
- READ_UNCOMMITTED:允许读取未提交的数据,会出现脏读、不可重复读、幻读。
- READ_COMMITTED:只允许读取已提交的数据,避免脏读,但可能出现不可重复读、幻读。
- REPEATABLE_READ:确保同一事务内多次读取的数据是一致的,防止脏读和不可重复读,但可能出现幻读。
- SERIALIZABLE:最高的隔离级别,完全避免脏读、不可重复读、幻读,但性能最差。
事务的传播行为
- REQUIRED(默认):默认事务传播行为,存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。
- REQUIRE_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
- NESTED:如果当前存在事务,就嵌套当前事务中去执行,如果当前没有事务,那么就新建一个事务,类似 REQUIRE_NEW这个样一个传播行为。
- SUPPORTS:表示支持当前当前的事务,如果当前不存在事务,就以非事务的方式去执行。
- NOT_SUPPORT: 总是非事务地执行,并挂起任何存在的事务。
- MANDATORY:如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
- NEVER:就是以非事务的方式来执行,如果存在事务则抛出一个异常。
事务失效场景
6.Spring的常用注解
Web:
-
@Controller:组合注解(组合了@Component注解),应用在MVC层(控制层)。
-
@RestController:该注解为一个组合注解,相当于@Controller和@ResponseBody的组合,注解在类上,意味着,该Controller的所有方法都默认加上了@ResponseBody。
-
@RequestMapping:用于映射Web请求,包括访问路径和参数。如果是Restful风格接口,还可以根据请求类型使用不同的注解:
-
@GetMapping
-
@PostMapping
-
@PutMapping
-
@DeleteMapping
-
-
@ResponseBody:支持将返回值放在response内,而不是一个页面,通常用户返回json数据。
-
@RequestBody:允许request的参数在request体中,而不是在直接连接在地址后面。
-
@PathVariable:用于接收路径参数,比如@RequestMapping(“/hello/{name}”)申明的路径,将注解放在参数中前,即可获取该值,通常作为Restful的接口实现方法。
-
@RestController:该注解为一个组合注解,相当于@Controller和@ResponseBody的组合,注解在类上,意味着,该Controller的所有方法都默认加上了@ResponseBody。
容器:
-
@Component:表示一个带注释的类是一个“组件”,成为Spring管理的Bean。当使用基于注解的配置和类路径扫描时,这些类被视为自动检测的候选对象。同时@Component还是一个元注解。
-
@Service:组合注解(组合了@Component注解),应用在service层(业务逻辑层)。
-
@Repository:组合注解(组合了@Component注解),应用在dao层(数据访问层)。
-
@Autowired:Spring提供的工具(由Spring的依赖注入工具(BeanPostProcessor、BeanFactoryPostProcessor)自动注入)。
-
@Qualifier:该注解通常跟 @Autowired 一起使用,当想对注入的过程做更多的控制,@Qualifier 可帮助配置,比如两个以上相同类型的 Bean 时 Spring 无法抉择,用到此注解
-
@Configuration:声明当前类是一个配置类(相当于一个Spring配置的xml文件)
-
@Value:可用在字段,构造器参数跟方法参数,指定一个默认值,支持 #{} 跟 ${} 两个方式。一般将 SpringbBoot 中的 application.properties 配置的属性值赋值给变量。
-
@Bean:注解在方法上,声明当前方法的返回值为一个Bean。返回的Bean对应的类中可以定义init()方法和destroy()方法,然后在@Bean(initMethod=”init”,destroyMethod=”destroy”)定义,在构造之后执行init,在销毁之前执行destroy。
-
@Scope:定义我们采用什么模式去创建Bean(方法上,得有@Bean) 其设置类型包括:Singleton 、Prototype、Request 、 Session、GlobalSession。
AOP:
-
@Aspect:声明一个切面(类上) 使用@After、@Before、@Around定义建言(advice),可直接将拦截规则(切点)作为参数。
-
@After
:在方法执行之后执行(方法上)。 -
@Before
:在方法执行之前执行(方法上)。 -
@Around
:在方法执行之前与之后执行(方法上)。 -
@PointCut
:声明切点 在java配置类中使用@EnableAspectJAutoProxy注解开启Spring对AspectJ代理的支持(类上)。
-
事务:
-
@Transactional:在要开启事务的方法上使用@Transactional注解,即可声明式开启事务。
第一类是:声明bean,有@Component、@Service、@Repository、@Controller
第二类是:依赖注入相关的,有@Autowired、@Qualifier、@Resourse
第三类是:设置作用域 @Scope
第四类是:spring配置相关的,比如@Configuration,@ComponentScan 和 @Bean
第五类是:跟aop相关做增强的注解 @Aspect,@Before,@After,@Around,@Pointcut
7.什么是Bean
在 Spring 框架中,Bean 是最基本的概念之一。Spring IoC(Inversion of Control,控制反转)容器负责管理这些 Bean,它们是组成应用程序的核心组件。在 Spring 中,所有的 Bean 都是由 IoC 容器来创建、初始化和管理的。
在 Spring 框架中,Bean 是指由 Spring 容器管理的对象。Bean 可以是任何类型的对象,通常是服务层、数据访问层或者业务逻辑层的组件。Spring 通过 ApplicationContext
容器创建并管理这些对象。
Bean的生命周期
Bean的注入方式(DI)
Spring 容器负责管理 Bean 的依赖注入(Dependency Injection,DI)。通过依赖注入,Spring 可以将一个 Bean 的依赖对象(其他 Bean)自动注入到这个 Bean 中,避免了手动管理依赖的繁琐。
- 构造函数注入,构造器依赖注入通过容器触发一个类的构造器来实现的,该类有一系列参数,每个参数代表一个对其他类的依赖。
- Setter 方法注入,Setter方法注入是容器通过调用无参构造器或无参static工厂 方法实例化bean之后,调用该bean的setter方法,即实现了基于setter的依赖注入。
Bean的作用域
Spring 提供了多种作用域来定义 Bean 的生命周期管理方式,常见的作用域包括:
-
singleton(默认值):整个 Spring 容器中只会创建一个 Bean 实例,容器启动时创建,所有请求共享同一个实例。
-
prototype:每次请求都会创建一个新的 Bean 实例,适合需要频繁变化的对象。
-
request(仅限 Web 应用):每个 HTTP 请求都会创建一个新的 Bean 实例,请求结束后,Bean 实例被销毁。
-
session(仅限 Web 应用):每个 HTTP 会话创建一个 Bean 实例,会话结束后,实例被销毁。
-
globalSession(仅限 Web 应用):类似于 session 作用域,但适用于全局会话环境(如 Portlet 应用)。
Spring框架中的单例bean是线程安全的吗?
Spring中的单例Bean不是线程安全的。
因为单例Bean,是全局只有一个Bean,所有线程共享。如果说单例Bean,是一个无状态的,也就是线程中的操作不会对Bean中的成员变量执行查询以外的操作,那么这个单例Bean是线程安全的。比如Spring mvc 的 Controller、Service、Dao等,这些Bean大多是无状态的,只关注于方法本身。
假如这个Bean是有状态的,也就是会对Bean中的成员变量进行写操作,那么可能就存在线程安全的问题。
循环依赖问题
循环依赖:循环依赖其实就是循环引用,也就是两个或两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于A
循环依赖:A依赖B,B依赖C,C依赖A,形成了闭环。
①构造器的循环依赖: 这种依赖spring是处理不了的,直接抛出异常。
②单例模式下的setter循环依赖: 通过"三级缓存"处理循环依赖,能处理。
③多例模式下的setter循环依赖: 不能处理,会一直产生新的Bean,导致OOM。
循环依赖在spring中是允许存在,spring框架依据三级缓存已经解决了大部分的循环依赖
一级缓存:单例池,缓存已经经历了完整的生命周期,已经初始化完成的bean对象
二级缓存:缓存早期的bean对象(生命周期还没走完)
三级缓存:缓存的是ObjectFactory,表示对象工厂,用来创建某个对象的
SpringMVC
Spring MVC 是 Spring 框架中的一部分,用于构建基于 Web 的应用程序。它是一个 Model-View-Controller (MVC) 模式的实现,帮助开发者将 Web 应用程序的业务逻辑、用户界面和数据分离开来,从而提高代码的模块化和可维护性。
Spring MVC 的核心组件
- DispatcherServlet(前端控制器):所有请求都会首先经过
DispatcherServlet
,它是 Spring MVC 的核心组件,负责将请求分发给适当的处理器(Controller)。 - Handler Mapping(处理器映射器):根据请求 URL,查找能够处理该请求的
Controller
。 - HandlerAdapter(处理器适配器) : 在编写Handler的时候要按照HandlerAdapter要求的规则去编写,这样适配器HandlerAdapter才可以正确的去执行Handler。
- Controller(控制器):处理客户端的请求,通常会获取用户输入、调用业务逻辑层处理请求,并返回一个视图名和模型数据。
- ModelAndView:将业务处理后的数据模型与视图名称绑定在一起,返回给
DispatcherServlet
。 - View Resolver(视图解析器):根据
ModelAndView
中的视图名解析并生成实际的视图,通常是 JSP、HTML、Thymeleaf 等。 - Model:存储处理完的业务数据,提供给视图用于渲染页面。
SpringMVC的工作流程
1、用户发送出请求到前端控制器DispatcherServlet接待,这是一个调度中心
2、DispatcherServlet收到请求调用HandlerMapping(处理器映射器)。
3、HandlerMapping找到具体的处理器(可查找xml配置或注解配置),生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet。
4、DispatcherServlet调用HandlerAdapter(处理器适配器)。
5、HandlerAdapter经过适配调用具体的处理器(Handler/Controller)。
6、Controller执行完成返回ModelAndView对象。
7、HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet。
8、DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)。
9、ViewReslover解析后返回具体View(视图)。
10、DispatcherServlet根据View进行渲染视图(即将model模型数据填充至视图中)得到JSP。
11、DispatcherServlet将JSP响应用户。
SpringMVC Restful风格的接口的流程是什么样的
-
客户端向服务端发送一次请求,这个请求会先到前端控制器DispatcherServlet
-
DispatcherServlet接收到请求后会调用HandlerMapping处理器映射器。由此得知,该请求该由哪个Controller来处理
-
DispatcherServlet调用HandlerAdapter处理器适配器,告诉处理器适配器应该要去执行哪个Controller
-
Controller被封装成了ServletInvocableHandlerMethod,HandlerAdapter处理器适配器去执行invokeAndHandle方法,完成对Controller的请求处理
-
HandlerAdapter执行完对Controller的请求,会调用HandlerMethodReturnValueHandler去处理返回值,主要的过程:
5.1. 调用RequestResponseBodyMethodProcessor,创建ServletServerHttpResponse(Spring对原生ServerHttpResponse的封装)实例
5.2.使用HttpMessageConverter的write方法,将返回值写入ServletServerHttpResponse的OutputStream输出流中
5.3.在写入的过程中,会使用JsonGenerator(默认使用Jackson框架)对返回值进行Json序列化
-
执行完请求后,返回的ModealAndView为null,ServletServerHttpResponse里也已经写入了响应,所以不用关心View的处理
SpringMVC的常用注解
Spring MVC 通过注解简化了开发,常见的注解有:
@Controller
:用于标记类为控制器,处理客户端的请求。@RequestMapping
:用于映射 URL 到控制器的具体方法,可以作用在类或方法上。@GetMapping
、@PostMapping
:是@RequestMapping
的简化版本,用于处理 GET 和 POST 请求。@RequestParam
:用于获取请求中的参数。@PathVariable
:用于获取 URL 中的路径变量。@ModelAttribute
:用于绑定请求参数到模型对象中。@ResponseBody
:用于将控制器方法的返回值直接作为 HTTP 响应体(通常用于返回 JSON 数据)。
在前后端分离的开发模式中,以下是 Spring MVC 的关键组件和它们的作用:
-
@RestController
:它是@Controller
和@ResponseBody
的组合,表明这是一个控制器,并且方法的返回值会自动转换为 JSON 或 XML 格式,而不是返回视图名。 -
HttpMessageConverter
:负责将 Java 对象转换为客户端请求的数据格式(如 JSON、XML),也可以将客户端发送的数据转换为 Java 对象。默认使用Jackson
库来处理 JSON 格式数据。 -
@RequestMapping
、@GetMapping
、@PostMapping
等注解:用于映射不同的 HTTP 请求到控制器的相应方法,如GET
、POST
、PUT
和DELETE
。 -
@PathVariable
和@RequestParam
:用于获取 URL 路径变量或请求参数,处理前端传递的数据。 -
@ResponseBody
:告诉 Spring MVC 直接将方法返回的 Java 对象转换为 HTTP 响应体中的数据(如 JSON),而不是视图。