天天看点

Spring (二) IOC bean加载及bean依赖设置

上篇说到,spring就像一个第三方,为你注入你需要的类,而不用自己new,这里就引入了spring是如何发现我们编写的类并进行创建的,又是怎么来判断类与类之间的依赖关系的,并进行注入的。

总的来说,spring提供了三种方式进行类装配:

在XML文件中配置:类的定义和类之间的依赖关系通过xml文件描述,然后spring去加载这个xml文件,来创建bean和他们之间的依赖关系。

在Java配置类中进行配置:专门写一个java配置来描述bean和bean之间的依赖关系,spring通过这个类来进行加载

通过注解的方式配置:通过给类加上注解,spring会扫描所有添加了注解的类,依次创建bean和他们之间的依赖关系

  1. 自动装配

    目前主流的使用是第三种注解的方式,自动装配。因为比较方便,不用去维护xml文件和java类文件。所以先来学习一下通过注解方式。

    通过注解进行装配主要有两个过程:

    组件扫描:spring会扫描所有加了组件注解的类,并生成bean实例

    自动装配:根据注解识别出类之间的依赖关系,进行依赖bean的注入。

    下面通过一个小例子来说一下这个过程:

    同样,以上面的国王和将军来做例子,先来看将军,定义一个将军的接口;

    Spring (二) IOC bean加载及bean依赖设置
    然后创建一个他的实现类:
    Spring (二) IOC bean加载及bean依赖设置

    注意到这个类上面加了一个@Component,这个类的作用就是标识我是一个组件类, spring在启动的时候,会对扫描所有的类,当扫描到类使用这个注解进行标识的时候,就要对其进行装载。

    不过默认情况下,spring启动的时候是不会对类进行扫描的,所以必须通过配置开启扫描功能,开启的方式其实也是对应之前的另外两种装配方式:

    一、在一个java配置类中开启,如下:

    Spring (二) IOC bean加载及bean依赖设置

    这里先不关心类里面的内容,先来看看他的两个注解。

    第一个注解@Configuration表示当前类是一个配置类,用于配置spring扫描、装配信息的一个单独的java类。

    第二个注解@ComponentScan就代表开启了扫描功能,如果不进行其他的配置,spring会默认扫描与当前这个类所处同一包下的所有类,并进行自动装配。

    二、通过xml文件进行配置。

    通过在spring的配置文件中设置context标签的组件扫描功能,即可开启:

    Spring (二) IOC bean加载及bean依赖设置

    并且base-package中配置了在扫描的范围是在 com.springIOC这个包下。

    通过java配置类的方式同样也可以设置扫描的范围:

    Spring (二) IOC bean加载及bean依赖设置
    也可以设置多个包:
    Spring (二) IOC bean加载及bean依赖设置
    除了可以在注解中设置包以外,还可以设置类:
    Spring (二) IOC bean加载及bean依赖设置

    它会告诉,spring在扫描的时候扫描General.class类所在的包。

    一般我们不会采取设置类这样的方式,因为如果有个时候这个类突然被删掉了,这里就会出错。当然你也可以在包中专门建立一个空类来设置,这样就不用担心这种问题了。

    通过以上配置好了之后,spring就能够在启动的时候扫描相应的包,然后装载带有@Component注解的类。

    然后就到了第二步,怎么发现类与类之间的依赖关系,并进行注入的问题。

    依然是通过注解,来看国王类:

    Spring (二) IOC bean加载及bean依赖设置
    首先同样是在国王类上添加注解@Component,来告知spring我是个组件类,要扫描并加载我。然后第二个是@Autowired,这个注解的作用就是告诉spring,我需要依赖general,所以你要给我准备好genenral类,在我需要的时候直接用。这里的注解是放在构造器上的,通过构造器注入。除此之外还可以放在setter上面注入:
    Spring (二) IOC bean加载及bean依赖设置

    这样,spring就会在他加载的bean中寻找国王需要依赖的这个类,进行注入,如果只有一个bean满足条件的话(就像现在的情况,只有一个将军类GenaralA),就会将GeneralA加载进来。如果没有找到合适的bean,比如我们这里的将军类没有加上@component,spring中就不存在这个bean,所以就无法为国王类进行装配,这个时候spring就会抛出异常。

    为了避免这个异常,可以设置注解@Autowired的参数:

    Spring (二) IOC bean加载及bean依赖设置

    当spring无法找到bean的时候就会让国王类处于未装配状态,不会抛出异常。但是对于程序本身来说,在执行国王类的时候,就需要判断依赖的将军类是否存在,不然会因为将军类的不存在而抛出空指针异常。

    还有一种情况在于,spring中加载了多个类适合装配,比如之前提到的两个将军GeneralA和GeneralB,如果都加上@component,那么spring该选择哪个来进行装备,这种场景会有专门的配置,放到后面去学习。

    这里spring发现依赖关系是通过@Autowired来实现,这个注解是spring提供的。其实java本身也提供了一种注入规范,@Jnject注解同样更能达到相同的效果:

    Spring (二) IOC bean加载及bean依赖设置

    大部分情况下,@Autowire和@Inject是可以互换的。

    现在,来建立一个测试类,验证一下自动装配是否成功,这里,为了使得装配的时候只有一个将军类,我们把GeneralB的注解给去掉,不去加载,那么sping就会只加载generalA,并装配到国王类中。下面是国王测试类(这里是用spring的单元测试类进行测试):

    Spring (二) IOC bean加载及bean依赖设置
    这里我直接使用@Autowird把将军类注入(没有自己新建),然后在外部的将军类中,只将GeneralA添加组件的注解,GeneralB不添加,这样sping就会默认注入generalA。
    Spring (二) IOC bean加载及bean依赖设置
    Spring (二) IOC bean加载及bean依赖设置
    来看测试结果:
    Spring (二) IOC bean加载及bean依赖设置

