java - Spring - Ioc容器

132 阅读4分钟

IoC容器

容器是一种为某种特定组件的运行提供必要支持的一个软件环境

通常来说,使用容器运行组件,除了提供一个组件运行环境之外,容器还提供了许多底层服务。例如,Servlet容器底层实现了TCP连接,解析HTTP协议等非常复杂的服务

Spring的核心就是提供了一个IoC容器,它可以管理所有轻量级的JavaBean组件,提供的底层服务包括组件的生命周期管理、配置和组装服务、AOP支持,以及建立在AOP基础上的声明式事务服务等

主要介绍Spring容器如何对组件进行生命周期管理和配置组装服务

IoC原理

不直接new一个DataSource,而是注入一个DataSource,这个小小的改动虽然简单,却带来了一系列好处:

  • BookService不再关心如何创建DataSource,因此,不必编写读取数据库配置之类的代码;
  • DataSource实例被注入到BookService,同样也可以注入到UserService,因此,共享一个组件非常简单;
  • 测试BookService更容易,因为注入的是DataSource,可以使用内存数据库,而不是真实的MySQL配置。 因此,IoC又称为依赖注入(DI:Dependency Injection),它解决了一个最主要的问题:将组件的创建+配置与组件的使用相分离,并且,由IoC容器负责管理组件的生命周期。

依赖注入方式

Spring的IoC容器同时支持属性注入和构造方法注入,并允许混合使用。

无侵入容器

在设计上,Spring的IoC容器是一个高度可扩展的无侵入容器。所谓无侵入,是指应用程序的组件无需实现Spring的特定接口,或者说,组件根本不知道自己在Spring的容器中运行。这种无侵入的设计有以下好处:

  • 应用程序组件既可以在Spring的IoC容器中运行,也可以自己编写代码自行组装配置;
  • 测试的时候并不依赖Spring容器,可单独进行测试,大大提高了开发效率。

使用Annotation配置

使用Annotation配合自动扫描能大幅简化Spring的配置,我们只需要保证:

  • 每个Bean被标注为@Component并正确使用@Autowired注入;
  • 配置类被标注为@Configuration@ComponentScan
  • 所有Bean均在指定包以及子包内。

image.png

定制Bean

Scope

容器都返回一个新的实例,这种Bean称为Prototype(原型),它的生命周期显然和Singleton不同。声明一个Prototype的Bean时,需要添加一个额外的@Scope注解:

image.png

注入List

要指定List中Bean的顺序,可以加上@Order注解:

image.png

可选注入

默认情况下,当我们标记了一个@Autowired后,Spring如果没有找到对应类型的Bean,它会抛出NoSuchBeanDefinitionException异常。

可以给@Autowired增加一个required = false的参数:

@Component
public class MailService {    
    @Autowired(required = false)    
    ZoneId zoneId = ZoneId.systemDefault();    
    ...
}

创建第三方Bean

如果一个Bean不在我们自己的package管理之类,例如ZoneId,如何创建它?

答案是我们自己在@Configuration类中编写一个Java方法创建并返回它,注意给方法标记一个@Bean注解:

初始化和销毁

在Bean的初始化和清理方法上标记@PostConstruct@PreDestroy

Spring容器会对上述Bean做如下初始化流程:

  • 调用构造方法创建MailService实例;
  • 根据@Autowired进行注入;
  • 调用标记有@PostConstructinit()方法进行初始化。

而销毁时,容器会首先调用标记有@PreDestroyshutdown()方法。

使用别名

相同类型的Bean只能有一个指定为@Primary,其他必须用@Quanlifier("beanName")指定别名;

注入时,可通过别名@Quanlifier("beanName")指定某个Bean。

使用Resource

Spring提供了一个org.springframework.core.io.Resource(注意不是javax.annotation.Resource),它可以像Stringint一样使用@Value注入:

image.png

注入配置

Spring容器看到@PropertySource("app.properties")注解后,自动读取这个配置文件,然后,我们使用@Value正常注入:

 复制代码
@Value("${app.zone:Z}")String zoneId;

使用条件装配

Spring允许通过@Profile配置不同的Bean;

Spring还提供了@Conditional来进行条件装配,Spring Boot在此基础上进一步提供了基于配置、Class、Bean等条件进行装配。