有没有办法 @Autowire 需要构造函数参数的 bean?

发布于 2024-11-25 01:50:29 字数 556 浏览 6 评论 0原文

我正在使用 Spring 3.0.5 并尽可能为我的类成员使用 @Autowire 注释。我需要自动装配的 bean 之一需要其构造函数的参数。我浏览了 Spring 文档,但似乎找不到任何关于如何注释构造函数参数的参考。

在 XML 中,我可以将其用作 bean 定义的一部分。 @Autowire注释有类似的机制吗?

例如:

@Component
public class MyConstructorClass{

  String var;
  public MyConstructorClass( String constrArg ){
    this.var = var;
  }
...
}


@Service
public class MyBeanService{
  @Autowired
  MyConstructorClass myConstructorClass;

  ....
}

在此示例中,如何使用 @Autowire 注释指定 MyBeanService 中“constrArg”的值?有什么办法可以做到这一点吗?

谢谢,

埃里克

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.

In XML, I can use as part of the bean definition. Is there a similar mechanism for @Autowire annotation?

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?

Thanks,

Eric

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

趁微风不噪 2024-12-02 01:50:29

您需要 @Value 注释。

一个常见的用例是使用分配默认字段值
"#{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;
  }

  // ...
}

参见: 表达式语言>注释配置


更清楚地说:在您的场景中,您将连接两个类,MybeanServiceMyConstructorClass,如下所示:

@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
    }
}

更新:如果您需要多个类具有不同值的 MyConstructorClass 的不同实例,您应该 使用限定符注释

You need the @Value annotation.

A common use case is to assign default field values using
"#{systemProperties.myProp}" style expressions.

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, MybeanService and MyConstructorClass, something like this:

@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 MyConstructorClass with different values, you should use Qualifier annotations

萌逼全场 2024-12-02 01:50:29

嗯,我有时会遇到同样的问题。据我所知,当想要向构造函数添加动态参数时,无法做到这一点。然而,工厂模式可能会有所帮助。

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 */);
    }
}

现在,MyBean 本身并不是 spring bean,但它已经足够接近了。依赖注入是有效的,尽管我注入的是工厂而不是 bean 本身——如果想要替换它,就必须在自己的新 MyBean 实现之上注入一个新工厂。

此外,MyBean 可以访问其他 bean - 因为它可以访问工厂的自动装配内容。

人们显然可能想向 getMyBean 函数添加一些逻辑,这是我允许的额外努力,但不幸的是我没有更好的解决方案。由于问题通常是动态参数来自外部源,例如数据库或用户交互,因此我必须仅在运行中实例化该 bean,只有当该信息随时可用时,所以 Factory< /code> 应该足够了。

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, MyBean is 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 MyBean implementation if one wants to replace it.

Further, MyBean has access to other beans - because it may have access to the factory's autowired stuff.

And one might apparently want to add some logic to the getMyBean function, 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 Factory should be quite adequate.

所谓喜欢 2024-12-02 01:50:29

在此示例中,如何使用 @Autowire 注释指定 MyBeanService 中“constrArg”的值?有什么办法可以做到这一点吗?

不,不是按照你的意思。表示 MyConstructorClass 的 bean 必须是可配置的,而不需要任何客户端 bean,因此 MyBeanService 对于如何配置 MyConstructorClass 没有发言权。

这不是一个自动装配问题,这里的问题是 Spring 如何实例化 MyConstructorClass,假设 MyConstructorClass 是一个 @Component (而你'正在使用组件扫描,因此没有在配置中显式指定 MyConstructorClass)。

正如 @Sean 所说,这里的一个答案是在构造函数参数上使用 @Value ,以便 Spring 将从系统属性或属性文件中获取构造函数值。另一种方法是让 MyBeanService 直接实例化 MyConstructorClass,但如果这样做,那么 MyConstructorClass 就不再是 Spring bean。

In this example, how do I specify the value of "constrArg" in MyBeanService with the @Autowire annotation? Is there any way to do this?

No, not in the way that you mean. The bean representing MyConstructorClass must be configurable without requiring any of its client beans, so MyBeanService doesn't get a say in how MyConstructorClass is configured.

This isn't an autowiring problem, the problem here is how does Spring instantiate MyConstructorClass, given that MyConstructorClass is a @Component (and you're using component-scanning, and therefore not specifying a MyConstructorClass explicitly in your config).

As @Sean said, one answer here is to use @Value on the constructor parameter, so that Spring will fetch the constructor value from a system property or properties file. The alternative is for MyBeanService to directly instantiate MyConstructorClass, but if you do that, then MyConstructorClass is no longer a Spring bean.

还在原地等你 2024-12-02 01:50:29

您还可以像这样配置您的组件:

package mypackage;
import org.springframework.context.annotation.Configuration;
   @Configuration
   public class MyConstructorClassConfig {


   @Bean
   public MyConstructorClass myConstructorClass(){
      return new myConstructorClass("foobar");
   }
  }
}

使用 Bean 注释,您可以告诉 Spring 在 BeanFactory 中注册返回的 bean。

所以你可以根据需要自动装配它。

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 Bean annotation, you are telling Spring to register the returned bean in the BeanFactory.

So you can autowire it as you wish.

