前言
为什么我们要使用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的方式一步一步进行配置,使得逻辑更加清晰,最外层的<beans> 为bean的集合,在开头需要写上验证方式,分别为 xsd和dtd,有兴趣可以自行百度。<bean> 指的就是一个对象,而依赖的对象使用<properties> 标签,这里需要注意的是依赖对象的属性,需要存在set方法才能注入成功,否则会报错。
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
<bean/>
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中会显得臃肿,所以可以将其拆分成多个文件,使用
<import resource="classpath:文件路径" />
到后来,我们可以使用JavaConfig的方式进行配置,代码开始变得小清新。
使用@Configuration代替原来整个xml的配置,然后在其中定义一个方法,配上@Bean注解,返回new 的对象,就相当于之前我们在xml 中的<bean>,bean中引用了别的bean只需要从方法参数传入即可。如果分离成了多个配置类使用@Import引入即可。
Spring Bean的创建
Bean的容器
BeanFactory在初始化Bean的时候,有延迟的特性,是一种懒加载,在从容器中取构造器的时候才会执行构造函数。
ApplicationContext在构建Spring容器的时候,就会立刻把管理的Bean进行初始化。但是Bean有一个参数(lazy-init)决定是否延迟初始化。
Bean的实例化方式
通过无参构造函数进行构建,使用的最多
静态工厂方式
-
实例工厂方法
<bean id="" class="Factory的类"/> <bean id="" factory-bean="实例化Bean的名称" facotry-method="实例化方法"/>
-
实现FactoryBean实例化,实例工厂方法变种,与Mybatis结合使用较多。
配置可以直接写为:
<bean id="bean名称" class="实现了FactoryBean的工厂类名"/>
Tips: 这里有个地方需要注意,我们因为规定了获得对象为getObject() 所以才不需要自己去指定实例化的方法。而且拿到的对象并不是工厂类对象而是通过FactoryBean拿到的。
Bean的作用域
Bean的对象相对于其他Bean的可见范围。
- singleton
- prototype
- request
- session
- websocket
- application
Bean 初始化以及销毁
在定义bean的时候只需要指定init-method方法,就相当于告诉spring在创建之后,进行初始化
同样的也存在destroy-method方法,注意: 这个销毁时候的方法被执行的前提是容器被关闭,如果时prototype的情况下,销毁方法是不会执行的,因为容器不知道我们会什么时候想要销毁它
Bean的生命周期
依赖注入的具体细节
在xml中可以使用以下代码注入(已经不推荐了):
<bean id="" class="" autowire="byName/byType/constructor/no"/>
注入方式:
-
set方法注入
在bean元素里加入以下即可注入:
<property name="属性名称" value="属性的值" />
-
构造器注入
在bean元素里加入以下即可注入:
<constructor-arg type="构造器中的类型" name="构造器中的名字" value="构造器中的值" />
type和name任选其一。
Tips:如果一个bean只想在构造器里面使用可以把bean标签写在<constructor-args></constructor-arg>内部
注入值的类型:
- 常量类型 ,在xml中配置使用value字段
- 对象类型, 在xml中配置使用ref字段
- 引用类型,在xml中配置使用各自集合对应的元素:set,list...
元素的继承
当bean中的变量大多数相同,那么我们就可以像Java中抽取共同一样给提取出来提取的bean,但是其本质是xml配置的拷贝:
<bean id="base" abstract="true">
<property />
</bean>
然后在对应的需要base bean的地方
<bean id="bean1" parent="base">
</bean>
属性占位符
Property-placeholder, 在引入了 spring context之后,我们可以通过这个功能将变量提取出来,然后配置文件中读取变量,代码如下:
<context:property-placeholder location="classpath:配置文件的路径"/>
然后在bean中直接使用spel ${变量名}
即可。
上面的代码通过注解的方式可以定义一个类,类上加入:
@PropertySource("classpath:配置文件的路径")
然后定义属性,在属性上加入@Value即可。
@Value(${属性名})
private String xx;
使用注解实现DI
在前面的时候有说过,通过xml的方式,分为常量注入和引用注入。
那么在注解注入时,也有所体现:
引用注入:@Autowired,@Resource
常量注入:@Value
@Autowired
- 这个注解可以添加在属性上
- 可以添加在set方法上
- 也可以在set方法中注入多个属性值
- 还可以注入spring的内置对象 例如: BeanFactory, ApplicationContext
那么问题来了@Autowired是怎么帮助我们进行依赖注入的呢?
通过DI注解解析器
<context: annotation-config/>
寻找应用的方式:
1.首先通过注入的类型,使用setter方法或字段直接注入
2.如果找到了多个,则按照名字进行查找,如果没有匹配则报错
3.如果类型相同,名字不同,还可以通过@Qualifier("")定义具体注入的是哪个bean
@Resource
和Autowired作用是一样的, JAVAEE规范的注入注解.
但是寻找bean的顺序是不同的:
1.首先查找名字@Resource(name="bean 的名称")
2.如果有多个名字相同的,查找类型相同的
@Value
常量注入注解,加在变量上,为变量进行付值
使用注解实现IOC
IOC注解解析器,我们把需要组建进行了配置,也有引用这个注解的地方,但是还有一点很重要的就是IOC注解解析器:
<context: component-scan back-package="" />
@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
可以参考我的另一篇文章传送门