执行的是GenealA将军的代码,说明注入成功。

当然有人可能会问,如果把两个将军类都添加@component,那么spring在装配的时候会装配哪一个?

真对这个问题,Spring也提供了一些办法,来加载指定的类。在学习这个之前,我们先来看看其他两种方式注入:java配置类和xml方式注入。

  1. java配置类装配

    自动装配是一种很强大的装配方式,但是可能会注意到,自动装配都是基于我们自己写的类,如果在应用中你使用到了第三方库的类,这些类已经封装好或者不允许修改,无法在类上面定义注解怎么办(例如第三方的数据库连接池),这时候就无法使用自动装配,需要通过显示的装配来进行。

    Java配置类的方式,是将类的信息和类与类之间的关系在一个单独的java类中描述,在sping进行加载的时候,会读取这个类,以此来进行bean的创建和装配。值得注意的是,这个类只描述bean和bean之间的关系,所以不应该有多余的其他代码,比如描述业务的、或者其他信息的,最好的方式就是把这个配置类单独放到一个包中进行管理,与其他代码区别开来。

    其实自一开始,我们在自动装配的时候,就已经创建偶一个配置类了,就是ScanConfig:

    Spring (二) IOC bean加载及bean依赖设置

    只是,这里设置了@ComponentSan来进行自动扫描包,找到我们设置有注解类,当把这个注解去掉会后,就会产生异常,因为spring找不到相关的类进行创建和注入。

    Java显示配置就是,不使用自动扫包,也不给相关类加入注解,通过把关系写在ScanConfig类中,当然为了表明他是一个配置类,@Configuratio注解还是少不了的。

    Spring (二) IOC bean加载及bean依赖设置
    首先看看spring创建Bean的配置,以GnenralA为例
    Spring (二) IOC bean加载及bean依赖设置
    @Bean注解的注解的作用在于告诉spring,当前方法会返回一个bean对象,spring要对这个对象进行加载,而这里返回的对象就是GeneralA。默认这里装载的bean名字是类名字的小写,也就是generalA,如果想自定义bean的名字,可以通过注解的name属性进行设置
    Spring (二) IOC bean加载及bean依赖设置
    同样GeneralB:
    Spring (二) IOC bean加载及bean依赖设置
    通过这样的配置方式,告诉spring他需要加载的类。再来看看他是如何进行装配的,这里以国王类为例子,给它装配GeneralA:
    Spring (二) IOC bean加载及bean依赖设置

    与上面的区别点在于,在返回的构造器中加入了之前定义GeneralA的方法,返回一个将军类到king的构造器中,达到装配的目的。但是前提是将军A类的GeneralA也必须设置到配置文件中,因为你要保证子国王加载的时候,将军类也会被加载。

    除此之外,更为推荐的是第二种方法:

    Spring (二) IOC bean加载及bean依赖设置

    这样的好处在于,这里直接引入了General,所以对于将军类的定义你不必仅仅限制于子java配置类中使用bean定义,还可以通过自动装配和xml的方式来加载,最终目的在于general能够被spring发现。

    另外一定要注意的就是,这里的方法是同过构造器进行注入,也可以使用set方法进行注入:

    Spring (二) IOC bean加载及bean依赖设置
    能达到相同的效果。
  2. 通过Xml装配

    虽然通过xml进行装配,是现在用的比较少的方式,但是少数场景下xml的配置还是比较适合的。同样,先来看看设置一个简单的bean:

    Spring (二) IOC bean加载及bean依赖设置

    这里设置了国王King的bean,id代表bean自定义的命名,不设置的默认是小写king,class设置的是类所在的路径(包含包命称)。

    再来看看依赖的注入,依赖的注入有很多种,先来看一下基于构造器的注入:

    Spring (二) IOC bean加载及bean依赖设置

    在声明了geneal之后,设置到King类构造器标签的ref值,以达到构造器注入效果。

    构造器还有一种简写的方式,命名空间:

    Spring (二) IOC bean加载及bean依赖设置

    第一个注意的是要引入头部文件,让xml识别c命名空间;

    第二就是配置方法;c代表命名空间,0-ref代表构造器的第一个参数,构造器如果有多个参数,可依次配置1-ref,2-ref等。

    上面说到的注入方式,都是注入一个对象,如果是注入一个字面量的话,比如简单的int或者string类型,spring提供了更加灵活地方式,比如下面这个类:

    Spring (二) IOC bean加载及bean依赖设置
    需要注入的两个都是简单的String类型,那在xml中可以直接这样配置:
    Spring (二) IOC bean加载及bean依赖设置
    通过构造器,使用value属性直接值注入。也可以使用命名空间:
    Spring (二) IOC bean加载及bean依赖设置
    通过用c:指定属性名字进行注入,也可以使用属性的顺序进行设置:
    Spring (二) IOC bean加载及bean依赖设置
    如果装备的是一个集合,比如下面这样一个:
    Spring (二) IOC bean加载及bean依赖设置
    那针对于构造器注入,是这样的配置:
    Spring (二) IOC bean加载及bean依赖设置

    在构造器下设置list标签,这里的list标签也可以替换为set标签,区别在于set会把重复的给去除掉,并且构建的是set集合。

    以上说的都是通过构造器注入的,现在来讲讲如何通过set方法注入,同样先用国王类来看:

    Spring (二) IOC bean加载及bean依赖设置
    有了国王类的set方法,就可以在xml中进行set方法注入的配置
    Spring (二) IOC bean加载及bean依赖设置
    同样,针对于set注入,也有命名空间的方式:
    Spring (二) IOC bean加载及bean依赖设置
    和构造器差不多的方式,字面量和list也是同样的方式:
    Spring (二) IOC bean加载及bean依赖设置
    首先保证相关类中有set方法存在,然后配置xml:
    Spring (二) IOC bean加载及bean依赖设置
    关于构造器和属性的注入就学习到这里,当然针对与不同的特殊场景还在其他的配置方式和格式,这里就不写了。
  3. 混合装配

    自动装配、java配置文件、xml文件,这三种方式各有各的好处。一般来说,是以自动装配为主,然后针对与不同的场景,采用java配置类和xml混合的方式来进行配置,现在就来学习一下混合配置。

    在java配置类中引入其他java配置类、xml配置文件

    这种场景是,比如你设置一个ScanConfig配置类,但是这个类里面所用的依赖、用到的bean是定义到另外一个java配置类或者xml文件中的,那么就可以通过引入这个配置类和xml文件来加载。

    比如,在ScanConfig中定义了国王类:

    Spring (二) IOC bean加载及bean依赖设置
    但是针对于国王类需要的将军类并没有定义在里面,是定义在另一个java配置文件中GenealConfig:
    Spring (二) IOC bean加载及bean依赖设置
    如果直接加载的话,就会出错,因为ScanConfig中找不到Genearal这个依赖,所以必须把在GeneralConfig中定义的General类引入到ScanConfig中,才能正确加载:
    Spring (二) IOC bean加载及bean依赖设置

    通过使用@import注解,就可以达到这样的效果。

    同样,如果General是在xml配置文件中定义的,那么这里是通过引入xml的配置:

    Spring (二) IOC bean加载及bean依赖设置

    在xml上引入java配置类、xml文件

    国王类定义xml文件中,并设置依赖,但不定义依赖的bean

    Spring (二) IOC bean加载及bean依赖设置
    依赖定义在java配置类或者其他xml文件中,就要通过引入的方式:
    Spring (二) IOC bean加载及bean依赖设置

    以上就是关于sping IOC的bean加载和依赖注入的配置,可能会存有些和大家理解不一样的地方,还有就是我写的比较简略,存在还有很多没涉及到的地方,欢迎和我讨论,帮组我进行改善,感激不尽!

    下一篇文章讲一讲spring不常用到的一些配置方式,其实说是不常用到,但是实际中还是会有使用的场景的。

继续阅读