Spring 面试总结(值得收藏)

(1)说一说什么是IOC

spring是一个ioc容器,容器就是放数据的,ioc容器实际上就是个map(key,value),里面存的是各种对象(在xml里配置的bean节点||repository、service、controller、component),在项目启动的时候会读取配置文件里面的bean节点,根据全限定类名使用反射new对象放到map里;扫描到打上上述注解的类还是通过反射new对象放到map里。

这个时候map里就有各种对象了,接下来我们在代码里需要用到里面的对象时,再通过DI注入(autowired、resource等注解,xml里bean节点内的ref属性,项目启动的时候会读取xml节点ref属性根据id注入,也会扫描这些注解,根据类型或id注入;id就是对象名)


(2) 说一说IOC容器的创建过程

首先需要个BeanFactory 容器,如果是xml配置的方式还需要一个(BeanDefinitionReader )资源解析器,然后呢就可以通过 BeanDefinitionReader 加载 XML 配置文件资源,Spring 会通过 XmlBeanDefinitionReader 加载该 XML 文件,获取该 Resource 资源的,解析的过程中会先进行校验,将 XML 文件资源转换成 Document 对象,根据 Document 对象解析 <beans /> 标签,遍历所有的子标签,其中 <bean /> 会被解析出一个 GenericBeanDefinition 对象,然后进行注册到beanDefinitionMap中

如果是<context:component-scan /> 、<context:annotation-config />这些非默认命名空间的标签,就会找到对应的NamespaceHandler 对象进行解析,最后,然后呢这些标签都有一个对应的BeanDefinitionParser 解析器,找到解析器就会调用一个parse方法进行解析,比如
ComponentScanBeanDefinitionParser解析器就是专门解析component-scan标签的,会根据basePackages 这个属性扫描指定包路径下的 BeanDefinition(带有 @Component 注解或其派生注解的 Class 类),然后注册到beanDefinitionMap中


(3)简述 FactoryBean

FactoryBean 关联一个 Bean 对象,提供了一个 getObject() 方法用于返回这个目标 Bean 对象,FactoryBean 对象在被依赖注入或依赖查找时,实际得到的 Bean 就是通过 getObject() 方法获取到的目标类型的 Bean 对象。如果想要获取 FactoryBean 本身这个对象,在 beanName 前面添加 & 即可获取。

我们可以通过 FactoryBean 帮助实现复杂的初始化逻辑,例如在 Spring 继集成 MyBatis 的项目中,Mapper 接口没有实现类是如何被注入的?其实 Mapper 接口就是一个 FactoryBean 对象,当你注入该接口时,实际的到的就是其 getObject() 方法返回的一个代理对象,关于数据库的操作都是通过该代理对象来完成。


(4) ObjectFactory、FactoryBean 和 BeanFactory 的区别?

ObjectFactory、FactoryBean 和 BeanFactory 均提供依赖查找的能力。

ObjectFactory 提供的是延迟依赖查找,想要获取某一类型的 Bean,需要调用其 getObject() 方法才能依赖查找到目标 Bean 对象。ObjectFactory 就是一个对象工厂,想要获取该类型的对象,需要调用其 getObject() 方法生产一个对象。

FactoryBean 不提供延迟性,在被依赖注入或依赖查找时,得到的就是通过 getObject() 方法拿到的实际对象。FactoryBean 关联着某个 Bean,可以说在 Spring 中它就是某个 Bean 对象,无需我们主动去调用 getObject() 方法,如果想要获取 FactoryBean 本身这个对象,在 beanName 前面添加 & 即可获取。

BeanFactory 则是 Spring 底层 IoC 容器,里面保存了所有的单例 Bean,ObjectFactory 和 FactoryBean 自身不具备依赖查找的能力,能力由 BeanFactory 输出。


(5)BeanDefinition 是什么?

BeanDefinition 是 Spring Bean 的“前身”,其内部包含了初始化一个 Bean 的所有元信息,在 Spring 初始化一个 Bean 的过程中需要根据该对象生成一个 Bean 对象并进行一系列的初始化工作。


