Spring面试题
1.Spring原理
- IoC(控制反转)和 DI(依赖注入):
- IoC(Inversion of Control):IoC是一种设计思想,它将程序的控制权交给容器或框架,由容器来负责对象的创建和管理。Spring的IoC容器,即ApplicationContext,负责管理Bean的生命周期。
- DI(Dependency Injection):DI是IoC的一种实现方式,它通过注入的方式将一个对象的依赖关系交给容器管理,而不是在对象内部直接创建依赖对象。这样做提高了组件的可重用性和可测试性。
- AOP(面向切面编程):
- AOP(Aspect-Oriented Programming):AOP是一种编程范式,它允许将横切关注点(如日志、安全、事务管理等)从业务逻辑中剥离出来,使得系统关注点的变化不影响业务逻辑。在Spring中,AOP通过代理机制实现,常见的代理方式有基于JDK动态代理和基于CGLIB的代理。
- Bean的生命周期和作用域:
- Bean的生命周期:Bean的生命周期包括实例化、初始化、使用和销毁四个阶段。Spring容器负责Bean的实例化和初始化,而销毁阶段则由容器负责。你可以提及
InitializingBean和DisposableBean接口以及@PostConstruct和@PreDestroy注解来控制Bean的初始化和销毁操作。 - Bean的作用域:Spring定义了多种Bean的作用域,包括singleton(单例,默认)、prototype(原型)、request(每个HTTP请求一个实例)、session(每个HTTP Session一个实例)和global session(全局HTTP Session一个实例)等。
- Bean的生命周期:Bean的生命周期包括实例化、初始化、使用和销毁四个阶段。Spring容器负责Bean的实例化和初始化,而销毁阶段则由容器负责。你可以提及
- Spring的事务管理:
- 声明式事务管理:通过使用
@Transactional注解或XML配置来声明事务,使得事务的控制更加方便。Spring提供了对编程式事务管理的支持,可以通过PlatformTransactionManager接口进行编程式事务控制。
- 声明式事务管理:通过使用
- Spring的数据访问与集成:
- 数据访问:Spring提供了JdbcTemplate等模板类,简化了JDBC的操作,同时支持ORM框架(如Hibernate、MyBatis)的集成,提供了对持久层的支持。
- 集成其他技术:Spring能够与其他技术(如消息队列、缓存、搜索引擎等)进行集成,提供了丰富的集成方案。
- Spring框架的核心组件:
- ApplicationContext:Spring的IoC容器,负责管理Bean的生命周期。
- BeanFactory:是IoC容器的基础接口,提供了基本的IoC功能。
- BeanPostProcessor:在Bean初始化前后执行一些操作。
- BeanDefinition:定义了Bean的配置信息。
2.什么是控制反转?什么是依赖注入
控制反转(IoC,Inversion of Control)和依赖注入(DI,Dependency Injection)是面向对象编程中两个重要的概念,它们是实现松耦合(Loose Coupling)的关键。
控制反转(IoC):
控制反转是一种设计原则,它将程序的控制权从程序代码本身转移到了外部容器或框架。在传统的程序设计中,程序代码通常负责对象的创建、依赖关系的管理以及资源的释放。而在IoC中,这些职责被反转,由外部容器负责。IoC使得系统更加灵活,易于扩展和维护。
在IoC中,对象的创建和管理由IoC容器负责。Spring的ApplicationContext就是一个经典的IoC容器。它负责实例化Bean、注入Bean之间的依赖关系、管理Bean的生命周期等。开发者只需定义好Bean和它们之间的关系,由容器负责具体的实例化和管理。
依赖注入(DI):
依赖注入是IoC的一种具体实现方式。在依赖注入中,组件的依赖关系由外部容器在组件被创建时注入。依赖关系通常以构造函数、Setter方法或接口注入的方式实现。
假设有一个类UserService依赖于UserRepository,传统的做法是在UserService类内部创建UserRepository的实例。但在依赖注入中,UserService不再负责创建UserRepository,而是通过构造函数或Setter方法接受一个UserRepository的实例。容器在创建UserService时,将UserRepository的实例注入到UserService中。
public class UserService { |
依赖注入使得对象之间的关系更加灵活,容易被替换和测试。Spring框架提供了多种依赖注入的方式,包括构造函数注入、Setter方法注入、接口注入等。
3.Spring的生命周期
Spring框架中的Bean(Java对象)的生命周期由Spring容器来管理,包括Bean的创建、初始化、使用和销毁等阶段。以下是Spring Bean的生命周期:
- 实例化(Instantiation): 当Spring容器接收到Bean的定义后,它会通过构造函数或工厂方法来实例化Bean对象。
- 设定Bean的属性(Population of Properties): Spring容器将配置文件或注解中定义的属性值或引用注入到Bean中。
- Bean的初始化(Bean Initialization): 如果Bean实现了
InitializingBean接口,Spring将调用其afterPropertiesSet方法进行初始化。另外,如果在配置文件中使用了init-method属性,指定了初始化方法,Spring容器会在属性设置完成后调用这个指定的方法。 - Bean的使用(Bean is ready to use): 此时,Bean已经可以被应用程序使用了。
- Bean的销毁(Bean Destruction): 如果Bean实现了
DisposableBean接口,Spring容器在Bean不再需要时调用其destroy方法进行销毁。或者,如果在配置文件中使用了destroy-method属性,指定了销毁方法,Spring容器在需要销毁Bean时调用这个指定的方法。
在整个生命周期中,Spring容器负责管理Bean的创建、依赖注入、初始化和销毁等工作,确保Bean在应用程序中的正确运作。
4.Spring事务
Spring框架提供了丰富的事务管理功能,支持编程式事务和声明式事务两种方式。事务是用来保持数据库的一致性和完整性的机制,当一组相关的操作要么全部成功,要么全部失败。
在Spring中,你可以使用注解或XML配置来声明式地管理事务。以下是一些关于Spring事务的关键概念:
事务管理器(Transaction Manager): 事务管理器负责实际管理事务。Spring支持各种事务管理器,如
DataSourceTransactionManager(针对关系型数据库)、JtaTransactionManager(用于分布式事务)等。事务定义(Transaction Definition): 事务定义定义了事务的隔离级别、传播行为、超时等属性。Spring使用
org.springframework.transaction.TransactionDefinition接口来表示事务定义。事务传播行为(Transaction Propagation): 事务传播行为定义了在方法调用中的事务如何传播。例如,一个方法A调用另一个方法B,B是否应该加入A的事务。Spring定义了多种传播行为,例如
REQUIRED、REQUIRES_NEW、NESTED等。隔离级别(Isolation Level): 隔离级别定义了多个事务并发执行时,彼此之间的可见性。Spring支持不同的隔离级别,包括
DEFAULT(使用数据库默认隔离级别)、READ_UNCOMMITTED(允许读取未提交的数据)、READ_COMMITTED(只能读取已提交的数据)、REPEATABLE_READ(可重复读取)、SERIALIZABLE(串行化)等。声明式事务管理: 通过注解或XML配置,你可以在方法上声明事务的属性。例如,使用
@Transactional注解来标识一个方法应该在事务中执行。
public void someTransactionalMethod() {
// 事务性操作
}编程式事务管理: 在代码中通过编程的方式控制事务。Spring提供了
TransactionTemplate来简化编程式事务管理。transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
// 事务性操作
}
});
通过合适的配置,Spring事务管理器可以确保事务的正确性和一致性,保障了数据操作的可靠性。
5.AOP是什么?AOP代理方式有哪些?
AOP(Aspect-Oriented Programming)是一种编程范式,它允许你定义横切关注点(cross-cutting concerns),如日志记录、性能监控、事务管理等,并将这些关注点模块化,然后将它们自动应用到应用程序的特定部分,而无需修改这些部分的代码。AOP的目的是提高代码的模块性,降低耦合度,使代码更易于维护和扩展。
在AOP中,关注点(Aspect)是一个模块化的、可重用的模块,它包含了通知(Advice)和切点(Pointcut)两个主要概念。通知是关注点的具体行为,例如在方法执行前后执行的操作,而切点是指在何处应用通知的定义。
AOP代理方式主要有两种:
- 基于代理的AOP(Proxy-Based AOP): 这是最常见的AOP代理方式。在基于代理的AOP中,AOP框架创建一个目标对象(被代理的对象)的代理对象,并将通知织入到代理对象的方法调用中。Spring的AOP默认采用基于代理的AOP实现。基于代理的AOP主要有两种代理方式:
- JDK动态代理:如果目标对象实现了接口,Spring将使用JDK动态代理来创建代理对象。
- CGLIB代理:如果目标对象没有实现接口,Spring将使用CGLIB库来创建代理对象。
- 基于字节码的AOP(AspectJ AOP): 基于字节码的AOP通过直接在类的字节码上织入切面,因此不需要代理对象。这种方式更为强大和灵活,但也更复杂,通常需要使用特定的编译器或者在运行时进行字节码操纵。AspectJ是一个常用的基于字节码的AOP框架。
6.Spring的Bean有哪些创建方式?
在Spring框架中,有多种方式可以创建Bean:
XML配置方式:在XML配置文件中使用
<bean>标签定义Bean的配置信息。<bean id="myBean" class="com.example.MyBean"/>
注解方式:使用注解标记类,然后在配置类中使用
@ComponentScan或者@Component等注解进行扫描和定义Bean。
public class MyBean {
// ...
}Java配置方式:通过Java类配置Bean,使用
@Configuration和@Bean注解。
public class AppConfig {
public MyBean myBean() {
return new MyBean();
}
}工厂方法方式:使用工厂方法创建Bean,即在XML配置文件或者Java配置类中定义工厂方法。
<bean id="myBean" class="com.example.MyBeanFactory" factory-method="createInstance"/>
public class MyBeanFactory {
public static MyBean createInstance() {
return new MyBean();
}
}实例工厂方法方式:使用实例工厂方法创建Bean,需要定义一个实例工厂,然后在XML配置文件或Java配置类中引用该工厂。
<bean id="myBean" factory-bean="myBeanFactory" factory-method="createInstance"/>
public class MyBeanFactory {
public MyBean createInstance() {
return new MyBean();
}
}
以上是常见的Spring Bean创建方式,选择适合项目需求的方式可以提高代码的灵活性和可维护性。
7.Spring的Bean作用域有哪些?
在Spring框架中,Bean的作用域(Scope)定义了Bean实例的生命周期范围。Spring支持以下几种Bean的作用域:
Singleton(单例):在整个Spring容器中,只存在一个Bean实例。无论有多少个Bean的定义,Spring容器都只会创建一个Bean实例,并在需要时返回给每个请求。
<bean id="myBean" class="com.example.MyBean" scope="singleton"/>
Prototype(原型):每次请求Bean时,容器都会创建一个新的Bean实例。每个请求都会得到一个全新的Bean对象。
<bean id="myBean" class="com.example.MyBean" scope="prototype"/>
Request(请求):在一次HTTP请求中,容器会返回同一个Bean实例。该作用域仅在使用Spring Web应用时有效。
<bean id="myBean" class="com.example.MyBean" scope="request"/>
Session(会话):在一个HTTP会话中,容器会返回同一个Bean实例。该作用域同样只在Spring Web应用时有效。
<bean id="myBean" class="com.example.MyBean" scope="session"/>
Global Session(全局会话):在一个全局HTTP会话中,容器会返回同一个Bean实例。该作用域通常用于Portlet应用环境。
<bean id="myBean" class="com.example.MyBean" scope="globalSession"/>
Application(应用):在整个Web应用中,容器会返回同一个Bean实例。
<bean id="myBean" class="com.example.MyBean" scope="application"/>
这些作用域允许你控制Bean实例的生命周期和共享程度,根据需求选择合适的作用域可以提高系统的性能和资源利用率。
8.Spring如何解决Bean循环依赖问题?
在Spring中,Bean的循环依赖是指两个或多个Bean相互依赖,形成一个循环引用的关系。Spring容器默认是不支持循环依赖的,但是它提供了一种机制来处理部分循环依赖情况。
Spring使用了三级缓存解决Bean的循环依赖问题:
- SingletonObjects缓存:Spring容器创建Bean时,会将正在创建的Bean放入SingletonObjects缓存中,标记为正在创建中。
- EarlySingletonObjects缓存:当发现循环依赖时,Spring会将早期暴露的Bean放入EarlySingletonObjects缓存中,标记为早期暴露的Bean。
- SingletonFactories缓存:当Bean创建完成后,会放入SingletonFactories缓存中,表示该Bean可以被其他Bean引用。
Spring容器在创建Bean时,会先从SingletonObjects缓存中查找是否存在Bean的实例。如果存在,直接返回。如果不存在,会先创建一个ObjectFactory,用于生成Bean的实例。然后,将该ObjectFactory放入SingletonFactories缓存中。接着,Spring会递归地创建Bean的依赖关系。如果在创建依赖关系的过程中,发现了循环依赖,Spring会从EarlySingletonObjects缓存中获取早期暴露的Bean,而不是直接创建新的实例。这样,就避免了循环依赖的问题。
需要注意的是,虽然Spring提供了这种机制来处理部分循环依赖情况,但是过多的循环依赖可能会导致系统设计存在问题,因此在设计时,尽量避免复杂的循环依赖关系。