当前位置: 首页 > news >正文

04.Spring 框架注解体系详解

Spring 框架注解体系详解

本文详细介绍 Spring、Spring Boot 及 Spring Cloud 中常用注解的用途、生命周期及使用方式,帮助开发者更深入理解 Spring 注解驱动编程模式。

参考来源:Spring、SpringMVC、SpringBoot、SpringCloud 框架常用注解说明

目录

  • 注解基础知识
    • 什么是注解
    • 注解的定义方式
    • 注解的生命周期
  • Spring 核心注解
    • @Bean vs 其他注解
    • 配置类相关注解
    • 组件注册相关注解
    • 依赖注入相关注解
  • Bean 的生命周期
    • 生命周期概述
    • 初始化和销毁方法
    • Bean 后处理器
    • 完整生命周期流程
  • Spring MVC 注解
    • 控制器相关注解
    • 请求映射相关注解
    • 请求参数相关注解
  • Spring Boot 注解
    • 自动配置相关注解
    • 属性配置相关注解
  • Spring Cloud 注解
    • 服务注册与发现注解
    • 断路器相关注解
    • 配置中心相关注解

注解基础知识

什么是注解

注解(Annotation)是 Java 5 引入的一种特殊类型的接口,它是一种元数据,用于提供有关程序的数据,但不属于程序本身。注解对于它所修饰的代码没有直接影响,但可以被工具或框架读取,用于生成代码、编译检查或在运行时通过反射处理。

Spring 框架大量使用注解来简化配置,提高开发效率,实现声明式编程。

注解的定义方式

在 Java 中,注解通过@interface关键字定义:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation {String value() default "";int count() default 0;
}

其中:

  • @Target:指定注解可以应用的元素类型(类、方法、字段等)
  • @Retention:指定注解的保留策略(源码、编译时、运行时)
  • @Documented:表示注解应该被文档工具记录
  • 注解可以定义属性(如value()count())以及它们的默认值

注解的生命周期

注解的生命周期由@Retention决定:

  1. SOURCE:注解仅在源码中保留,编译时被丢弃
  2. CLASS:注解在编译时被保留在类文件中,但不会被 VM 加载(默认行为)
  3. RUNTIME:注解在运行时可以通过反射 API 获取

Spring 框架中的注解通常都使用RUNTIME级别,这样才能在应用运行时通过反射机制读取注解信息并做相应处理。

Spring 核心注解

@Bean vs 其他注解

@Bean是 Spring 最基础的注解之一,它与其他组件注解(如@Component@Service等)有以下区别:

  1. 定义方式不同

    • @Bean用于方法级别,标注在方法上
    • @Component等注解用于类级别,标注在类上
  2. 使用场景不同

    • @Bean通常用于注册第三方库中的类实例或需要复杂初始化的 Bean
    • @Component等用于将自己编写的类注册为 Spring Bean
  3. 配置灵活性

    • @Bean允许通过方法体进行复杂逻辑处理和初始化
    • @Component等注解配置相对简单,主要依靠其他注解配合使用

示例对比:

// 使用@Bean注解
@Configuration
public class AppConfig {@Beanpublic MyService myService() {MyService service = new MyService();service.setProperty("someValue");return service;}
}// 使用@Component注解
@Component
public class MyService {private String property;public void setProperty(String property) {this.property = property;}
}

配置类相关注解

@Configuration

标记一个类作为 Bean 定义的源,等同于 XML 配置中的<beans>标签。

生命周期:在应用上下文初始化时处理,Spring 会创建该类的 CGLIB 代理以确保@Bean 方法的内部调用也会通过容器。