(6)@Bean 的处理流程是怎样的?

Spring 应用上下文生命周期,在 BeanDefinition(@Component 注解、XML 配置)的加载完后,会执行所有
BeanDefinitionRegistryPostProcessor 类型的处理器,Spring 内部有一个 ConfigurationClassPostProcessor 处理器,它会对所有的配置类进行处理,解析其内部的注解(@PropertySource、@ComponentScan、@Import、@ImportResource、@Bean),其中 @Bean 注解标注的方法会生成对应的 BeanDefinition 对象并注册。


(7)Spring中Bean的生命周期

他主要分几个阶段:

元信息阶段:通过面向资源(XML 或 Properties)、注解、 API 进行配置,然后对配置信息进行解析,解析成 BeanDefinition 对象,该对象包含定义 Bean 的所有信息,用于实例化一个 Spring Bean,然后将 BeanDefinition 配置元信息 保存至 BeanDefinitionRegistry 的 ConcurrentHashMap 集合中

合并阶段:定义的 Bean 可能存在层次性关系,则需要将它们进行合并,存在相同配置则覆盖父属性,BeanDefinition ,最终生成一个 RootBeanDefinition 对象

实例化阶段:首先的通过类加载器加载出一个 Class 对象,通过这个 Class 对象的构造器创建一个实例对象,在实例化阶段 Spring 提供了实例化前后两个扩展点(
InstantiationAwareBeanPostProcessor 
postProcessBeforeInstantiation方法和postProcessAfterInstantiation)这个可以用来生成代理对象,不过你直接返回代理对象的话,他直接执行postProcessAfterInitialization方法后就直接return了

在 Spring 实例化后,会执行
InstantiationAwareBeanPostProcessor的postProcessProperties需要对其相关属性进行赋值,注入依赖的对象,依赖注入的实现通过
CommonAnnotationBeanPostProcessor(@Resource、@PostConstruct、@PreDestroy)和
AutowiredAnnotationBeanPostProcessor(@Autowired、@Value)两个处理器实现的。

Aware 接口回调阶段:如果 Spring Bean 是 Spring 提供的 Aware 接口类型(例如 BeanNameAware),这里会进行接口的回调,注入相关对象(例如 beanName)

初始化阶段前置处理:就会执行BeanPostProcessor的
postProcessBeforeInitialization像你实现了ApplicationContextAware接口的setApplicationContext()方法, InitializingBean 接口的afterPropertiesSet()方法,标注了@PostConstruct ,都会在这个阶段执行

执行顺序:先处理 Aware 接口的回调,@PostConstruct 注解标注的方法的调用,然后执性法InitializingBean接口的回调,最后执行init-method的自定义方法

初始化阶段后置处理:执行所有 BeanPostProcessor 
postProcessAfterInitialization 方法 ,例如 Spring 内部有一个
ApplicationListenerDetector 处理器,如果是单例 Bean 且为 ApplicationListener 类型,则添加到 Spring 应用上下文,和 Spring 事件相关

初始化完成阶段,在所有的 Bean(不是抽象、单例模式、不是懒加载方式)初始化后,Spring 会再次遍历所有初始化好的单例 Bean 对象,如果是
SmartInitializingSingleton 类型则调用其 afterSingletonsInstantiated() 方法

销毁阶段:当 Spring 应用上下文关闭或者你主动销毁某个 Bean 时则进入 Spring Bean 的销毁阶段,执行顺序:@PreDestroy 注解的销毁动作、实现了 DisposableBean 接口的 Bean 的回调、destroy-method 自定义的销毁方法。


(8) Spring的依赖循环是如何解决的?

Spring使用了三级缓存解决了循环依赖的问题。在populateBean()给属性赋值阶段里面Spring会解析你的属性,并且赋值,当发现,A对象里面依赖了B,此时又会走getBean方法,但这个时候,你去缓存中是可以拿的到的。因为我们在对createBeanInstance对象创建完成以后已经放入了缓存当中,所以创建B的时候发现依赖A,直接就从缓存中去拿,此时B创建完,A也创建完,一共执行了4次。至此Bean的创建完成,最后将创建好的Bean放入单例缓存池中。(非单例的实例作用域是不允许出现循环依赖)

