Pengzna's blog 👋

Jul 05, 2022

Spring 学习-1

1. IoC 容器

  • Inversion of Control (IoC)

1.1. Spring IoC Container & Bean

IoC 也成为依赖注入,是对象仅通过*构造函数参数、工厂方法的参数 或在对象实例被构造 或从工厂方法返回后在对象实例上设置的属性来定义它们的依赖项。*

  • 容器在创建 Bean 时注入这些依赖性。这个过程是 Bean 本身的逆过程,因此得名控制反转
  • 在 Spring 中,构成应用程序主干并由 Spring IoC 容器管理的对象称为 bean。
  • bean 是由 Spring IoC 容器实例化、组装和管理的对象。 否则,bean 只是应用程序中众多对象之一。 Bean 以及它们之间的依赖关系反映在容器使用的配置元数据中。

1.2. 容器概述

org.springframework.context.ApplicationContext 接口代表 Spring IoC 容器,负责实例化、配置和组装 bean。 容器通过读取配置元数据来获取有关要实例化、配置和组装哪些对象的指令。 配置元数据以 XML、**Java 注解**或 Java 代码表示。 它可以让您表达组成应用程序的对象以及这些对象之间丰富的相互依赖关系。

  • 一般来说,创建并初始化了ApplicationContext后,就有了一个完全配置且可执行的系统或应用程序

1.2.1. 配置元数据

方式主要有三

  • 基于 XML(基本用不到,不详述)
  • 基于注解(后文提到)
  • 基于 Java(后文提到)

1.2.2. 实例化容器

  • Spring 的实例化用的比较少了,略

1.2.3. 使用容器

  • Spring 的实例化用的比较少了,略

1.3. Bean 概述

1.4. 依赖关系

1.4.1. 依赖注入

  • 基于构造函数的依赖注入
    • 基于构造函数的 DI 是通过容器调用具有多个参数的构造函数来完成的,每个参数代表一个依赖项。 调用带有特定参数的 static 工厂方法来构造 bean 几乎是等效的,本讨论将类似地处理构造函数和 static 工厂方法的参数。 以下示例显示了一个只能使用构造函数注入进行依赖注入的类
public class SimpleMovieLister {

// the SimpleMovieLister has a dependency on a MovieFinder
private final MovieFinder movieFinder;

// a constructor so that the Spring container can inject a MovieFinder
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}

// business logic that actually uses the injected MovieFinder is omitted...
}

SpringBoot 中多用注解结合构造函数进行 DI

  • 构造函数参数解析
    • 构造函数参数解析匹配通过使用参数的类型发生。 如果 bean 定义的构造函数参数中不存在潜在的歧义,那么在 bean 定义中定义构造函数参数的顺序就是在实例化 bean 时将这些参数提供给适当的构造函数的顺序。
  • Spring 中构造函数消歧义的问题
    • 略,SpringBoot 中很少碰到
  • 依赖解析流程
    • 容器执行 bean 依赖解析如下:
      • ApplicationContext 是用描述所有 bean 的配置元数据创建和初始化的。 配置元数据可以由 XML、Java 代码或注解指定。
      • 对于每个 bean,它的依赖关系以属性、构造函数参数或静态工厂方法的参数(如果您使用它而不是普通构造函数)的形式表示。 在实际创建 bean 时,将这些依赖关系提供给 bean。
      • 每个属性或构造函数参数都是要设置的值的实际定义,或者是对容器中另一个 bean 的引用。
      • 作为值的每个属性或构造函数参数都从其指定格式转换为该属性或构造函数参数的实际类型。 默认情况下,Spring 可以将字符串格式提供的值转换为所有内置类型,例如 intlongStringboolean 等等。

1.5. 基于注解的容器配置

1.5.1. @Required

@Required 注解适用于 bean 属性 setter 方法,如下例所示:

public class SimpleMovieLister {

private MovieFinder movieFinder;

@Required
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}

// ...
}

此注解表明必须在配置时通过 bean 定义中的显式属性值或通过自动装配来填充受影响的 bean 属性。 如果尚未填充受影响的 bean 属性,则容器将引发异常。 避免以后出现 NullPointerException 实例等。

@Required 注解和 RequiredAnnotationBeanPostProcessor 从 Spring Framework 5.1 开始正式弃用,赞成使用构造函数注入进行所需设置(或 InitializingBean.afterPropertiesSet 的自定义实现 () 或自定义 @PostConstruct 方法以及 bean 属性 setter 方法)

1.5.2. @Autowired

您可以将 @Autowired 注解应用于构造函数

public class MovieRecommender {

private final CustomerPreferenceDao customerPreferenceDao;

@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}

// ...
}

从 Spring Framework 4.3 开始,如果目标 bean 只定义了一个构造函数,则不再需要在这样的构造函数上添加 @Autowired 注解。 但是,如果有多个构造函数可用且没有主/默认构造函数,则必须至少用 @Autowired 注解其中一个构造函数,以便指示容器使用哪一个。

您还可以将 @Autowired 注解应用于 传统 setter 方法,如以下示例所示:

public class SimpleMovieLister {

private MovieFinder movieFinder;

@Autowired
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}

// ...
}

您还可以将注解应用于具有任意名称和多个参数的方法,如以下示例所示:

public class MovieRecommender {

private MovieCatalog movieCatalog;

private CustomerPreferenceDao customerPreferenceDao;

@Autowired
public void prepare(MovieCatalog movieCatalog,
CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}

// ...
}

