Spring注解学习

了解spring的启动顺序和过程

Spring 初始化顺序

  1. 如果在spring的配置文件中配置bean,然后加载spring配置文件的情况下,spring会按照配置文件中bean的顺序,依次初始化xml中所有bean

  2. 如果某个类的头上,使用@Component/@Repository/@Service/@Controller注解,spring会将这个对象作为Bean注册近spring容器中。

  3. 如果通过实现ApplicationContextAware接口来加载spring上下文环境,

    • spring会在初始化bean后,调用setApplicationContext()方法
  4. 如果实现了InitializingBean接口,spring会在初始化bean后,并在setApplicationContext(如果有)执行完后, 调用afterPropertiesSet()方法

使用spring管理的bean时,无需对调用的对象进行new操作,是需要在使用时,加上@Autowired,spring会将对应的bean注入本类中。

以上是旧时写的内容


以下是 20210813 写的内容

几个常用概念理解

  1. “装配”,是把类转为由spring容器管理的bean。
  2. “注入”,是把spring容器管理的bean赋值给类中的成员变量,即属性。(也可以赋值给 set 方法,构造函数,效果一样)

常见用词搭配,自动装配,依赖注入

目的,解决对象间相互依赖协作创建新对象的问题。例如一个类A可以作为另一个类B中的一个属性,如果实例化B,就需要实例化A

在一个Bean中调用另外一个Bean,就需要依赖注入。

集成 Spring 时的两个常用名词

  • JavaConfig ,就是 @Configuration 的形式
  • XML ,就是使用使用 XML 的形式,把所有内容写再在 XML 中解析成 java object 。

Spring 官方文档中常提及的几个含义类似的单词

下面都是指一个实体类 ,它们在 Spring 中都可以通过添加相关注解被注册为一个 Bean 。

  • 虽然表达的含义有些许不同,但是在理解文档时可以粗略的把它们理解为一类东西。

  • entity / entity class

  • domain

    • instances of domain objects
    • domain class
  • POJO

在上下文提到的是 class / object 中出现的,都是指类中的字段

  • property
  • property names of the entity
  • field
  • attribute

Spring 的 Bean 创建以及注入

下面是关于 Spring 中 Bean 的创建,注入,已经与其相关的常用注解的说明整理。

什么是 Bean ? 为什么交给 Spring 管理 ?

在 Spring 中创建,并交由 Spring 管理的对象,称为 Bean。

Spring 会对注册到其容器中的 Bean 做管理,包括 Bean 的配置信息,依赖关系,初始化,作用域,生命周期等方面集中做判断处理。

可以理解为是 Spring 框架的要求,也可以理解为是它的特性。

使用spring管理的bean时,无需对调用的对象进行new操作,只需要在使用时,加上@Autowired,spring会将对应的bean注入本类中。

与 Bean 有关,常用的注解有哪些?分别怎么用?

从 Bean 的装配和注入两个角度看。

Bean 的装配(/创建)相关注解 (IoC 的体现)

bean配置有三种方法:

  • 基于 xml 配置 Bean 【不推荐】
  • 使用注解定义 Bean 【推荐】
  • 基于 Java 类提供 Bean 的定义信息 【推荐】

从作用对象上分类

  • @Component ,作用于类
  • @Bean , 作用于方法

与 @Component 相关的内容

Spring 提供了多个与 @Component 注解功能类似,但是含义略有不同的注解,都可以实现将标注的类注册为 Spring 管理的 Bean。

以下内容主要参考 Spring Boot常用注解(一) - 声明Bean的注解

总结:

@Component, @Service, @Repository, @Controller, @RestController 这 5 个注解的区别

  1. 从源码级别来看,@Service, @Repository, @Controller, @RestController 都是 @Component 注解的别名,效果都一样。Spring 会将被这 5 个注解标注的类加载到 Spring 上下文中。
  2. 从业务分层使用的角度来看,他们用于标注不同的业务层。具体看下面介绍。
    • 在项目中,却建议你严格按照除 @Component 外的其余几个注解的含义使用在项目各层中。这对分层结构的 web 架构很有好处。