@Configuration
public class AppConfig {@Beanpublic DataSource dataSource() {return new DriverManagerDataSource("jdbc:mysql://localhost:3306/mydb");}@Beanpublic JdbcTemplate jdbcTemplate() {// 内部调用dataSource()会返回容器中的单例return new JdbcTemplate(dataSource());}
}
@ComponentScan

指定要扫描的包,寻找带有@Component及其派生注解的类,自动将它们注册为 Bean。

@Configuration
@ComponentScan(basePackages = "com.example.app")
public class AppConfig {// 配置代码
}

或者使用类型安全的方式:

@Configuration
@ComponentScan(basePackageClasses = {UserRepository.class, ProductService.class})
public class AppConfig {// 配置代码
}
@Import

用于导入其他配置类。

@Configuration
@Import({DatabaseConfig.class, SecurityConfig.class})
public class AppConfig {// 应用主配置
}
@PropertySource

加载外部属性文件。

@Configuration
@PropertySource("classpath:application.properties")
public class AppConfig {@Autowiredprivate Environment env;@Beanpublic DataSource dataSource() {String url = env.getProperty("jdbc.url");String username = env.getProperty("jdbc.username");String password = env.getProperty("jdbc.password");return new DriverManagerDataSource(url, username, password);}
}

组件注册相关注解

@Component

基础组件注解,表示该类是一个 Spring 组件,会自动注册到 Spring 容器中。

@Component
public class MyComponent {// 组件实现
}
@Repository

标记数据访问层的类,是@Component的特化。Spring 会为这些类提供持久化异常转换功能。

@Repository
public class UserRepository {public User findById(Long id) {// 数据访问逻辑return user;}
}
@Service

标记业务逻辑层的类,是@Component的特化,主要用于业务服务的实现类。

@Service
public class UserService {@Autowiredprivate UserRepository userRepository;public User getUserById(Long id) {return userRepository.findById(id);}
}
@Controller / @RestController

标记表现层的类,用于处理 Web 请求。

  • @Controller返回视图
  • @RestController等同于@Controller + @ResponseBody,直接返回数据
@Controller
public class UserController {@Autowiredprivate UserService userService;@GetMapping("/users/{id}")public String getUser(@PathVariable Long id, Model model) {model.addAttribute("user", userService.getUserById(id));return "userDetails"; // 返回视图名}
}@RestController
public class UserApiController {@Autowiredprivate UserService userService;@GetMapping("/api/users/{id}")public User getUser(@PathVariable Long id) {return userService.getUserById(id); // 直接返回对象,自动转为JSON}
}

依赖注入相关注解

@Autowired

自动装配依赖,可用于构造函数、字段、方法参数等。

@Service
public class UserService {private final UserRepository userRepository;// 构造器注入(推荐)@Autowiredpublic UserService(UserRepository userRepository) {this.userRepository = userRepository;}// 字段注入@Autowiredprivate EmailService emailService;// 方法注入@Autowiredpublic void setLogger(Logger logger) {this.logger = logger;}
}
@Qualifier

当有多个相同类型的 Bean 时,指定要注入的具体 Bean。

@Service
public class NotificationService {private final MessageSender messageSender;@Autowiredpublic NotificationService(@Qualifier("emailSender") MessageSender messageSender) {this.messageSender = messageSender;}
}@Component("emailSender")
public class EmailSender implements MessageSender {// 实现
}@Component("smsSender")
public class SmsSender implements MessageSender {// 实现
}
@Value

注入外部配置的属性值或表达式结果。

@Component
public class AppSettings {// 从配置文件注入@Value("${app.name}")private String appName;// 默认值@Value("${app.timeout:30}")private int timeout;// SpEL表达式@Value("#{systemProperties['user.region']}")private String region;
}
@Primary

当有多个相同类型的 Bean 时,指定默认的首选 Bean。

@Component
@Primary
public class PrimaryDataSource implements DataSource {// 实现
}@Component
public class SecondaryDataSource implements DataSource {// 实现
}

Bean 的生命周期

生命周期概述

Bean 的生命周期是指 Bean 从创建、初始化到销毁的整个过程。在 Spring 中,Bean 的生命周期由 IoC 容器管理,主要分为四个阶段:

