Spring Core Annotations



原文地址:http://www.baeldung.com/spring-core-annotations 此篇为译文,由于英语水平有限,可能对原文的理解有误,欢迎指正,不甚感激!

1. 概览

我们可以利用org.springframework.beans.factory.annotationorg.springframework.context.annotation这两个包提供的注解功能使用Spring的依赖注入。 我们经常称它为”Spring core annotations”,我将在本教程对此进行回顾。

2. DI有关的注解

2.1. @Autowired

我们可以使用@Autowired注解标记需要Spring去解析和注入的依赖项。

构造函数注入:

1
2
3
4
5
6
7
8
class Car{
    Engine engine;

    @Autowired
    Car(Engine engine){
        this.engine=engine;
    }
}

Setter注入:

1
2
3
4
5
6
7
8
class Car{
    Engine engine;

    @Autowired
    void setEngine(Engine engine){
        this.engine=engine;
    }
}

字段注入:

1
2
3
4
class Car{
    @Autowired
    Engine engine;
}

@Autowired注解有一个布尔类型的名为required的参数,默认值为true。它可以调整Spring在没有找到所需的bean时的行为,当值为true时,会抛出异常,值为false时,则什么都不会发生,只不过在调用“注入”的变量时,可能会得到一个null引用。 Note:如果使用的时构造函数注入,那么所有构造函数的参数都是必须的。 从Spring4.3版本开始,我们可以直接省略@Autowired注解,就可以实现依赖的注入,除非我们声明了两个或以上的构造函数,这时才需要指定一个构造函数用@Autowired修饰。 更多的信息可以阅读我们的其他文章@Autowiredconstructor injection

2.2. @Bean

@Bean标记一个实例化Spring bean的工厂方法:

1
2
3
4
@Bean
Engine engine(){
    return new Engine();
}

当需要返回类型的新实例时,Spring会调用这些方法。 返回的bean的name和工厂方法的名称一致,如果想要不同的名称,可以在@Bean注解中传入一个String类型的值,这个值会作为返回的bean的name:

1
2
3
4
@Bean("engine")
Engine getEngine(){
    return new Engine();
}

Note:用@Bean注解标记的所有方法,必须在@Configuration标记的类中。

2.3. @Qualifier

我们使用@Qualifier@Autowired注解,在一些使用不明确的情况下,提供所需的bean name或者bean id。 举个例子,下面的两个bean实现了同一个接口:

1
2
3
class Bike implements Vehicle{}

class Car implements Vehicle{}

如果Spring需要注入一个Vehicle bean,但是Vehicle有多个定义(实现),在这个案例中,我们可以使用@Qualifier注解显示的提供一个bean name

使用构造函数注入:

1
2
3
4
@Autowired
Biker(@Qualifier("bike") Vehicle vehicle){
    this.vehicle = vehicle;
}

使用setter注入:

1
2
3
4
@Autowired
void setVihicle(@Qualifier("bike") Vehicle vehicle){
    this.vehicle = vehicle;
}

或者:

1
2
3
4
5
@Autowired
@Qualifier
void setVihicle(Vehicle vehicle){
    this.vehicle = vehicle;
}

使用字段注入:

1
2
3
@Autowired
@Qualifier("bike")
Vehicle vehicle;

更多的信息,请阅读这篇文章

2.4. @Required

@Required注解用来在setter方法上标记我们想通过XML填入的依赖:

1
2
3
4
@Required
void setColor(String color){
    this.color = color;
}
1
2
3
<bean class="com.baeldung.annotations.Bike">
    <property name="color" value="green" />
</bean>

否则,会抛出BeanInitializationException异常。

2.5. @Value

我们可以使用@Value注解将属性值注入到bean中,@Value注解兼容构造函数注入、setter注入、字段注入。

构造函数注入:

1
2
3
Engine(@Value("8") int cyLinderCount){
    this.cyLinderCount = cyLinderCount;
}

setter注入:

1
2
3
4
@Autowired
void setCyLinderCount(@Value("8") int cyLinderCount){
    this.cyLinderCount = cyLinderCount; 
}

或者:

1
2
3
4
@Value("8")
void setCyLinderCount(int cyLinderCount){
    this.cyLinderCount = cyLinderCount; 
}

字段注入:

1
2
@Value("8")
int cylinderCount;

当然了,注入静态值是没有用的,不过我们可以在@Value中使用“占位字符串”的方式,写入一个外部定义的源数据。例如,在.properties或者.yaml文件。 假设有以下的.properties文件:

1
engine.fuelType=petrol

我们可以将engine.fuelType的值注入:

1
2
@Value("${engine.fuelType}")
String fuelType;

我们还可以使用@ValueSpEL表达式,关于更多的高级用法,可以阅读我们关于@Value的文章

2.6. @DependsOn

我们可以使用@DependsOn注解标注Spring在初始化当前(被@DependsOn标记)的bean之前,先初始化其他bean。通常情况下,这个依赖注入的关系是自动完成的,Spring会查找bean之间的显式的依赖关系自动初始化。 我们仅仅需要在初始化隐式依赖关系bean的时候使用@DependsOn注解,例如JDBC驱动加载或者静态变量初始化(静态变量会先于类加载,所以需要提前初始化其他的bean,博主注)。 我们可以使用@DependsOn指定一个当前bean依赖的另一个bean的名字,@DependsOn注解的参数需要一个String类型的数组(或者String字符串):

1
2
3
4
5
@DependsOn("engine")
class Car implements Vehicle {}

@DependsOn({"engine"})
class Car implements Vehicle {}

另外的,如果我们使用@Bean注解定义一个bean,那么这个工厂方法应该使用@DependsOn注解:

1
2
3
4
5
@Bean
@DependsOn("fuel")
Engine engine() {
    return new Engine();
}

