spring 面试题
一、Spring 基础概念
- 什么是 Spring 框架? 
- Spring 是一个开源的 Java 应用程序框架,它提供了一种轻量级的、非侵入式的方式来构建企业级应用。Spring 的核心功能包括依赖注入(Dependency Injection,DI)、面向切面编程(Aspect - Oriented Programming,AOP)、事务管理、数据访问等,旨在简化企业级 Java 开发,提高开发效率和代码的可维护性。
 
 - Spring 框架的核心模块有哪些? 
- 核心容器(Core Container) 
- spring - core:提供了框架的基本核心功能,如 IOC(控制反转)和依赖注入(DI)的基础实现。
 - spring - beans:提供了 BeanFactory,这是 Spring 框架的核心工厂接口,用于管理和配置应用程序中的对象(beans)。
 - spring - context:建立在核心容器之上,提供了一种更高级的应用程序上下文(ApplicationContext),它是 BeanFactory 的子接口,除了具有 BeanFactory 的功能外,还提供了诸如国际化、事件传播、资源加载等附加功能。
 - spring - context - support:提供了对第三方库(如缓存、邮件、调度等)的集成支持。
 
 - AOP(Aspect - Oriented Programming) 
- spring - aop:提供了面向切面编程的基础架构,允许在运行时将横切关注点(如日志记录、安全检查等)织入到应用程序的核心业务逻辑中。
 - spring - aspectj:对 AspectJ 框架的集成支持,AspectJ 是一种功能强大的 AOP 实现,Spring - aspectj 使得在 Spring 应用中可以使用 AspectJ 的注解和语法进行更灵活的 AOP 编程。
 
 - 数据访问与集成(Data Access/Integration) 
- spring - jdbc:提供了对 JDBC(Java Database Connectivity)的简化操作,包括数据库连接的获取、SQL 语句的执行、结果集的处理等,减少了编写 JDBC 代码的繁琐。
 - spring - tx:提供了事务管理的抽象层,支持编程式事务和声明式事务,可与多种底层事务管理器(如 JDBC 事务、JTA 事务等)集成。
 - spring - orm:对多种对象 - 关系映射(ORM)框架(如 Hibernate、MyBatis 等)提供集成支持,方便在 Spring 应用中使用 ORM 框架进行数据库操作。
 
 - Web(Spring Web) 
- spring - web:提供了基础的 Web 开发功能,如 Web 应用上下文、Servlet 集成、文件上传等。
 - spring - webmvc:Spring 的 MVC(Model - View - Controller)框架,用于构建 Web 应用程序,实现了请求处理、视图渲染、模型绑定等功能。
 - spring - webflux:提供了基于反应式编程(Reactive Programming)的 Web 框架,适用于构建高性能、非阻塞的 Web 应用,如处理大量并发请求的微服务。
 
 
 - 核心容器(Core Container) 
 - 解释一下控制反转(IOC)和依赖注入(DI)的概念。 
- 控制反转(Inversion of Control,IOC) 
- 在传统的编程模式中,对象的创建和对象间的依赖关系是由程序代码直接控制的。而在 IOC 模式下,这种控制权被转移到了容器(如 Spring 容器)中。程序代码不再负责对象的创建和依赖关系的维护,而是由容器来负责创建对象,并将它们装配在一起。这就好比在传统模式下,你自己做饭(自己创建和管理对象),而在 IOC 模式下,你去餐馆吃饭(由容器来提供和管理对象)。
 
 - 依赖注入(Dependency Injection,DI) 
- 依赖注入是实现控制反转的一种具体方式。它是指当一个对象(依赖方)需要依赖于另一个对象(被依赖方)时,由容器在创建依赖方对象时,将被依赖方对象注入到依赖方对象中。依赖注入有三种常见的方式:
 - 构造函数注入(Constructor Injection):通过构造函数将依赖对象传入。例如: 
public class MyService {private MyRepository myRepository;public MyService(MyRepository myRepository) {this.myRepository = myRepository;} }** setter 注入(Setter Injection)**:通过 setter 方法将依赖对象传入。例如:
public class MyService {private MyRepository myRepository;public void setMyRepository(MyRepository myRepository) {this.myRepository = myRepository;} }字段注入(Field Injection):直接在类的字段上使用注解注入依赖对象,但这种方式会使类与 Spring 框架耦合较紧密,例如:
public class MyService {@Autowiredprivate MyRepository myRepository; } 
 
 - 控制反转(Inversion of Control,IOC) 
 
二、Spring Bean
- Spring Bean 的生命周期是怎样的? 
- 实例化(Instantiation): 
- Spring 首先根据配置(如 XML 配置、注解等)确定要创建的 Bean 类型,然后使用 Java 反射机制创建 Bean 的实例。例如,对于一个简单的使用 @Component 注解的类: 
@Component public class MyBean {public MyBean() {// 这是构造函数,在实例化时被调用} }属性赋值(Populate Properties):
 - 在实例化后,Spring 会对 Bean 的属性进行赋值。如果使用了依赖注入,会将依赖的其他 Bean 注入到当前 Bean 中。例如: 
@Component public class MyBean {@Autowiredprivate AnotherBean anotherBean; } -  
- 这里 Spring 会找到 AnotherBean 的实例并注入到 MyBean 的 anotherBean 属性中。
 