  1. 实例化(Instantiation):创建 Bean 的实例
  2. 属性赋值(Populate):为 Bean 的属性设置值和对其他 Bean 的引用
  3. 初始化(Initialization):调用 Bean 的初始化方法
  4. 销毁(Destruction):在容器关闭时,调用 Bean 的销毁方法

需要注意的是,对于**单例(Singleton)Bean,实例化和初始化是在容器启动时完成的,销毁是在容器关闭时执行的;而对于原型(Prototype)**Bean,实例化和初始化是在每次获取 Bean 时执行的,但 Spring 不会管理原型 Bean 的销毁过程。

初始化和销毁方法

Spring 提供了多种方式定义 Bean 的初始化和销毁方法:

1. init-methoddestroy-method属性

在使用@Bean注解时,可以通过initMethoddestroyMethod属性指定初始化和销毁方法:

@Configuration
public class AppConfig {@Bean(initMethod = "init", destroyMethod = "cleanup")public DatabaseClient databaseClient() {DatabaseClient client = new DatabaseClient();client.setUrl("jdbc:mysql://localhost:3306/mydb");return client;}
}public class DatabaseClient {private String url;public void setUrl(String url) {this.url = url;}// 初始化方法public void init() {System.out.println("DatabaseClient initializing with URL: " + url);// 连接数据库等初始化操作}// 销毁方法public void cleanup() {System.out.println("DatabaseClient cleaning up resources");// 关闭连接等清理操作}
}
2. InitializingBeanDisposableBean接口

Bean 可以实现这两个接口,分别提供初始化和销毁的回调方法:

@Component
public class CacheManager implements InitializingBean, DisposableBean {private Map<String, Object> cache;@Overridepublic void afterPropertiesSet() throws Exception {// 在所有属性设置完成后调用this.cache = new ConcurrentHashMap<>();System.out.println("CacheManager initialized");}@Overridepublic void destroy() throws Exception {// 在容器销毁Bean之前调用this.cache.clear();System.out.println("CacheManager destroyed");}
}
3. @PostConstruct@PreDestroy注解

这是 JSR-250 规范定义的注解,可以标记在 Bean 的方法上:

@Component
public class UserService {private DatabaseClient dbClient;@Autowiredpublic void setDbClient(DatabaseClient dbClient) {this.dbClient = dbClient;}@PostConstructpublic void initialize() {// 在依赖注入完成后调用System.out.println("UserService is initialized");}@PreDestroypublic void cleanup() {// 在容器销毁Bean之前调用System.out.println("UserService is cleaning up");}
}

各种初始化方法的调用顺序(从先到后):

  1. @PostConstruct注解标记的方法
  2. InitializingBean接口的afterPropertiesSet()方法
  3. @Bean(initMethod="...")指定的初始化方法

各种销毁方法的调用顺序(从先到后):

  1. @PreDestroy注解标记的方法
  2. DisposableBean接口的destroy()方法
  3. @Bean(destroyMethod="...")指定的销毁方法

Bean 后处理器

BeanPostProcessor是 Spring 框架中非常重要的扩展点,它允许在 Bean 初始化前后进行自定义处理:

public interface BeanPostProcessor {// 初始化前的处理default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}// 初始化后的处理default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;}
}

实现BeanPostProcessor接口的例子:

@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if (bean instanceof AuditableBean) {System.out.println("Before initialization of bean: " + beanName);((AuditableBean) bean).setCreatedDate(new Date());}return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (bean instanceof TransactionalBean) {System.out.println("After initialization of bean: " + beanName);// 可以返回原始Bean的代理对象,例如添加事务处理return createProxy(bean);}return bean;}private Object createProxy(Object bean) {// 创建代理对象的逻辑return bean; // 简化示例}
}

Spring 框架内部使用大量的BeanPostProcessor实现来处理各种功能,例如:

  • AutowiredAnnotationBeanPostProcessor:处理@Autowired注解
  • CommonAnnotationBeanPostProcessor:处理@PostConstruct@PreDestroy等注解
  • ApplicationContextAwareProcessor:处理各种Aware接口的回调,如ApplicationContextAware

完整生命周期流程

Spring Bean 的完整生命周期流程如下:

