Spring常见注解
1. 概述
我们都知道Spring最核心的特性就是IOC(控制反转)+ AOP(⾯向切⾯编程),IOC的原理就是实现了一个Spring容器,用来管理所有Spring Bean实例,DI也就是依赖注入,是我们作为开发者需要关心的核心话题,如何注入依赖,注入哪个依赖,是我们需要明确知道的。 很久之前使用xml配置文件来声明一个bean,但现在,使用注解和代码来完成DI的过程。
我们可以使用 org.springframework.beans.factory.annotation
和 org.springframework.context.annotation
包中的注释来启用 Spring DI 引擎的功能。
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;
}
}
变量注入
class Car {
@Autowired
Engine engine;
}
2.2 @Bean
@Bean 标记实例化 Spring bean 的工厂方法:
@Bean
Engine engine() {
return new Engine();
}
当需要返回类型的新实例时,Spring 会调用这些方法。
生成的 bean 与工厂方法同名。 如果我们想以不同的方式命名,我们可以使用此注释的名称或值参数(参数值是参数名称的别名):
@Bean("engine")
Engine getEngine() {
return new Engine();
}
这是一种非常常见的bean声明方式,因为很多的bean并不是我们一开始就在代码里定义好的,它可能需要基于运行时环境来进行按需构建。
我们可以自由地声明和定义Bean,并且也可以赋予它自定义的bean名称。
注意,所有用@Bean 注释的方法都必须在@Configuration 类中。
举例
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.apache.kafka.common.serialization.StringSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
import org.springframework.kafka.config.KafkaListenerContainerFactory;
import org.springframework.kafka.core.*;
import org.springframework.kafka.listener.ConcurrentMessageListenerContainer;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class CloudKafkaConfig {
@Resource
private AppConfig appConfig;
@Bean
public KafkaTemplate<String, String> kafkaCloudTemplate() {
return new KafkaTemplate<>(producerFactory());
}
@Bean(name = "kafkaCloudContainerFactory")
KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<Integer, String>> kafkaCloudContainerFactory() {
ConcurrentKafkaListenerContainerFactory<Integer, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(consumerFactory());
factory.getContainerProperties().setPollTimeout(3000);
return factory;
}
private ProducerFactory<String, String> producerFactory() {
return new DefaultKafkaProducerFactory<>(producerConfigs());
}
private ConsumerFactory<Integer, String> consumerFactory() {
return new DefaultKafkaConsumerFactory<>(consumerConfigs());
}
private Map<String, Object> producerConfigs() {
Map<String, Object> props = new HashMap<>(3);
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, appConfig.getKafkaServers());
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
return props;
}
private Map<String, Object> consumerConfigs() {
Map<String, Object> props = new HashMap<>(4);
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, appConfig.getKafkaServers());
props.put(ConsumerConfig.GROUP_ID_CONFIG, appConfig.getKafkaGroupId());
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);
props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "100");
props.put(ConsumerConfig.REQUEST_TIMEOUT_MS_CONFIG, "30000");
props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "15000");
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG,true);
return props;
}
}
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;
变量注入
@Autowired
@Qualifier("bike")
Vehicle vehicle;
这个注解我们可能平常用的不多,但是当一个接口有多个实现类时,它就会经常派上用场!
2.4 @Required (很少使用)
@Required 在 setter 方法上标记我们想要通过 XML 填充的依赖项:
@Required
void setColor(String color) {
this.color = color;
}
xml
<bean class="com.baeldung.annotations.Bike">
<property name="color" value="green" />
</bean>
@Required 注解是 Spring 框架中的一个注解,用于确保依赖注入的属性必须被设置。如果 Spring 容器在创建 bean 时发现被 @Required 注解标记的属性没有被设置,它会抛出一个 BeanInitializationException,从而阻止 bean 的创建。
使用场景
@Required 注解通常用于确保某些关键属性在配置 bean 时被设置,以防止在 bean 使用过程中出现未初始化的属性导致的错误。
使用方法
@Required 注解通常与 setter 方法一起使用,确保相应的 setter 方法必须被调用以设置属性值。
2.5 @Value
非常常用的一个注解,因为我们很多时候都需要从application.properties或者其他配置文件中来读取配置属性值。
engine.fuelType=petrol
@Value("${engine.fuelType}")
String fuelType;
2.6 @Lazy
使用场景
- 性能优化:对于一些耗资源或启动时间长的 bean,希望在启动时不创建它们,而是在首次使用时才进行创建。
- 避免循环依赖:延迟初始化某些 bean 可以帮助解决循环依赖问题。
- 懒加载特性:有时需要根据具体使用场景决定是否创建某个 bean。
使用方法
- @Lazy 注解可以应用于类、字段、方法和配置方法。
示例
以下是一些使用 @Lazy 注解的示例:
- 懒加载类
当整个类被标记为 @Lazy 时,Spring 容器在启动时不会初始化这个类,而是在第一次需要它时进行初始化。
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@Component
@Lazy
public class ExpensiveService {
public ExpensiveService() {
System.out.println("ExpensiveService is initialized");
}
public void performService() {
System.out.println("Performing expensive service...");
}
}
- 懒加载字段
在字段级别使用 @Lazy 注解,只有在访问该字段时才会初始化它。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@Component
public class UserService {
@Autowired
@Lazy
private ExpensiveService expensiveService;
public void useService() {
System.out.println("UserService is calling ExpensiveService");
expensiveService.performService();
}
}
- 懒加载方法
在方法级别使用 @Lazy 注解,方法返回的 bean 会延迟初始化。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
@Configuration
public class AppConfig {
@Bean
@Lazy
public ExpensiveService expensiveService() {
return new ExpensiveService();
}
}
2.7 @Profile
@Profile 注解在 Spring 框架中用于指定 bean 在特定环境(或配置文件)下才会被创建和加载。它可以帮助开发者根据不同的环境(如开发、测试、生产)来加载不同的配置和 bean。
使用场景
- 环境配置:在不同的环境中加载不同的 bean。
- 测试和开发:在测试环境中加载模拟(mock)bean,在生产环境中加载实际 bean。
- 多环境支持:根据环境变量或配置文件动态切换配置。
public interface MessageService {
String getMessage();
}
@Profile("dev")
@Component
public class DevMessageService implements MessageService {
@Override
public String getMessage() {
return "Hello from Development";
}
}
@Profile("prod")
@Component
public class ProdMessageService implements MessageService {
@Override
public String getMessage() {
return "Hello from Production";
}
}
配置文件方式
除了在代码中设置 spring.profiles.active,也可以在外部配置文件(如 application.properties 或 application.yml)中进行设置。
spring.profiles.active=dev