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
相关推荐
ZeroToOneDev2 小时前
Java(泛型和JUnit)
java·开发语言·笔记
迪尔~4 小时前
Apache POI中通过WorkBook写入图片后出现导出PDF文件时在不同页重复写入该图片问题,如何在通过sheet获取绘图对象清除该图片
java·pdf·excel
现在,此刻4 小时前
leetcode 11. 盛最多水的容器 -java
java·算法·leetcode
DKPT5 小时前
Java设计模式之开闭原则介绍与说明
java·设计模式·开闭原则
hyy27952276845 小时前
企业级WEB应用服务器TOMCAT
java·前端·tomcat
布朗克1685 小时前
Spring Boot项目通过Feign调用三方接口的详细教程
java·spring boot·feign
Arva .5 小时前
Spring基于XML的自动装配
xml·java·spring
帅得不敢出门8 小时前
Android Framework定制长按电源键关机的窗口
android·java·framework
fatfishccc8 小时前
循序渐进学 Spring (上):从 IoC/DI 核心原理到 XML 配置实战
xml·java·数据库·spring·intellij-idea·ioc·di
小厂永远得不到的男人8 小时前
一篇文章搞懂 java 反射
java·后端