  1. 实例化前:调用InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()
  2. 实例化:通过构造器或工厂方法创建 Bean 实例
  3. 实例化后:调用InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()
  4. 属性赋值前:调用InstantiationAwareBeanPostProcessor.postProcessPropertyValues()
  5. 属性赋值:设置 Bean 属性值
  6. Aware 接口回调:如果 Bean 实现了相关的 Aware 接口(如BeanNameAwareBeanFactoryAwareApplicationContextAware等),调用对应的方法
  7. 初始化前:调用BeanPostProcessor.postProcessBeforeInitialization()
  8. 初始化:按顺序调用@PostConstruct注解方法、InitializingBean.afterPropertiesSet()方法、自定义的init-method
  9. 初始化后:调用BeanPostProcessor.postProcessAfterInitialization()
  10. 使用 Bean:Bean 可以在应用中使用了
  11. 销毁前:如果 Bean 实现了DestructionAwareBeanPostProcessor接口,调用postProcessBeforeDestruction()
  12. 销毁:按顺序调用@PreDestroy注解方法、DisposableBean.destroy()方法、自定义的destroy-method

对于单例 Bean,容器负责管理其完整生命周期;而对于原型 Bean,容器只负责创建,不负责销毁。

示例代码:一个完整展示 Bean 生命周期的示例

@Component
public class LifecycleDemoBean implements InitializingBean, DisposableBean,BeanNameAware, BeanFactoryAware, ApplicationContextAware {private String beanName;public LifecycleDemoBean() {System.out.println("1. Constructor: Bean is being created");}@Overridepublic void setBeanName(String name) {this.beanName = name;System.out.println("2. BeanNameAware: Bean name is " + name);}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {System.out.println("3. BeanFactoryAware: Bean factory has been set");}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {System.out.println("4. ApplicationContextAware: Application context has been set");}@PostConstructpublic void postConstruct() {System.out.println("5. @PostConstruct: Post-construct method called");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("6. InitializingBean: Properties have been set");}public void customInit() {System.out.println("7. Custom init method: Initialization completed");}public void doSomething() {System.out.println("8. Bean is in use");}@PreDestroypublic void preDestroy() {System.out.println("9. @PreDestroy: Bean is about to be destroyed");}@Overridepublic void destroy() throws Exception {System.out.println("10. DisposableBean: Bean is being destroyed");}public void customDestroy() {System.out.println("11. Custom destroy method: Bean has been destroyed");}
}@Configuration
public class AppConfig {@Bean(initMethod = "customInit", destroyMethod = "customDestroy")public LifecycleDemoBean lifecycleDemoBean() {return new LifecycleDemoBean();}
}

如果使用上面的配置,并在应用中获取和使用该 Bean,控制台将按顺序打印出所有生命周期阶段的日志。

Spring MVC 注解

控制器相关注解

@Controller

标记类为 Spring MVC 控制器,处理 HTTP 请求。

@Controller
public class HomeController {@GetMapping("/")public String home() {return "home"; // 返回视图名}
}
@RestController

组合注解,等同于@Controller + @ResponseBody,用于创建 RESTful Web 服务。

@RestController
@RequestMapping("/api")
public class UserApiController {@GetMapping("/users")public List<User> getAllUsers() {// 返回用户列表,自动转换为JSONreturn userService.findAll();}
}
@ControllerAdvice / @RestControllerAdvice

用于定义全局控制器异常处理器、绑定数据或模型属性。

@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(UserNotFoundException.class)public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException ex) {ErrorResponse error = new ErrorResponse("USER_NOT_FOUND", ex.getMessage());return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);}
}

请求映射相关注解

@RequestMapping

映射 Web 请求到特定处理器方法。

@Controller
@RequestMapping("/users") // 类级别映射
public class UserController {@RequestMapping(method = RequestMethod.GET) // 方法级别映射public String getAllUsers(Model model) {// 处理GET /usersmodel.addAttribute("users", userService.findAll());return "userList";}@RequestMapping(value = "/{id}", method = RequestMethod.GET)public String getUserById(@PathVariable Long id, Model model) {// 处理GET /users/{id}model.addAttribute("user", userService.findById(id));return "userDetails";}
}
@GetMapping / @PostMapping / @PutMapping / @DeleteMapping / @PatchMapping

@RequestMapping的特化版本,简化特定 HTTP 方法的请求映射。

@RestController
@RequestMapping("/api/users")
public class UserApiController {@GetMapping // GET /api/userspublic List<User> getAllUsers() {return userService.findAll();}@GetMapping("/{id}") // GET /api/users/{id}public User getUser(@PathVariable Long id) {return userService.findById(id);}@PostMapping // POST /api/userspublic User createUser(@RequestBody User user) {return userService.create(user);}@PutMapping("/{id}") // PUT /api/users/{id}public User updateUser(@PathVariable Long id, @RequestBody User user) {return userService.update(id, user);}@DeleteMapping("/{id}") // DELETE /api/users/{id}public void deleteUser(@PathVariable Long id) {userService.delete(id);}
}

请求参数相关注解

@RequestParam

绑定请求参数到方法参数。

@GetMapping("/search")
public String searchUsers(@RequestParam("query") String query,@RequestParam(value = "page", defaultValue = "1") int page,@RequestParam(value = "size", required = false) Integer size,Model model
) {// 处理 /search?query=john&page=2&size=10List<User> users = userService.search(query, page, size != null ? size : 20);model.addAttribute("users", users);return "searchResults";
}
@PathVariable

绑定 URL 路径变量到方法参数。

@GetMapping("/users/{userId}/posts/{postId}")
public String getPost(@PathVariable("userId") Long userId,@PathVariable Long postId,Model model
) {// 处理 /users/123/posts/456Post post = postService.findByUserAndPostId(userId, postId);model.addAttribute("post", post);return "postDetails";
}
@RequestBody

绑定 HTTP 请求体到方法参数。

@PostMapping("/users")
public ResponseEntity<User> createUser(@RequestBody User user) {User createdUser = userService.create(user);return new ResponseEntity<>(createdUser, HttpStatus.CREATED);
}
@RequestHeader

绑定请求头到方法参数。

@GetMapping("/greeting")
public String greeting(@RequestHeader("User-Agent") String userAgent,@RequestHeader(value = "Accept-Language", required = false) String language
) {// 根据请求头信息处理return "greeting";
}

Spring Boot 注解

自动配置相关注解

@SpringBootApplication

组合注解,等同于@Configuration + @EnableAutoConfiguration + @ComponentScan

@SpringBootApplication
public class MyApplication {public static void main(String[] args) {SpringApplication.run(MyApplication.class, args);}
}
@EnableAutoConfiguration

启用 Spring Boot 的自动配置机制,根据类路径中的依赖自动配置 Spring 应用。

@Configuration
@EnableAutoConfiguration
public class MinimalConfig {// 配置代码
}
@ConditionalOnBean / @ConditionalOnMissingBean

条件注解,根据 Bean 的存在或不存在来决定是否创建特定的 Bean。

@Configuration
public class DatabaseConfig {@Bean@ConditionalOnMissingBeanpublic DataSource dataSource() {// 只有在没有DataSource bean的情况下才会创建return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build();}@Bean@ConditionalOnBean(name = "dataSource")public JdbcTemplate jdbcTemplate(DataSource dataSource) {// 只有在存在dataSource bean的情况下才会创建return new JdbcTemplate(dataSource);}
}
@ConditionalOnProperty

根据配置属性的值决定是否创建特定的 Bean。

@Configuration
public class CacheConfig {@Bean@ConditionalOnProperty(name = "app.cache.enabled", havingValue = "true")public CacheManager cacheManager() {// 只有当app.cache.enabled=true时才创建return new ConcurrentMapCacheManager();}
}

属性配置相关注解

@ConfigurationProperties

将外部配置属性绑定到类的字段。

@Component
@ConfigurationProperties(prefix = "app.mail")
public class MailProperties {private String host;private int port;private String username;private String password;private boolean ssl;// getters and setters
}// 在application.properties中:
// app.mail.host=smtp.example.com
// app.mail.port=587
// app.mail.username=user
// app.mail.password=pass
// app.mail.ssl=true
@EnableConfigurationProperties

启用@ConfigurationProperties注解的类并将其注册为 Spring Bean。

@Configuration
@EnableConfigurationProperties(MailProperties.class)
public class MailConfig {@Beanpublic JavaMailSender mailSender(MailProperties properties) {JavaMailSenderImpl sender = new JavaMailSenderImpl();sender.setHost(properties.getHost());sender.setPort(properties.getPort());sender.setUsername(properties.getUsername());sender.setPassword(properties.getPassword());return sender;}
}

Spring Cloud 注解

服务注册与发现注解

@EnableEurekaServer

启用 Eureka 服务器,用于服务注册和发现。

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {public static void main(String[] args) {SpringApplication.run(EurekaServerApplication.class, args);}
}
@EnableDiscoveryClient / @EnableEurekaClient

启用服务发现客户端功能,将应用注册到注册中心。

@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceApplication {public static void main(String[] args) {SpringApplication.run(UserServiceApplication.class, args);}
}

断路器相关注解

@EnableCircuitBreaker

启用断路器模式,当依赖服务失败时提供回退机制。

@SpringBootApplication
@EnableCircuitBreaker
public class PaymentServiceApplication {public static void main(String[] args) {SpringApplication.run(PaymentServiceApplication.class, args);}
}
@HystrixCommand

标记可能失败的方法,提供回退策略。

@Service
public class UserService {@Autowiredprivate RestTemplate restTemplate;@HystrixCommand(fallbackMethod = "getDefaultUser")public User getUserById(Long id) {return restTemplate.getForObject("http://user-service/users/" + id, User.class);}public User getDefaultUser(Long id) {// 回退方法,当getUserById方法失败时调用return new User(id, "Default User", "default@example.com");}
}

配置中心相关注解

@EnableConfigServer

启用 Spring Cloud Config 服务器,集中管理分布式系统的配置。

@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {public static void main(String[] args) {SpringApplication.run(ConfigServerApplication.class, args);}
}
@RefreshScope

标记需要在配置更改时重新创建的 Bean。

@RestController
@RefreshScope
public class ConfigController {@Value("${greeting.message}")private String message;@GetMapping("/greeting")public String getGreeting() {return message;}
}

注解的工作原理

Spring 注解的工作原理基于 Java 反射机制和 Spring 的BeanPostProcessor机制:

