前言
为什么我们要使用Spring呢?如果答案只是停留在大家都在用,那么知识一定是停留在人云亦云阶段的。正因为我想开始更加深入的了解Spring并能更好的使用它,于是开启了源码学习的道路,并使用这篇文章记录我所学习的东西和一个思考的过程
正文
最前方写下Spring的官方文档,没有那篇文档写的比官网更详细了Spring 官网
为什么要使用Spring呢?
我现在所知道的原因一共有三个:
- 响应开闭原则:面向接口进行编程,不会把实现硬编码到代码中。
- 解决循环调用问题。
- 解决"模版"设计模式,使用重复代码的问题。
Spring 的主要思想 IOC/DI
把对象初始化以及实例化的主动权交给容器
Don't call me, I will call you. Spring会帮我们把对象初始化,实例化,并且把相关依赖也处理妥当。
举一个类似的例子,最近我也有在看"响应式编程"相关的思想,这宗思想也提倡的是不要总是惹人烦的轮询来问我,如果我准备好了一个,我会主动通知你的。两者有异曲同工之妙。
Spring 容器的流程
- 首先通过ClassPathResource读取Xml文件或者是配置好ComponentScan的配置文件,创建资源对象。
- 然后根据资源对象,创建IOC的Context容器
- 从容器中获取各种需要的bean进行使用。
通过Xml的方式一步一步进行配置,使得逻辑更加清晰,最外层的
Spring技术的KeyWord
-
BeanFatory: 管理Bean的生命周期,下面是官网的介绍:
The
BeanFactory
API provides the underlying basis for Spring’s IoC functionality. Its specific contracts are mostly used in integration with other parts of Spring and related third-party frameworks, and itsDefaultListableBeanFactory
implementation is a key delegate within the higher-levelGenericApplicationContext
container. -
Bean:
- 如果对象被Spring进行管理,并且有相应的生命周期
- 令一点比较重要的是bean 的实例化与添加依赖,使用的是反射以及内省机制。
- Bean的查找一共有三种方式
- 通过bean名称
- 通过类的类型
- 通过bean名称和类的类型
- 关于Bean的官网介绍:
A Spring IoC container manages one or more beans. These beans are created with the configuration metadata that you supply to the container (for example, in the form of XML
definitions).Within the container itself, these bean definitions are represented as
BeanDefinition
objects -
ApplicationContext
- 同样具有BeanFacotry,管理对象以及其相关依赖的功能,但是在应用中大多使用ApplicationContext,而不使用底层接口BeanFactory,因为ApplicationContext 功能更多,并且启动容器时帮我们管理所有的bean
Spring 基本配置
在原来没有出现注解的时候,我们都是使用xml进行配置,在一个xml中定义好命名空间,然后在下面设置一堆配置,其实还是很繁琐的。
要注意Bean元素起名字要使用id,而不能使用name,即使name可以使用多个别名,但是用处并不大。
当许多的配置都写进了同一个xml中会显得臃肿,所以可以将其拆分成多个文件,使用
到后来,我们可以使用JavaConfig的方式进行配置,代码开始变得小清新。
使用@Configuration代替原来整个xml的配置,然后在其中定义一个方法,配上@Bean注解,返回new 的对象,就相当于之前我们在xml 中的
Spring Bean的创建
Bean的容器
BeanFactory在初始化Bean的时候,有延迟的特性,是一种懒加载,在从容器中取构造器的时候才会执行构造函数。
ApplicationContext在构建Spring容器的时候,就会立刻把管理的Bean进行初始化。但是Bean有一个参数(lazy-init)决定是否延迟初始化。
Bean的实例化方式
通过无参构造函数进行构建,使用的最多
静态工厂方式
-
实例工厂方法
-
实现FactoryBean实例化,实例工厂方法变种,与Mybatis结合使用较多。
配置可以直接写为:
Tips: 这里有个地方需要注意,我们因为规定了获得对象为getObject() 所以才不需要自己去指定实例化的方法。而且拿到的对象并不是工厂类对象而是通过FactoryBean拿到的。
Bean的作用域
Bean的对象相对于其他Bean的可见范围。
- singleton
- prototype
- request
- session
- websocket
- application
Bean 初始化以及销毁
在定义bean的时候只需要指定init-method方法,就相当于告诉spring在创建之后,进行初始化
同样的也存在destroy-method方法,注意: 这个销毁时候的方法被执行的前提是容器被关闭,如果时prototype的情况下,销毁方法是不会执行的,因为容器不知道我们会什么时候想要销毁它
Bean的生命周期
依赖注入的具体细节
在xml中可以使用以下代码注入(已经不推荐了):
注入方式:
-
set方法注入
在bean元素里加入以下即可注入:
-
构造器注入
在bean元素里加入以下即可注入:
type和name任选其一。
Tips:如果一个bean只想在构造器里面使用可以把bean标签写在
内部
注入值的类型:
- 常量类型 ,在xml中配置使用value字段
- 对象类型, 在xml中配置使用ref字段
- 引用类型,在xml中配置使用各自集合对应的元素:set,list...
元素的继承
当bean中的变量大多数相同,那么我们就可以像Java中抽取共同一样给提取出来提取的bean,但是其本质是xml配置的拷贝:
然后在对应的需要base bean的地方
属性占位符
Property-placeholder, 在引入了 spring context之后,我们可以通过这个功能将变量提取出来,然后配置文件中读取变量,代码如下:
然后在bean中直接使用spel ${变量名}
即可。
上面的代码通过注解的方式可以定义一个类,类上加入:
@PropertySource("classpath:配置文件的路径")
然后定义属性,在属性上加入@Value即可。
@Value(${属性名})
private String xx;
使用注解实现DI
在前面的时候有说过,通过xml的方式,分为常量注入和引用注入。
那么在注解注入时,也有所体现:
引用注入:@Autowired,@Resource
常量注入:@Value
@Autowired
- 这个注解可以添加在属性上
- 可以添加在set方法上
- 也可以在set方法中注入多个属性值
- 还可以注入spring的内置对象 例如: BeanFactory, ApplicationContext
那么问题来了@Autowired是怎么帮助我们进行依赖注入的呢?
通过DI注解解析器
寻找应用的方式:
1.首先通过注入的类型,使用setter方法或字段直接注入
2.如果找到了多个,则按照名字进行查找,如果没有匹配则报错
3.如果类型相同,名字不同,还可以通过@Qualifier("")定义具体注入的是哪个bean
@Resource
和Autowired作用是一样的, JAVAEE规范的注入注解.
但是寻找bean的顺序是不同的:
1.首先查找名字@Resource(name="bean 的名称")
2.如果有多个名字相同的,查找类型相同的
@Value
常量注入注解,加在变量上,为变量进行付值
使用注解实现IOC
IOC注解解析器,我们把需要组建进行了配置,也有引用这个注解的地方,但是还有一点很重要的就是IOC注解解析器:
@Component
被用来代替xml中配置bean的步骤
在@Component后,如果组建的功能更加具体,我们可以使用具体的注解
- @Repository
- @Service
- @Controller
@Scope
用来指定对象的生命周期,最常用的就是singleton和prototype,默认为单例(singleton)的。
@PostConstruct
构建对象之后立刻执行,相当于之前使用bean xml定义的 init-method
@PreDestroy
在销毁对象之前执行,相当于之前我们在bean xml 定义的 destroy-method
Spring之AOP
可以参考我的另一篇文章传送门
Spring之Transaction
可以参考我的另一篇文章传送门