美文网首页
Spring @Value如何支持静态注入

Spring @Value如何支持静态注入

作者: 杭二 | 来源:发表于2024-10-14 14:00 被阅读0次

该文章为原创(转载请注明出处):Spring @Value如何支持静态注入? - 简书 (jianshu.com)

真实业务场景

一些工具类或者业务上使用的比较通用的类使用了@Value,但是必须指定为@Component作为bean才能支持@Value,使用起来会稍显冗余(对于开发功能来说并没有什么问题)。

需要达成的目的

会催生这一类需求,希望使用 @Value的注入到静态属性上

public class BizUtils {
    @Value("${app.test.biz-type}")
    private static String testBizType;
}

达成目的阻碍

由于Spring @Value 注入是基于实例的,因此无法针对类的静态属性注入

方案思路

  1. 扫描所有带@Value的静态属性
  2. 模仿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)

相关文章

网友评论

      本文标题:Spring @Value如何支持静态注入

      本文链接:https://www.haomeiwen.com/subject/iuqzrjtx.html