生成代理对象产生的循环依赖:使用@Lazy注解,延迟加载,使用@DependsOn注解,指定加载先后关系修改文件名称,改变循环依赖类的加载顺序使用

@DependsOn产生的循环依赖,这类循环依赖问题要找到@DependsOn注解循环依赖的地方,迫使它不循环依赖就可以解决问题。

多例循环依赖:这类循环依赖问题可以通过把bean改成单例的解决。

构造器循环依赖:这类循环依赖问题可以通过使用@Lazy注解解决。


(9)什么是三级缓存?

第一级缓存:singletonObjects,一级缓存,用于保存实例化、注入、初始化完成的bean实例

第二级缓存:earlySingletonObjects 早期提前暴露的对象缓存,用于保存实例化完成的bean实例。(属性还没有值对象也没有被初始化)

第三级缓存:singletonFactories单例对象工厂缓存,用于保存bean创建工厂,以便于后面扩展有机会创建代理对象。


(10)Spring的后置处理器

BeanPostProcessor

Bean的后置处理器,主要在bean初始化前后工作,比如
ApplicationContextAwareProcessor类就是实现了BeanPostProcessor接口,Spring很多类都会实现ApplicationContextAware接口的,这个ApplicationContextAwareProcessor类就会去比较你是不是实现了ApplicationContextAware接口,然后把applicationContext给你传进来。还有一些比如
BeanValidationPostProcessor类主要是用来为bean进行校验操,
AutowiredAnnotationBeanPostProcessor处理标注了@Autowired注解的变量或方法,
CommonAnnotationBeanPostProcessor 会先解析出 @Resource 注解


InstantiationAwareBeanPostProcessor:接口继承BeanPostProcessor接口,它内部提供了3个方法,再加上BeanPostProcessor接口内部的2个方法,所以实现这个接口需要实现5个方法。
InstantiationAwareBeanPostProcessor接口的主要作用在于目标对象的实例化过程中需要处理的事情,包括实例化对象的前后过程以及实例的属性设置


postProcessBeforeInstantiation方法是最先执行的方法,它在目标对象实例化之前调用,该方法的返回值类型是Object,我们可以返回任何类型的值。由于这个时候目标对象还未实例化,所以这个返回值可以用来代替原本该生成的目标对象的实例(比如代理对象)。如果该方法的返回值代替原本该生成的目标对象,后续只有postProcessAfterInitialization方法会调用,其它方法不再调用;否则按照正常的流程走


postProcessAfterInstantiation方法在目标对象实例化之后调用,这个时候对象已经被实例化,但是该实例的属性还未被设置,都是null。因为它的返回值是决定要不要调用postProcessPropertyValues方法的其中一个因素(因为还有一个因素是mbd.getDependencyCheck());如果该方法返回false,并且不需要check,那么postProcessPropertyValues就会被忽略不执行;如果返回true, postProcessPropertyValues就会被执行

postProcessProperties方法对属性值进行修改(这个时候属性值还未被设置,但是我们可以修改原本该设置进去的属性值)。如果
postProcessAfterInstantiation方法返回false,该方法可能不会被调用。可以在该方法内对属性值进行修改

BeanFactoryPostProcessor

BeanFactoryPostProcessor:Bean工厂的后置处理器,在bean定义(bean definitions)加载完成后,bean尚未初始化前执行。


BeanDefinitionRegistryPostProcessor:继承于BeanFactoryPostProcessor。其自定义的方法
postProcessBeanDefinitionRegistry会在bean定义(bean definitions)将要加载,bean尚未初始化前真执行,即在BeanFactoryPostProcessor的postProcessBeanFactory方法前被调用。


(11)@Autowired 注解的实现原理

