spring 有没有办法@Autowire 一个需要构造函数参数的bean?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6739566/
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
Is there a way to @Autowire a bean that requires constructor arguments?
提问by Eric B.
I'm using Spring 3.0.5 and am using @Autowire annotation for my class members as much as possible. One of the beans that I need to autowire requires arguments to its constructor. I've looked through the Spring docs, but cannot seem to find any reference to how to annotate constructor arguments.
我正在使用 Spring 3.0.5,并尽可能为我的班级成员使用 @Autowire 注释。我需要自动装配的 bean 之一需要为其构造函数提供参数。我已经浏览了 Spring 文档,但似乎找不到任何关于如何注释构造函数参数的参考。
In XML, I can use as part of the bean definition. Is there a similar mechanism for @Autowire annotation?
在 XML 中,我可以将其用作 bean 定义的一部分。@Autowire 注释是否有类似的机制?
Ex:
前任:
@Component
public class MyConstructorClass{
String var;
public MyConstructorClass( String constrArg ){
this.var = var;
}
...
}
@Service
public class MyBeanService{
@Autowired
MyConstructorClass myConstructorClass;
....
}
In this example, how do I specify the value of "constrArg" in MyBeanService with the @Autowire annotation? Is there any way to do this?
在此示例中,如何使用 @Autowire 注释在 MyBeanService 中指定“constrArg”的值?有没有办法做到这一点?
Thanks,
谢谢,
Eric
埃里克
采纳答案by Sean Patrick Floyd
You need the @Valueannotation.
您需要@Value注释。
A common use case is to assign default field values using
"#{systemProperties.myProp}"style expressions.
一个常见的用例是使用
"#{systemProperties.myProp}"样式表达式分配默认字段值 。
public class SimpleMovieLister {
private MovieFinder movieFinder;
private String defaultLocale;
@Autowired
public void configure(MovieFinder movieFinder,
@Value("#{ systemProperties['user.region'] }"} String defaultLocale) {
this.movieFinder = movieFinder;
this.defaultLocale = defaultLocale;
}
// ...
}
See:Expression Language > Annotation Configuration
请参阅:表达式语言 > 注释配置
To be more clear: in your scenario, you'd wire two classes, MybeanServiceand MyConstructorClass, something like this:
更清楚地说:在您的场景中,您将连接两个类,MybeanService和MyConstructorClass,如下所示:
@Component
public class MyBeanService implements BeanService{
@Autowired
public MybeanService(MyConstructorClass foo){
// do something with foo
}
}
@Component
public class MyConstructorClass{
public MyConstructorClass(@Value("#{some expression here}") String value){
// do something with value
}
}
Update: if you need several different instances of MyConstructorClasswith different values, you should use Qualifier annotations
更新:如果您需要MyConstructorClass具有不同值的多个不同实例,则应使用 Qualifier 注释
回答by Ivan Ketler
Well, from time to time I run into the same question. As far as I know, one cannot do that when one wants to add dynamic parameters to the constructor. However, the factory pattern may help.
好吧,我不时遇到同样的问题。据我所知,当想要向构造函数添加动态参数时,不能这样做。但是,工厂模式可能会有所帮助。
public interface MyBean {
// here be my fancy stuff
}
public interface MyBeanFactory {
public MyBean getMyBean(/* bean parameters */);
}
@Component
public class MyBeanFactoryImpl implements MyBeanFactory {
@Autowired
WhateverIWantToInject somethingInjected;
public MyBean getMyBean(/* params */) {
return new MyBeanImpl(/* params */);
}
private class MyBeanImpl implements MyBean {
public MyBeanImpl(/* params */) {
// let's do whatever one has to
}
}
}
@Component
public class MyConsumerClass {
@Autowired
private MyBeanFactory beanFactory;
public void myMethod() {
// here one has to prepare the parameters
MyBean bean = beanFactory.getMyBean(/* params */);
}
}
Now, MyBeanis not a spring bean per se, but it is close enough. Dependency Injection works, although I inject the factory and not the bean itself - one has to inject a new factory on top of his own new MyBeanimplementation if one wants to replace it.
现在,MyBean它本身不是春豆,但已经足够接近了。依赖注入有效,尽管我注入的是工厂而不是 bean 本身——MyBean如果要替换它,必须在他自己的新实现之上注入一个新工厂。
Further, MyBeanhas access to other beans - because it may have access to the factory's autowired stuff.
此外,MyBean可以访问其他 bean - 因为它可能可以访问工厂的自动装配的东西。
And one might apparently want to add some logic to the getMyBeanfunction, which is extra effort I allow, but unfortunately I have no better solution. Since the problem usually is that the dynamic parameters come from an external source, like a database, or user interaction, therefore I must instantiate that bean only in mid-run, only when that info is readily available, so the Factoryshould be quite adequate.
人们可能显然想为getMyBean函数添加一些逻辑,这是我允许的额外努力,但不幸的是我没有更好的解决方案。由于问题通常是动态参数来自外部源,如数据库或用户交互,因此我必须仅在运行中实例化该 bean,仅当该信息易于获得时,所以Factory应该足够了。
回答by skaffman
In this example, how do I specify the value of "constrArg" in
MyBeanServicewith the@Autowireannotation? Is there any way to do this?
在此示例中,如何
MyBeanService在@Autowire注释中指定“constrArg”的值?有没有办法做到这一点?
No, not in the way that you mean. The bean representing MyConstructorClassmust be configurable without requiring any of its client beans, so MyBeanServicedoesn't get a say in how MyConstructorClassis configured.
不,不是你说的那样。表示的 beanMyConstructorClass必须是可配置的,而不需要它的任何客户端 bean,因此MyBeanService在如何MyConstructorClass配置方面没有发言权。
This isn't an autowiring problem, the problem here is how does Spring instantiate MyConstructorClass, given that MyConstructorClassis a @Component(and you're using component-scanning, and therefore not specifying a MyConstructorClassexplicitly in your config).
这不是自动装配问题,这里的问题是 Spring 如何实例化MyConstructorClass,因为它MyConstructorClass是 a @Component(并且您正在使用组件扫描,因此没有MyConstructorClass在您的配置中明确指定 a )。
As @Sean said, one answer here is to use @Valueon the constructor parameter, so that Spring will fetch the constructor value from a system property or properties file. The alternative is for MyBeanServiceto directly instantiate MyConstructorClass, but if you do that, then MyConstructorClassis no longer a Spring bean.
正如@Sean 所说,这里的一个答案是@Value在构造函数参数上使用,以便 Spring 从系统属性或属性文件中获取构造函数值。另一种方法是MyBeanService直接实例化MyConstructorClass,但如果你这样做了,那么MyConstructorClass就不再是一个 Spring bean。
回答by Zakaria
You can also configure your component like this :
您还可以像这样配置您的组件:
package mypackage;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConstructorClassConfig {
@Bean
public MyConstructorClass myConstructorClass(){
return new myConstructorClass("foobar");
}
}
}
With the Beanannotation, you are telling Spring to register the returned bean in the BeanFactory.
通过Bean注释,您告诉 Spring 在BeanFactory.
So you can autowire it as you wish.
因此,您可以根据需要自动装配它。
回答by Fahim Farook
回答by Jason Glez
Another alternative, if you already have an instance of the object created and you want to add it as an @autowired dependency to initialize all the internal @autowired variables, could be the following:
另一种选择,如果您已经创建了对象的实例,并且想要将其添加为 @autowired 依赖项以初始化所有内部 @autowired 变量,则可能是以下内容:
@Autowired
private AutowireCapableBeanFactory autowireCapableBeanFactory;
public void doStuff() {
YourObject obj = new YourObject("Value X", "etc");
autowireCapableBeanFactory.autowireBean(obj);
}
回答by george
An alternative would be instead of passing the parameters to the constructor you might have them as getter and setters and then in a @PostConstruct initialize the values as you want. In this case Spring will create the bean using the default constructor. An example is below
另一种方法是,不是将参数传递给构造函数,而是将它们作为 getter 和 setter,然后在 @PostConstruct 中根据需要初始化值。在这种情况下,Spring 将使用默认构造函数创建 bean。下面是一个例子
@Component
public class MyConstructorClass{
String var;
public void setVar(String var){
this.var = var;
}
public void getVar(){
return var;
}
@PostConstruct
public void init(){
setVar("var");
}
...
}
@Service
public class MyBeanService{
//field autowiring
@Autowired
MyConstructorClass myConstructorClass;
....
}
回答by AntoineB
Most answers are fairly old, so it might have not been possible back then, but there actually is a solution that satisfies all the possible use-cases.
大多数答案都很旧,所以当时可能不可能,但实际上有一个解决方案可以满足所有可能的用例。
So right know the answers are:
所以正确的答案是:
- Not providing a real Spring component (the factory design)
- or does not fit every situation (using
@Valueyou have to have the value in a configuration file somewhere)
- 不提供真正的 Spring 组件(工厂设计)
- 或不适合所有情况(使用
@Value您必须在某处的配置文件中使用该值)
The solution to solve those issues is to create the object manually using the ApplicationContext:
解决这些问题的解决方案是使用以下命令手动创建对象ApplicationContext:
@Component
public class MyConstructorClass
{
String var;
public MyConstructorClass() {}
public MyConstructorClass(String constrArg) {
this.var = var;
}
}
@Service
public class MyBeanService implements ApplicationContextAware
{
private static ApplicationContext applicationContext;
MyConstructorClass myConstructorClass;
public MyBeanService()
{
// Creating the object manually
MyConstructorClass myObject = new MyConstructorClass("hello world");
// Initializing the object as a Spring component
AutowireCapableBeanFactory factory = applicationContext.getAutowireCapableBeanFactory();
factory.autowireBean(myObject);
factory.initializeBean(myObject, myObject.getClass().getSimpleName());
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
applicationContext = context;
}
}
This is a cool solution because:
这是一个很酷的解决方案,因为:
- It gives you access to all the Spring functionalities on your object (
@Autowiredobviously, but also@Asyncfor example), - You can use any source for your constructor arguments (configuration file, computed value, hard-coded value, ...),
- It only requires you to add a few lines of code without having to change anything.
- It can also be used to dynamically create a unknown number of instances of a Spring-managed class (I'm using it to create multiple asynchronous executors on the fly for example)
- 它使您可以访问对象上的所有 Spring 功能(
@Autowired显然,但也是@Async例如), - 您可以为构造函数参数使用任何来源(配置文件、计算值、硬编码值等),
- 它只需要您添加几行代码而无需更改任何内容。
- 它还可用于动态创建 Spring 管理类的未知数量的实例(例如,我使用它来动态创建多个异步执行程序)
The only thing to keep in mind is that you have to have a constructor that takes no arguments (and that can be empty) in the class you want to instantiate (or an @Autowiredconstructor if you need it).
唯一要记住的是,您必须在要实例化的类中拥有一个不带任何参数(并且可以为空)的@Autowired构造函数(如果需要,也可以使用构造函数)。

