【问题标题】:Injecting config values into a Spring Boot application, without using @Value将配置值注入 Spring Boot 应用程序,而不使用 @Value
【发布时间】:2022-08-22 14:36:22
【问题描述】:

我想将配置数据注入 Spring Boot 应用程序。我查看了@Value,这将是完美的,但文本的格式不适合我的用例(我希望格式与我们当前在 JEE 中的实现向后兼容,它将与我们的 SpringBoot 实现并列,并且有很多目前使用这种方法的代码)。

因此,我不想说 @Configuration(\"${somevalue}\") 和正在注入的配置,而是说 @Configuration(\"somevalue\") 并且它仍然被注入,而不是仅仅放置 \"somevalue\"变量中的文本。

其中 Configuration 是 @Value 的别名,如下所示:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Value(\"\")
public @interface Configuration {
    @AliasFor(annotation=Value.class, attribute=\"value\")
    String value() default \"\";
}

我确实尝试更改占位符的前后后缀,因此它们是 \"\" 但这导致 SpringBoot 根本无法启动。

@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
    PropertySourcesPlaceholderConfigurer p = new PropertySourcesPlaceholderConfigurer();
    p.setIgnoreResourceNotFound(true);
    p.setPlaceholderPrefix(\"\");
    p.setPlaceholderSuffix(\"\");
    p.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
    return p;
}

我还尝试在运行时更改注释,以便扫描类路径并将所有没有 ${...} 的注释更改为 ${...},但这也不起作用,因为代理处理程序值每次都会更改您进行类查找,这意味着您更改了正在查看的实例,但没有更改 Spring 使用的实例。

所以我想我会有自己的@Value 实现,像这样使用:

@RestController
public class PingResource {

    @Inject
    public PingResource(@Configuration(\"someValue\") String someValue) {
       ...
    }
}

使用这样的 Bean 生产者:

@Configuration
public class ConfigurationProducer {

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public String getString(final InjectionPoint injectionPoint) {
      ....
    }
}

但是 Spring 中的限定符看起来不支持注释,所以它找不到 bean,我得到了这个异常:

NoSuchBeanDefinitionException: No qualifying bean of type \'java.lang.String\' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@bob.Configuration(value=someValue)}

如果我从参数中取出注释,那么它会找到 bean 工厂并且一切都很好......除了我不知道我应该注入什么配置值,因为没有注释可以告诉我。

有点卡住了,我查看了 Spring 代码,看看它是如何实现 @Value 的,看看我是否可以重新创建它,但这是一个很难理解的老鼠窝。

任何帮助或建议下一步该去哪里解决这个问题,感觉就像我非常接近,但只是错过了一些我不知道的 Spring 魔法。

非常感谢

  • 您可以使用反射来更改 spring bean 中的字段值。那对你有用吗?
  • 是的,我尝试使用: InvocationHandler handler = Proxy.getInvocationHandler(annotation);字段 f = handler.getClass().getDeclaredField(\"memberValues\"); f.setAccessible(true); Map<String, Object> memberValues = (Map<String, Object>) f.get(handler); memberValues.put(key, newValue);如果您使用相同的“getDeclaredConstructors()”返回变量获取注释值,它的工作原理是。但是,当您再次执行此操作时,更改将丢失。我认为这是因为每次您进行查找时代理值都不同,因此任何更改都会丢失。
  • 您可以使用 Springs 应用程序上下文来访问所有 Spring bean,然后对其进行操作。 for (String bean : applicationContext.getBeanDefinitionNames()) ... 然后单独访问它们。不过只适用于春豆的田地

标签: java spring spring-boot jakarta-ee


【解决方案1】:

我实现了一些东西,你可以试试:

我的注释类:

@Qualifier
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SomeAnnotation{

    String value();

}

然后我使用一个 Injector 类将所有值注入到 spring bean 中。

@Component
public class Injector {

    private static final Logger logger = LoggerFactory.getLogger(Injector.class.getName());

    private final ApplicationContext applicationContext;

    @Autowired
    public Injector(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    public void inject() {

        for (String bean : applicationContext.getBeanDefinitionNames()) {

            for (Field field : applicationContext.getBean(bean).getClass().getDeclaredFields()) {

                if (field.isAnnotationPresent(SomeAnnotation.class)) {
                    // Instantiate Annotation Values
                    SomeAnnotationvalue value = field.getAnnotation(SomeAnnotation.class);

                    // Instantiate Resource
                    String resource = ...

                    // Set accessibility of field
                    field.setAccessible(true);

                    try {
                        // Set Value to field
                        field.set(applicationContext.getBean(bean), resource);

                        // Reverse setting accessibility
                        field.setAccessible(false);
                    } catch (IllegalAccessException | MissingResourceException e) {
                        logger.error("Error while trying to inject resources into bean {}: {}", bean, Arrays.toString(e.getStackTrace()));
                    }
                }

            }

        }

    }

}

您需要在应用程序启动时调用它。

注意:这仅适用于您的注释字段是 Spring Bean(@Service、@Component、@Controller、@Repository 等...)

然后类中的示例将如下所示:

@SomeAnnotation("some.property.value")
private String propertyValue

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-04-30
    • 2013-10-03
    • 2016-06-12
    • 1970-01-01
    • 1970-01-01
    • 2020-10-31
    • 2018-11-07
    • 1970-01-01
    相关资源
    最近更新 更多