Spring框架应该是作为Java开发耳熟能详的一个,并且要能够熟练掌握。但是里面知识点的零碎可能随着时间的流逝,一些知识也会变的陌生起来。Spring这一系列的文章主要是将自己对Spring的一些认识梳理一下,其中这篇文章讲的主要是IOC相关的。
在很早的时候也写过两篇将IOC容器的文章,供君参考,这篇文章更多的是对IOC中相关知识的梳理,内容在下面的两篇文章中也都有:
https://blog.youkuaiyun.com/lmlzww/article/details/105363279?spm=1001.2014.3001.5502
https://blog.youkuaiyun.com/lmlzww/article/details/105418953?spm=1001.2014.3001.5502
Spring是一个体量小、可以独立使用的轻量级框架,开源并且是为简化企业应用开发的复杂性而生。就像其中的IOC是为Bean容器的创建以及降低耦合度而生,而AOP作为动态代理为原理实现的特性,也可以从分离业务逻辑以及非业务逻辑的角度来降低系统的耦合行,提升可扩展能力,他的优势如下:
- 支持AOP框架
- 方便测试
- 可以很好的组合第三方框架
- 方便事务管理
0、IOC容器是什么
那么IOC容器到底是什么呢?我们可以分开理解:IOC(Inversion of Control)控制反转、容器——在IOC容器中提供了提供了我们在配置文件中配置的对象,程序中使用到的对象我们可以通过这个获取,而不需要在程序中通过new创建,正是因为不需要通过new创建了,所以对对象的创建权利实现了翻转,以前通过new创建对象对于程序来说是主动的,但是现在对象是通过Spring框架的IOC容器提供得来的,相对来说是被动的。
在我们学习IOC的时候实际也就围绕两个核心展开——实例的创建、实例的应用,对象的创建主要关注亮点一个是对象创建的形式、还有一个就是对象各种属性的赋值也就是说依赖注入。既然有对象的创建那么就会有他的生命周期,这里要与AOP中的区分开来。总而言之一切都是围绕对象展开的!
1、IOC容器的底层实现原理
底层主要通过xml解析、工厂模式、反射(通过获取到class文件从而再来创建具体对象)三个模块进行实现。主要的一个流程就是:
获取XML并进行解析——>根据配置配置信息获取到class文件——>根据class生成对象实例
2、IOC的容器对象
❓如果要是用指定的容器获取对象那么框架为给我们默认单例、多例,如果要是我们指定了单例、多例那么系统也会给我们选默认的容器。
- BeanFactory:内部实现的,一般是IOC容器内部使用,开发人员一般不适用;加载配置文件的时候并不会直接创建里面的对象,只有在获取或是使用的时候才会去创建,是多例模式
- ApplicationContext:是上面的一个子类;加载配置文件的时候就会完成对对象的创建,单例模式,单例的时候需要考虑类公共属性存在的一个线程安全问题
ApplicationContext是一个接口,他有很多不同类型的实现子类:
3、IOC容器对象的创建
IOC帮助我们完成实例的创建,我们可以理解通常我们有几种对象的创建方式,实际上就是构造函数以及工厂方法,工厂方法又根据方法是否为静态的可以分为两种配置形式。
普通Bean——指定什么类型就是什么类型
- 构造函数构造
- 通过普通方法构造
- 通过静态方法构造
<!-- 第一种方式:使用默认构造函数创建。
在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时。
采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建。-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>
<!-- 第二种方式: 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)-->
<bean id="instanceFactory" class="com.itheima.factory.InstanceFactory"></bean>
<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>
<!-- 第三种方式:使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)-->
<bean id="accountService" class="com.itheima.factory.StaticFactory" factory-method="getAccountService"></bean>
- id: 给对象起一个别名,用来获取对象
- class: 类的全路径
- name: 与id作用相同,相对于id来说可以添加一些特殊 字符,主要服务于struct1框架,现在不用
工厂Bean——XML中定义的bean类型可以与返回的类型不一样
创建类,让这个类作为工厂Bean,实现接口FactoryBean
实现接口里面的方法实现的方法中返回的Bean类型
注解形式的实现
上面说的是通过xml配置文件形式来进行容器对象创建进行制定,通过xml我们也可以使用p名称空间注入简化xml中的配置。开发中我们更常用的是注解形式:@Component、@Service、@Controller、@Repository这四个注解功能是一样的都可以用来创建Bean实例,主要是为了开发人与进行区分。
4、IOC容器的依赖注入
这里说的容器依赖注入理解为一个Bean依赖于另一个Bean,也就是说以属性的形式存在,属性我们通常会设置为private,这时候想要对对象进行赋值就有两种方式:
- 构造函数
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<constructor-arg name="name" value="泰斯特"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
<!-- 配置一个日期对象,这里体现的就是可以传递其他的Bean类型 -->
<bean id="now" class="java.util.Date"></bean>
- setter方法
<bean id="accountService3" class="com.itheima.service.impl.AccountServiceImpl3">
<property name="myStrs">
<set>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</set>
</property>
<property name="myList">
<array>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</array>
</property>
<property name="mySet">
<array>
<value>222222</value>
</array>
</property>
<property name="myMap">
<props>
<prop key="testC">ccc</prop>
<prop key="testD">ddd</prop>
</props>
</property>
<property name="myProps">
<map>
<entry key="testA" value="aaa"></entry>
<entry key="testB">
<value>BBB</value>
</entry>
</map>
</property>
</bean>
xml中间配置集合抽取出来需要引入util名称空间
如果注入的依赖有特殊值,则需要特殊处理:
-
设置为null值是通过null标签,而非是标签里的value值
-
特殊字符的话通过转义符<或是> ;把特殊符号内容写到cdata的接口
注入的如果是对象进行级联赋值或是内部Bean的使用
- 一级节联赋值
- 多级节联赋值,当使用JSON表达能力进行使用需要给dept属性给一个get方法
注解方式赋值
上面也说到依赖注入实际上就是给属性赋值,那么我们也可以通过注解的方式来实现:
-
@AutoWired:根据属性类型进行自动注入
-
@Qualifier:根据属性的名称进行注入,需要与@Autowired配合使用,当一个接口有多个实现类的时候就可以根据名称进行赛选了
-
@Resource:可以根据属性进行注入也可以根据名称进行注入,在使用
-
@Value:注入普通类型的属性
这里可以联想到容器对象构建的时候@Component、@Bean等注解可以给穿件的Bean命名
5、IOC操作中Bean的声明周期
-
scope属性:singleton—单实例(加载配置文件的时候就会创建一个单实例对象);prototype—多实例对象(不是在加载配置文件的时候创建对象而是在调用getBean的时候创建多实例对象);request—每次创建对象会凡在request中;session—创建之后放在session中
-
声明周期——从对象的创建到对象的销毁
-
通过构造器创建bean
-
为bean的属性设置和对其他bean的引用
-
初始化之前吧bean实例传递给bean的前置处理器方法
-
调用bean的初始化方法(需要进行配置)
-
把bean的实例传递给bean的后置处理器方法
-
bean可以使用了——对象已经获取到
-
当容器关闭的时候,调用bean的销毁方法(需要进行配置)
-
-
后置处理器的实现方法——实现BeanPostProcessor
6、引入外部的配置文件
-
创建一个外部的配置文件
-
把外部的属性文件引入到spring的配置为文件中,并配置名称空间