Spring5学习总结

前言

为什么我们要使用Spring呢?如果答案只是停留在大家都在用,那么知识一定是停留在人云亦云阶段的。正因为我想开始更加深入的了解Spring并能更好的使用它,于是开启了源码学习的道路,并使用这篇文章记录我所学习的东西和一个思考的过程

正文

最前方写下Spring的官方文档,没有那篇文档写的比官网更详细了Spring 官网

为什么要使用Spring呢?

我现在所知道的原因一共有三个:

  1. 响应开闭原则:面向接口进行编程,不会把实现硬编码到代码中。
  2. 解决循环调用问题。
  3. 解决"模版"设计模式,使用重复代码的问题。

Spring 的主要思想 IOC/DI

把对象初始化以及实例化的主动权交给容器

Don't call me, I will call you. Spring会帮我们把对象初始化,实例化,并且把相关依赖也处理妥当。

举一个类似的例子,最近我也有在看"响应式编程"相关的思想,这宗思想也提倡的是不要总是惹人烦的轮询来问我,如果我准备好了一个,我会主动通知你的。两者有异曲同工之妙。

Spring 容器的流程

  1. 首先通过ClassPathResource读取Xml文件或者是配置好ComponentScan的配置文件,创建资源对象。
  2. 然后根据资源对象,创建IOC的Context容器
  3. 从容器中获取各种需要的bean进行使用。

通过Xml的方式一步一步进行配置,使得逻辑更加清晰,最外层的<beans> 为bean的集合,在开头需要写上验证方式,分别为 xsddtd,有兴趣可以自行百度。<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 its DefaultListableBeanFactoryimplementation is a key delegate within the higher-level GenericApplicationContext 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的实例化方式
  1. 通过无参构造函数进行构建,使用的最多

  2. 静态工厂方式

  3. 实例工厂方法

    <bean id="" class="Factory的类"/>
    <bean id="" factory-bean="实例化Bean的名称" facotry-method="实例化方法"/>
    
  4. 实现FactoryBean实例化,实例工厂方法变种,与Mybatis结合使用较多。

    配置可以直接写为:

    <bean id="bean名称" class="实现了FactoryBean的工厂类名"/>
    

    Tips: 这里有个地方需要注意,我们因为规定了获得对象为getObject() 所以才不需要自己去指定实例化的方法。而且拿到的对象并不是工厂类对象而是通过FactoryBean拿到的。

Bean的作用域

Bean的对象相对于其他Bean的可见范围。

  1. singleton
  2. prototype
  3. request
  4. session
  5. websocket
  6. application
Bean 初始化以及销毁

在定义bean的时候只需要指定init-method方法,就相当于告诉spring在创建之后,进行初始化

同样的也存在destroy-method方法,注意: 这个销毁时候的方法被执行的前提是容器被关闭,如果时prototype的情况下,销毁方法是不会执行的,因为容器不知道我们会什么时候想要销毁它

Bean的生命周期
依赖注入的具体细节

在xml中可以使用以下代码注入(已经不推荐了):

<bean id="" class="" autowire="byName/byType/constructor/no"/>

注入方式:

  1. set方法注入

    在bean元素里加入以下即可注入:

    <property name="属性名称" value="属性的值" />

  2. 构造器注入

    在bean元素里加入以下即可注入:

    <constructor-arg type="构造器中的类型" name="构造器中的名字" value="构造器中的值" />

    type和name任选其一。

    Tips:如果一个bean只想在构造器里面使用可以把bean标签写在<constructor-args></constructor-arg>内部

注入值的类型:

  1. 常量类型 ,在xml中配置使用value字段
  2. 对象类型, 在xml中配置使用ref字段
  3. 引用类型,在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
  1. 这个注解可以添加在属性上
  2. 可以添加在set方法上
  3. 也可以在set方法中注入多个属性值
  4. 还可以注入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

可以参考我的另一篇文章传送门

推荐阅读更多精彩内容