 - 初始化(Initialization): 
- 执行 Bean 的初始化方法。有两种常见的初始化方式: 
- 实现 InitializingBean 接口 
@Component public class MyBean implements InitializingBean {@Overridepublic void afterPropertiesSet() throws Exception {// 初始化逻辑} }使用 @PostConstruct 注解:
@Component public class MyBean {@PostConstructpublic void init() {// 初始化逻辑} } 
 - 实现 InitializingBean 接口 
 
 - 执行 Bean 的初始化方法。有两种常见的初始化方式: 
 
 - Spring 首先根据配置(如 XML 配置、注解等)确定要创建的 Bean 类型,然后使用 Java 反射机制创建 Bean 的实例。例如,对于一个简单的使用 @Component 注解的类: 
 
 - 实例化(Instantiation): 
 
- 使用(In - use): 
- 初始化完成后,Bean 就可以被应用程序使用了,例如被其他组件调用方法等。
 
 - 销毁(Destruction): 
- 当容器关闭时,会销毁 Bean。如果 Bean 实现了 DisposableBean 接口,会调用其 destroy 方法:
 
 
@Component
public class MyBean implements DisposableBean {@Overridepublic void destroy() throws Exception {// 销毁逻辑}
} 
或者使用 @PreDestroy 注解来定义销毁方法:
@Component
public class MyBean {@PreDestroypublic void cleanUp() {// 销毁逻辑}
} 
- 如何配置 Spring Bean?有哪些方式? 
- 基于 XML 配置: 
- 在 XML 文件中定义 Bean,例如: 
xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema - instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring - beans.xsd"><bean id="myBean" class="com.example.MyBean"><property name="propertyName" value="propertyValue"/></bean> </beans>
这里定义了一个 id 为 myBean 的 Bean,其类是 com.example.MyBean,并设置了一个属性。 
 - 在 XML 文件中定义 Bean,例如: 
 - 基于注解配置: 
- 使用 Spring 的注解来标识 Bean 及其配置。常见的注解有: 
- @Component:通用的组件注解,用于标识一个普通的 Spring 组件。例如: 
java
@Component public class MyComponent {// 组件逻辑 } - @Service:用于标识业务逻辑层的组件,通常在服务类上使用。例如: 
java
@Service public class MyService {// 服务逻辑 } - @Repository:用于标识数据访问层(如数据库操作)的组件,例如: 
java
@Repository public class MyRepository {// 数据访问逻辑 } - @Controller:用于标识 Web 应用中的控制器组件,例如在 Spring MVC 中: 
java
@Controller public class MyController {// 控制器逻辑 } - @Configuration 和 @Bean:用于 Java 配置类来定义 Bean。例如: 
java
@Configuration public class AppConfig {@Beanpublic MyBean myBean() {return new MyBean();} } 
 - @Component:通用的组件注解,用于标识一个普通的 Spring 组件。例如: 
 
 - 使用 Spring 的注解来标识 Bean 及其配置。常见的注解有: 
 - 基于 Java 配置(Java - based configuration): 
- 除了 @Configuration 和 @Bean 的方式外,还可以通过继承特定的抽象类来配置 Spring。例如,继承 WebMvcConfigurerAdapter(在 Spring 5 中已被 WebMvcConfigurer 取代)来配置 Spring MVC: 
java
@Configuration public class WebMvcConfig extends WebMvcConfigurer {// 配置视图解析器等@Overridepublic void configureViewResolvers(ViewResolverRegistry registry) {// 配置逻辑} } 
 - 除了 @Configuration 和 @Bean 的方式外,还可以通过继承特定的抽象类来配置 Spring。例如,继承 WebMvcConfigurerAdapter(在 Spring 5 中已被 WebMvcConfigurer 取代)来配置 Spring MVC: 
 
 - 基于 XML 配置: 
 
三、Spring AOP
- 什么是面向切面编程(AOP)?Spring AOP 的原理是什么? 
- 面向切面编程(AOP) 
- AOP 是一种编程范式,它允许将横切关注点(如日志记录、安全检查、事务管理等)从应用程序的核心业务逻辑中分离出来,以独立的模块(切面)进行处理。这样可以提高代码的模块化程度和可维护性。例如,在一个没有 AOP 的应用中,如果每个业务方法都需要添加日志记录代码,会导致日志记录代码分散在各个业务方法中,而使用 AOP,可以将日志记录逻辑定义在一个切面中,然后自动织入到需要的业务方法中。
 