本节主要介绍注解:

  • @Component , 没有明确角色的组件,当暂时不知道或不确定某个类该分为哪一层时,使用。
  • @Service , 与 @Component 作用一样,在业务逻辑层(service 层)使用。
  • @Repository , 作用同上,在数据访问层(dao 层)使用。
  • @Controller , 作用同上,在访问控制层(web 层) 使用
  • @RestController , 是 @Controller + @ResponseBody 的组合
  • @Scope , 主要作用是在注册时,提供修改 Bean 的作用域。

下面几个是与 @Controller/@RestController 结合使用的注解,放在 Web 章节中可能更好

  • @RequestMapping
  • @PathVariable
  • @ResponseBody
@Component 注解
  1. 作用在类上
  2. 作用域默认为 singleton
  3. 使用注解配置和类路径扫描时,被 @Component 注解标注的类,会被 Spring 扫描并注册为 Bean 。
  4. @Component 注解,一般在不确定 Bean 为哪一层的时候使用,可以作用在任何层次,把普通 POJO 注册到 Spring 容器。
  5. 不推荐使用 @Component 注解,而应该使用它的扩展,如,@Service,@Repository …,它们的功能虽然一样,但后者职能更清晰,利于维护。

使用特点:

Spring 提供 @Component 注解两种使用方式,

  • @Component

    没有传参数时,Bean 名称默认为当前类的类名,首字母小写。
    @Component(value=”xxx”) / @Component(“xxx)
    使用参数时,xxx 作为 Bean 名称

1
// TODO(SLi): 添加测试说明代码
@Service 注解
  1. @Service 是 @Component 注解的一个特例,作用在类上
  2. 默认作用域为 singleton
  3. 使用注解配置和类路径扫描时,被 @Service 注解标注的类,会被 Spring 扫票并注册为 Bean 。
  4. @Service 用于标注业务层组件,表示定义一个 Bean 。
  5. @Service 使用时没有传参数时,Bean 名称默认为当前类的名称,首字母小写。
  6. @Service(“xxx”) 或 @Service(“xxx”) 使用时传参数,使用 xxx 作为 Bean 名称
1
// TODO(SLi): 添加测试说明代码
@Scope 注解
  1. @Scope 注解作用在类上和方法上;
  2. @Scope 注解用来配置 Spring Bean 的作用域默认为单例模式);
  3. @Scope 与 其他类@Component 注解(例如,@Component,@Service,@Repository,@Controller )组合使用可以修改 Bean 的作用域;

@Scope 有 5 种取值:

基本作用域:

  1. “singleton” 单例模式,Spring 容器中有且只有一个 Bean 实例,只要 Spring 容器不销毁或退出,该 Bean 实例初始化后就会一直存活。所有调用处的对象地址都一样。

    singleton 模式 每次得到的 Bean 对象相同,单例

    prototype 模式,每次得到的 Bean 对象是不同的。

  2. “prototype” 原型模式, 每次获取 Bean 的时候,就会创建一个新的实例,实例创建后,Spring 可以对其初始化,但不负责销毁,即不对该模式下的 Bean 实例的整个生命周期负责。

Web 作用域,下面三个只适用于 Web 程序:

  1. “request” request 模式,每次 HTTP 请求都会产生一个新的 Bean,同时该 Bean 仅在当前 HTTP request 内有效,当请求结束后,该对象的生命周期即结束。
  2. “session” session 模式,也是 每次 HTTP 请求都会产生一个新的 Bean,同时该 Bean 仅在当前 HTTP session 内有效。
  3. “application” application 模式,全局作用域。
1
2
// TODO(SLi): 添加测试说明代码
@Service@Scope 一起使用,测试两种基本作用域的使用,通过比较两种作用域生成的对象的地址来对比效果的不同。
@Repository 注解
  1. @Repository 注解作用在类上
  2. 作用域默认是 singleton
  3. 使用注解配置和类路径扫描时,被 @Repository 注解标注的类会被 Spring 扫描并注解为 Bean;
  4. @Repository 注解用于标注数据访问层(组件),即 DAO 组件;
  5. @Repository 注解的作用不只是将类注册为 Bean ,同时它还能将所标注的类中抛出的数据访问异常封装为 Spring 的数据访问异常类。
