java 动态地将属性注入弹簧

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25423148/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 08:00:55  来源:igfitidea点击:

dynamically injecting property into spring

javaspringdependency-injection

提问by user93796

I have a spring config xml file which creates multiple beans and autowires one into others

我有一个 spring config xml 文件,它创建多个 bean 并将一个自动装配到其他 bean

Eg

例如

<bean id="a" class="com.xyz.A">
        <property name="prop1" value="?" />
   </bean>

   <bean id="b" class="com.xyz.B">
        <property name="prop2" ref="a" />   
   </bean>

My creates application context by reading the above spring file. But the value of prop1 of A is dynamically known at run time.How do i inject this property dynamically?By dynamic i mean when the application starts.For example some properties come as command line inputs to the application. And this property should then be set as property of beans

我通过阅读上面的 spring 文件来创建应用程序上下文。但是 A 的 prop1 的值在运行时是动态知道的。我如何动态注入这个属性?动态我的意思是当应用程序启动时。例如,某些属性作为应用程序的命令行输入。然后这个属性应该被设置为bean的属性

I had given this example to simplify the problem.In real my dynamic parameter is database server url and i want to create connection pool dynamically

我给出了这个例子来简化问题。实际上我的动态参数是数据库服务器 url,我想动态创建连接池

采纳答案by Andrei Stefan

A bit more involved, but here's one approach:

涉及更多,但这是一种方法:

  • in your main()method define another application context along the one you are already creating. The idea is that you are defining a Listwhere the command line arguments will be stored and define this list as a bean (with the id args) in this application context:
  • 在您的main()方法中,沿着您已经创建的上下文定义另一个应用程序上下文。这个想法是你正在定义一个List命令行参数将被存储的位置,并将这个列表定义为args这个应用程序上下文中的一个 bean(带有 id ):
public static void main(String[] args) {
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    BeanDefinition beanDefinition = BeanDefinitionBuilder
            .rootBeanDefinition(Arrays.class, "asList")
            .addConstructorArgValue(args)
            .getBeanDefinition();
    beanFactory.registerBeanDefinition("args", beanDefinition);

    GenericApplicationContext parentContext= new GenericApplicationContext(beanFactory);
    parentContext.refresh();
    ...
}
  • Use the previously defined application context as a parent for the application context you already have in your main()method. Assuming you are creating a ClassPathXmlApplicationContextthis would be the code:
  • 使用先前定义的应用程序上下文作为您的main()方法中已有的应用程序上下文的父级。假设您正在创建一个ClassPathXmlApplicationContext这将是代码:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
            new String[] { "app-context.xml" }, parentContext);

where parentContextis the context created previously to hold the Listof arguments.

哪里parentContext是先前创建的用于保存List参数的上下文。

  • then, in your xml, you'd use SPeL to easily access the list of arguments:
  • 然后,在您的 xml 中,您将使用 SPeL 轻松访问参数列表:
    <bean id="a" class="com.foo.bar.ClassA">
        <property name="prop1" value="#{args[0]}" />
    </bean>

    <bean id="b" class="com.foo.bar.ClassB">
        <property name="prop2" ref="a" />
    </bean>

notice in xml the name of the argument used in the SPeL expression is argswhich is the exact name used to define the Listin the parentContext.

在通知中的XML SpeI位表达式中使用的参数的名称是args它是用于限定的确切名称ListparentContext

回答by Steve Park

Here is another way to inject property. Mine is similar to that geoandsuggested, since I also use java configuration than xml. But little more simpler by using SimpleCommandLinePropertySource and also little more flexible. Below my example does work with or without using spring boot.

这是注入属性的另一种方法。我的类似于geoand建议,因为我也使用 java 配置而不是 xml。但是使用 SimpleCommandLinePropertySource 更简单,也更灵活。下面我的示例在使用或不使用 spring boot 的情况下都可以工作。

Main class

主班

public static void main(String [] args) {
    SimpleCommandLinePropertySource ps = new SimpleCommandLinePropertySource(args);
    @SuppressWarnings("resource")
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.getEnvironment().getPropertySources().addFirst(ps);
    ctx.register(ApplicationConfig.class);
    ctx.refresh();
}

ApplicationConfig.java

应用程序配置文件

This is java configuration class and read application.properties inside of package as default, but also try to read external property file from command line option --config.location=/some/path/app.properties If external property file is not given, then it will be gracefully ignored by ignoreResourceNotFound = true

这是java配置类,默认读取包内的application.properties,但也尝试从命令行选项读取外部属性文件--config.location=/some/path/app.properties 如果没有给出外部属性文件,然后它会被ignoreResourceNotFound = true优雅地忽略