原理就是
AutowiredAnnotationBeanPostProcessor处理器找到注解标注的字段(或方法),创建对应的注入元信息对象,然后根据该元信息对象进行注入(反射机制),底层都会通过
DefaultListableBeanFactory#resolveDependency 方法实现的,找到符合条件的 Bean(根据类型),然后筛选出最匹配的那个依赖注入对象。


(12)BeanFactory和ApplicationContext的联系和区别

1.BeanFactory是Spring里面最低层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能。

2.ApplicationContext应用上下文,继承BeanFactory接口,它是Spring的一各更高级的容器,提供了更多的有用的功能。如国际化,访问资源,载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,消息发送、响应机制,AOP等。

3.BeanFactory在启动的时候不会去实例化Bean,中有从容器中拿Bean的时候才会去实例化。ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化

ApplicationContext


什么是 AOP?

简述 AOP 的使用场景?

讲讲 CGLIB 动态代理?


讲讲 JDK 动态代理?

JDK 动态代理和 CGLIB 动态代理有什么不同?


说一说Spring的@Transactional

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。

8.NESTED:嵌套事务,如果当前事务存在,那么在嵌套的事务中执行。如果当前事务不存在,则表现跟REQUIRED一样。


其他

@Configuration

告诉Spring这是一个配置类,代替传统xml的方式配置bean

@ComponentScan

通过指定的包或其子包中的类上标注了@Repository、@Service、@Controller、@Component注解的类都会被扫描到,并将这个类注入到Spring容 器中。

@Bean

代替传统XMLbean的配置方式

@Scope

设置组件的作用域

@Conditional

@Conditional注解可以按照一定的条件进行判断,满足条件向容器中注册bean,不满足条件就不向容器中注册bean

@Lazy

懒加载,就是Spring容器启动的时候,先不创建对象,在第一次使用(获取)bean的时候再来创建对象,并进行一些初始化。

@Import

向Spring容器中导入一个组件

@Primary

在存在相同的组件情况下,通过@Primary指定组件的优先级

@PropertySource,@PropertySources

@PropertySource注解是Spring 3.1开始引入的配置类注解。通过@PropertySource注解可以将properties配置文件中的key/value存储到Spring的Environment中,Environment接口提供了方法去读取配置文件中的值

@Profile

在容器中如果存在同一类型的多个组件,那么可以使用@Profile注解标识要获取的是哪一个bean。

@Component, @Controller, @Repository, @Service 有何区别?

@Component:这将 java 类标记为 bean。它是任何 Spring 管理组件的通用构造型。spring 的组件扫描机制现在可以将其拾取并将其拉入应用程序环境中。

@Controller:这将一个类标记为 Spring Web MVC 控制器。标有它的 Bean 会自动导入到 IoC 容器中。

@Service:此注解是组件注解的特化。它不会对 @Component 注解提供任何其他行为。您可以在服务层类中使用 @Service 而不是 @Component,因为它以更好的方式指定了意图。

@Repository:这个注解是具有类似用途和功能的 @Component 注解的特化。它为 DAO 提供了额外的好处。它将 DAO 导入 IoC 容器,并使未经检查的异常有资格转换为 Spring DataAccessException。

@Required 注解有什么作用

这个注解表明bean的属性必须在配置的时候设置,通过一个bean定义的显式的属性值或通过自动装配,若@Required注解的bean属性未被设置,容器将抛出
BeanInitializationException。示例:

@Autowired 注解有什么作用

@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。@Autowired 注解提供了更细粒度的控制,包括在何处以及如何完成自动装配。它的用法和@Required一样,修饰setter方法、构造器、属性或者具有任意名称和/或多个参数的PN方法。

@Resource

@Autowired和@Resource之间的区别

@Autowired可用于:构造函数、成员变量、Setter方法

@Autowired和@Resource之间的区别

@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。

@Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。

@Qualifier 注解有什么作用

当您创建多个相同类型的 bean 并希望仅使用属性装配其中一个 bean 时,您可以使用@Qualifier 注解和 @Autowired 通过指定应该装配哪个确切的 bean 来消除歧义。

学习更多JAVA知识与技巧,关注与私信博主(学习)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值