该文章为原创(转载请注明出处):Spring @Value如何支持静态注入? - 简书 (jianshu.com)
真实业务场景
一些工具类或者业务上使用的比较通用的类使用了@Value,但是必须指定为@Component作为bean才能支持@Value,使用起来会稍显冗余(对于开发功能来说并没有什么问题)。
需要达成的目的
会催生这一类需求,希望使用 @Value
的注入到静态属性上
public class BizUtils {
@Value("${app.test.biz-type}")
private static String testBizType;
}
达成目的阻碍
由于Spring @Value 注入是基于实例的,因此无法针对类的静态属性注入
方案思路
- 扫描所有带@Value的静态属性
- 模仿Spring解析文本属性的方式 注入到类的静态属性中
代码实现
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.reflections.Reflections;
import org.reflections.scanners.Scanners;
import org.reflections.util.ConfigurationBuilder;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanExpressionContext;
import org.springframework.beans.factory.support.AbstractBeanFactory;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import static java.util.Objects.isNull;
import static java.util.stream.Collectors.groupingBy;
import static org.springframework.util.ReflectionUtils.makeAccessible;
/**
* @author fuhangbo.hanger.uhfun
**/
@Slf4j
@Component
public class StaticValueInjector
implements BeanFactoryAware, ApplicationContextAware, SmartInitializingSingleton {
private AbstractBeanFactory beanFactory;
private ApplicationContext applicationContext;
public Map<String, List<Field>> scanAndInjectStaticFields() {
// 获取基础包名
String basePackage = getApplicationBasePackage();
// 使用Reflections扫描带有@Value注解的静态字段
Reflections reflections = new Reflections(new ConfigurationBuilder()
.forPackage(basePackage)
.addScanners(Scanners.FieldsAnnotated)
);
return reflections.getFieldsAnnotatedWith(Value.class)
.stream().filter(f -> Modifier.isStatic(f.getModifiers()))
.collect(groupingBy(f -> f.getAnnotation(Value.class).value()));
}
private String getApplicationBasePackage() {
// 通过主类的包名获取基础包
return applicationContext.getBeansWithAnnotation(SpringBootApplication.class)
.values()
.iterator()
.next()
.getClass().getPackage().getName();
}
@Override
public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* @param value value
* @return object
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency(org.springframework.beans.factory.config.DependencyDescriptor,
* java.lang.String, java.util.Set, org.springframework.beans.TypeConverter)
*/
private Object resolveValue(String value) {
String strVal = beanFactory.resolveEmbeddedValue(value);
if (isNull(beanFactory.getBeanExpressionResolver())) {
return strVal;
}
return beanFactory.getBeanExpressionResolver().evaluate(strVal, new BeanExpressionContext(beanFactory, null));
}
@Override
public void setBeanFactory(@NotNull BeanFactory beanFactory) throws BeansException {
this.beanFactory = (AbstractBeanFactory)beanFactory;
}
@SneakyThrows
@Override
public void afterSingletonsInstantiated() {
Map<String, List<Field>> injectStaticFieldsMap = scanAndInjectStaticFields();
for (Entry<String, List<Field>> entry : injectStaticFieldsMap.entrySet()) {
String value = entry.getKey();
List<Field> fields = entry.getValue();
Object injectedValue = resolveValue(value);
for (Field targetField : fields) {
makeAccessible(targetField);
targetField.set(null, injectedValue);
log.info("Static @Value {}.{} 注入value {} = {}",
targetField.getDeclaringClass().getName(), targetField.getName(), value, injectedValue);
}
}
}
}
该文章为原创(转载请注明出处):Spring @Value如何支持静态注入? - 简书 (jianshu.com)
网友评论