SpringContextUtils 工具类实现
下面是一个完整的 Spring 上下文工具类实现,用于从 Spring 容器中获取 Bean。这个工具类考虑了线程安全、性能优化和易用性,并提供了多种获取 Bean 的方式。
完整实现代码
java
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* Spring 上下文工具类,用于获取容器中的Bean
*
* 功能包括:
* 1. 获取ApplicationContext
* 2. 通过名称获取Bean
* 3. 通过类型获取Bean
* 4. 通过名称和类型获取Bean
* 5. 获取指定类型的所有Bean
* 6. 获取当前环境配置
*/
@Component
public class SpringContextUtils implements ApplicationContextAware {
/**
* 静态ApplicationContext
*/
private static ApplicationContext applicationContext;
/**
* 设置ApplicationContext
*/
@Override
public void setApplicationContext(@Nullable ApplicationContext applicationContext) throws BeansException {
if (SpringContextUtils.applicationContext == null) {
SpringContextUtils.applicationContext = applicationContext;
}
}
/**
* 获取ApplicationContext
*/
public static ApplicationContext getApplicationContext() {
checkApplicationContext();
return applicationContext;
}
/**
* 通过名称获取Bean
* @param name Bean名称
* @return Bean实例
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
checkApplicationContext();
return (T) applicationContext.getBean(name);
}
/**
* 通过类型获取Bean
* @param clazz Bean类型
* @return Bean实例
*/
public static <T> T getBean(Class<T> clazz) {
checkApplicationContext();
return applicationContext.getBean(clazz);
}
/**
* 通过名称和类型获取Bean
* @param name Bean名称
* @param clazz Bean类型
* @return Bean实例
*/
public static <T> T getBean(String name, Class<T> clazz) {
checkApplicationContext();
return applicationContext.getBean(name, clazz);
}
/**
* 获取指定类型的所有Bean
* @param clazz Bean类型
* @return Bean名称与实例的映射
*/
public static <T> Map<String, T> getBeansOfType(Class<T> clazz) {
checkApplicationContext();
return applicationContext.getBeansOfType(clazz);
}
/**
* 获取当前环境配置
* @param key 配置键
* @return 配置值
*/
public static String getProperty(String key) {
checkApplicationContext();
return applicationContext.getEnvironment().getProperty(key);
}
/**
* 检查ApplicationContext是否已注入
*/
private static void checkApplicationContext() {
if (applicationContext == null) {
throw new IllegalStateException("ApplicationContext未注入,请在Spring配置中定义SpringContextUtils");
}
}
}
功能说明
1. 核心功能
- 获取 ApplicationContext :
getApplicationContext()
- 通过名称获取 Bean :
getBean(String name)
- 通过类型获取 Bean :
getBean(Class<T> clazz)
- 通过名称和类型获取 Bean :
getBean(String name, Class<T> clazz)
- 获取指定类型的所有 Bean :
getBeansOfType(Class<T> clazz)
- 获取环境配置 :
getProperty(String key)
2. 线程安全性
- 使用静态变量存储 ApplicationContext,保证全局唯一
- 通过
@Component
注解让 Spring 管理工具类实例 - 实现
ApplicationContextAware
接口自动注入上下文
3. 异常处理
- 检查 ApplicationContext 是否已注入,未注入时抛出明确异常
- 使用
@Nullable
注解标记可为空的参数 - 添加泛型支持,减少类型转换
使用示例
1. 基本使用
java
// 获取ApplicationContext
ApplicationContext context = SpringContextUtils.getApplicationContext();
// 通过名称获取Bean
UserService userService = SpringContextUtils.getBean("userService");
// 通过类型获取Bean
UserService userService = SpringContextUtils.getBean(UserService.class);
// 通过名称和类型获取Bean
UserService userService = SpringContextUtils.getBean("userService", UserService.class);
2. 获取所有同类型Bean
java
// 获取所有实现UserService接口的Bean
Map<String, UserService> userServices = SpringContextUtils.getBeansOfType(UserService.class);
userServices.forEach((name, service) -> {
System.out.println("Bean名称: " + name + ", 实例: " + service);
});
3. 获取环境配置
java
// 获取application.properties/yml中的配置
String appName = SpringContextUtils.getProperty("spring.application.name");
String serverPort = SpringContextUtils.getProperty("server.port");
高级用法
1. 在非Spring管理的类中使用
java
public class NonSpringClass {
public void someMethod() {
// 获取Spring容器中的Bean
UserRepository repository = SpringContextUtils.getBean(UserRepository.class);
repository.save(new User());
}
}
2. 在静态方法中使用
java
public class UtilityClass {
public static void doSomething() {
// 静态方法中获取Bean
EmailService emailService = SpringContextUtils.getBean(EmailService.class);
emailService.send("[email protected]", "Hello");
}
}
3. 在JUnit测试中使用
java
@SpringBootTest
public class UserServiceTest {
@Test
public void testGetUser() {
// 在测试中获取Bean
UserService userService = SpringContextUtils.getBean(UserService.class);
User user = userService.getUserById(1L);
assertNotNull(user);
}
}
实现原理
-
ApplicationContextAware 接口:
- Spring 会在初始化时自动调用
setApplicationContext
方法 - 将 ApplicationContext 注入到工具类的静态变量中
- Spring 会在初始化时自动调用
-
静态方法访问:
- 所有方法都是静态的,方便在任何地方调用
- 内部检查 ApplicationContext 是否已初始化
-
泛型支持:
- 减少显式类型转换,提高代码安全性
- 编译器会检查类型是否匹配
注意事项
-
初始化时机:
- 确保 Spring 容器完全初始化后再使用工具类
- 不要在 Bean 的构造方法中使用,因为此时 Bean 还未完全初始化
-
单元测试:
- 在测试中需要先启动 Spring 上下文
- 可以使用
@SpringBootTest
注解
-
多线程安全:
- ApplicationContext 是线程安全的
- 工具类本身不包含可变状态,也是线程安全的
-
替代方案:
- 在 Spring Boot 中可以直接使用依赖注入
- 这个工具类主要用在无法使用依赖注入的场景
扩展功能
如果需要更多功能,可以扩展以下方法:
java
/**
* 获取当前环境profile
*/
public static String[] getActiveProfiles() {
checkApplicationContext();
return applicationContext.getEnvironment().getActiveProfiles();
}
/**
* 判断当前是否是指定环境
*/
public static boolean isProfileActive(String profile) {
checkApplicationContext();
String[] activeProfiles = getActiveProfiles();
return Arrays.asList(activeProfiles).contains(profile);
}
/**
* 发布事件
*/
public static void publishEvent(Object event) {
checkApplicationContext();
applicationContext.publishEvent(event);
}
这个 SpringContextUtils 工具类提供了一种简洁的方式来访问 Spring 容器中的 Bean,特别适合在非 Spring 管理的类中获取 Spring Bean 的场景。