1
// TODO(SLi): 添加测试说明代码
@Controller 注解
  1. 作用在类上
  2. 使用注解配置和类路径扫描时,被 @Controller 注解标注的类会被 Spring 扫描并注册为 Bean ;
  3. @Controller 用于标注 Web 中控制层组件;
  4. 被 @Controller 注解标注的类,负责处理由 DispatcherServlet 分发的请求,它把用户请求的数据经过业务层处理之后封装为一个 Model,然后再把该 Model 返回给对应的 View 进行展示。
  5. @Controller 和 @RequestMapping、@RequestParam 等一些注解共同处理 URL 的映射。
@RestController 注解
  1. 是一个组合注解,@RestController=@Controller + @ResponseBody
  2. @RestController 注解注解将返回的对象输出到客户端;
  3. 如果返回字符串,直接返回
  4. 如果返回的不是字符串,默认使用 Jackson 将对象序列化成 JSON 字符串后输出。

@Controller 和 @RestController 的详细内容参考

与 @Bean 相关的内容

Spring Boot 同样推荐使用 Java 配置完全代替 XML 配置,Java 配置是通过 @Configuration@Bean 注解实现的。

  • @Configuration 注解声明当前是一个配置类,相当于 Spring 中的一个 XML 文件 ;
  • @Bean 注解作用在方法上,声明当前方法的返回值是一个 Bean ,相当于 Spring XML 文件中的 ;
@Bean 注解
  1. 作用在方法上;
  2. 表示方法的返回值是一个 Spring 容器管理的 Bean ;
  3. @Bean 注解一般和 @Component 或者 @Configuration 一起使用
  4. @Bean 注解默认作用域为单例 singleton, 同样可以通过 @Scope(“prototype”) 设置为原型模式作用域

Bean 名称

  • @Bean 注解标注的方法的 Bean 名称,默认为方法名,首字母小写;
  • @Bean 注解可以使用 value 属性或者别名 name 重新定义 Bean 名称;
  • @Bean 注解接受一个 String 数组,允许为一个 Bean 配置多个名称(包含一个主名称和一个或多个别名)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Bean
public CommandLineRunner demo(CustomerRepository repository) {
return args -> {};
}

@Bean("custRepository") // 此时 Bean 的名字为 custRepository,而不是默认的 demo
public CommandLineRunner demo(CustomerRepository repository) {
return args -> {}
}

// 通过 context 中所有的 Bean 名称可以知道,上面代码的 Bean 名称分别为 demo, custRepository
Iterator<String> iterator = context.getBeanFactory().getBeanNamesIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}

@Bean 与其他注解一起使用

  • @Profile 注解为在不同环境下使用不同的配置提供了支持,如开发环境和生产环境的数据库配置用不同的;
  • @Scope 注解将 Bean 的作用域从单例模式改变为指定的作用域;
  • @Lazy 注解只有在默认单例作用域的 情况下才有效;
  • @DependsOn 注解表示在当前 Bean 实例化之前需要先实例化依赖的其他 Bean;
  • @Primary 注解,如果需要注入单个目标组件但多个 bean 按类型匹配,@Primary 是一种在注入点级别解决歧义的机制。(这样相当于当没有指定Bean名称,且存在多个相同类型的 bean 时,默认取被 @Primary 标注的 bean)

Bean 的初始化和销毁
在实际开发中,经常会遇到在 Bean 使用之前或使用之后需要做一些必要的操作,Spring 对 Bean 的生命周期的操作提供了支持。

  • Java 配置方式: 使用 Bean 的 initMethoddestrodMethod
  • 注解方法: 利用 JSR-250 的 @ConstConstruct@PreDestroy
1
2
3
4
5
6
7
8
9
// TODO(SLi): 添加测试说明代码
// 定义该 Bean 的名称为 yourBean,同时调用初始化方法 init(),
@Bean(value = "yourBean", initMethod = "init")
// 依赖一个名称为 myBean 的 Bean
@DependsOn("myBean")
@Lazy
public MyBean myBean2() {
return new MyBean("yourBean");
}

proxyBeanMethods这个参数简单来说就是
当在 @Configuration 类中使用 @Bean 注解时

  1. proxyBeanMethods=true,
    @Bean Methods in @Configuration Classes

    • 默认支持AOP拦截,进行方法增强,
    • 同时支持 “inter-bean references”(bean 间引用),即同一个 @Configuration 类中的 @Bean 方法可以不通过 @Autowired等方法注入,像普通java方法那样直接调用,但同时能确保调用的Bean是增强后的(应该不同 @Configuration 间的 Bean也能直接调用,这个需要测试)
  2. proxyBeanMethods=false
    @Bean Lite Mode

    • 不支持 “inter-bean references”(bean 间引用)
    • 此时