§对你不离不弃 2024-12-02 01:50:29

另一种方法是不将参数传递给构造函数,而是将它们作为 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;

  ....
}

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

@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;

  ....
}
墨落成白 2024-12-02 01:50:29

大多数答案都相当古老,所以当时可能不可能,但实际上有一个解决方案可以满足所有可能的用例。

所以正确的答案是:

  • 没有提供真正的 Spring 组件(工厂设计)
  • 或不适合所有情况(使用 @Value 你必须在配置文件中的某处拥有该值

)解决这些问题的方法是使用 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;
    }
}

这是一个很酷的解决方案,因为:

  • 它使您可以访问对象上的所有 Spring 功能(显然 @Autowired ,还有 @Async 例如),
  • 您可以使用任何源作为构造函数参数(配置文件、计算值、硬编码值……),
  • 它只需要您添加几行代码,而无需更改任何内容。
  • 它还可以用于动态创建未知数量的 Spring 管理类的实例(例如,我使用它来动态创建多个异步执行器)

唯一要记住的是,您必须有一个在要实例化的类中不带任何参数(并且可以为空)的构造函数(或者如果需要,则为 @Autowired 构造函数)。

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 @Value you have to have the value in a configuration file somewhere)

The solution to solve those issues is to create the object manually using the 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 (@Autowired obviously, but also @Async for 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)

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 @Autowired constructor if you need it).

月棠 2024-12-02 01:50:29

另一种选择是,如果您已经创建了对象的实例,并且想要将其添加为 @autowired 依赖项以初始化所有内部 @autowired 变量,则可能如下所示:

@Autowired 
private AutowireCapableBeanFactory autowireCapableBeanFactory;

public void doStuff() {
   YourObject obj = new YourObject("Value X", "etc");
   autowireCapableBeanFactory.autowireBean(obj);
}

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 
private AutowireCapableBeanFactory autowireCapableBeanFactory;

public void doStuff() {
   YourObject obj = new YourObject("Value X", "etc");
   autowireCapableBeanFactory.autowireBean(obj);
}
半世晨晓 2024-12-02 01:50:29

我喜欢 Zakaria 的答案,但是如果您所在的项目中您的团队不想使用这种方法,并且您必须尝试使用​​字符串、整数、浮点数或基本类型从属性文件构建某些内容,构造函数,然后您可以在构造函数中的参数上使用 Spring 的 @Value 注解。

例如,我遇到了一个问题,我试图将字符串属性拉入用 @Service 注释的类的构造函数中。我的方法适用于 @Service,但我认为这种方法应该适用于任何 spring java 类,如果它有注释(例如 @Service@Component 等),这表明 Spring 将是构造该类实例的一个。

假设在某个 yaml 文件(或您正在使用的任何配置)中,您有这样的内容:

some:
    custom:
        envProperty: "property-for-dev-environment"

并且您有一个构造函数:

@Service // I think this should work for @Component, or any annotation saying Spring is the one calling the constructor.
class MyClass {
...
    MyClass(String property){
    ...
    }
...
}

这不会运行,因为 Spring 将无法找到字符串 envProperty。因此,这是获取该值的一种方法:

class MyDynamoTable
import org.springframework.beans.factory.annotation.Value;
...
    MyDynamoTable(@Value("${some.custom.envProperty}) String property){
    ...
    }
...

在上面的构造函数中,Spring 将调用该类并知道使用从我的 yaml 配置中提取的 String "property-for-dev-environment"调用它。

注意:我相信 @Value 注释适用于字符串、整数,并且我相信原始类型。如果您尝试传递自定义类(bean),则可以使用上面定义的答案中的方法。

I like Zakaria's answer, but if you're in a project where your team doesn't want to use that approach, and you're stuck trying to construct something with a String, integer, float, or primative type from a property file into the constructor, then you can use Spring's @Value annotation on the parameter in the constructor.

For example, I had an issue where I was trying to pull a string property into my constructor for a class annotated with @Service. My approach works for @Service, but I think this approach should work with any spring java class, if it has an annotation (such as @Service, @Component, etc.) which indicate that Spring will be the one constructing instances of the class.

Let's say in some yaml file (or whatever configuration you're using), you have something like this:

some:
    custom:
        envProperty: "property-for-dev-environment"

and you've got a constructor:

@Service // I think this should work for @Component, or any annotation saying Spring is the one calling the constructor.
class MyClass {
...
    MyClass(String property){
    ...
    }
...
}

This won't run as Spring won't be able to find the string envProperty. So, this is one way you can get that value:

class MyDynamoTable
import org.springframework.beans.factory.annotation.Value;
...
    MyDynamoTable(@Value("${some.custom.envProperty}) String property){
    ...
    }
...

In the above constructor, Spring will call the class and know to use the String "property-for-dev-environment" pulled from my yaml configuration when calling it.

NOTE: this I believe @Value annotation is for strings, intergers, and I believe primative types. If you're trying to pass custom classes (beans), then approaches in answers defined above work.

半﹌身腐败 2024-12-02 01:50:29

您需要使用@Autowired 和@Value。请参阅此帖子了解更多信息这个话题。

You need to use @Autowired and @Value. Refer this post for more information on this topic.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文