  1. 扫描阶段:Spring 容器启动时,会扫描类路径下的类,查找带有特定注解的类
  2. 解析阶段:识别这些注解并解析其元数据
  3. 注册阶段:根据注解信息注册 Bean 定义
  4. 后处理阶段:通过各种BeanPostProcessor实现对 Bean 进行增强,如依赖注入
  5. 初始化阶段:按照生命周期回调初始化 Bean

@Autowired为例,其工作流程如下:

  1. Spring 通过AutowiredAnnotationBeanPostProcessor处理带有@Autowired注解的字段或方法
  2. 在 Bean 创建后、初始化前,该处理器查找所有带@Autowired注解的注入点
  3. 对每个注入点,从容器中查找匹配的 Bean
  4. 如果找到唯一匹配,则注入该 Bean;如果找到多个匹配,则查找@Primary或使用@Qualifier进行筛选
  5. 如果未找到匹配且required=true(默认),则抛出异常;如果required=false,则不进行注入

最佳实践

  1. 选择合适的注入方式

    • 优先使用构造器注入,特别是对于必需的依赖
    • 避免过度使用字段注入,这会使测试变得困难
  2. 合理使用组件扫描

    • 明确指定要扫描的包,避免扫描范围过大
    • 对于复杂项目,考虑使用多个配置类分组管理 Bean
  3. 善用条件注解