当在例如 @Component 这些其他 Bean 注解标注的类中使用 @Bean 注解时
是true 或者 false ,没有影响。在这些类中使用 @Bean 时,本身就不支持 “inter-bean references”(bean 间引用) ,即需要通过 @Autowired 等注解来注入 Bean ,而不能直接调用 @Bean 标注的方法。

@Bean Lite Mode 的翻译:
https://docs.spring.io/spring-framework/docs/5.3.9/javadoc-api/org/springframework/context/annotation/Bean.html

@Bean 方法也可以在没有用 @Configuration 注释的类中声明。例如,bean 方法可以在 @Component 类中声明,甚至可以在普通的旧类中声明。在这种情况下,@Bean 方法将以所谓的“精简”模式进行处理。

精简模式下的 Bean 方法将被容器视为普通工厂方法(类似于 XML 中的工厂方法声明),并正确应用范围和生命周期回调。在这种情况下,包含类保持不变,并且包含类或工厂方法没有异常约束。

与@Configuration 类中 bean 方法的语义相反,精简模式不支持“bean 间引用”。相反,当一个@Bean-method 在精简模式下调用另一个 @Bean-method 时,该调用是标准的 Java 方法调用;Spring 不会通过 CGLIB 代理拦截调用。这类似于 @Transactional 间的方法调用,在代理模式下,Spring 不会拦截调用——Spring 只在 AspectJ 模式下这样做。

关于 @Component@Bean 一些总结性的内容:

关于 @Component@Bean 一些总结性的内容:

  1. @Component 作用对象是类(class),这个是 a type-level annotation 类级别的注解;@Bean 作用对象是方法(method),是a method-level annotation 方法级别的注解;
  2. Bean 的类型,从这个角度看,@Component 标注的类,即为Bean的类型,xxx.class;@Bean 标注的方法的返回值为 Bean 的类型;
  3. Bean 的名称,@Component 默认不用参数时,@Component 标注的类名首字母小写即为 Bean 的名称;@Component 使用参数时,@Component(“xxx”) 或 @Component(value=”xxx”) 效果一样,此时,xxx 即为 Bean 的名称;
  4. Bean 的名称,@Bean 默认不用参数时,@Bean 标注的方法的方法名即为 Bean 的名称;@Bean 使用参数时,@Bean(“xxx”) 或 @Bean(value=”xxx”) 效果一样,此时,xxx即为 Bean 的名称;同时,@Bean 还支持指定多个名字,@Bean({“xxx1”,”xxx2”,…});
  5. 注册到 Spring 容器中的方式不同;
    • @Component 通常是通过路径扫描(用@ComponentScan注解定义要扫描的路径,从中找出标识了需要装配的类,并自动装配到 Spring 的 Bean 容器中)来自动侦测已经自动装配到 Spring 容器中;
    • @Bean 注解通常是在需要定义产生一个Bean 的方法上标注该注解,@Bean 告诉 Spring 这是某个类的实例,当我们需要使用它的时候还给我。
  6. @Bean 注解比 @Component 注解的自定义性更强。当某些场景 @Component 注解无法定义我们需要的 Bean 时,我们就只能通过 @Bean 注解来实现,例如,当我们引用第三方库中的类装配到 Spring 中时,只能通过 @Bean来实现;
@Configuration 注解

以下内容主要参考
Spring Boot常用注解(三) - 实现Java配置的注解

Annotation Type Configuration

Spring 提供的 @Configuration 注解,可以用来配置多个 Bean ,

