1. 说说 Spring 里用到了哪些设计模式?
单例模式
:Spring 中的 Bean 默认状况下都是单例的。无需多说。
工厂模式
:工厂模式次要是通过 BeanFactory 和 ApplicationContext 来生产 Bean 对象。
代理模式
:最常见的 AOP 的实现形式就是通过代理来实现,Spring 次要是应用 JDK 动静代理和 CGLIB 代理。
模板办法模式
:次要是一些对数据库操作的类用到,比方 JdbcTemplate、JpaTemplate,因为查询数据库的建设连贯、执行查问、敞开连贯几个过程,十分实用于模板办法。
2. 谈谈你对 IOC 和 AOP 的了解?他们的实现原理是什么?
IOC 叫做管制反转,指的是通过 Spring 来治理对象的创立、配置和生命周期,这样相当于把控制权交给了 Spring,不须要人工来治理对象之间简单的依赖关系,这样做的益处就是解耦。在 Spring 外面,次要提供了 BeanFactory 和 ApplicationContext 两种 IOC 容器,通过他们来实现对 Bean 的治理。
AOP 叫做面向切面编程,他是一个编程范式,目标就是进步代码的模块性。Srping AOP 基于动静代理的形式实现,如果是实现了接口的话就会应用 JDK 动静代理,反之则应用 CGLIB 代理,Spring 中 AOP 的利用次要体现在 事务、日志、异样解决等方面,通过在代码的前后做一些加强解决,能够实现对业务逻辑的隔离,进步代码的模块化能力,同时也是解耦。Spring 次要提供了 Aspect 切面、JoinPoint 连接点、PointCut 切入点、Advice 加强等实现形式。
3. JDK 动静代理和 CGLIB 代理有什么区别?
JDK 动静代理次要是针对类实现了某个接口,AOP 则会应用 JDK 动静代理。他基于反射的机制实现,生成一个实现同样接口的一个代理类,而后通过重写办法的形式,实现对代码的加强。
而如果某个类没有实现接口,AOP 则会应用 CGLIB 代理。他的底层原理是基于 asm 第三方框架,通过批改字节码生成成成一个子类,而后重写父类的办法,实现对代码的加强。
4. Spring AOP 和 AspectJ AOP 有什么区别?
Spring AOP 基于动静代理实现,属于运行时加强。
AspectJ 则属于编译时加强,次要有 3 种形式:
- 编译时织入:指的是加强的代码和源代码咱们都有,间接应用 AspectJ 编译器编译就行了,编译之后生成一个新的类,他也会作为一个失常的 Java 类装载到 JVM。
- 编译后织入:指的是代码曾经被编译成 class 文件或者曾经打成 jar 包,这时候要加强的话,就是编译后织入,比方你依赖了第三方的类库,又想对他加强的话,就能够通过这种形式。
- 加载时织入:指的是在 JVM 加载类的时候进行织入。
总结下来的话,就是 Spring AOP 只能在运行时织入,不须要独自编译,性能相比 AspectJ 编译织入的形式慢,而 AspectJ 只反对编译前后和类加载时织入,性能更好,性能更加弱小。
5. FactoryBean 和 BeanFactory 有什么区别?
BeanFactory 是 Bean 的工厂,ApplicationContext 的父类,IOC 容器的外围,负责生产和治理 Bean 对象。
FactoryBean 是 Bean,能够通过实现 FactoryBean 接口定制实例化 Bean 的逻辑,通过代理一个 Bean 对象,对办法前后做一些操作。
6.SpringBean 的生命周期说说?
SpringBean 生命周期简略概括为 4 个阶段:
- 实例化,创立一个 Bean 对象
- 填充属性,为属性赋值
-
初始化
- 如果实现了
xxxAware
接口,通过不同类型的 Aware 接口拿到 Spring 容器的资源 - 如果实现了 BeanPostProcessor 接口,则会回调该接口的
postProcessBeforeInitialzation
和postProcessAfterInitialization
办法 - 如果配置了
init-method
办法,则会执行init-method
配置的办法
- 如果实现了
-
销毁
- 容器敞开后,如果 Bean 实现了
DisposableBean
接口,则会回调该接口的destroy
办法 - 如果配置了
destroy-method
办法,则会执行destroy-method
配置的办法
- 容器敞开后,如果 Bean 实现了
7.Spring 是怎么解决循环依赖的?
首先,Spring 解决循环依赖有两个前提条件:
- 不全是结构器形式的循环依赖
- 必须是单例
基于下面的问题,咱们晓得 Bean 的生命周期,实质上解决循环依赖的问题就是三级缓存,通过三级缓存提前拿到未初始化的对象。
第一级缓存:用来保留实例化、初始化都实现的对象
第二级缓存:用来保留实例化实现,然而未初始化实现的对象
第三级缓存:用来保留一个对象工厂,提供一个匿名外部类,用于创立二级缓存中的对象
假如一个简略的循环依赖场景,A、B 相互依赖。
A 对象的创立过程:
- 创建对象 A,实例化的时候把 A 对象工厂放入三级缓存
- A 注入属性时,发现依赖 B,转而去实例化 B
- 同样创建对象 B,注入属性时发现依赖 A,一次从一级到三级缓存查问 A,从三级缓存通过对象工厂拿到 A,把 A 放入二级缓存,同时删除三级缓存中的 A,此时,B 曾经实例化并且初始化实现,把 B 放入一级缓存。
- 接着持续创立 A,顺利从一级缓存拿到实例化且初始化实现的 B 对象,A 对象创立也实现,删除二级缓存中的 A,同时把 A 放入一级缓存
- 最初,一级缓存中保留着实例化、初始化都实现的 A、B 对象
因而,因为把实例化和初始化的流程离开了,所以如果都是用结构器的话,就没法拆散这个操作,所以都是结构器的话就无奈解决循环依赖的问题了。
8. 为什么要三级缓存?二级不行吗?
不能够,次要是为了生成代理对象。
因为三级缓存中放的是生成具体对象的匿名外部类,他能够生成代理对象,也能够是一般的实例对象。
应用三级缓存次要是为了保障不论什么时候应用的都是一个对象。
假如只有二级缓存的状况,往二级缓存中放的显示一个一般的 Bean 对象,BeanPostProcessor
去生成代理对象之后,笼罩掉二级缓存中的一般 Bean 对象,那么多线程环境下可能取到的对象就不统一了。
9.Spring 事务流传机制有哪些?
- PROPAGATION_REQUIRED:如果以后没有事务,就创立一个新事务,如果以后存在事务,就退出该事务,这也是通常咱们的默认抉择。
- PROPAGATION_REQUIRES_NEW:创立新事务,无论以后存不存在事务,都创立新事务。
- PROPAGATION_NESTED:如果以后存在事务,则在嵌套事务内执行。如果以后没有事务,则按 REQUIRED 属性执行。
- PROPAGATION_NOT_SUPPORTED:以非事务形式执行操作,如果以后存在事务,就把以后事务挂起。
- PROPAGATION_NEVER:以非事务形式执行,如果以后存在事务,则抛出异样。
- PROPAGATION_MANDATORY:反对以后事务,如果以后存在事务,就退出该事务,如果以后不存在事务,就抛出异样。
- PROPAGATION_SUPPORTS:反对以后事务,如果以后存在事务,就退出该事务,如果以后不存在事务,就以非事务执行。‘
10. 最初,说说 Spring Boot 启动流程吧?
这个流程,网上一搜根本都是这张图了,我也不想再画一遍了。那其实次要的流程就几个步骤:
- 筹备环境,依据不同的环境创立不同的 Environment
- 筹备、加载上下文,为不同的环境抉择不同的 Spring Context,而后加载资源,配置 Bean
- 初始化,这个阶段刷新 Spring Context,启动利用
- 最初完结流程