0. 前言
Spring比较重要的特性就包含IOC和DI,其中的也会产生循环依赖,线程安全等问题。
bean中的各个参数
id : 唯一标识
name : bean的名字,一个bean标签可以有多个别名,多个别名间使用空格/逗号/分号分割。
alias :bean的新的名字,一个bean标签可以有多个别名,多个别名间使用空格/逗号/分号分割。
class : 当前bean对象的具体类名
abstract : 对象不可直接使用。 必须被其他的bean对象继承。
设定对象的模板,可以减少一定的配置。
parent : 继承已定义的对象。 继承的父对象类型必须兼容子对象类型。
父对象的类型必须是子对象类型的同类型或父类型。
factory-bean : 引用已定义的动态工厂对象
factory-method : 调用工厂类中的工厂方法
scope : 对象的作用域。 对象如何创建,创建多少个对象。
可选值 :
singleton : 默认值,单例的,在读取配置文件的同时创建唯一的一个对象
prototype : 多例的。每次通过getBean方法获取对象时,创建对象。
request : 请求变量作用域, 一个请求创建一个对象
session : 一个HttpSession会话创建一个对象
global-session : 在portlet技术中的servletContext,一个应用一个对象
application : 基础web项目中的应用, 一个应用一个对象。
1. IOC
IOC是指Inversion of Control, 即控制反转。
单纯的作用就是是将对象的生命周期控制权限交由Spring容器管理。并且通过Spring容器实现了各层代码之间的解耦, 让代码没有依赖关系. 且实现了代码的可插拔性。
1.1. 构造方法创建
构造方法分为无参构造和有参构造
在配置文件xml(applicationContext.xml)中 ,配置bean
构造方法创建
<!-- 无参构造 -->
<bean id="" class="" />
<!-- 有参构造 -->(配置的参数必须和构造方法一致)
<bean id="" class="" >
<constructor-arg index="" name="" type="" value="" />
</bean>
1.2. 工厂方法管理
工厂方法分为静态工厂构造和动态工厂构造
<!-- 静态工厂 -->
<bean id=""
class="静态工厂类的全命名"
factory-method="静态工厂方法名" />
<!-- 动态工厂 -->(配置的参数必须和构造方法一致)
<bean id="factory" class="" />
<bean id="product" factory-bean="factory"
factory-method="factoryMethodName" >
<constructor-arg index="" name="" type="" value="" />
</bean>
2. DI依赖注入
为Spring容器管理的对象中的属性,注入数据。
2.1. 构造方法注入
与上边IOC有参构造方法创建一致(bean对应实体类必须有参构造方法才能使用构造方法注入,并且入参要和参数一致)
<bean id="" class="" >
<constructor-arg index="" name="" type="" value="" />
</bean>
2.2. setter方法注入
<bean id="" class="" >
<property name="" value="" />
<property name="" ref="" />
</bean>
3. 自动装配
自动装配的配置的位置,以及各种形式的匹配规则
3.1. beans配置自动装配
<beans default-autowire="default|no|byName|byType|constructor">
3.2. bean配置自动装配
<bean autowire="default|no|byName|byType|constructor">
3.3. 各种方式的含义
default : 默认值, 与no含义一致。代表没有自动装配
no : 无自动装配
byName : 使用property的名称和bean的名称进行匹配,如果匹配一致则装配。
只能自动装配复杂类型。如String等简单类型无法自动装配
byType : 使用property的类型和bean的类型进行匹配。如果匹配一致则装配。
有错误可能。 如果有多个bean对象类型一致或匹配,则可能抛出异常
只能自动装配复杂类型。如String等简单类型无法自动装配
constructor : 使用有参数的构造方法进行数据的注入。
先使用byName进行装配,如果没有命名一致的bean,使用byType进行自动装配
4. 循环依赖的问题
循环依赖:bean之间存在直接或间接的依赖关系,例如A和B互相依赖,或者A依赖B,B依赖C,C依赖A,或者A依赖A。
三级缓存(存放不同类型的Bean)
- singletonObjects:第一级缓存,里面放置的是初始化后的单例对象;
- earlySingletonObjects:第二级缓存,里面存放的是提前曝光的单例对象;原始对象,未赋值。
- singletonFactories:第三级缓存,里面存放的是要被实例化的对象的对象工厂。用来生成原始Bean并放入二级缓存。
假设A和B互相依赖
核心:当一个Bean调用构造函数进行实例化后,即使属性还未填充,就可以通过三级缓存向外暴露依赖的引用值(所以循环依赖问题的解决也是基于Java的引用传递)。即Bean的实例化以及依赖注入两个过程分离。
- bean之间通过构造方法构造依赖?
通过@Lazy注解。 - 多实例(prototype)的Setter注入?
不能解决,需要单例。 - 单例的代理对象Setter注入?
使用@Lazy注解或者使用@DependsOn注解指定加载先后关系。
5. 线程安全问题
- Bean默认是singleton,所以Spring框架里除了pojo基本都是单例对象,不更改对应Bean的成员变量就不会出现线程安全问题,例如Controller层,Servise层,dao层等,自始至终只有一个;好处就是为了防止每次请求都会重新建立对象,造成性能问题;
- 实体pojo都是新的,每次返回都不会互相干扰;
- 有些对象会依靠ThreadLocal和线程绑定,不会出现线程安全问题(数据库连接等)。