@Configuration 与 @Bean 两个注解的关系,就像 XML 中 的关系

  1. @Configuration 注解作用在类、接口(包含注解)上;
  2. 用于定义配置类,可替换 XML 配置文件;
  3. @Configuration 注解标注的类中可以声明一个或多个 @Bean 方法;
  4. @Configuration 注解标注的类不能是 final 类型;(意味着能被继承extends,可以有子类)
  5. 嵌套的(nested) @Configuration 类必须是 static 的;
    • 因为是嵌套类,所以 嵌套的被 @Configuration 标注的类中被 @Bean 标注的方法,会被自动注册(will be registered automatically)为 Bean。
  6. 可以单独使用,也可以结合 Enviroment 这个 Bean APi,@PropertySource,@Value,@Profile 等注解一起使用;
    • 如果与 @Lazy 注解结合使用,表明这个类中所有被 @Bean 标记的方法都将延迟实例化
    • 同时,@Lazy 可以与每个 @Bean 标注的方法单独结合使用。
    • @Profile 结合使用,用于不同的环境中, 当然 @Bean 也可以单独与 @Profile 结合使用
  7. @Configuration 注解从源码上看也是 @Component 注解的别名,所以配置组件扫描(@ComponentScan)时也能被扫到。
  8. @Configuration 注解标注的类也可以利用 @ComponentScan 配置自己的组件扫描;
  9. 也可以像任何常规 @Component 一样利用 @Autowired

下面这句话,是 @Configuration 注解 API 中的内容,表明了 Bean 是何时被实例化(或者初始化)的,be eagerly instantiated at container bootstrap time 在IoC 容器启动时就实例化

1
By default, @Bean methods will be eagerly instantiated at container bootstrap time. To avoid this, @Configuration may be used in conjunction with the @Lazy annotation to indicate that all @Bean methods declared within the class are by default lazily initialized. Note that @Lazy may be used on individual @Bean methods as well. 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// TODO(SLi): 添加测试说明代码

public class MyBean {
// define a class
}

@Configuration
public class MyConfigration {

// 此时 Bean 的名称为 方法名 myBean,类型是 MyBean
@Bean
public MyBean myBean() {
// instantiate, configure and return bean ...
return new MyBean();
}
}


// 这个是在说 第5点,嵌套类必须为 static
@Configuration
public class AppConfig {

@Autowired DataSource dataSource;

@Bean
public MyBean myBean() {
return new MyBean(dataSource);
}

@Configuration
static class DatabaseConfig {
@Bean
DataSource dataSource() {
return new EmbeddedDatabaseBuilder().build();
}
}
}

// 利用 @ComponentScan 配置自己的组件扫描 问题,当什么情况会配置多个组件扫描?
@Configuration
@ComponentScan("com.acme.app.services")
public class AppConfig {
// various @Bean definitions ...
}

@Configuration + @Bean + Environment

通过使用@Autowired}注释将org.springframework.core.env.Environment注入 @Configuration类,可以查找外部值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
public class MyConfig {

// Spring 内置的包含所有配置属性的 Bean
@Autowired
private Environment environment;

@Bean("myEnvBean")
public MyBean myBean() {
MyBean myBean = new MyBean();
myBean.setPort(environment.getProperty("server.port", "8080"));
return myBean;
}
}

@Configuration + @Bean + Environment + @PropertySource

@PropertySource 注解可以读取自定义的 properties 配置文件,自定义的 properties 文件放在 src/main/resources 文件路径下

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
@PropertySource("classpath:properties/app.properties")
public class MyPropertySourceConfigration {

@Autowired
private Environment environment;

@Bean("myPSBean")
public MyBean myBean() {
MyBean myBean = new MyBean(environment.getProperty("jdbc.username", "请设置用户名"), environment.getProperty("jdbc.password", "请设置密码"));
return myBean;
}
}

也可以结合 @Value 获取 Environment 中的属性值。样例略

TODO proxyBeanMethods

这个在 Spring 5.2加入的新参数什么意思?后面再研究一下
内容参考 官方 API

public abstract boolean proxyBeanMethods

Specify whether @Bean methods should get proxied in order to enforce bean lifecycle behavior, e.g. to return shared singleton bean instances even in case of direct @Bean method calls in user code. This feature requires method interception, implemented through a runtime-generated CGLIB subclass which comes with limitations such as the configuration class and its methods not being allowed to declare final.

The default is true, allowing for ‘inter-bean references’ via direct method calls within the configuration class as well as for external calls to this configuration’s @Bean methods, e.g. from another configuration class. If this is not needed since each of this particular configuration’s @Bean methods is self-contained and designed as a plain factory method for container use, switch this flag to false in order to avoid CGLIB subclass processing.

Turning off bean method interception effectively processes @Bean methods individually like when declared on non-@Configuration classes, a.k.a. “@Bean Lite Mode” (see @Bean’s javadoc). It is therefore behaviorally equivalent to removing the @Configuration stereotype.

