前言:在面试中spring是打开面试话题的经典,spring懂的多,面试印象分高,但是spring充满了套路,所以在回答spring相关面试题时要回答我们的思路,把面试官带入场景,一起了解透彻"他懂你也懂”。
你好欢迎参加公司面试,请回答一下
spring的启动原理
Spring的启动原理如下:
-
加载Spring配置文件:Spring启动时会加载配置文件,包括XML文件、Annotation、Java Config等方式。
-
创建和组装BeanDefinition:Spring会读取配置文件中定义的Bean,并根据Bean的类型、依赖关系等创建相应的BeanDefinition。
-
扫描和注册Bean:Spring会扫描所有的Bean定义,将其注册到容器中,可以通过@Bean、@Component等注解进行扫描和注册。
-
实例化Bean:Spring会根据BeanDefinition创建相应的Bean实例。
-
注入属性:Spring会对Bean进行属性注入,包括基本类型、集合类型、引用类型等。
-
初始化Bean:Spring会调用Bean的初始化方法(如果有),可以通过@PostConstruct、init-method注解指定初始化方法。
-
提供Bean:Spring会将实例化和初始化后的Bean提供给应用程序,供其使用。
-
销毁Bean:当应用程序关闭时,Spring会调用Bean的销毁方法(如果有),可以通过@PreDestroy、destroy-method注解指定销毁方法。
以上就是Spring的启动原理
spring的生命周期
先创造beanFactory工厂 再创造注解配置读取器
加载bean定义放入beanDefinitionMap中
创建bean对象
选取构造方法准备参数
填充属性 三级缓存
初始化实例 调用Aware接口
填充属性后执行postprocessor
初始化方法 afterPropertiesSet方法
执行bean后置处理器
自定义Init方法
// An highlighted block
public class MyBean implements InitializingBean, DisposableBean {
// 构造函数
public MyBean() {
System.out.println("MyBean 构造函数");
}
// Bean属性注入
private String name;
public void setName(String name) {
this.name = name;
}
// 初始化Bean
public void afterPropertiesSet() throws Exception {
System.out.println("MyBean 初始化 - afterPropertiesSet()");
}
public void initMethod() {
System.out.println("MyBean 初始化 - initMethod()");
}
public void destroyMethod() {
System.out.println("MyBean 销毁 - destroyMethod()");
}
// 销毁Bean
public void destroy() throws Exception {
System.out.println("MyBean 销毁 - destroy()");
}
}
AOP
利用@EnableAspectJAutoProxy注解来开启AOP功能
-
事务开始是利用@EnableAspectJAutoProxy注解来开启AOP功能
-
首先,在创建IOC容器的过程中,会调用refresh()方法来刷新容器,而在刷新容器的过程中有一步是来注册后置处理器的,如下所示:
registerBeanPostProcessors(beanFactory); // 注册后置处理器,在这一步会创建AnnotationAwareAspectJAutoProxyCreator对象, 其实,这一步会为所有后置处理器都创建对象。
刷新容器时注册代理AOP注解后置处理器***
在刷新容器的过程中还有一步是来完成BeanFactory的初始化工作的,如下所示:
finishBeanFactoryInitialization(beanFactory); // 完成BeanFactory的初始化工作。所谓的完成BeanFactory的初始化工作,其实就是来创建剩下的单实例bean的。
- beanFactory初始化后创建单实例bean 业务逻辑类和切面类
很显然,剩下的单实例bean自然就包括MathCalculator(业务逻辑类)和LogAspects(切面类)这两个bean,因此这两个bean就是在这儿被创建的。 创建业务逻辑组件和切面组件
在这两个组件创建的过程中,最核心的一点就是AnnotationAwareAspectJAutoProxyCreator(后置处理器)会来拦截这俩组件的创建过程
- 判断bean增强,创建代理对象
怎么拦截呢?主要就是在组件创建完成之后,判断组件是否需要增强。如需要,则会把切面里面的通知方法包装成增强器,然后再为业务逻辑组件创建一个代理对象。我们也认真仔细探究过了,在为业务逻辑组件创建代理对象的时候,使用的是cglib来创建动态代理的。当然了,如果业务逻辑类有实现接口,那么就使用jdk来创建动态代理。一旦这个代理对象创建出来了,那么它里面就会有所有的增强器。
这个代理对象创建完以后,IOC容器也就创建完了。接下来,便要来执行目标方法了。
- 执行目标方法
此时,其实是代理对象来执行目标方法
使用CglibAopProxy类的intercept()方法来拦截目标方法的执行,拦截的过程如下:
得到目标方法的拦截器链,所谓的拦截器链其实就是每一个通知方法又被包装为了方法拦截器,即MethodInterceptor利用拦截器的链式机制,依次进入每一个拦截器中进行执行
- 最终,整个的执行效果就会有两套:
目标方法正常执行:前置通知→目标方法→后置通知→返回通知
目标方法出现异常:前置通知→目标方法→后置通知→异常通知
事务传播方式
事务传播方式是指在数据库中,一个事务对其他事务的影响如何传播的方式。主要包括以下几种方式:
PROPAGATION_REQUIRED:默认值,表示必须要有一个事务存在,如果当前有事务,则在当前事务中执行,否则开启一个新事务。
PROPAGATION_SUPPORTS:表示当前方法支持事务,如果当前有事务,则在当前事务中执行,否则不开启事务。
PROPAGATION_MANDATORY:表示当前方法必须在一个已经存在的事务中执行,如果当前没有事务,则抛出异常。
PROPAGATION_REQUIRES_NEW:表示当前方法必须开启一个新的事务,如果当前有事务,则将其挂起。
PROPAGATION_NOT_SUPPORTED:表示当前方法不支持事务,如果当前有事务,则将其挂起。
PROPAGATION_NEVER:表示当前方法不能在一个事务中执行,如果当前有事务,则抛出异常。
PROPAGATION_NESTED:表示当前方法开启一个嵌套事务,如果当前有事务,则在嵌套事务中执行,否则开启一个新事务。嵌套事务是外层事务的一部分,只有当外层事务提交时,嵌套事务才会被提交。
不同的事务传播方式适用于不同的场景,可以根据具体的业务需求来选择使用。
spring的注解
Spring框架通过注解的方式来实现对象的依赖注入和控制反转。以下是Spring常见的注解:
- @Autowired:自动装配Bean对象
- @Component:声明当前类是一个组件,Spring会自动扫描并将其加入到容器中进行管理
- @Controller:声明当前类是一个控制器
- @Service:声明当前类是一个服务层组件
- @Repository:声明当前类是一个数据访问层组件
- @Qualifier:当一个接口有多个实现类时,可以使用该注解指定具体要使用的实现类
- @Value:注入配置文件中的属性值
- @Transactional:声明当前方法需要使用事务
以上是Spring常见的注解,还有很多其他注解可以根据需要进行使用。
spring的事务失效的原因
-
没有正确配置事务管理器:Spring事务是通过事务管理器来控制的,如果没有正确配置事务管理器,事务就会失效。
-
事务管理器不支持当前的事务操作:事务管理器的实现依赖于具体的数据访问技术,如果该技术不支持当前的事务操作,事务就会失败。
-
事务方法没有被正确地注解:Spring事务是通过在方法上使用@Transactional注解来控制的,如果该注解未被正确地应用到方法上,事务就会失效。
-
事务传播级别设置错误:Spring事务中有不同的传播级别,如果事务方法被错误地设置为不合适的传播级别,事务就会失效。
-
数据库本身不支持事务:某些数据库可能没有提供事务支持,这将导致Spring事务无法工作。
-
事务方法被错误地调用:如果事务方法被错误地调用,或者没有按照事务要求的方式进行调用,事务就会失效。
·