2.7. @Lazy

我们使用@Lazy注解标记我们想要延迟初始化的bean,默认情况,Spring会在application context startup/bootstrapping时第一时间创建单例bean。然而,有时我们不需要在application startup时创建bean。 @Lazy注解的行为有些不同,取决与我们在什么位置使用它,我们可以在下列情况使用: - 用在@Bean标记的工厂方法上,会延迟该方法的调用(对应的,工厂方法中的bean也会延迟创建)。 - @Configuration标记的类和@Bean标记的方法都会收到影响。 - 用在@Component标记的类上,如果它不是一个@Configuration类,那么就会被延迟初始化。 - 用在@Autowired标记的构造函数、setter方法或者字段上面,可以延迟加载这个依赖。

@Lazy注解有一个bool类型的参数,默认值为true,这个参数会改变@Lazy注解的行为。 例如,在一个全局设置为懒加载时,第一时间加载;或者配置具体的@Bean工厂方法先于@Configuration类加载:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Configuration
@Lazy
class VehicleFactoryConfig {
 
    @Bean
    @Lazy(false)
    Engine engine() {
        return new Engine();
    }
}

更近一步的阅读,请查阅this article

2.8. @Lookup

@Lookup注解的方法告诉Spring在我们调用它时返回方法返回类型的一个实例。 更多的信息请查看关于Lookup注解的文章can be found in this article

2.9. @Primary

有时我们需要定义同类型的多个bean,这时Spring的依赖注入时不会成功的,因为Spring并不知道我们需要注入哪一个bean。 我们可能已经知道该问题的解决选项:使用@Qualifier标记所有的连接点,并且指定bean的name。 然而大多数时候我们只需要一个具体的bean,并不需要其他的bean,这时我们可以使用@Primary注解简化这个案例:我们可以用@Primary标注那个最常使用的bean,当注入点没有使用@Qualifier指定注入bean名称时,默认选择这个bean:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@Component
@Primary
class Car implements Vehicle {}
 
@Component
class Bike implements Vehicle {}
 
@Component
class Driver {
    @Autowired
    Vehicle vehicle; // 默认会注入@Primary标记的Car bean
}
 
@Component
class Biker {
    @Autowired
    @Qualifier("bike")
    Vehicle vehicle;
}

在上面的例子中,Car是被@Primary标记的,因此在Driver类中,Spring会默认注入Car bean。当然了在Biker bean中vehicle变量会赋值一个Bike对象,因为它是被@Qualifier明确指定为bike name的bean。

2.10. @Scope

我们可以使用@Scope注解定义@Component或者Bean作用域。作用域可以是singletonprototyperequestsessionglobalSession中的一个,或者自定义的作用域。 举例:

1
2
3
@Component
@Scope("prototype")
class Engine {}

3. 上下文配置注解

我们可以使用本节描述的内容来对应用上下文进行配置。

3.1. @Profile

如果我们希望Spring仅在特定配置文件处于活动状态时才使用@Component类或@Bean方法,我们可以用@Profile来标记它,我们可以在@Profile注解的参数中传入配置文件的名称。

1
2
3
@Component
@Profile("sportDay")
class Bike implements Vehicle {}

你可以从这篇文章中获取更多关于@Profile的信息。

3.2. @Import

使用@Import注解可以使用具体的@Configuration类而不是使用组件扫描,我们可以提供一个@Configuration类给@Import作为参数:

1
2
@Import(VehiclePartSupplier.class)
class VehicleFactoryConfig {}

博主注:@Import注解其实就是把多个分散的@Configuration配置类给整合到一起,使用的时候用这个一个类就可以了,举个例子:

 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
@Configuration
public class JavaConfigService {
    @Bean(name="userService")
    public Car getUerService(){
        return new UserService();
    }
}

@Configuration
public class JavaConfigModel {
    @Bean(name="user")
    public Car getUser(){
        return new User();
    }
}

//将两个配置合并起来
@Configuration
@Import({JavaConfigService.class,JavaConfigModel.class})
public class GlobalConfig {
    // 其他的bean的定义
}

public static void main (String args[]){
    // 在spring中就可以使用合并配置,而不用一个一个去实例化配置了
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(GlobalConfig.class);
    User user = (User)context.getBean("user");
    UserService us = (UserService)context.getBean("userService");
    context.close();
}

3.3. @ImportResource

我们可以使用该注解导入指定的XML配置,可以使用具体的XML文件的本地路径、或者别名作为该注解的参数:

1
2
3
@Configuration
@ImportResource("classpath:/annotations.xml")
class VehicleFactoryConfig {}

3.4. @PropertySource

通过这个注解,我们可以为应用程序定义属性文件:

1
2
3
@Configuration
@PropertySource("classpath:/annotations.properties")
class VehicleFactoryConfig {}

@PropertySource利用Java 8的repeating annotations功能,这意味着我们可以使用多种手段标记一个类:

1
2
3
4
@Configuration
@PropertySource("classpath:/annotations.properties")
@PropertySource("classpath:/vehicle-factory.properties")
class VehicleFactoryConfig {}

3.5. @PropertySources

我们可以使用这个注解来指定多个@PropertySource配置:

1
2
3
4
5
6
@Configuration
@PropertySources({ 
    @PropertySource("classpath:/annotations.properties"),
    @PropertySource("classpath:/vehicle-factory.properties")
})
class VehicleFactoryConfig {}

注意,从Java 8开始,我们可以通过repeating annotations功能实现上述功能。

4. 总结

在本文中,我们看到了最常见的Spring核心注解概述。 我们看到了如何配置bean注入和应用程序上下文,以及如何为组件扫描标记类。这些例子都可以在我们的GitHub上获得。