Spring中如何在@Value注解中动态注入拼接后的配置值?
解决Spring中动态根据ID注入配置属性的问题
问题场景
现有application.properties配置了多组图书属性:
book.name1 = War and Piece book.author1 = Leo Tolstoy book.name2 = Crime and Punishment book.author2 = Dostoevsky book.name3 = To Kill a Mockingbird book.author3 = Harper Lee
原代码尝试通过Book类的静态bookid自增,在@Value注解中拼接ID动态获取配置,但调用后输出未解析的表达式${book.name.concat(bookid)} by ${book.author.concat(bookid)},无法达到预期效果。
问题根源
- 时机不匹配:Spring的
@Value注解在Bean定义解析阶段就会处理表达式,而bookid是在Book构造函数执行时才自增,此时表达式已经完成解析,无法动态更新。 - 语法错误:
@Value不支持直接用concat(bookid)这种自定义拼接逻辑,即使使用SpEL表达式引用静态变量,也只能获取解析时的初始值,后续自增不会生效。 - 线程安全隐患:静态
bookid在多线程创建Book实例时会出现值混乱的问题。
正确实现方案
方案一:用@ConfigurationProperties批量绑定配置
这是最简洁的批量读取同前缀配置的方式,支持列表或Map格式。
1. 定义配置接收类
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import java.util.Map; @Component @ConfigurationProperties(prefix = "book") public class BookProperties { // 用Map接收原格式的配置,key为数字字符串(如"1"、"2") private Map<String, String> name; private Map<String, String> author; // Getter & Setter public Map<String, String> getName() { return name; } public void setName(Map<String, String> name) { this.name = name; } public Map<String, String> getAuthor() { return author; } public void setAuthor(Map<String, String> author) { this.author = author; } }
2. 配置类创建Book实例列表
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.ArrayList; import java.util.List; @Configuration public class LibraryConfig { private final BookProperties bookProperties; public LibraryConfig(BookProperties bookProperties) { this.bookProperties = bookProperties; } @Bean public List<Book> books() { List<Book> bookList = new ArrayList<>(); int bookCount = bookProperties.getName().size(); for (int i = 1; i <= bookCount; i++) { String id = String.valueOf(i); Book book = new Book(); book.setName(bookProperties.getName().get(id)); book.setAuthor(bookProperties.getAuthor().get(id)); bookList.add(book); } return bookList; } @Bean public UniversityLibrary universityLibrary(List<Book> books) { return new UniversityLibrary(books); } }
3. 简化Book类
去掉静态变量和@Value,仅保留属性和基础方法:
public class Book { private String name; private String author; // Getter & Setter public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public String getBook() { return name + " by " + author; } }
方案二:通过Environment动态获取配置
直接利用Spring的Environment对象,根据传入的ID读取对应配置。
1. 修改Book类
通过构造函数注入Environment和ID,动态读取配置:
import org.springframework.core.env.Environment; public class Book { private String name; private String author; public Book(Environment environment, int bookId) { this.name = environment.getProperty("book.name" + bookId); this.author = environment.getProperty("book.author" + bookId); } public String getBook() { return name + " by " + author; } }
2. 配置类创建实例
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import java.util.ArrayList; import java.util.List; @Configuration public class LibraryConfig { private final Environment environment; public LibraryConfig(Environment environment) { this.environment = environment; } @Bean public List<Book> books() { List<Book> bookList = new ArrayList<>(); // 可通过配置项指定图书数量,更灵活 int bookCount = Integer.parseInt(environment.getProperty("book.count", "3")); for (int i = 1; i <= bookCount; i++) { bookList.add(new Book(environment, i)); } return bookList; } @Bean public UniversityLibrary universityLibrary(List<Book> books) { return new UniversityLibrary(books); } }
此时可在application.properties中添加book.count=3来指定图书数量。
方案三:手动构造Book实例
如果不需要Spring管理属性注入,可直接在配置类中读取配置创建实例:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import java.util.ArrayList; import java.util.List; @Configuration public class LibraryConfig { private final Environment environment; public LibraryConfig(Environment environment) { this.environment = environment; } @Bean public List<Book> books() { List<Book> bookList = new ArrayList<>(); Book book1 = new Book(); book1.setName(environment.getProperty("book.name1")); book1.setAuthor(environment.getProperty("book.author1")); bookList.add(book1); Book book2 = new Book(); book2.setName(environment.getProperty("book.name2")); book2.setAuthor(environment.getProperty("book.author2")); bookList.add(book2); Book book3 = new Book(); book3.setName(environment.getProperty("book.name3")); book3.setAuthor(environment.getProperty("book.author3")); bookList.add(book3); return bookList; } @Bean public UniversityLibrary universityLibrary(List<Book> books) { return new UniversityLibrary(books); } }
Book类保持方案一中的简化版本即可。
内容的提问来源于stack exchange,提问作者Korol_ER
相关产品推荐
相关产品推荐