@Configuration
@EnableScheduling
@ComponentScan("com.mycompany.package")
@PropertySource(
        value = {"classpath:/application.properties", "file:${config.location}"},
        ignoreResourceNotFound = true
    )
public class ApplicationConfig {

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertyConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }
}

Property Injection Annotation

属性注入注解

Now any place has @Valueannotation or other property injection annotation like @Scheduledexpect the value to be bound by the property value given on external file or command line option both.

现在任何地方都有@Value注释或其他属性注入注释,如@Scheduled期望值受外部文件或命令行选项上给出的属性值的约束。

@Component
public class MyComponent {

    @Value("${my.property.data}")
    private String myPropertyData;


    @Scheduled(fixedDelayString = "${schedule.delay.period}")
    public void run() {
         :
    }
}

So, you can put below property on external file and give the file through command line option --config.location=/some/path/app.properties

因此,您可以将以下属性放在外部文件上,并通过命令行选项--config.location=/some/path/app.properties 提供文件

app.properties

app.properties

my.property.data=test
schedule.delay.period=60000

or you can inject each property value separately via command line

或者您可以通过命令行分别注入每个属性值

--my.property.data=test --schedule.delay.period=60000

FYI, spring-boot uses --spring.config.location command line option for external property file already.

仅供参考,spring-boot 已经将 --spring.config.location 命令行选项用于外部属性文件。

回答by geoand

Since what you are looking to do is to inject command line arguments into beans, I suggest you take a look at this.

由于您要做的是将命令行参数注入到 bean 中,因此我建议您查看.

The essence of what is mentioned there is that you can add a CommandLinePropertySource(in the exact same way you would use a property source for reading arguments from a properties file) and then just to refer to them using regular Spring methods (either getting them through the environment or referring to them using Spring EL).

那里提到的本质是您可以添加一个CommandLinePropertySource(以与使用属性源从属性文件中读取参数完全相同的方式),然后仅使用常规 Spring 方法来引用它们(或者让它们通过环境或使用 Spring EL 引用它们)。

For completeness I am posting the code that is found in the first link

为了完整起见,我发布了在第一个链接中找到的代码

public class Main {
    public static void main(String... args) {
        //initialize the command line parsing stuff
        OptionParser parser = new OptionParser();
        parser.accepts("greeting").withRequiredArg();
        OptionSet options = parser.parse(args);

        //create the actual Spring PropertySource
        PropertySource<?> ps = new JOptCommandLinePropertySource(options);

        //setup the Spring context
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        ctx.getEnvironment().getPropertySources().addLast(ps); //register the property source with the environment

        ctx.register(Greeter.class);
        ctx.refresh();
        Greeter greeter = ctx.getBean(Greeter.class);
        greeter.sayGreeting();
    }
}

@Component
class Greeter {
    @Inject private Environment env;


    //the following would also work
    //@Value("${greeting}")
    //private String greeting;        

    /**
     * Print out the 'greeting' property if it exists, and otherwise, "Welcome!".
     */
    public void sayGreeting() {
        System.out.println(env.getProperty("greeting", "Welcome!"));
    }
}

In your case, if you use the CommandLinePropertySourcethe xml configuration would look like:

在您的情况下,如果您使用CommandLinePropertySourcexml 配置将如下所示:

   <bean id="a" class="com.xyz.A">
        <property name="prop1" value="${db.url}" />
   </bean>

   <bean id="b" class="com.xyz.B">
        <property name="prop2" ref="a" />   
   </bean>

On a final note, if you are using Spring Boot, then Spring Boot will automatically add the command line properties to the Spring environment which means that you don't have to register the PropertySource on your own (check thisfor more details).

最后要注意的是,如果您使用的是 Spring Boot,那么 Spring Boot 会自动将命令行属性添加到 Spring 环境中,这意味着您不必自己注册 PropertySource(查看了解更多详细信息)。

回答by antoine

You can use the Supplierpattern (comes with Java 8 or Guava library for example)

您可以使用该Supplier模式(例如带有 Java 8 或 Guava 库)

<bean id="a" class="com.xyz.A">
    <property name="prop1" value="prop1Supplier" />
</bean>

<bean id="b" class="com.xyz.B">
    <property name="prop2" ref="a" />   
</bean>

<bean id="prop1Supplier" class="com.xyz.Prop1Supplier"/>

Then you'll need to use prop1.get()in order to retrieve the value at runtime

然后您需要使用prop1.get()以在运行时检索值