Files
emo-grab/src/main/java/quant/rich/emoney/util/SpringContextHolder.java
Administrator 6a5e13974c First Commit
2025-05-12 12:04:42 +08:00

215 lines
8.3 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package quant.rich.emoney.util;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.Validate;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.util.ProxyUtils;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import quant.rich.emoney.config.ConstructionGuard;
/**
* 以静态变量保存Spring ApplicationContext, 可在任何代码任何地方任何时候取出ApplicaitonContext.
*
*/
@Component
@Slf4j
@Lazy(false)
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
private static ApplicationContext applicationContext = null;
/**
* 取得存储在静态变量中的ApplicationContext.
*/
public static ApplicationContext getApplicationContext() {
assertContextInjected();
return applicationContext;
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
assertContextInjected();
return (T) applicationContext.getBean(name);
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(Class<T> requiredType) {
assertContextInjected();
return applicationContext.getBean(requiredType);
}
/**
* 清除SpringContextHolder中的ApplicationContext为Null.
*/
public static void clearHolder() {
if (log.isDebugEnabled()) {
log.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
}
applicationContext = null;
}
/**
* 实现ApplicationContextAware接口, 注入Context到静态变量中.
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
SpringContextHolder.applicationContext = applicationContext;
}
/**
* 实现DisposableBean接口, 在Context关闭时清理静态变量.
*/
@Override
public void destroy() throws Exception {
SpringContextHolder.clearHolder();
}
public static boolean updateBean(String name, Object newBean) {
log.debug("Enter updateBean for {}'s update", name);
if (SpringContextHolder.applicationContext != null) {
if (!applicationContext.containsBean(name)) {
log.debug("updateBean is only for initialized bean's updating, but {} doesn't exist or hasn't been initialized yet");
return false;
}
try {
Class<?> beanType = SpringContextHolder.applicationContext.getType(name);
if (ConstructionGuard.isConstructing(beanType)) {
log.debug("updateBean is called but {} is initializing", name);
return false;
}
Object bean = SpringContextHolder.applicationContext.getBean(name);
Class<?> newBeanType = newBean.getClass();
if (beanType != newBeanType) {
log.warn("Cannot set new bean type {} to {}", newBeanType.getName(), name);
return false;
}
if (bean != null) {
if (AopUtils.isAopProxy(bean)) {
bean = ProxyUtils.getUserClass(bean);
}
if (getNeverRefreshable().contains(bean.getClass().getName())) {
return false;
}
}
Field[] declaredFields = beanType.getDeclaredFields();
for (Field field : declaredFields) {
int modifiers = field.getModifiers();
if (Modifier.isFinal(modifiers) || Modifier.isStatic(modifiers)) {
continue;
}
field.setAccessible(true);
try {
setFieldData(field, bean, field.get(newBean));
} catch (Exception e) {
log.error("Change bean failed", e);
}
field.setAccessible(false);
}
rebindSingleton(name, bean);
return true;
} catch (Exception e) {
log.error("Cannot rebind to " + name, e);
}
}
return false;
}
private static void setFieldData(Field field, Object bean, Object obj) throws Exception {
Class<?> type = field.getType();
if (obj == null) {
field.set(bean, null);
} else if (type.equals(String.class)) {
field.set(bean, String.valueOf(obj));
} else if (type.equals(Integer.class)) {
field.set(bean, Integer.valueOf(String.valueOf(obj)));
} else if (type.equals(Long.class)) {
field.set(bean, Long.valueOf(String.valueOf(obj)));
} else if (type.equals(Double.class)) {
field.set(bean, Double.valueOf(String.valueOf(obj)));
} else if (type.equals(Short.class)) {
field.set(bean, Short.valueOf(String.valueOf(obj)));
} else if (type.equals(Byte.class)) {
field.set(bean, Byte.valueOf(String.valueOf(obj)));
} else if (type.equals(Boolean.class)) {
field.set(bean, Boolean.valueOf(String.valueOf(obj)));
} else if (type.equals(Date.class)) {
field.set(bean, new Date(Long.valueOf(String.valueOf(obj))));
} else if (obj instanceof List) {
field.set(bean, new ArrayList<>((List<?>) obj));
} else {
field.set(bean, obj);
}
}
/**
* 重绑定单例模式 Bean
*
* @param name
* @return
* @see <a href=
* "http://blog.gxitsky.com/2023/01/31/SpringBoot-63-Auto-Refresh-Environment-Config-Data/">
* Spring Boot 2系列(六十三)动态刷新环境配置和Bean属性值</a>
*/
@ManagedOperation
public static boolean rebindSingleton(String name, Object newBean) {
if (SpringContextHolder.applicationContext != null) {
try {
Object bean = SpringContextHolder.applicationContext.getBean(name);
DefaultSingletonBeanRegistry registry = (DefaultSingletonBeanRegistry) SpringContextHolder.applicationContext
.getAutowireCapableBeanFactory();
if (bean != null) {
if (AopUtils.isAopProxy(bean)) {
bean = ProxyUtils.getUserClass(bean);
}
// TODO: determine a more general approach to fix this.
// see https://github.com/spring-cloud/spring-cloud-commons/issues/571
if (getNeverRefreshable().contains(bean.getClass().getName())) {
return false; // ignore
}
registry.destroySingleton(name);
}
registry.registerSingleton(name, newBean);
return true;
} catch (Exception e) {
log.error("Cannot rebind to " + name, e);
}
}
return false;
}
@ManagedAttribute
public static Set<String> getNeverRefreshable() {
String neverRefresh = SpringContextHolder.applicationContext.getEnvironment()
.getProperty("spring.cloud.refresh.never-refreshable", "com.zaxxer.hikari.HikariDataSource");
return StringUtils.commaDelimitedListToSet(neverRefresh);
}
/**
* 检查ApplicationContext不为空.
*/
private static void assertContextInjected() {
Validate.validState(applicationContext != null, "applicaitonContext 属性未注入,请检查程序是否未正常启动");
}
}