Spring使用篇(一)—— Spring IOC入门

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_36378917/article/details/86132719

1、相关名词概念

1.1 POJO、JavaBean与bean

  POJO对象是“Plain Old Java object”的缩写。它是被用来描述一个简单的Java对象,该对象没有任何的依赖类,接口或者框架,是一个普通的JavaBean。

  JavaBean是用Java实现的可重用的组件。为写成JavaBean,类必须是具体的和公共的,并且具有无参数的构造器。JavaBean 通过提供符合一致性设计模式的公共方法将内部域暴露成员属性,set和get方法获取。众所周知,属性名称符合这种模式,其他Java 类可以通过反射机制(自省机制)发现和操作这些JavaBean 的属性。

  Spring的一个重要的作用就是降低各个组件之间的耦合性。在Spring中用bean或者JavaBean来表示应用组件,但并不意味着Spring组件必须要遵循JavaBean规范。一个Spring组件可以是任何形式的POJO。在本系列博客中,认为POJO与JavaBean为同义词。

1.2 控制反转IoC

  控制反转(Inversion of Control,缩写为IoC)是一种通过描述(在Java中可以通过XML或者注解的方式)并通过第三方(如Spring中的Spring IoC容器)去产生或获取特定对象的方式,即将对象的创建与装配的权利交由第三方管理。

1.3 依赖关系

  依赖关系表示类与类之间的连接,表示一个类依赖于另外一个类的定义,依赖关系时是单向的。简单理解就是类A使用到了类B,这种依赖具有偶然性、临时性,是非常弱的关系。但是类B的变化会影响到类A。例如一个人A需要在写字的时候需要用到铅笔B,当A写完字之后,A与B之间的关系就解除掉了。在代码层面,为类B作为参数被类A在某个方法中使用。在Java中,依赖表现为:局部变量,方法中的参数和对静态方法的调用。
在这里插入图片描述

1.4 依赖注入DI

  依赖注入(Dependency Injection,缩写为DI)是Spring实现控制反转的实现方法,可以理解为Spring IOC容器通过依赖注入的方式创建bean对象。

  通过DI,对象的依赖关系将由Spring IoC容器在创建对象的时候进行设定。对象无需自行创建或管理它们的依赖关系,依赖关系将被自动注入到需要它们的对象当中去。

1.5 装配

  创建应用组件之间的行为通常称为装配(wiring),也就是说当一个对象A的属性是另一个对象B时,在实例化对象A时,需要为这个对象属性B进行实例化。

1.6 Spring IoC容器

  在使用Spring时,应用对象生存于Spring容器(container)中。Spring IoC容器负责创建对象,装配它们,配置它们并管理它们的整个生命周期。

2、对象的创建方式

  控制反转(IoC)的核心是对象的创建权的问题。

  在实际生活中,人们要用到一样东西,人们的基本想法是找到东西,比如想喝杯橙汁,在没有饮品店的日子里,最直观的做法是,要买果汁机、橙子,准备开水。请注意这是你自己“主动”创造的过程,也就是一杯橙汁需要主动创造。然后到了今时今日,由于饮品店的盛行,已经没有必要自己去榨橙汁了。想喝橙汁的想法一出现,第一个想法是找到饮品店的联系方式,通过电话或微信等渠道描述你的需要、地址、联系方式等,下订单等待,过会就会有人上橙汁了。请注意你并没有“主动”创造橙汁,即橙汁是由饮品店创造的,而不是你,但是也完全达到了你的要求。

  针对上面的实际生活场景,对应了两种创建对象的方式:主动创建对象和被动创建对象。

2.1 主动创建对象

  如果需要橙汁,那么就等于需要橙汁、开水、糖,这些是原料,而搅拌机是工具。如果需要主动创造橙汁,那么要对此创建对应的对象——JuiceMaker(制作橙汁的人)和Blender(搅拌机)。

  搅拌机代码如下所示:

package com.ccff.spring.ioc.accidence;

//搅拌机类
public class Blender {
    /**
     * 搅拌
     * @param water
     * @param fruit
     * @param sugar
     * @return
     */

    public String mix(String water, String fruit, String sugar){
        String juice = "这是一杯由液体:"+water+"\n水果:"+fruit+"\n糖量:"+sugar+"\n组成的果汁";
        return juice;
    }

}

  果汁制作类代码如下所示:

package com.ccff.spring.ioc.accidence;

//制作果汁类
public class JuiceMaker {

    private Blender blender = null; //搅拌机
    private String water;   //水描述
    private String fruit;   //水果描述
    private String suger;   //糖量描述

    /**
     * 生成果汁
     * @return
     */
    public String makeJuice(){
        blender = new Blender();
        return blender.mix(water,fruit,suger);
    }

    public Blender getBlender() {
        return blender;
    }