    • 使用条件注解创建更灵活的配置
    • 利用 profiles 处理不同环境的配置
  4. 遵循单一职责原则

    • 每个 Bean 应该有明确的职责
    • 避免创建过于复杂的配置类
  5. 注意 Bean 的作用域

    • 默认情况下 Bean 是单例的,注意并发问题
    • 对于有状态的 Bean,考虑使用prototyperequestsession作用域

相关文章:

  • L2-006 树的遍历
  • Logisim数字逻辑实训——寄存器设计与应用
  • 【datawhaleAI春训营第一期笔记】AI+航空安全
  • openbmb/MiniCPM-V-2_6 和 AIDC-AI/Ovis2-1B 的网络结构体对比
  • 专著出版能为评职助力吗?
  • IPTV电视信息发布直播点播系统:营造数字化个性化融合化多媒体IPTV电视信息发布平台
  • 数据结构学习笔记 :二叉搜索树与高效查找算法详解
  • 认知觉醒是什么? 如何做到 ? ( 持续更新ing )
  • FairMOT算法详解
  • 《软件设计师》复习笔记(12.3)——质量管理、风险管理
  • 《数据牢笼》-来自DeepSeek
  • iPaaS应用集成平台在交通运输行业有哪些应用场景
  • 第一期第16讲,17讲21:50
  • 淘宝商品搜索爬虫:Python 实现教程
  • Animated Raindrop Ripples In HLSL
  • Ext系列⽂件系统
  • C语言---FILE结构体
  • 21【干获】如何用GIS快速统计每种地类面积?
  • 梯度下降代码
  • yaffs_write_new_chunk()函数解析
  • 调查显示特朗普在经济问题上的支持率跌至其总统生涯最低
  • 解读丨连续两日施压,特朗普为何着急让美联储降息
  • 秦洪看盘|量能虽萎缩,但交易情绪尚可
  • 支持民营企业上市融资,上海将有什么新举措?
  • 云南景洪回应“游客打车遭临时加价并拒载”:对司机顶格罚款500元并停运学习
  • 上海专家绘制迄今为止分辨率最高的“野生稻-栽培稻泛基因组图谱”