Spring专题2: DI,IOC 控制反转和依赖注入
- 合集目录
- Spring专题2: DI,IOC 控制反转和依赖注入
https://docs.spring.io/spring/docs/2.5.x/reference/aop.html
https://docs.spring.io/spring/docs/2.5.x/reference/aop.html#aop-understanding-aop-proxies
Spring 框架中的核心组件只有三个:Core、Context 和 Beans. 它们构建起了整个 Spring 的骨骼架构. 没有它们就不可能有 AOP、Web 等上层的特性功能
Bean的作用域
singleton This scopes the bean definition to a single instance per Spring IoC container (default).
prototype This scopes a single bean definition to have any number of object instances.
request This scopes a bean definition to an HTTP request. Only valid in the context of a web-aware Spring ApplicationContext.
session This scopes a bean definition to an HTTP session. Only valid in the context of a web-aware Spring ApplicationContext.
global-session This scopes a bean definition to a global HTTP session. Only valid in the context of a web-aware Spring ApplicationContext.
怎么理解面向切面编程的切面?
讲解OOP与AOP的简单对比?
AOP的好处: 把和主业务无关的事情, 放到代码外面去做
AOP像OOP一样, 只是一种编程范式, AOP并没有规定说, 实现AOP协议的代码, 要用什么方式去实现.
Spring AOP就是基于动态代理的, 如果要代理的对象, 实现了某个接口, 那么Spring AOP会使用JDK Proxy
但是不是所有AOP的实现都是在运行时进行织入的, 因为这样效率太低了, 而且只能针对方法进行AOP, 无法针对构造函数、字段进行AOP. 可以在编译成class时就织入啊, 比如AspectJ
讲解Spring 框架中如何基于 AOP 实现的事务管理?
pring 为事务管理提供了丰富的功能支持. Spring 事务管理分为编码式和声明式的两种方式. 编程式事务指的是通过编码方式实现事务;声明式事务基于 AOP,将具体业务逻辑与事务处理解耦. 声明式事务管理使业务代码逻辑不受污染, 因此在实际使用中声明式事务用的比较多. 声明式事务有两种方式, 一种是在配置文件(xml)中做相关的事务规则声明, 另一种是基于@Transactional 注解的方式. 注释配置是目前流行的使用方式
在应用系统调用声明@Transactional 的目标方法时, Spring Framework 默认使用 AOP 代理, 在代码运行时生成一个代理对象, 根据@Transactional 的属性配置信息, 这个代理对象决定该声明@Transactional 的目标方法是否由拦截器 TransactionInterceptor 来使用拦截, 在 TransactionInterceptor 拦截时, 会在在目标方法开始执行之前创建并加入事务, 并执行目标方法的逻辑, 最后根据执行情况是否出现异常, 利用抽象事务管理器AbstractPlatformTransactionManager 操作数据源 DataSource 提交或回滚事务
谈谈对控制反转的设计思想的理解?
怎么理解 Spring IOC 容器?
IoC是 Ioc—Inversion of Control, 即“控制反转”, 是一种设计思想. 在Java开发中, Ioc意味着将你设计好的对象交给容器控制, 而不是传统的在你的对象内部直接控制.
传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象, 也就是正转;而反转则是由容器来帮忙创建及注入依赖对象
它能指导我们如何设计出松耦合、更优良的程序. 传统应用程序都是由我们在类内部主动创建依赖对象, 从而导致类与类之间高耦合, 难于测试;有了IoC容器后, 把创建和查找依赖对象的控制权交给了容器, 由容器进行注入组合对象, 所以对象与对象之间是 松散耦合, 这样也方便测试, 利于功能复用, 更重要的是使得程序的整个体系结构变得非常灵活
IoC 是通过Dependency Injection, 依赖注入来实现的
. 比如对象A需要操作数据库, 以前我们总是要在A中自己编写代码来获得一个Connection对象, 有了 spring我们就只需要告诉spring, A中需要一个Connection, 至于这个Connection怎么构造, 何时构造, A不需要知道. 在系统运行时, spring会在适当的时候制造一个Connection, 注入到A当中, 这样就完成了对各个对象之间关系的控制. A需要依赖 Connection才能正常运行, 而这个Connection是由spring注入到A中的, 依赖注入的名字就这么来的.
那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射 reflection, 它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性, spring就是通过反射来实现注入的
Spring IOC 怎么管理 Bean 之间的依赖关系, 怎么避免循环依赖?
对Spring IOC 容器的依赖注入的理解?
Spring的循环依赖的理论依据其实是基于Java的引用传递, 当我们获取到对象的引用时, 对象的field或则属性是可以延后设置的, 但是构造器必须是在获取引用之前
.
Spring的单例对象的初始化主要分为三步:
- createBeanInstance:实例化, 其实也就是调用对象的构造方法实例化对象
- populateBean:填充属性, 这一步主要是多bean的依赖属性进行填充
- initializeBean:调用spring xml中的init 方法.
从上面讲述的单例bean初始化步骤我们可以知道, 循环依赖主要发生在第一、第二部. 也就是构造器循环依赖和field循环依赖.
那么我们要解决循环引用也应该从初始化过程着手, 对于单例来说, 在Spring容器整个生命周期内, 有且只有一个对象, 所以很容易想到这个对象应该存在Cache中, Spring为了解决单例的循环依赖问题, 使用了三级缓存, 这三级缓存分别指
- singletonFactories : 单例对象工厂的cache
- earlySingletonObjects :提前暴光的单例对象的Cache
- singletonObjects:单例对象的cache
Spring首先从一级缓存singletonObjects中获取. 如果获取不到, 并且对象正在创建中, 就再从二级缓存earlySingletonObjects中获取. 如果还是获取不到且允许singletonFactories通过getObject()获取, 就从三级缓存singletonFactory.getObject()(三级缓存)获取
所以setter和field的依赖可以解决, 但是Spring不能解决A的构造方法中依赖了B的实例对象, 同时B的构造方法中依赖了A的实例对象
这类问题, 因为加入singletonFactories三级缓存的前提是执行了构造器, 所以构造器的循环依赖没法解决.
说说对Spring IOC 的单例模式和高级特性?
lazy-init
如果你不想让一个singleton bean在ApplicationContext实现初始化时被提前实例化, 那么可以将bean设置为延时实例化. 延时加载, 设置为lazy 的bean将不会在ApplicationContext启动时提前被实例化, 而是第一次向容器通过getBean索取bean时实例化的. 如果一个设置了立即加载的bean1, 引用了一个延时加载的bean2, 那么bean1在容器启动时被实例化, 而bean2 由于被bean1引用, 所以也被实例化, 这种情况也符合延时加载的bean在第一次调用时才被实例化的规则.
FactoryBean的实现
概念:工厂bean, 本质也是bean.
作用:产生其他bean实例提供一个工厂方法, 该方法用来返回其他bean实例.
@Autowire注解实现
BeanFactory 和 FactoryBean 有什么区别?
BeanFactory 是bean工厂, Spring IoC最顶层的接口, 作用是管理bean, 即定位、配置对象、实例化和建立对象之间的依赖, getObject()方法由实现类调用, 来创建bean实例化
FactoryBean 工厂bean, 作用是产生其它bean实例, 提供工厂方法, 返回其它bean实例, 一般Spring容器担任工厂角色
BeanFactory 和 ApplicationContext 又有什么不同?
BeanFactory
BeanFactory是spring中较原始的Factory, BeanFactory不支持spring插件例如: AOP, Web应用等功能
ApplicationContext
ApplicationContext是BeanFactory的子类, ApplicationContext基本上代替了BeanFactory的工作, 以一种更面向框架的工作方式以及对上下文进行分层和实现继承, 并在这个基础上对功能进行扩展
- MessageSource, 提供国际化的消息访问
- 资源访问(如URL和文件)
- 事件传递
- Bean的自动装配
- 各种不同应用层的Context实现
- 如果使用ApplicationContext, 如果配置的bean是singleton, 那么不管你有没有或想不想用它, 它都会被实例化. 好处是可以预先加载, 坏处是浪费内存.
- BeanFactory, 当使用BeanFactory实例化对象时, 配置的bean不会马上被实例化, 而是等到你使用该bean的时候(getBean)才会被实例化. 好处是节约内存, 坏处是速度比较慢. 多用于移动设备的开发.
- 没有特殊要求的情况下, 应该使用ApplicationContext完成. 因为BeanFactory能完成的事情, ApplicationContext都能完成, 并且提供了更多接近现在开发的功能
Spring 在 Bean 创建过程中是如何解决循环依赖的?
pring中一共有三种循环依赖: 构造器循环依赖, Setter循环依赖, Prototype作用域的循环依赖.
对于这三种循环依赖, Spring并不是都解决的:
构造器循环依赖, 不能解决
Setter循环依赖, 可以解决
Prototype作用域的循环依赖, 不能解决
获取到对象的引用时, 对象的field或则属性是可以延后设置的, 但是构造器必须是在获取引用之前
. Spring的单例对象的初始化主要分为三步:
- createBeanInstance:实例化, 其实也就是调用对象的构造方法实例化对象
- populateBean:填充属性, 这一步主要是多bean的依赖属性进行填充
- initializeBean:调用spring xml中的init 方法.
所以setter和field的依赖可以解决, 但是Spring不能解决A的构造方法中依赖了B的实例对象, 同时B的构造方法中依赖了A的实例对象
这类问题, 因为加入singletonFactories三级缓存的前提是执行了构造器, 所以构造器的循环依赖没法解决.
谈谈Spring Bean 创建过程中的设计模式?
工厂设计模式: Spring使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象
单例设计模式: Spring 中的 Bean 默认都是单例的
代理设计模式: Spring AOP 功能的实现
模板方法: Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类, 它们就使用到了模板模式
观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用
适配器模式: Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller
装饰者模式: Spring 中用到的包装器模式在类名上含有 Wrapper或者 Decorator. 这些类基本上都是动态地给一个对象添加一些额外的职责