面试7_Spring
参考资料
- 优快云_Spring面试题
- 简书_Spring常见面试题
- 知乎_Spring面试题IOC系列
- 优快云_面试题 Spring IOC相关
- 有道云_面试3_Spring
- SpringBoot自动配置注解原理解析
- Cglib和jdk动态代理的区别
- Spring事务实现机制
一、spring概述
1. Spring组成模块
核心容器
(Core Container)AOP
(Aspect Oriented Programming)和设备支持(Instrmentation)数据访问与集成
(Data Access/Integeration)Web
消息
(Messaging)Test
2. Spring使用的设计模式
工厂模式
:BeanFactory,用来创建对象的实例;单例模式
:Bean默认为单例模式。代理模式
:AOP功能模板方法
:解决代码重复的问题观察者模式
3. Spring事件类型
上下文更新事件
:在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。上下文开始事件
:当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。上下文停止事件
:当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。上下文关闭事件
:当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。请求处理事件
:在Web应用中,当一个http请求(request)结束触发该事件。
4. 核心容器模块详情
BeanFactory 是 任何以spring为基础的应用的核心。是工厂模式的一个实现,提供了控制反转功能。
二、IOC
1. IOC、DI
- IOC是
控制反转
,依赖对象的创建及维护是由外部容器负责的
- DI也就是
依赖注入
,实现IOC的方式之一
。运行期间由外部容器
动态地将依赖对象注入到组件
中
2. IOC作用
- 管理
对象的创建
,依赖关系的维护
解耦
,由容器去维护具体的对象托管类产生过程
- 容易测试,
单元测试不再需要单例和JNDI查找机制
。 - 支持加载服务时的
饿汉式初始化
和懒加载
3. IOC支持的功能
依赖注入
依赖查找
自动装配
- 支持集合
- 指定初始化方法和销毁方法
4. IOC实现原理
**工厂模式
+ 反射机制
5. IOC初始化
- Resource 定位
- Bean 注册
- BeanDefinitionReader
读取、解析bean的配置信息
- 將bean的配置信息
转换为 BeanDefinition 对象
,并保存在内存中 - registerBeanDefinition 将 BeanDefinition
注入到 Bean定义注册表
。
- BeanDefinitionReader
- Bean 实例化
- 遍历Bean定义注册表,调用
getBean方法
拿出Class对象通过反射机制
进行实例化
,保存在Bean缓存池
中。
- 遍历Bean定义注册表,调用
6. DI实现方式、区别
接口
注入Setter
方法注入构造器
注入
区别 | 构造函数注入 | setter 注入 |
---|---|---|
部分注入 | 没有 | 有 |
覆盖 setter 属性 | 不会 | 会 |
任意修改会创建新实例 | 会 不会 | |
适用范围 | 适用于设置很多属性 | 适用于设置少量属性 |
7. BeanFactory、 FactoryBean区别
- BeanFactory是
IOC的底层容器
,用于管理Bean
,通过getBean
进行依赖查找
,若 Bean 未初始化,则从底层查找或构建。 - FactoryBean是特殊的
Bean
,需注册到 IoC 容器,通过容器的getBean
获取FactoryBean#getObject()
方法的内容,作用主要是自定义实际Bean的产生过程
。
8. BeanFactory、ApplicationContext区别
-
ApplicationContext实现BeanFactory接口,提供更多功能:
- 提供
国际化
的消息访问, MessageSource 统一的资源文件访问
方式, ResourceLoader事件传递
:通过实现ApplicationContextAware接口。消息发送、响应机制(ApplicationEventPublisher)Bean的自动装配
- 载入多个(有继承关系)上下文 ,使得
每一个上下文都专注于一个特定的层次
。各种不同应用层的Context实现
- 提供
-
区别
区别 | BeanFactory | ApplicationContext |
---|---|---|
·加载方式· | 懒加载 ,使用时才被实例化 | 启动时实例化 可以为Bean配置lazy-init=true来让Bean延迟实例化 能尽早的发现系统中的 配置问题 |
·创建方式· | 编程式 | 编程式 + 声明式 |
·注册方式· BeanPostProcessor、BeanFactoryPostProcessor的使用 | 手动注册 | 自动注册 |
9. Bean 自动装配
自动装配类型
no
:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean。byName
:通过bean的名称进行自动装配,如果一个bean的 property 与另一bean 的name 相同,就进行自动装配。byType
:通过参数的数据类型进行自动装配。constructor
:利用构造函数进行装配,并且构造函数的参数通过byType进行装配。autodetect
:自动探测,如果有构造方法,通过 construct的方式自动装配,否则使用 byType的方式自动装配。
自动装配注解原理
- @SpringBootApplication:启动类
- @EnableAutoConfiguration:开启自动配置类
- @AutoConfigurationPackage:自动配置包
- 返回当前主程序类的
同级以及子级
的包组件
- 返回当前主程序类的
- @Import:导入自动配置的组件
- 继承了 ImportSelector,来加载 **
META-INF/spring.factories
**外部文件
- 继承了 ImportSelector,来加载 **
- @AutoConfigurationPackage:自动配置包
- @EnableAutoConfiguration:开启自动配置类
10. Bean 生命周期
bean实例化
;属性填充
:将值和bean的引用注入到bean对应的属性中;检查Aware相关接口并设置相关依赖
;BeanPostProcessor前置处理
;- 调用
InitializingBean接口的afterPropertiesSet()
方法; BeanPostProcessor后置处理
;bean准备就绪
;销毁bean
- 调用
DisposableBean的destroy()
方法。 调用定制的销毁方法
- 调用
11. Bean 作用域
- bean标签里面有
scope属性
用于设置单实例还是多实例
- singleton
加载 spring 配置文件时
就会创建单实例对象 - prototype 在
调用getBean 方法时
创建多实例对象
作用域 | 说明 |
---|---|
singleton | 在 Spring 容器中仅存在一个 bean 的实例,bean 以单例形式存在。 这是99默认的作用域99 |
prototype | 每次从容器中获取 bean 时,都将生成一个新的实例, 即相当于每次都执行 new xxxBean() |
request | 在 HTTP 请求 (request) 的完整生命周期中,将创建并使用单个实例。 该作用域仅适用于 WebApplicatonContext 环境 |
session | 在 HTTP 会话 (session) 的完整生命周期中,将创建并使用单个实例。 该作用域仅适用于 WebApplicationContext 环境 |
global-Session | 在全局的 HTTP 会话 (session) 的完整生命周期中,将创建并使用单个实例。 该作用域仅适用于 WebApplicationContext 环境,且通常只能用在 Portlet 环境中。 |
12. 单例bean线程安全?
- 单例bean不是线程安全的
- 大部分时候 spring bean 无状态的,某种程度上来说 bean 也是安全的
- bean 有状态的话,要开发者去保证线程安全,
改变 bean 的作用域
,把“singleton”变更为“prototype”- 有状态就是有数据存储功能。
- 无状态就是不会保存数据。
12. 如何保证 Controller 并发的安全
- Controller对象是单例的,Spring 多线程请求过来调用的Controller对象都是同一个
使用ThreadLocal来保存类变量
- 添加注解
@Scope(“prototype”)
让Controller以多实例形式创建
13. 如何解决循环依赖
- Spring
三个缓存区
setter方法注入
形成的单例循环依赖能够解决- 通过ObjectFactory提前曝光,
放到三级缓存
14. 为什么需要三个缓存,而不是直接两个缓存?
保证单例
- 使用AOP情况下,只使用一级、三级缓存无法解决循环依赖的问题
- 每次从三级缓存中拿到singleFactory对象,在执行getObject()时会产生新的代理对象
产生的代理对象放到二级缓存中
。然后把这个三级缓存删除掉,这样以后就使用二级缓存的同一个对象。
15. MVC 的工作原理
三、AOP
1. AOP的实现原理
静态代理
的代表为AspectJ
动态代理
则以 Spring AOP 为代表
2. JDK动态代理、cglib动态代理
- JDK动态代理,
拦截器+反射
,Proxy利用InvokeHandler来生成目标类的代理对象
,只能对实现了接口的类生成代理。 - cglib动态代理,通过
修改其字节码生成子类来处理
,覆盖其中特定方法并添加增强代码。
3. 关注点和横切关注的区别
- 关注点是
应用中一个模块的行为
,一个关注点可能会被定义成一个我们想实现的一个功能。 - 横切关注点是一个关注点,此关注点是
整个应用都会使用的功能
,并影响整个应用
4. 通知类型
- 前置通知(Before):在目标方法被调用之前调用通知功能;
- 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
- 返回通知(After-returning ):在目标方法成功执行之后调用通知;
- 异常通知(After-throwing):在目标方法抛出异常后调用通知;
- 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
- 同一个aspect,不同
advice的执行顺序
- around before advice
- before advice
- target method 执行
- around after advice
- after advice
- afterReturning | afterThrowing:异常发生
四、Spring数据访问
1. 数据持久化、多数据源配置
-
数据持久化
- Spring Boot整合JdbcTemplate
- Spring Boot整合MyBatis
- Spring Boot整合Jpa
-
MyBatis多数据源配置
pom.xml
修改druid-spring-boot-starter
依赖application.yml
添加数据库信息编写配置文件
来处理各自的mapper- 配置
DataSourceConfig
配置多个MyBatisConfig来对应不同的mapper
- 配置
2. 分页
五、Spring事务
1. 事务管理类型
编程式事务管理
声明式事务管理
:将代码和事务管理分离。
2. Spring 事务管理机制
- spring 事务底层是基于
数据库事务
和aop 机制
实现的 - spring 会对使用了@Transaction 注解的 Bean,通过 aop 的方式创建一个代理对象
- 当调用代理对象的方法的时候,会进入到 spring 事务拦截器中,这里面会先判断方法上是否有@Transaction 注解
- 如果加了,那么会利用事务管理器创建一个数据库连接
- 并且修改数据库的连接的 autocommit 属性为 false,也就是禁止此链接自动提交,这是实现 spring 事务非常重要的一步
- 然后将会将这个连接放在 ThreadLocal 中,这里也是一个关键点,最终执行 sql 的连接需要和 spring 事务中的连接是同一个连接,就通过 ThreadLocal 共享连接的
- 然后执行当前业务方法,方法中准备执行 sql,通常我们会使用 orm 框架去执行 sql,比如我们常用的 mybatis、hibernate 等,当这些框架执行 sql 的时候,会从上面的 ThreadLocal 中拿到刚才创建的数据库连接,然后使用这个连接去执行 sql
- 执行完业务方法之后,如果没有异常则 spring 会直接提交事务
- 如果出现了异常,且这个异常是需要回滚的,则会回滚事务,否则仍然会提交事务
- spring 事务中的隔离级别其实对应的就是数据库的隔离级别,spring 会通过数据库的连接来设置隔离级别
- spring 事务的传播机制是基于数据库连接来做的,一个数据库连接对应一个事务,如果传播机制配置为需要新开一个事务,那么实际上就会新建一个连接,在这个新的连接上去执行 sql
- 上面说的这个过程稍微是简化版的,如果大家有兴趣的话,可以去研究一下@EnableTransactionManagement,这个注解的作用是启用
spring 事务管理的功能,会对需要 spring 管理事务的 bean 创建代理对象,上面还提到了 spring 事务拦截器,也是非常重要的一个点,他会拦截事务方法的执行,事务的所有操作都是在这个拦截器中完成的,对应的类是:TransactionInterceptor
3. Spring 事务传播行为及其作用
传播类型 | 作用 |
---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。 |
PROPAGATION_MANDATORY | 支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 创建新事务,无论当前存不存在事务,都创建新事务。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。 |
4. spring 事务隔离级别
ISOLATION_DEFAULT:默认值
,用底层数据库的设置隔离级别,数据库设置的是什么我就用什么;- ISOLATION_READ_UNCOMMITTED:未提交读,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读);
- ISOLATION_READ_COMMITTED:提交读,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读),SQL server 的默认级别;
- ISOLATION_REPEATABLE_READ:可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读),MySQL 的默认级别;
- ISOLATION_SERIALIZABLE:序列化,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读。
六、Spring注解
1. @Component, @Controller, @Repository, @Service
将 java 类标记为 bean,@Component为父接口
2. @Required
bean的属性必须在配置时设置
,通过一个bean定义的显式的属性值或通过自动装配
3. @Autowired、@Resource的区别
-
@Autowired可用于:
构造函数、成员变量、Setter方法
-
@Autowired默认按
类型
装配注入,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。 -
@Resource默认按
名称
装配注入,找不到才按类型
装配注入。
4. @Qualifier
创建多个相同类型的 bean 并希望仅使用属性装配其中一个 bean 时,使用@Qualifier 注解和 @Autowired 通过指定应装配确切的 bean 来消除歧义
5. @RequestMapping
- 将特定 HTTP 请求方法映射到将处理相应请求的控制器中的特定类/方法。