问题:

1
2
3
4
5
6
7
8
9
10
11
@SpringBootApplication
public class ComponentApplication {

public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication
.run(ComponentApplication.class, args);
... // others
Iterator<String> iterator = context.getBeanFactory().getBeanNamesIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}

这段代码扫描获取的 Bean ,为什么包含主类本身?@SpringBootApplication = @Configuration + @EnableAutoConfiguration + @ComponentScan ,但是类中并没有哪里标注 ComponentApplication 这个类,为什么会扫描到?

componentApplication

根包目录下的另一个 Application 中的方法为什么也会一起执行?能扫描到我能理解,但是不理解被 @Bean 标注的方法为什么会执行?

1
2
@Bean
public CommandLineRunner demo(CustomerRepository repository) {

ComponentScan

下面内容主要参考 Annotation Type ComponentScan

用于配置 @Configuration 类的组件扫描指令。一般会在主配置类上配置。

可以指定 basePackageClasses() 或 basePackages()(或其别名 value())来定义要扫描的特定包。

如果没有定义特定的包,将从声明该注解的类所在包开始扫描。

只有能被@ComponentScan扫描的包及包下的被 @Component 或其扩展注解 标记的类才能识别为 Bean 。

使用:

@ComponentScan(“包”) 指定哪个包,就扫描哪个包下的注解并识别。

属性 value

是 basePackages 的别名

例如:

1
@ComponentScan("org.my.pkg") instead of @ComponentScan(basePackages = "org.my.pkg")

属性 basePackageClasses

basePackages() 的类型安全(type-safe)替代方案,用于指定要扫描带注解组件的包。

将扫描指定的每个类的包。

The package of each class specified will be scanned.

define 定义

declare 声明

declarations

Bean 的注入 (DI 的体现)

除 XML 注入外,使用注解的注入方式有

  1. 字段注入
  2. set 方法注入
  3. 构造函数注入
    上面的三种注入方式效果一样,无需同时存在

可以使用下面的注解:

  • @Autowired
  • @Resource

相同点:

  1. @Resource 和 @Autowired 都是做 bean 的注入时使用
  2. 两者都可以写在字段和 setter 方法上。两者如果都写在字段上,那么就不需要再写 setter 方法。

不同点,详见后面介绍。

@Autowired

下面内容主要参考 Spring Boot常用注解(二) - 注入Bean的注解

spring框架-bean的装配与注入

Annotation Type Autowired

  1. 这是 JSR-330 @Inject 注解的替代方法;

  2. @Autowired 注解是按照类型(byType)注入依赖对象

  3. @Autowired 注解作用在构造函数,方法,方法参数,类字段以及注解上

    @Target(value={CONSTRUCTOR,METHOD,PARAMETER,FIELD,ANNOTATION_TYPE})

  4. 默认情况下,它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。

  5. 如果想要使用按照名称(byName)来注入,可以结合 @Qualifier 注解一起使用。

样例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Service
public class UserServiceImpl implements IService {
// 下面这两种 @Autowired 只要使用一种即可
@Autowired
private UserDaoImpl userDao; // 用于字段上

@Autowired
public void setUserDao(UserDaoImpl userDao) { // 用于属性的 set 方法上
this.userDao = userDao;
}
}

public class TestServiceImpl {
@Autowired
@Qualifier("userDao")
private UserDao userDao;
}

@Autowired 注解原理

在Spring Boot 应用启动时,Spring 容器会自动装载一个 org.springframework.beans.factory.annonation.AutowiredAnnotationBeanPostProcessor 处理器,当容器扫描器扫描到 @Autowired 注解时,就会在 IoC 容器就会找到相应类型的 Bean,并实现注入。

总结:
在使用@Autowired注解时,首先在容器中查询对应类型的bean

  1. 如果查询结果Bean刚好为一个,自动注入
  2. 如果查询结果Bean不止一个,通过 @Qualifier 注解按照名称指定自动注入的Bean
  3. 如果没有查询到对应类型的Bean,由于默认@Autowired(required=true),会抛出常,解决方法是使用@Autoiwired(quired=false)
  4. @Autowired(quired=true)意味着依赖是必须的
  5. @Autowired(quired=false)等于告诉 Spring:在找不到匹配 Bean 时也不报错
@Autowired 用在构造函数上
  1. 一般情况,任何给定的 bean 类只有一个构造函数可以声明这个注解,并将 required() 属性设置为 true,构造函数在用作 Spring bean 时会自动装配。

the constructor to autowire when used as a Spring bean.

  1. 此外,如果 required 属性设置为 true,则只能使用 @Autowired 注释单个构造函数。

  2. 如果注解标注了多个非必需的构造函数,则这些构造函数将被视为自动装配的候选者。将选择通过匹配 Spring 容器中的 bean 可以满足的依赖项数量最多的构造函数。如果没有一个候选可以满足,那么将使用主/默认构造函数(如果存在)。

  3. 类似地,如果一个类声明了多个构造函数,但没有一个用 @Autowired 标注,则将使用主/默认构造函数(如果存在)。

  4. 如果一个类只声明一个构造函数,那么它的 @Autowired 注解可以省略。

  5. 带注解的构造函数不必是 public 的。

用在字段上
  1. 标注 @Autowired 注解的字段不必是 public 的;
@Autowired 用在 method 上
  1. 配置方法(methods)可以有任意名称和任意数量的参数;

  2. 每个参数都将被 Spring container(容器)中匹配的 bean 自动装配。

    each of those arguments will be autowired with a matching bean in the Spring container.

  3. Bean 属性 setter 方法(setter methods)实际上只是这种通用配置方法的一个特例。

  4. 这样的配置方法不必是 public 的。

Autowired Parameters

主要用在单元测试中
the JUnit Jupiter support in the spring-test module (see the TestContext framework reference documentation for details).

TODO

下面两部分后面需要再研究,主要涉及 在Arrays,Collection 和 Maps 中的注入,还有不支持的部分。

  1. Autowiring Arrays, Collections, and Maps

In case of an array, Collection, or Map dependency type, the container autowires all beans matching the declared value type. For such purposes, the map keys must be declared as type String which will be resolved to the corresponding bean names. Such a container-provided collection will be ordered, taking into account Ordered and @Order values of the target components, otherwise following their registration order in the container. Alternatively, a single matching target bean may also be a generally typed Collection or Map itself, getting injected as such.

  1. Not supported in BeanPostProcessor or BeanFactoryPostProcessor

Note that actual injection is performed through a BeanPostProcessor which in turn means that you cannot use @Autowired to inject references into BeanPostProcessor or BeanFactoryPostProcessor types. Please consult the javadoc for the AutowiredAnnotationBeanPostProcessor class (which, by default, checks for the presence of this annotation).

1. @Qualifier 注解

@Autowired + @Qualifier = @Resource

解释:

  1. Spring 容器中如果有多个相同类型的 Bean,这时如果直接通过 @Autowired 注解标注获取该类型 Bean,就会报错,这时需要结合 @Qualifier 注解来限定,即指定需要自动装配的 Bean 名称。

@Resource

  1. @Resource 注解可以指定 name 和 type,默认按照名称(byName)自动注入,由J2EE 提供,需要导入包 javax.annonation.Resource
  2. 有两个属性,所以,
    • 如果使用 name 属性,那么就是按照 Bean 的名称注入,
    • 如果使用 type 属性,就是按照 Bean 的类型注入 (此时就与 @Autowired 一样)

// IService 的实现类 UserServiceImpl 缺少 @Service 注解,所以启动 Spring Boot 的时候会报错
Field userService in com.example.accessingdatajpa.controller.AutowiredController required a bean of type ‘com.example.accessingdatajpa.service.IService’ that could not be found.

The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)

// IDao 的实现类有两个,在 Service 实现类中 @Autowired 标注的 IDao 无法获得唯一的 Bean ,从提示中可以得知,使用 @Qualifier 来限定
Description:

Field userDao in com.example.accessingdatajpa.service.UserServiceImpl required a single bean, but 2 were found:
- adminDaoImpl: defined in file [C:\Tableware\codes\spring-study-test\accessing-data-jpa\target\classes\com\example\accessingdatajpa\dao\AdminDaoImpl.class]
- userDaoImpl: defined in file [C:\Tableware\codes\spring-study-test\accessing-data-jpa\target\classes\com\example\accessingdatajpa\dao\UserDaoImpl.class]

Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022-2023 ligongzhao
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信