JAVA面试之spring篇
(搜索总结)该篇总结的不多,还会继续添加!
Spring
什么是三级缓存
Spring 使用三级缓存来解决循环依赖问题。三级缓存分别是:
一级缓存(Singleton Objects)
1、存储已经完全初始化完成的单例 Bean。
2、数据结构:Map<String, Object>,键是 Bean 的名称,值是对应的 Bean 实例。
3、作用:当一个 Bean 已经完全初始化后,它会被放入一级缓存,供后续使用。
二级缓存(Early Singleton Objects)
1、存储提前暴露的、尚未完全初始化的 Bean 实例(即“早期引用”)。
2、数据结构:Map<String, Object>,键是 Bean 的名称,值是 Bean 的早期引用。
3、作用:在 Bean 初始化过程中,如果其他 Bean 需要依赖当前 Bean,Spring 会将当前 Bean 的“早期引用”放入二级缓存,以供其他 Bean 使用。
三级缓存(Singleton Factories)
1、存储用于创建早期引用的工厂对象(ObjectFactory)。
2、数据结构:Map<String, ObjectFactory<?>>,键是 Bean 的名称,值是一个工厂对象。
3、作用:当一个 Bean 尚未完全初始化,但需要被其他 Bean 依赖时,Spring 会通过三级缓存中的工厂对象生成一个“早期引用”,并将其放入二级缓存
Spring如何解决循环依赖问题
Spring使用了三级缓存解决了循环依赖的问题。在populateBean()给属性赋值阶段里面Spring会解析你的属性,并且赋值,当发现,A对象里面依赖了B,此时又会走getBean方法,但这个时候,你去缓存中是可以拿的到的。因为我们在对createBeanInstance对象创建完成以后已经放入了缓存当中,所以创建B的时候发现依赖A,直接就从缓存中去拿,此时B创建完,A也创建完,一共执行了4次。至此Bean的创建完成,最后将创建好的Bean放入单例缓存池中。(非单例的实例作用域是不允许出现循环依赖)
BeanFactory和ApplicationContext的区别
1. BeanFactory是Spring里面最低层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能。
2. ApplicationContext应用上下文,继承BeanFactory接口,它是Spring的一各更高级的容器,提供了更多的有用的功能。如国际化,访问资源,载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,消息发送、响应机制,AOP等。
3. BeanFactory在启动的时候不会去实例化Bean,中有从容器中拿Bean的时候才会去实例化。ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化
动态代理的实现方式,AOP的实现方式
1. JDK动态代理:利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
2. CGlib动态代理:利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
3. 区别:JDK代理只能对实现接口的类生成代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,这种通过继承类的实现方式,不能代理final修饰的类。
@Transactional错误使用失效场景
1. @Transactional 在private上:当标记在protected、private、package-visible方法上时,不会产生错误,但也不会表现出为它指定的事务配置。可以认为它作为一个普通的方法参与到一个public方法的事务中。
2. @Transactional 的事务传播方式配置错误。
3. @Transactional 注解属性 rollbackFor 设置错误:Spring默认抛出了未检查unchecked异常(继承自 RuntimeException 的异常)或者 Error才回滚事务;其他异常不会触发回滚事务。
4. 同一个类中方法调用,导致@Transactional失效:由于使用Spring AOP代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。
5. 异常被 catch 捕获导致@Transactional失效。
6. 数据库引擎不支持事务。
Spring中的事务传播机制
1. REQUIRED(默认,常用):支持使用当前事务,如果当前事务不存在,创建一个新事务。eg:方法B用REQUIRED修饰,方法A调用方法B,如果方法A当前没有事务,方法B就新建一个事务(若还有C则B和C在各自的事务中独立执行),如果方法A有事务,方法B就加入到这个事务中,当成一个事务。
2. SUPPORTS:支持使用当前事务,如果当前事务不存在,则不使用事务。
3. MANDATORY:强制,支持使用当前事务,如果当前事务不存在,则抛出Exception。
4. REQUIRES_NEW(常用):创建一个新事务,如果当前事务存在,把当前事务挂起。eg:方法B用REQUIRES_NEW修饰,方法A调用方法B,不管方法A上有没有事务方法B都新建一个事务,在该事务执行。
5. NOT_SUPPORTED:无事务执行,如果当前事务存在,把当前事务挂起。
6. NEVER:无事务执行,如果当前有事务则抛出Exception。
7. NESTED:嵌套事务,如果当前事务存在,那么在嵌套的事务中执行。如果当前事务不存在,则表现跟REQUIRED一样。
Spring中Bean的生命周期
Spring框架中,Bean的生命周期指的是从Bean的创建到销毁的一系列过程。这个过程通常涉及到几个关键的步骤,包括Bean的实例化、属性设置、初始化、使用以及销毁。下面详细介绍Spring中Bean的生命周期的主要阶段和相关的配置方法。
1. Bean的创建
当Spring容器启动时,它会根据配置文件(如XML配置、注解配置等)创建并初始化Bean。
2. Bean的实例化
通过构造器创建:当你在类中定义了构造器,并且Spring的配置文件中使用了<bean>标签或者使用了@Component、@Service、@Repository等注解时,Spring会调用这个类的构造器来创建Bean的实例。
通过静态工厂方法:可以通过配置<bean>标签的class属性指定一个静态工厂方法,该方法返回Bean的实例。
通过实例工厂方法:可以通过配置<bean>标签的factory-bean属性指定一个工厂Bean,然后通过factory-method属性指定一个工厂方法。
3. Bean的属性设置
一旦Bean被实例化,Spring会通过依赖注入(Dependency Injection, DI)来设置Bean的属性。这可以通过构造器注入、Setter方法注入或者字段注入等方式实现。
4. Bean的初始化
在Bean的属性被设置之后,Spring会调用一些特定的方法来进行初始化。这可以通过以下几种方式实现:
实现InitializingBean接口:实现afterPropertiesSet()方法。
使用@PostConstruct注解:在方法上添加@PostConstruct注解,该方法将在依赖注入完成后执行。
自定义初始化方法:可以在XML配置文件中使用init-method属性指定一个初始化方法,或者在类上使用@Bean(initMethod="methodName")指定初始化方法名。
5. Bean的使用
一旦Bean被初始化,它就可以被应用程序中的其他组件使用了。
6. Bean的销毁
当Spring容器关闭时,它会管理Bean的销毁过程。这可以通过以下方式实现:
实现DisposableBean接口:实现destroy()方法。
使用@PreDestroy注解:在方法上添加@PreDestroy注解,该方法将在容器销毁Bean之前执行。
自定义销毁方法:可以在XML配置文件中使用destroy-method属性指定一个销毁方法,或者在类上使用@Bean(destroyMethod="methodName")指定销毁方法名。
如下图可以增深理解
Spring的后置处理器
1. BeanPostProcessor:Bean的后置处理器,主要在bean初始化前后工作。(before和after两个回调中间只处理了init-method)
2. InstantiationAwareBeanPostProcessor:继承于BeanPostProcessor,主要在实例化bean前后工作(TargetSource的AOP创建代理对象就是通过该接口实现)
3. BeanFactoryPostProcessor:Bean工厂的后置处理器,在bean定义(bean definitions)加载完成后,bean尚未初始化前执行。
4. BeanDefinitionRegistryPostProcessor:继承于BeanFactoryPostProcessor。其自定义的方法postProcessBeanDefinitionRegistry会在bean定义(bean definitions)将要加载,bean尚未初始化前真执行,即在BeanFactoryPostProcessor的postProcessBeanFactory方法前被调用。