您也可以将 @Autowired 应用于字段,甚至将其与构造函数混合,如下例所示:

public class MovieRecommender {

private final CustomerPreferenceDao customerPreferenceDao;

@Autowired
private MovieCatalog movieCatalog;

@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}

// ...
}

您还可以通过将 @Autowired 注解添加到需要该类型数组的字段或方法来指示 Spring 从 ApplicationContext 提供特定类型的所有 bean,如 以下示例显示:

public class MovieRecommender {

@Autowired
private MovieCatalog[] movieCatalogs;

// ...
}

默认情况下,当给定注入点没有匹配的候选 bean 可用时,自动装配失败。 对于声明的数组、集合或映射,至少需要一个匹配元素。

默认行为是将带注解的方法和字段视为指示所需的依赖项。 您可以更改此行为,如下例所示,通过将不可满足的注入点标记为非必需(即,通过在 @ 自动连线false):

public class SimpleMovieLister {

private MovieFinder movieFinder;

@Autowired(required = false)
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}

// ...
}

1.5.3. @Primary

  • @Primary 表示当多个 bean 是自动装配到单值依赖项的候选者时,应优先考虑特定 bean。 如果候选中恰好存在一个主要 bean,则它成为自动装配的值。
@Configuration
public class MovieConfiguration {

@Bean
@Primary
public MovieCatalog firstMovieCatalog() { ... }

@Bean
public MovieCatalog secondMovieCatalog() { ... }

// ...
}

1.5.4. 使用泛型作为自动装配限定符

  • 可以用泛型和@Autowired搭配装配

1.5.5. @Resource

Spring 还通过在字段或 bean 属性 setter 方法上使用 JSR-250 @Resource 注解 (javax.annotation.Resource) 来支持注入。

在没有指定显式名称的 @Resource 用法的唯一情况下,与 @Autowired 类似,@Resource 找到主要类型匹配而不是 一个特定的命名 bean 并解析众所周知的可解析依赖项:BeanFactoryApplicationContextResourceLoaderApplicationEventPublisherMessageSource 接口。

1.5.6. @Value

@Value 通常用于注入外化属性:

@Component
public class MovieRecommender {

private final String catalog;

public MovieRecommender(@Value("${catalog.name}") String catalog) {
this.catalog = catalog;
}
}

使用以下配置:

@Configuration
@PropertySource("classpath:application.properties")
public class AppConfig { }

以及以下 application.properties 文件:

catalog.name=MovieCatalog

在这种情况下,catalog 参数和字段将等于 MovieCatalog 值。

Spring 提供了一个默认的宽松嵌入值解析器。 它将尝试解析属性值,如果无法解析,则属性名称(例如 ${catalog.name})将作为值注入。 如果你想对不存在的值保持严格的控制,你应该声明一个 PropertySourcesPlaceholderConfigurer bean,如下例所示:

@Configuration
public class AppConfig {

@Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}

Spring Boot 默认配置一个 PropertySourcesPlaceholderConfigurer bean,它将从 application.propertiesapplication.yml 文件中获取属性。

@Value 包含 SpEL 表达式 时,该值将在运行时动态计算,如下例所示:

  • 注:SpEL 表达式是 Spring 表达式语言
@Component
public class MovieRecommender {

private final String catalog;

public MovieRecommender(@Value("#{systemProperties['user.catalog'] + 'Catalog' }") String catalog) {
this.catalog = catalog;
}
}

1.5.7. @Component 和进一步的定型注解

@Repository 注解是任何实现存储库角色或构造型(也称为数据访问对象或 DAO)的类的标记。 此标记的用途之一是自动转换异常。

Spring 提供了更多构造型注解:@Component@Service@Controller@Component 是任何 Spring 管理的组件的通用构造型。

@Repository@Service@Controller@Component 的特化,用于更具体的用例(在分别为持久层、服务层和表示层)。因此,您可以使用 @Component 注解您的组件类,但是,通过使用 @Repository@Service@ 注解它们Controller 相反,您的类更适合由工具处理或与切面相关联。例如,这些构造型注解是切入点的理想目标。 @Repository@Service@Controller 还可以在 Spring Framework 的未来版本中携带额外的语义。因此,如果您在服务层使用 @Component@Service 之间做出选择,@Service 显然是更好的选择。同样,如前所述,@Repository 已经被支持作为持久层中自动异常转换的标记。

1.6. 基于 Java 的容器配置

1.6.1. @Bean 和@Component

Spring 的新 Java 配置支持中的核心工件是 @Configuration 注解类和 @Bean 注解方法。

@Bean 注解用于指示一个方法实例化、配置和初始化一个由 Spring IoC 容器管理的新对象。 对于熟悉 Spring 的 ``XML 配置的人来说, @Bean注解与元素扮演着相同的角色。 您可以将带有@Bean注解的方法与任何 Spring@Component一起使用。 然而,它们最常与@Configuration` bean 一起使用。

@Configuration 注解一个类表明它的主要目的是作为 bean 定义的来源。 此外,@Configuration 类允许通过调用同一类中的其他 @Bean 方法来定义 bean 间的依赖关系。 最简单的 @Configuration 类如下:

@Configuration
public class AppConfig {

@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
使用 Component 注解

@Configuration 是一个类级别的注解,表明一个对象是 bean 定义的来源。 @Configuration 类通过带有 @Bean 注解的方法来声明 bean。 对 @Configuration 类上的 @Bean 方法的调用也可用于定义 bean 间的依赖关系。

OLDER > < NEWER