我有一个spring config xml文件,可以创建多个bean并将其自动装入其他bean
例如
<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>
我通过阅读上面的spring文件来创建应用程序上下文。但是A的prop1的值在运行时是动态已知的。如何动态注入此属性?动态是指应用程序启动时的情况。例如,某些属性作为命令行输入到应用程序。 然后应将此属性设置为bean的属性
我已经给出了这个例子来简化问题。真正的我的动态参数是数据库服务器url,我想动态创建连接池
答案 0 :(得分:4)
更多涉及,但这是一种方法:
main()
方法中定义了您正在创建的应用程序上下文。您的想法是定义一个List
,其中将存储命令行参数,并在此应用程序上下文中将此列表定义为bean(标识为args
):
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();
...
}
main()
方法中已有的应用程序上下文的父级。假设您正在创建ClassPathXmlApplicationContext
,则代码为:ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
new String[] { "app-context.xml" }, parentContext);
其中parentContext
是先前创建的用于保存List
参数的上下文。
<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>
注意在xml中,SPeL表达式中使用的参数名称为args
,这是用于定义List
中parentContext
的确切名称。
答案 1 :(得分:2)
这是另一种注入财产的方法。我的建议类似于 geoand ,因为我也使用java配置而不是xml。但是使用SimpleCommandLinePropertySource并且更灵活一点也更简单。 在我的例子下面可以使用或不使用弹簧启动。
主要课程
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 强>
这是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();
}
}
属性注入注释
现在任何地方都有 @Value 注释或其他属性注入注释,例如 @Scheduled ,期望该值受外部文件或命令行选项上给出的属性值的约束都。
@Component
public class MyComponent {
@Value("${my.property.data}")
private String myPropertyData;
@Scheduled(fixedDelayString = "${schedule.delay.period}")
public void run() {
:
}
}
因此,您可以将下面的属性放在外部文件上,并通过命令行选项 - config.location = / some / path / app.properties
提供该文件<强> app.properties 强>
my.property.data=test
schedule.delay.period=60000
或者您可以通过命令行单独注入每个属性值
--my.property.data=test --schedule.delay.period=60000
仅供参考,spring-boot已经为外部属性文件使用了--spring.config.location命令行选项。
答案 2 :(得分:1)
由于您要做的是将命令行参数注入bean,我建议您查看this。
提到的内容的本质是你可以添加CommandLinePropertySource(与使用属性源从属性文件中读取参数的方式完全相同)然后只使用常规引用它们Spring方法(通过环境获取它们或使用Spring EL引用它们)。
为了完整性,我发布了第一个链接中的代码
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!"));
}
}
在您的情况下,如果您使用CommandLinePropertySource
,xml配置将如下所示:
<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>
最后,如果你使用的是Spring Boot,那么Spring Boot会自动将命令行属性添加到Spring环境中,这意味着你不必自己注册PropertySource(查看this更多细节)。
答案 3 :(得分:0)
您可以使用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"/>
然后,您需要使用prop1.get()
才能在运行时检索值