依赖注入

概念

IoC(Inversion of Control,控制反转 ):控制权由应用代码中转到了外部容器,控制权的转移,所谓控制反转。

依赖注入:依赖注入是控制反转(IOC)的一个层面,用创建对象而只需要描述如何创建它们。

IoC 负责装配 Bean,即创建应用对象之间协作关系,是依赖注入(DI,Dependency injection)的本质。

装配方式优先级:自动装配 > Java 配置文件 > XML 配置文件

1 自动装配

Spring 容器可以自动配置相互协作 beans 之间的关联关系。这意味着 Spring 可以自动配置一个 bean 和其他协作 bean 之间的关系。

Spring 从两个角度来实现自动装配

1.1 组件扫描

Spring 自动发现 ApplicationContext 中所创建的 bean

1)@Component

让 Spring 为类自动创建 bean,默认 ID 是类名称的首字母小写。也可给注解传入指定的参数指定名字。

@Component  
@Component("lonelyHeartsClub")

还有 @Repository、@Service、@Controller 等

2)@ComponentScan

开启 Component 扫描,默认设置该目录以及子目录下所有被 @Component 注解修饰的类。也可指定扫描的包。

@ComponentScan("soundsystem")//扫描指定包  
@ComponentScan(basePackages = "soundsystem")
@ComponentScan(basePackages = {"soundsystem", "video"})
@ComponentScan(basePackageClasses = {CDPlayer.class, DVDPlayer.class})//扫描指定类所属的包

1.2 自动装配

Spring 自动满足 bean 之间的依赖

@Autowired:声明自动装配,可用在构造器、方法、成员变量上。

只要对应类型的 bean 有且只有一个,则会自动装配。推荐通过构造器注入以避免 NullPointerException 问题。

1)注入到 Java 集合类

@Autowired 可直接将接口的实现 Bean 注入到如下几种类型的集合中

  • list:用来注入一系列的值,允许有相同的值。
  • set:用来注入一些列的值,不允许有相同的值。
  • map:用来注入一组”键-值”对,key 必须为 String。
  • props:也可以用来注入一组”键-值”对,这里的键、值都字符串类型。
@Autowired
private List<BeanInterface> list;
@Autowired
private Map<String,Interface> map;

application.properties

table-info.list[0]=list_value1
table-info.list[1]=list_value2
table-info.map.map_key1=map_value1
table-info.map.map_key2=map_value2
table-info.maplist.map_key1[0]=maplist_value1
table-info.maplist.map_key1[1]=maplist_value2
table-info.maplist.map_key2[0]=maplist_value3

TableConfig

@ConfigurationProperties(prefix = "table-info")
public class TableConfig {
private List<String> list;
private Map<String, String> map;
private Map<String, List<String>> maplist;
}
2)允许 null 值

默认要求依赖对象必须存在,如允许 null 值,可设置 @Autowired(required=false)

还有 @Resource 等

3)推荐对构造函数进行注释

@Autowired
private EnterpriseDbService service;

改为

private final EnterpriseDbService service;

@Autowired
public EnterpriseDbController(EnterpriseDbService service) {
this.service = service;
}

可以明确成员变量的加载顺序

2 Java 配置文件

想要装配第三方库中的组件时,因为没办法在它的类上添加 @Component 和 @Autowired 注解,不能自动化配置,必须要采用显式装配。

2.1 创建配置类

@Configuration:表明是配置类

2.2 声明 bean

@Bean:声明 bean,需编写方法创建所需类型实例, 注解 @Bean。默认 ID 是方法名,也可通过 name 属性指定名字。

@Bean(name = "lonelyHeartsClub")
public CompactDisc sgtPeppers() {
return new SgtPeppers();
}

2.3 装配 bean

在 JavaConfig 中装配 bean 的最简单方式就是引用创建 bean 的方法。

默认情况下,Spring 中所有的 bean 都是单例模式,Spring 会拦截 @Bean 注解的函数调用,并返回之前创建好的 bean。

@Bean
public CDPlayer cdPlayer() {
return new CDPlayer(sgtPeppers());
}

3 XML 配置文件

维护已有的 XML 配置, 在完成新的 Spring 工作时, 使用自动化配置和 JavaConfig。

创建一个 XML 文件, 并且以 元素为根。使用 元素声明 bean 并指定 class 属性。 元素注入构造器

可以同时使用两种方式的依赖注入,最好的选择是使用构造器参数实现强制依赖注入,使用 setter 方法实现可选的依赖关系。

3.1 set 注入

首先容器会触发一个无参构造函数或无参静态工厂方法实例化对象,之后容器调用 bean 中的setter 方法完成 Setter 方法依赖注入。

private OrderServiceImp orderService;
public void setOrderService(OrderServiceImp orderService) {
this.orderService = orderService;
}

Spring 配置 XML 文件:其中配置声明 OrderAction 类存在属性 orderService。程式运行时候,会将已经实例化的 orderService 对象调用 setOrderService 方式注入。

<bean name="orderAction" class="com.pec.action.OrderAction">
<property name="orderService" ref="orderService"></property>
</bean>
<bean name="orderService" class="com.pec.service.imp.OrderServiceImp"></bean>

3.2 构造器注入

构造器依赖注入在容器触发构造器的时候完成,该构造器有一系列的参数,每个参数代表注入的对象。

private OrderServiceImp orderService;
public OrderAction(OrderServiceImp orderService) {
this.orderService = orderService;
}

Spring 配置 XML 文件

<bean name="orderAction" class="com.pec.action.OrderAction">
<constructor-arg ref="orderService"></constructor-arg>
</bean>
<bean name="orderService" class="com.pec.service.imp.OrderServiceImp"></bean>

4 多种配置方法混用

通常的做法是:无论使用 JavaConfig 或者 XML 装配,都要创建一个 root configuration;并且在这个配置文件中开启自动扫描机制

4.1 JavaConfig 中引用

  • @Import:导入其他的 JavaConfig
  • @ImportResource:导入 XML 配置文件
@Import({CDPlayerConfig.class, CDConfig.class})
@ImportResource("classpath: cd-config.xml")

4.2 XML 配置中引用

标签引入其他的 XML 配置文件

<import resource="classpath:/spring/calculate-web.xml"/>

标签导入 Java 配置文件

<bean class="soundsystem.CDConfig" />

5 BeanFactory 和 FactoryBean的区别

BeanFactory

BeanFactory,以Factory结尾,表示它是一个工厂类(接口), 它负责生产和管理bean的一个工厂。在Spring中,BeanFactory是IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。

FactoryBean

一般情况下,Spring通过反射机制利用的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean的形式

以Bean结尾,表示它是一个Bean,不同于普通Bean的是:它是实现了FactoryBean接口的Bean,根据该Bean的ID从BeanFactory中获取的实际上是FactoryBean的getObject()返回的对象,而不是FactoryBean本身,如果要获取FactoryBean对象,请在id前面加一个&符号来获取。

Author: iMine
Link: https://imine141.github.io/2020/07/13/Spring/IoC/%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.