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和应用程序上下文,以及如何为组件扫描标记类。