    public void setBlender(Blender blender) {
        this.blender = blender;
    }

    public String getWater() {
        return water;
    }

    public void setWater(String water) {
        this.water = water;
    }

    public String getFruit() {
        return fruit;
    }

    public void setFruit(String fruit) {
        this.fruit = fruit;
    }

    public String getSuger() {
        return suger;
    }

    public void setSuger(String suger) {
        this.suger = suger;
    }
}

  主动创造橙汁,需要我们实现可能不太熟悉的工作——如何搅拌橙汁,比如这里的mix方法,显然这不是一个好的办法,而对象果汁(juice)是需要依赖于水(water)、水果(fruit)、糖(sugar)和搅拌机(blender)去实现的,这个关系也会要求我们自己去维护。

  在现实世界中,一个复杂的系统面对着成百上千种情况,如果都需要这样维护,那么会十分复杂,更多的时候,我们并不希望了解这类过程,因为有些东西我们并不需要知道过程,只需要获得最后的结果。正如果汁的例子一样,显示生活中我们更加希望的是通过对饮品店的描述得到果汁,只想对结果进行描述,得到我们所需要的东西,这就是被动创建对象了。

2.2 被动创建对象

  假设已经提供了果汁制造器(JuiceMaker2),具体代码如下:

package com.ccff.spring.ioc.accidence;

public class JuiceMaker2 {

    private String beverageShop = null;
    private Source source = null;

    public String makeJuice(){
        String juice = "这是一杯由"+beverageShop+"饮品店提供的"+source.getSize()+source.getSugar()+source.getFruit();
        return juice;
    }

    public String getBeverageShop() {
        return beverageShop;
    }

    public void setBeverageShop(String beverageShop) {
        this.beverageShop = beverageShop;
    }

    public Source getSource() {
        return source;
    }

    public void setSource(Source source) {
        this.source = source;
    }
}

  那么只需要对其进行描述就可以得到果汁了。假设饮品店还会给我们提供这样的一个描述(Source),具体代码如下:

package com.ccff.spring.ioc.accidence;

public class Source {
    private String fruit;   //水果类型
    private String sugar;   //糖分描述
    private Integer size;   //大小杯

    public String getFruit() {
        return fruit;
    }

    public void setFruit(String fruit) {
        this.fruit = fruit;
    }

    public String getSugar() {
        return sugar;
    }

    public void setSugar(String sugar) {
        this.sugar = sugar;
    }

    public Integer getSize() {
        return size;
    }

    public void setSize(Integer size) {
        this.size = size;
    }
}

  显然我们并不需要去关注果汁是如何制造出来的,系统采用XML对这个清单进行描述,具体配置如下:

<bean id="source" class="com.ccff.spring.ioc.accidence.Source">
    <property name="fruit" value="橙汁" />
    <property name="sugar" value="少糖" />
    <property name="size" value="大杯" />
</bean>

  上面的配置对果汁进行了描述,接着需要选择饮品店,假设选择的是鲜果时间,那么配置如下:

<bean id="juiceMaker2" class="com.ccff.spring.ioc.accidence.JuiceMaker2">
    <property name="beverageShop" value="鲜果时间" />
    <property name="source" ref="source" />
</bean>

  上面的配置将饮品店设置为鲜果时间,这样就指定了鲜果时间饮品店为我们提供服务,而订单则引用我们之前的定义,这样使用下面的代码就可以得到一杯果汁了。

JuiceMaker2 juiceMaker2 = (JuiceMaker2) ctx.getBean("juiceMaker2");
String juice = juiceMaker2.makeJuice();

  这个过程,果汁是由鲜果时间所制造的,我们并不关心制造的过程,我们所需要关心的是对果汁如何描述,选择哪个店去制造果汁,这才是现今人们的习惯,这个理念也可以应用于程序开发。

3、Spring IoC阐述

  这里再次阐述控制反转(IoC)的概念:控制反转是一种通过描述(在Java中可以通过XML或者注解的方式)并通过第三方(如Spring中的Spring IoC容器)去产生或获取特定对象的方式,即将对象的创建与装配的权利交由第三方管理。

  在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入。正如上述橙汁的例子,果汁制造器依赖于饮品店和订单去制造果汁的,而饮品店是别人去创造的,我们只需要知道它能生产果汁就可以了,并不需要去理解如何创建果汁。

  Spring会提供IoC容器去管理对应的资源,正如橙汁的例子中的饮品店和订单资源,由它们形成依赖注入的关系,其中果汁制造器用到饮品店和订单两个资源。

  控制反转最大的好处在于降低对象之间的耦合,在一个系统中有些类,具体如何实现并不需要去理解,只需要知道它有什么用就可以了。只是这里对象的产生依靠于IoC容器,而不是开发者主动的行为。主动创建模式,责任归于开发者,而在被动的模式下,责任归于IoC容器。基于这样的被动形式,我们就说对象被控制反转了。

