第②篇 Spring IoC——容器

Spring最成功的是其提出的理念,而不是技术本身。

概念

Spring所依赖的两个核心理念:

  • 一个是控制反转(IoC)。
  • 另一个是面向切面编程(Aspect Oriented Programming,AOP)。

IoC是Spring的核心,可以说Spring是一种基于IoC编程的框架。因为Spring Boot是基于注解的开发Spring IoC,所以这章我会使用全注解来讲解Spring IoC,为后续打下基础。

IoC是一种通过描述来创建或者获取对象的技术,而这个技术不是Spring甚至不是Java独有的。Java初学者更多的时候熟悉的是使用new关键字来创建对象,而Spring是通过描述来创建对象的。Spring Boot并不建议使用XML,而是通过注解的描述生成对象,所以本章主要是通过注解来介绍Spring IoC技术。

一个系统可以创建各种对象,并且这些对象都需要进行管理。此外我们应该注意到对象之间并不是孤立的,它们之间还可能存在依赖的关系。例如,一个班级是由多个老师和同学组成的,那么班级就依赖于多个老师和同学了。为此Spring IoC还提供了依赖注入的功能,使得我们能够通过描述来管理各个对象之间的关系。

为了描述上述的班级、同学和老师这3个对象关系,我们需要一个容器去管理它们和它们之间的关系。在Spring中把每一个需要管理的对象称为Spring Bean(简称Bean),而Spring管理这些Bean的容器,称为Spring IoC容器(或者简称IoC容器)。IoC容器需要具备两个基本的功能:

  • 通过描述管理Bean,包括定义、发布、获取和销毁Bean;
  • 通过描述完成Bean之间的依赖关系。

在使用IoC之前,需要对IoC容器有一个基本的认识。

IoC容器的两个接口——BeanFactory和ApplicationContext

IoC容器会把Spring所创建的Bean给装配进来进行管理,所以我们主要就和IoC容器打交道,那么我们首先就需要去了解IoC容器底层的内容。对于IoC容器来说,最重要的就是BeanFactory和ApplicationContext这两个接口的定义。由于容器很复杂,一般来说我们只需要知道这两个接口大概能够提供的功能即可,也就是要知道要干什么,无需过度深入理解IoC容器的内容。

IoC容器的底层接口是BeanFactory,它的源码如下:

package org.springframework.beans.factory;

import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;

public interface BeanFactory {

    String FACTORY_BEAN_PREFIX = "&";

    Object getBean(String name) throws BeansException;

    <T> T getBean(String name, Class<T> requiredType) throws BeansException;

    Object getBean(String name, Object... args) throws BeansException;

    <T> T getBean(Class<T> requiredType) throws BeansException;

    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

    <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);

    <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);

    boolean containsBean(String name);

    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

    @Nullable
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;

    @Nullable
    Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;

    String[] getAliases(String name);
}

前面我们论述了Spring IoC技术是一个容器技术,那么所有的容器都会实现这个接口,这样就可以使用这个接口的方法,从IoC容器中获取Bean的相关信息了。

这里我们主要先谈多个getBean()方法,这些方法都是从IoC容器中获取Bean的。

注意:我们可以看到存在以按名称(by name)查找Bean的,也存在按类型(by type)查找Bean的,请注意这个非常重要,后续遇到的问题和面试的问题都会集中在这里。

BeanFactory是底层接口,它的实现类很多,如图1所示。

图1
图1

 图1中,可以看到存在很多的IoC容器,但是它们都会遵守BeanFactory的规范,我们了解BeanFactory接口,就能使用IoC容器了。

不过我们需要讨论另外一个十分重要的接口,那就是ApplicationContext,从图1可以看到它是BeanFactory的子接口。它的重要性在于扩展了许多的接口,极大的增强了IoC容器的功能

图2

从图2,可见ApplicationContext扩展了消息国际化接口(MessageSource)、环境可配置接口(EnvironmentCapable)、应用事件发布接口(ApplicationEventPublisher)和资源模式解析接口(ResourcePatternResolver),所以它的功能会更为强大。

 在Spring Boot中,因为不推荐使用XML,所以我不再介绍XML相关的内容。这里我推荐使用的是AnnotationConfigApplicationContext进行实践,因为它是一个全注解的Spring IoC容器,和Spring Boot的环境几乎一致。

开发第一个IoC容器

开发容器首先需要有存到容器的东西,那就是bean,为了创建Bean,我们先创建一个角色类,代码很简单。

package com.ykzhen.articles2.po;

public class Role {

    private Long id;
    private String roleName;
    private String note;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    public String getNote() {
        return note;
    }

    public void setNote(String note) {
        this.note = note;
    }
}

这个代码很简单,相信不用我做任何的解释。之前,我谈过IoC容器是通过描述去创建Bean的,那么如何描述呢?一般来说可以使用传统的XML,也可以使用Java配置文件,因为Spring Boot推荐使用Java配置文件,按趋势XML使用不多了,所以我这里使用Java配置文件。先给出代码,如下。

package com.ykzhen.articles2.conf;

import com.ykzhen.articles2.po.Role;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

// @Configuration标明这个文件是一个配置文件
@Configuration
public class AppConfig {

    // 将方法返回的值装配到IoC容器中,并且该Bean的名称为“role”
    @Bean("role")
    public Role initRole() {
        var role = new Role();
        role.setId(1L);
        role.setRoleName("role_name_1");
        role.setNote("note_1");
        return role;
    }

}

主要到下面的两个注解:

@Configuration:表示这个类是一个Java配置文件,这样就可以描述如何创建IoC容器。

@Bean:注解标注在方法上,表示将方法返回的值,装配到IoC容器中。

有了上面这两个文件,我们开始创建Spring IoC容器,代码如下。

package com.ykzhen.articles2;

import com.ykzhen.articles2.conf.AppConfig;
import com.ykzhen.articles2.po.Role;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class AppMain {
    public static void main(String[] args) {
        // 通过配置文件,创建IoC容器
        var ctx = new AnnotationConfigApplicationContext(AppConfig.class);
        try {
            // 获取Bean
            var role = ctx.getBean(Role.class);
            System.out.println(role.getRoleName());
        } finally {
            // 关闭IoC容器
            ctx.close();
        }
    }
}

我们运行代码,可以得到如下结果:

15:22:23.611 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@1cd072a9
15:22:23.620 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
15:22:23.711 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
15:22:23.712 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
15:22:23.713 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
15:22:23.715 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
15:22:23.719 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'appConfig'
15:22:23.723 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'role'
role_name_1
15:22:23.751 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@1cd072a9, started on Thu Jan 12 15:22:23 CST 2023
 

可见我们测试成功了。

总结:

上面,我们完成了装配Bean到IoC容器中,然后从IoC容器中去获取Bean的过程,这也是IoC容器最基本的功能。

对于装配Bean到IoC容器,我们还需要去讲述扫描的方式,还有其他相关的内容,这是后续文章需要做的事情了。

猜你喜欢

转载自blog.csdn.net/ykzhen2015/article/details/128294926