1.Spring启动流程
Spring框架的启动流程包括以下关键步骤:
-
加载配置文件: Spring会加载应用程序上下文的配置文件,通常是XML文件或基于Java的配置类。
-
创建Bean定义: Spring解析配置文件或注解,创建应用程序中定义的各种Bean。
-
Bean的实例化: Spring根据Bean定义,实例化相应的Bean对象。
-
Bean的依赖注入: Spring通过依赖注入将创建的Bean注入到其他Bean中,建立它们之间的关联关系。
-
Bean的初始化: 如果Bean实现了特定的接口或配置了特定的方法,Spring将调用这些方法进行初始化。
-
应用程序上下文的初始化: Spring初始化应用程序上下文,包括处理一些特殊的Bean,例如事件监听器。
-
完成启动: Spring完成初始化过程,应用程序变得可用。
这是一个简化的描述,实际上Spring的启动流程还涉及到很多细节和扩展点,具体取决于你的应用程序配置和使用的功能。
2.Spring Bean生命周期
Spring Bean 的生命周期包括以下几个阶段:
-
实例化(Instantiation): 当容器启动时,Spring 通过构造函数或工厂方法创建 Bean 实例。
-
设置属性值(Populate Properties): Spring 容器通过依赖注入等方式设置 Bean 的属性值。
-
BeanPostProcessor 的前置处理(PreInitialization): 在 Bean 的初始化前,执行 BeanPostProcessor 接口的 postProcessBeforeInitialization 方法。
-
初始化(Initialization): 在这个阶段,Spring 执行初始化方法,可以通过
init-method
属性指定自定义的初始化方法。 -
BeanPostProcessor 的后置处理(PostInitialization): 在 Bean 的初始化后,执行 BeanPostProcessor 接口的 postProcessAfterInitialization 方法。
-
销毁(Destruction): 当容器关闭时,Spring 执行销毁方法,可以通过
destroy-method
属性指定自定义的销毁方法。
你可以通过实现 InitializingBean
和 DisposableBean
接口,或者通过在 XML 配置中使用 init-method
和 destroy-method
属性来定义初始化和销毁方法。这些方法在 Bean 的生命周期中起到重要的作用。
3.Spring是怎么解决循环依赖的
Spring容器通过使用"提前暴露"(early exposure)的机制来解决循环依赖。这个机制分为三个步骤:
-
实例化Bean: Spring容器首先实例化所有需要的Bean,但在初始化它们之前,Bean的属性都是null。
-
属性注入: 容器继续将Bean的依赖注入到相应的属性中。对于循环依赖,这一步中如果A依赖B,而B又依赖A,Spring会提前暴露A的实例,并将其注入到B中,即使A还没有完全初始化。
-
初始化: 容器调用Bean的初始化方法。在这一步中,如果Bean实现了
InitializingBean
接口或者配置了init-method
,它将执行相应的初始化逻辑。
通过这个机制,Spring能够在解决循环依赖时确保每个Bean都至少在其初始化方法调用之前完成了实例化和属性注入。这样,尽管循环依赖仍然存在,但在初始化阶段,每个Bean都已经有了一个非null的引用,从而避免了潜在的NullPointerException等问题。这种提前暴露的机制是Spring解决循环依赖的关键。
4.Spring IOC
Spring IOC(Inversion of Control)的原理基于容器、Bean和依赖注入,下面是简要的IOC原理:
-
Bean的定义: 在Spring中,应用程序中的对象被称为Bean。这些Bean的定义包含了它们的类、属性以及配置元数据。
-
配置元数据: 配置元数据描述了Bean之间的关系、依赖关系以及其他配置信息。这可以通过XML配置、注解或JavaConfig等方式提供。
-
容器: Spring IOC容器负责管理Bean的生命周期。容器负责实例化、配置和组装Bean,并通过依赖注入将它们连接起来。
-
BeanFactory: 是Spring IoC容器的基础接口,定义了最基本的IoC容器功能,包括Bean的实例化、配置、装配和管理。
-
ApplicationContext: 是BeanFactory的扩展,提供了更多的功能,例如事件传播、AOP(Aspect-Oriented Programming)支持等。
-
依赖注入: 是IOC的核心概念之一。容器负责将Bean所需的依赖关系注入到Bean中,这可以通过构造函数注入、Setter方法注入或字段注入实现。
-
Bean的生命周期: Spring容器管理Bean的整个生命周期,包括实例化、初始化、使用和销毁。开发者可以通过实现特定的接口或者配置初始化和销毁方法来定制Bean的生命周期。
-
AOP(可选): 面向切面编程可以与IOC结合使用,通过代理等机制实现横切关注点的分离,提高模块化和可维护性。
总体而言,Spring IOC通过将对象的创建和管理交给容器来实现对对象之间关系的解耦。这样,开发者只需关注业务逻辑,而不用直接管理对象的生命周期和依赖关系,从而实现了更灵活、可维护和可测试的设计。
5.Spring AOP
在Spring框架中,AOP(Aspect-Oriented Programming)是一种编程范式,它允许你将横切关注点(cross-cutting concerns)从主要的业务逻辑中分离出来。AOP通过定义横切关注点,称为切面(Aspect),并将它们与应用程序的模块集成,从而提高代码的模块化和可维护性。
以下是Spring AOP的关键概念和用法:
-
切面(Aspect): 切面是横切关注点的模块化单元。它包含了一组切入点和通知。通知定义了在切入点执行前、后或异常时要执行的代码。
-
切入点(Join Point): 切入点是应用程序中可能被切面拦截的点。在Spring AOP中,切入点通常表示方法的执行。
-
通知(Advice): 通知是在切入点执行前、后或异常时执行的代码。常见的通知类型有前置通知(Before Advice)、后置通知(After Advice)、返回通知(After Returning Advice)和异常通知(After Throwing Advice)。
-
连接点(Pointcut): 连接点定义了切入点的集合。它是匹配切入点的谓词表达式,用于指定应该在哪里应用切面。
-
织入(Weaving): 织入是将切面应用到目标对象并创建代理对象的过程。织入可以发生在编译时、加载时或运行时。在Spring中,织入通常发生在运行时。
Spring AOP的使用方式包括:
-
基于XML配置: 通过在XML文件中定义切面、切入点和通知,然后将它们与目标Bean关联,实现AOP。
-
基于注解: 使用注解(如
@Aspect
、@Before
、@After
等)声明切面、切入点和通知,使AOP配置更加简洁。 -
基于JavaConfig: 使用Java配置类来定义切面、切入点和通知,代替XML配置。
Spring AOP是一种强大的工具,可用于处理横切关注点,如事务管理、日志记录、性能监测等,而无需直接修改主要的业务逻辑代码。
6.Spring Bean作用域
在Spring框架中,Bean的作用域定义了在容器中创建的Bean实例的生命周期和可见性范围。Spring支持以下几种Bean作用域:
-
Singleton(默认): 单例作用域表示在整个应用程序中只存在一个Bean实例。Spring容器在首次请求时创建该Bean,然后将其缓存起来供后续请求使用。
-
Prototype: 原型作用域表示每次请求都会创建一个新的Bean实例。每次通过容器获取该Bean时,都会返回一个新的实例。
-
Request: 请求作用域仅在Web应用程序中有效。它表示在每个HTTP请求中创建一个新的Bean实例。该作用域仅适用于Web-aware ApplicationContext。
-
Session: 会话作用域也仅在Web应用程序中有效,表示在每个用户会话中创建一个新的Bean实例。同样,该作用域仅适用于Web-aware ApplicationContext。
-
Global Session: 全局会话作用域仅在基于portlet的Web应用程序中有效。它表示在每个全局用户会话中创建一个新的Bean实例。
选择适当的作用域取决于应用程序的需求。默认情况下,Spring的Bean是单例的,这对于大多数情况都是合适的。然而,如果需要每次请求都有一个新的实例,或者在Web应用程序中需要特定的会话范围,那么可以选择其他作用域。
7.Spring Bean的单例模式是怎么实现的
Spring Bean的单例模式原理基于Spring容器对Bean的管理和生命周期的控制。以下是Spring Bean单例模式的实现原理:
-
缓存机制: 当Spring容器启动时,对于定义为单例的Bean,容器会在初始化阶段创建一个实例,并将其缓存起来。这个缓存是一个ConcurrentHashMap,允许多个线程同时访问。
-
默认作用域: 如果在Bean的定义中没有显式指定作用域,Spring默认将其作用域设置为单例(singleton)。
-
首次访问时创建: 当首次请求获取该单例Bean时,容器会检查缓存中是否存在该Bean的实例。如果存在,就直接返回缓存中的实例。如果不存在,容器会创建一个新的实例,并放入缓存。
-
后续访问直接返回: 在容器启动后,后续对该Bean的请求都会直接返回缓存中的实例,而不会重新创建。这就保证了整个应用程序中只有一个该Bean的实例。
-
线程安全性: Spring通过ConcurrentHashMap确保在多线程环境下对Bean的并发访问是安全的。
-
生命周期管理: 单例Bean的生命周期由Spring容器管理,容器负责初始化、销毁以及在需要时重新创建实例。
需要注意的是,虽然单例Bean的使用非常广泛,但在某些情况下,如Bean包含可变状态或依赖于外部资源,可能需要考虑使用其他作用域以避免潜在的问题。此外,应确保单例Bean是线程安全的,因为它可能被多个线程同时访问。
8.ApplicationContext
ApplicationContext
是Spring框架中最常用的接口之一,它是一个高级的容器,用于管理Spring应用中的Bean的生命周期以及它们的配置。
以下是 ApplicationContext
的一些主要特征和功能:
-
Bean管理:
ApplicationContext
负责管理应用程序中的Bean,包括实例化、配置、组装和初始化。 -
生命周期管理: 它管理Bean的整个生命周期,包括实例化、初始化、使用和销毁。
-
配置元数据:
ApplicationContext
通过配置元数据(XML配置、注解、JavaConfig等)描述了Bean之间的关系、依赖关系和其他配置信息。 -
资源访问: 它提供了一种方便的方式来访问应用程序中的资源,如文件、数据库、远程服务等。
-
事件传播:
ApplicationContext
支持事件传播机制,允许应用程序中的组件发布和监听事件。 -
AOP支持: 通过
ApplicationContext
,可以方便地使用AOP(面向切面编程)功能。 -
国际化支持: 支持国际化,使应用程序易于适应不同的语言和区域设置。
-
Web应用程序支持: 在Web应用程序中,
ApplicationContext
可以是WebApplicationContext
的实例,具有额外的功能,如在请求级别管理Bean(request scope)、会话级别管理Bean(session scope)等。
ApplicationContext
的实现类有多种,其中一些常见的包括:
-
ClassPathXmlApplicationContext: 从类路径加载配置文件创建
ApplicationContext
。 -
FileSystemXmlApplicationContext: 从文件系统路径加载配置文件创建
ApplicationContext
。 -
AnnotationConfigApplicationContext: 使用JavaConfig配置类创建
ApplicationContext
。 -
GenericWebApplicationContext: 用于Web应用程序的泛型
ApplicationContext
。
使用 ApplicationContext
时,开发者可以方便地获取和管理Bean,利用Spring框架的功能来提高应用程序的模块化、可维护性和可测试性。
9.Spring事务分类
Spring框架提供了多种事务管理机制,其中最常见的是声明式事务管理和编程式事务管理。以下是Spring框架中常用的事务管理方式:
-
声明式事务管理:
- 基于XML配置: 使用
<tx:advice>
和<aop:config>
元素配置事务属性。 - 基于注解: 使用
@Transactional
注解在方法或类级别声明事务。
- 基于XML配置: 使用
-
编程式事务管理:
- 使用
TransactionTemplate
: 通过TransactionTemplate
类编程式地管理事务。 - 使用
PlatformTransactionManager
接口: 直接使用PlatformTransactionManager
接口编程式地管理事务。
- 使用
-
JTA(Java Transaction API)事务管理:
- Spring提供对JTA的支持,可以使用JTA事务管理器进行分布式事务管理。
- 使用
JtaTransactionManager
或BitronixTransactionManager
等实现。
-
混合事务管理: 在一个应用程序中,可以同时使用声明式和编程式事务管理,根据需求选择合适的方式。
-
使用
@EnableTransactionManagement
: 通过在配置类上使用@EnableTransactionManagement
注解,启用Spring的事务管理功能。这通常与@Transactional
注解一起使用。
这些事务管理方式允许开发者根据应用程序的需求选择适当的事务管理策略。声明式事务管理通常更为常用,因为它提供了更清晰的配置和更简洁的代码。然而,编程式事务管理和JTA事务管理在某些复杂的场景下可能更为灵活。
10.Spring事务的传播机制
Spring事务的传播机制定义了在一个事务方法执行期间,另一个事务方法被调用时,这两个事务之间的交互规则。Spring提供了七种不同的事务传播行为,可以通过 @Transactional
注解或XML配置来指定。
以下是Spring事务传播机制的常见选项:
-
REQUIRED(默认): 如果当前存在事务,则加入该事务;如果不存在事务,则创建一个新的事务。这是最常见的传播行为,也是默认的行为。
-
SUPPORTS: 如果当前存在事务,则加入该事务;如果不存在事务,则以非事务的方式执行。适用于不需要强制事务的场景。
-
MANDATORY: 如果当前存在事务,则加入该事务;如果不存在事务,则抛出异常。适用于需要强制存在事务的场景。
-
REQUIRES_NEW: 总是创建一个新的事务,如果当前存在事务,则将其挂起。适用于需要独立事务执行的场景。
-
NOT_SUPPORTED: 以非事务的方式执行操作,如果当前存在事务,则将其挂起。适用于不需要事务支持的场景。
-
NEVER: 以非事务的方式执行操作,如果当前存在事务,则抛出异常。适用于不允许在事务中执行的场景。
-
NESTED: 如果当前存在事务,则在嵌套事务内执行;如果不存在事务,则创建一个新的事务。嵌套事务是外部事务的一部分,它有自己的保存点,可以独立地进行提交或回滚。
选择适当的传播机制取决于业务需求和应用程序的事务处理策略。
11.Spring事务什么情况失效
-
注解@Transactional配置的方法非public权限修饰;
- 注解@Transactional所在类非Spring容器管理的bean;
- 注解@Transactional所在类中,注解修饰的方法被类内部方法调用;
- 业务代码抛出异常类型非RuntimeException,事务失效;
- 业务代码中存在异常时,使用try…catch…语句块捕获,而catch语句块没有throw new RuntimeExecption异常;(最难被排查到问题且容易忽略)
- 注解@Transactional中Propagation属性值设置错误即Propagation.NOT_SUPPORTED(一般不会设置此种传播机制)
- mysql关系型数据库,且存储引擎是MyISAM而非InnoDB,则事务会不起作用(基本开发中不会遇到);
12.Spring常用注解
在Spring框架中,有许多常用的注解,它们用于简化配置和提供更强大的功能。以下是一些常见的Spring注解:
-
@Component:用于标识一个类为Spring容器管理的组件。
-
@Controller:用于标识一个类为Spring MVC控制器。
-
@Service:用于标识一个类为服务层组件。
-
@Repository:用于标识一个类为数据访问层组件,通常与持久化相关。
-
@Autowired:用于自动装配Bean,可以标注在构造方法、成员变量、Setter方法上。
-
@Qualifier:结合@Autowired使用,用于指定注入的Bean名称。
-
@Value:用于注入简单的数值、字符串等。
-
@Configuration:声明当前类是一个配置类,相当于传统的XML配置文件。
-
@Bean:用于声明一个Bean,由方法返回对象,同时可以指定Bean的名称。
-
@Scope:用于指定Bean的作用域,如单例、原型等。
-
@RequestMapping:用于映射请求URL和Controller方法。
-
@PathVariable:用于将URL模板中的变量绑定到方法参数。
-
@RequestParam:用于将请求参数绑定到方法参数。
-
@ResponseBody:用于指示方法返回值直接作为响应体,而不是视图名称。
-
@ResponseStatus:用于指定响应的HTTP状态码和原因。
这些注解是Spring开发中常用的一部分,能够简化配置、提高开发效率。