4、Spring IoC容器

  容器是Spring框架的核心。Spring容器使用依赖注入(DI)管理构成的应用组件,它会创建相互协作的组件的关联。毫无疑问,这些对象更简单干净,更易于理解,更易于重用并且更易于进行单元测试。

  Spring容器并不是只有一个,Spring自带了多个容器实现,可以归为两种不同的类型:bean工厂与应用上下文。

  bean工厂由org.springframework.beans.factory.BeanFactory接口定义,是最简单的容器,提供基本的DI支持。但bean工厂对大多数应用来说往往太低级 ,因此应用上下文要比bean工厂更受欢迎。

  应用上下文由org.springframework.context.ApplicationContext接口定义,是基于BeanFactory构建,并提供应用框架级别的服务,例如从属性文件解析文本信息以及发布应用事件给感兴趣的事件监听者。

  Spring自带了多种类型的应用上下文,以下几个是最有可能遇到的:

  AnnotationConfigApplicationContext: 从一个或多个基于Java的配置类中加载Spring应用上下文。

  AnnotationConfigWebApplicationContext: 从一个或多个基于Java的配置类中加载Spring Web应用上下文。

  ClassPathXmlApplicationContext: 从类路径下的一个或多个XML配置文件中加载上下文定义,把应用上下文的定义文件作为类资源。

  FileSystemXmlApplicationContext: 从文件系统下的一个或多个XML配置文件中加载上下文定义。

  XmlWebApplicationContext: 从Web应用下的一个或多个XML配置文件中加载上下文定义。

  应用上下文准备就绪后,我们就可以调用上下文的getBean()方法从Spring容器中获取bean。

5、Spring Bean的定义与初始化

  在Spring IoC中bean的定义和初始化是两大步骤,它是先定义,然后初始化和依赖注入的。

  bean的定义分为以下3步:

  第一步,Resource定位,这步是Spring IoC容器根据开发者的配置,进行资源定位,在Spring的开发中,通过XML或者注解都是十分常见的方式,定位的内容是由开发者所提供的。

  第二步,BeanDefinition的载入,这个时候只是将Resource定位到的信息,保存到Bean定义中(BeanDefinition),此时并不会创建Bean的实例。

  第三步,BeanDefinition的注册,这个过程就是将BeanDefinition的信息发布到Spring Ioc容器中,注意,此时仍旧没有对应的Bean的实例创建。

  做完了这3步,Bean就在Spring IoC容器中被定义了,而没有被初始化,更没有完成依赖注入,也就是没有注入其配置的资源给Bean,那么它还不能完全使用。对于初始化和依赖注入,Spring Bean还有一个配置选项——lazy-init,其含义就是时候初始化Spring Bean。在没有任何配置的情况下,它的默认值是default,实际值为false,也就是Spring IoC默认会自动初始化Bean。如果将其设置为true,那么只有当我们使用Spring IoC容器的getBean()方法获取它时,它才会进行Bean的初始化,完成依赖注入。

6、Spring Bean的生命周期

  对于Bean而言,在容器中存在其生命周期,它的初始化和销毁也需要一个过程,在一些需要自定义的过程中,我们可以插入代码去改变它们的一些行为,以满足特定的需求,这就需要使用到Spring eBean生命周期的知识。

  生命周期主要是为了了解Spring IoC容器初始化和销毁Bean的过程,通过对它的学习就可以知道如何在初始化和销毁的时候加入自定义的方法,以满足特定的需求。Bean的生命周期如下:
在这里插入图片描述
  Bean生命周期详细描述如下:

  ① Spring对bean进行实例化;

  ② Spring将值和bean的引用注入到bean对应的属性中;

  ③ 如果bean实现了BeanNameAware接口,Spring将bean的ID传递给setBeanName()方法;

  ④ 如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入;

  ⑤ 如果bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext()方法,将bean所在的应用上下文的引用传入进来;

  ⑥ 如果bean实现了BeanPostProcessor接口,Spring将调用它们的postProcessorforeInitialization()方法;

  ⑦ 如果bean实现了InitializingBean接口,Spring将调用它们的afterPropertiesSet()方法。类似的,如果bean使用init-method声明了初始化方法,该方法也会被调用;

  ⑧ 如果bean实现了BeanPostProcessor接口,Spring将调用它们的postProcessAfterInitialization()方法;

  ⑨ 此时,bean已经准备就绪,可以被用用程序使用了,它们将一直驻留在应用上下文中,知道该应用上下文被销毁;

  ⑩ 如果bean实现了DisposableBean接口,Spring将调用它的destroy()接口方法。同样,如果bean使用destroy-method声明了销毁方法,该方法也会被调用。

猜你喜欢

转载自blog.csdn.net/weixin_36378917/article/details/86132719