Spring 核心注解介绍

Spring是一款轻量级的IOC框架,Spring的核心就是Ioc和DI,并通过俩者解耦。Ioc(Inversion of control)控制反转,可以把创建对象和查找依赖对象的权限交给Ioc容器控制,而不是传统的由这些对象的使用方(消费者)进行创建初始化操作。IoC是一种让服务消费者不直接依赖于服务提供者的组件设计方式,是一种减少类与类之间依赖的设计原则。DI(Dependency Injection)依赖注入,指容器复制创建和维护对象之间的依赖关系,而不是通过对象本身复制自己的创建和解决自己的依赖。控制反转是通过依赖注入实现的。

1 概述

在Spring中可通过一些注解(Annotation)来使用Spring DI引擎功能,它们定义在org.springframework.beans.factory.annotation 和 org.springframework.context.annotation包中。我们通常将这些称为“Spring核心注解”,本文将对它们进行介绍。

2 DI相关注解

2.1 @Autowired

我们可以使用@Autowired来标记Spring将要解析和注入的依赖项。有三种注入方式:构造函数注入,setter注入或字段注入。

构造函数注入:

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

setter注入

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

Field注入

class Car {
    @Autowired
    Engine engine;
}

@Autowired有一个名为required的布尔参数,默认值为true。当它没有找到合适的bean来连接时,Spring 容器将抛出 BeanCreationException 异常。如果为false,表示忽略当前要注入的bean,如果有直接注入,没有跳过,不会报错。

注意,如果我们使用构造函数注入,则所有构造函数参数都是必需的。

从版本4.3开始,除非我们声明至少两个构造函数,否则我们不需要显式地使用@Autowired注解构造函数。

2.2 @Bean

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

@Bean
Engine engine() {
    return new Engine();
}

当需要返回类的新实例时,Spring会调用这些方法。

默认情况下bean的名称和方法名称相同,你也可以使用name属性来指定:

@Bean(name="myEngine")
Engine engine() {
    return new Engine();
}

请注意,所有使用@Bean注释的方法都必须在@Configuration类中。

2.3 @Qualifier

使用@Qualifier和@Autowired来提供我们想要在不明确的情况下使用的bean id或bean名称。

例如,以下两个bean实现相同的接口:

class Bike implements Vehicle {}
 
class Car implements Vehicle {}

如果Spring需要注入一个Vehicle bean,它最终会有多个匹配的定义。在这种情况下,我们可以使用@Qualifier注释显式提供bean的名称。

使用构造函数注入:

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

使用setter注入:

@Autowired
void setVehicle(@Qualifier("bike") Vehicle vehicle) {
    this.vehicle = vehicle;
}

@Autowired
@Qualifier("bike")
void setVehicle(Vehicle vehicle) {
    this.vehicle = vehicle;
}

使用Field注入:

@Autowired
@Qualifier("bike")
Vehicle vehicle;

2.4 @Required

@Required注解适用于bean属性setter方法,并表示受影响的bean属性必须在XML配置文件在配置时进行填充。否则,容器会抛出一个BeanInitializationException异常。

@Required
void setColor(String color) {
    this.color = color;
}

<bean class="com.peterwanghao.annotations.Bike">
    <property name="color" value="green" />
</bean>

2.5 @Value

可以使用@Value将属性值注入bean。它有三种方式:构造函数,setter和字段注入。

构造函数注入:

Engine(@Value("8") int cylinderCount) {
    this.cylinderCount = cylinderCount;
}

Setter注入:

@Autowired
void setCylinderCount(@Value("8") int cylinderCount) {
    this.cylinderCount = cylinderCount;
}
或
@Value("8")
void setCylinderCount(int cylinderCount) {
    this.cylinderCount = cylinderCount;
}

Field注入:

@Value("8")
int cylinderCount;

当然,注入静态值是没有用的。因此,可以使用@Value中的占位符字符串来连接外部资源中定义的值,例如,在.properties或.yaml文件中定义的值。

我们假设.properties文件中定义了:

engine.fuelType=petrol

我们可以使用以下内容注入engine.fuelType的值:

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

即使使用SpEL,我们也可以使用@Value。

2.6 @DependsOn

指定Spring容器初始化当前Bean之前先初始化所依赖的Bean。