 - Spring AOP 原理 
- Spring AOP 主要基于动态代理(Dynamic Proxy)技术。当一个目标对象(被代理对象)需要被织入切面逻辑时,Spring 会根据目标对象是否实现接口来选择使用 JDK 动态代理或 CGLIB 动态代理。 
- JDK 动态代理: 
- 如果目标对象实现了接口,Spring 会使用 JDK 动态代理。JDK 动态代理是通过实现目标对象的接口来创建代理对象的。例如,有一个接口和实现类: 
java
public interface MyInterface {void doSomething(); } public class MyClass implements MyInterface {@Overridepublic void doSomething() {// 业务逻辑} }
Spring 会创建一个实现 MyInterface 的代理对象,在代理对象的 doSomething 方法中,会在调用目标对象的 doSomething 方法前后添加切面逻辑。 
 - 如果目标对象实现了接口,Spring 会使用 JDK 动态代理。JDK 动态代理是通过实现目标对象的接口来创建代理对象的。例如,有一个接口和实现类: 
 - CGLIB 动态代理: 
- 如果目标对象没有实现接口,Spring 会使用 CGLIB(Code Generation Library)动态代理。CGLIB 是通过字节码生成技术来创建目标对象的子类作为代理对象。例如,有一个没有实现接口的类: 
java
public class MyClass {public void doSomething() {// 业务逻辑} }
Spring 会生成一个 MyClass 的子类作为代理对象,在子类的 doSomething 方法中添加切面逻辑。 
 - 如果目标对象没有实现接口,Spring 会使用 CGLIB(Code Generation Library)动态代理。CGLIB 是通过字节码生成技术来创建目标对象的子类作为代理对象。例如,有一个没有实现接口的类: 
 
 - JDK 动态代理: 
 
 - Spring AOP 主要基于动态代理(Dynamic Proxy)技术。当一个目标对象(被代理对象)需要被织入切面逻辑时,Spring 会根据目标对象是否实现接口来选择使用 JDK 动态代理或 CGLIB 动态代理。 
 
 - 面向切面编程(AOP) 
 - Spring AOP 中的通知(Advice)有哪些类型? 
- 前置通知(Before Advice): 
- 在目标方法执行之前执行的通知。例如,在一个方法调用前记录日志: 
java
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before;@Aspect public class LoggingAspect {@Before("execution(* com.example.service.MyService.*(..))")public void beforeAdvice(JoinPoint joinPoint) {System.out.println("Before method: " + joinPoint.getSignature().getName());} } 
 - 在目标方法执行之前执行的通知。例如,在一个方法调用前记录日志: 
 - 后置通知(After Advice): 
- 在目标方法执行之后执行的通知。有两种类型: 
- 正常返回后执行的后置通知(After Returning Advice): 
- 当目标方法正常返回时执行。例如: 
java
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect;@Aspect public class LoggingAspect {@AfterReturning(pointcut = "execution(* com.example.service.MyService.*(..))", returning = "result")public void afterReturningAdvice(JoinPoint joinPoint, Object result) {System.out.println("After returning method: " + joinPoint.getSignature().getName() + ", result: " + result);} } 
 - 当目标方法正常返回时执行。例如: 
 - 无论是否抛出异常都执行的后置通知(After Finally Advice): 
- 这种通知类似于 try - finally 块中的 finally 语句块,无论目标方法是否抛出异常都会执行。例如: 
java
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect;@Aspect public class LoggingAspect {@After("execution(* com.example.service.MyService.*(..))")public void afterFinallyAdvice(JoinPoint joinPoint) {System.out.println("After finally method: " + joinPoint.getSignature().getName());} } 
 - 这种通知类似于 try - finally 块中的 finally 语句块,无论目标方法是否抛出异常都会执行。例如: 
 
 - 正常返回后执行的后置通知(After Returning Advice): 
 
 - 在目标方法执行之后执行的通知。有两种类型: 
 - 环绕通知(Around Advice): 
- 环绕通知可以在目标方法执行前后都添加逻辑,它可以完全控制目标方法的执行。例如: 
java
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect;@Aspect public class LoggingAspect {@Around("execution(* com.example.service.MyService.*(..))")public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {System.out.println("Before method in around advice: " + pjp.getSignature().getName());Object result = pjp.proceed();System.out.println("After method in around advice: " + pjp.getSignature().getName());return result;} } 
 - 环绕通知可以在目标方法执行前后都添加逻辑,它可以完全控制目标方法的执行。例如: 
 - 异常通知(After Throwing Advice): 
- 当目标方法抛出异常时执行的通知。例如: 
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect;@Aspect public class LoggingAspect {@AfterThrowing(pointcut = "execution(* com.example.service.MyService.*(..))", throwing = "ex")public void afterThrowingAdvice(JoinPoint joinPoint, Exception ex) {System.out.println("Method: " + joinPoint.getSignature().getName() + " threw exception: " + ex.getMessage());} } 
 - 当目标方法抛出异常时执行的通知。例如: 
 
 - 前置通知(Before Advice): 
 
