First Commit
This commit is contained in:
215
src/main/java/quant/rich/emoney/util/SpringContextHolder.java
Normal file
215
src/main/java/quant/rich/emoney/util/SpringContextHolder.java
Normal file
@@ -0,0 +1,215 @@
|
||||
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 属性未注入,请检查程序是否未正常启动");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user