Spring常见注解

Spring常见注解

  • [1. 概述](#1. 概述)
  • [2. DI(依赖注⼊)相关注解](#2. DI(依赖注⼊)相关注解)
    • [2.1 @Autowired](#2.1 @Autowired)
    • [2.2 @Bean](#2.2 @Bean)
    • [2.3 @Qualifier](#2.3 @Qualifier)
    • [2.4 @Required (很少使用)](#2.4 @Required (很少使用))
    • [2.5 @Value](#2.5 @Value)
    • [2.6 @Lazy](#2.6 @Lazy)
    • [2.7 @Profile](#2.7 @Profile)

1. 概述

我们都知道Spring最核心的特性就是IOC(控制反转)+ AOP(⾯向切⾯编程),IOC的原理就是实现了一个Spring容器,用来管理所有Spring Bean实例,DI也就是依赖注入,是我们作为开发者需要关心的核心话题,如何注入依赖,注入哪个依赖,是我们需要明确知道的。 很久之前使用xml配置文件来声明一个bean,但现在,使用注解和代码来完成DI的过程。

我们可以使用 org.springframework.beans.factory.annotationorg.springframework.context.annotation 包中的注释来启用 Spring DI 引擎的功能。

2. DI(依赖注⼊)相关注解

2.1 @Autowired

我们可以使用 @Autowired 来标记 Spring 将要解析和注入的依赖项。 我们可以将此注释与构造函数、setter 或字段注入一起使用。

构造器注入:

java 复制代码
class Car {
    Engine engine;

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

Setter注入

java 复制代码
class Car {
    Engine engine;

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

变量注入

java 复制代码
class Car {
    @Autowired
    Engine engine;
}

2.2 @Bean

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

java 复制代码
@Bean
Engine engine() {
    return new Engine();
}

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

生成的 bean 与工厂方法同名。 如果我们想以不同的方式命名,我们可以使用此注释的名称或值参数(参数值是参数名称的别名):

java 复制代码
@Bean("engine")
Engine getEngine() {
    return new Engine();
}

这是一种非常常见的bean声明方式,因为很多的bean并不是我们一开始就在代码里定义好的,它可能需要基于运行时环境来进行按需构建。

我们可以自由地声明和定义Bean,并且也可以赋予它自定义的bean名称。

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

举例

java 复制代码
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 实现相同的接口:

java 复制代码
class Bike implements Vehicle {}

class Car implements Vehicle {}

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

构造器注入

java 复制代码
@Autowired
Biker(@Qualifier("bike") Vehicle vehicle) {
this.vehicle = vehicle;
}

Setter注入

java 复制代码
@Autowired
void setVehicle(@Qualifier("bike") Vehicle vehicle) {
this.vehicle = vehicle;
}

或者

java 复制代码
@Autowired
@Qualifier("bike")
void setVehicle(Vehicle vehicle) {
this.vehicle = vehicle;

变量注入

java 复制代码
@Autowired
@Qualifier("bike")
Vehicle vehicle;

这个注解我们可能平常用的不多,但是当一个接口有多个实现类时,它就会经常派上用场!

2.4 @Required (很少使用)

@Required 在 setter 方法上标记我们想要通过 XML 填充的依赖项:

java 复制代码
@Required
void setColor(String color) {
	this.color = color;
}

xml

java 复制代码
<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或者其他配置文件中来读取配置属性值。

java 复制代码
engine.fuelType=petrol

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

2.6 @Lazy

使用场景

  • 性能优化:对于一些耗资源或启动时间长的 bean,希望在启动时不创建它们,而是在首次使用时才进行创建。
  • 避免循环依赖:延迟初始化某些 bean 可以帮助解决循环依赖问题。
  • 懒加载特性:有时需要根据具体使用场景决定是否创建某个 bean。

使用方法

  • @Lazy 注解可以应用于类、字段、方法和配置方法。

示例

以下是一些使用 @Lazy 注解的示例:

  1. 懒加载类
    当整个类被标记为 @Lazy 时,Spring 容器在启动时不会初始化这个类,而是在第一次需要它时进行初始化。
java 复制代码
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...");
    }
}
  1. 懒加载字段
    在字段级别使用 @Lazy 注解,只有在访问该字段时才会初始化它。
java 复制代码
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();
    }
}
  1. 懒加载方法
    在方法级别使用 @Lazy 注解,方法返回的 bean 会延迟初始化。
java 复制代码
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。
  • 多环境支持:根据环境变量或配置文件动态切换配置。
java 复制代码
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)中进行设置。

java 复制代码
spring.profiles.active=dev
相关推荐
蓝天星空14 分钟前
spring cloud gateway 3
java·spring cloud
罗政19 分钟前
PDF书籍《手写调用链监控APM系统-Java版》第9章 插件与链路的结合:Mysql插件实现
java·mysql·pdf
一根稻草君25 分钟前
利用poi写一个工具类导出逐级合并的单元格的Excel(通用)
java·excel
kirito学长-Java28 分钟前
springboot/ssm网上宠物店系统Java代码编写web宠物用品商城项目
java·spring boot·后端
木头没有瓜42 分钟前
ruoyi 请求参数类型不匹配,参数[giftId]要求类型为:‘java.lang.Long‘,但输入值为:‘orderGiftUnionList
android·java·okhttp
奋斗的老史42 分钟前
Spring Retry + Redis Watch实现高并发乐观锁
java·redis·spring
high201144 分钟前
【Java 基础】-- ArrayList 和 Linkedlist
java·开发语言
老马啸西风1 小时前
NLP 中文拼写检测纠正论文 C-LLM Learn to CSC Errors Character by Character
java
Cosmoshhhyyy1 小时前
LeetCode:3083. 字符串及其反转中是否存在同一子字符串(哈希 Java)
java·leetcode·哈希算法