当依赖项是隐式时,我们只需要这个注释,例如,JDBC驱动程序加载或静态变量初始化。我们可以在依赖类上使用@DependsOn来指定依赖关系bean的名称。

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

2.7 @Lazy

一般情况下,Spring容器在启动时会创建所有的Bean对象,使用@Lazy注解可以将Bean对象的创建延迟到第一次使用Bean的时候。

这个注解会根据放置的位置产生不同的效果:

  • 与@Bean放在一起,用于延迟方法调用
  • 与@Configuration放在一起,影响类中所有@Bean的方法
  • 与@Component放在一起,这个bean将会懒初始化
  • 与@Autowired放在一起,将懒加载依赖项

此注解具有名为value的参数,其默认值为true。当为false时,代表不延迟立刻加载。

@Configuration
@Lazy
class VehicleFactoryConfig {
 
    @Bean
    @Lazy(false)
    Engine engine() {
        return new Engine();
    }
}

2.8 @Lookup

@Lookup注解是一个作用在方法上的注解,被其标注的方法会被重写,然后根据其返回值的类型,容器调用BeanFactory的getBean()方法来返回一个bean。

2.9 @Primary

有时我们需要定义多个相同类型的bean。在这种情况下,注入将失败,因为Spring不知道我们需要哪种bean。

我们已经看到了一个处理这种情况的方法:用@Qualifier标记所有连接点并指定所需bean的名称。

但是,大多数时候我们需要一个特定的bean而很少需要其他bean。我们可以使用@Primary来简化这种情况:如果我们用@Primary标记最常用的bean,它将在不确定的情况下作为首选:

@Component
@Primary
class Car implements Vehicle {}
 
@Component
class Bike implements Vehicle {}
 
@Component
class Driver {
    @Autowired
    Vehicle vehicle;
}
 
@Component
class Biker {
    @Autowired
    @Qualifier("bike")
    Vehicle vehicle;
}

在前面的例子中,Car是主要的载体。因此,在Driver类中,Spring注入了一个Car bean。当然,在Biker bean中,指定了值将是一个Bike对象。

2.10 @Scope

spring中scope是一个非常关键的概念,简单说就是对象在spring容器(IOC容器)中的生命周期,也可以理解为对象在spring容器中的创建方式。

目前,scope的取值有5种取值:

  • 在Spring 2.0之前,有singleton和prototype两种;
  • 在Spring 2.0之后,为支持web应用的ApplicationContext,增强另外三种:request,session和global session类型,它们只实用于web程序,通常是和XmlWebApplicationContext共同使用。
@Component
@Scope("prototype")
class Engine {}

3 上下文配置注释

3.1 @Profile

们可以在不同的环境中激活不同的配置文件来引导我们需要的bean。使用@Profile注释 - 我们将bean映射到该特定的配置文件。

假设,一个应用的工作环境有:dev、test、prod。我们有一个bean,它应该只在开发期间处于活动状态,但不会在生产中部署。我们使用“ dev ”配置文件注解该bean ,并且它只会在开发期间出现在容器中 - 在生产中,dev将不会处于活动状态:

@Component
@Profile("dev")
public class DevDatasourceConfig

3.2 @Import

导入资源。在应用中,有时没有把某个类注入到IOC容器中,但在运用的时候需要获取该类对应的bean,此时就需要用到@Import注解。

@Import(VehiclePartSupplier.class)
class VehicleFactoryConfig {}

3.3 @ImportResource

使用此注解导入XML配置。我们可以使用locations参数或其别名value参数指定XML文件位置:

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

3.4 @PropertySource

使用此注解,我们可以为应用程序设置定义的属性文件,将properties配置文件中的值存储到Spring的Environment中:

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

@PropertySource引用了Java 8重复注解功能,这意味着我们可以多次使用它标记一个类:

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

3.5 @PropertySources

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

@Configuration
@PropertySources({ 
    @PropertySource("classpath:/annotations.properties"),
    @PropertySource("classpath:/vehicle-factory.properties")
})
class VehicleFactoryConfig {}

注意,自Java 8以来,我们可以通过如上所述的重复注解功能实现相同的功能。

4 结论

在本文中,我们看到了最常见的Spring核心注解的介绍。我们了解了如何配置bean和应用程序上下文,以及如何为组件扫描标记类。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容