138

我正在使用 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;

  ....
}

在这个例子中,如何在 MyBeanService 中使用 @Autowire 注解指定“constrArg”的值?有没有办法做到这一点?

谢谢,

埃里克

4

9 回答 9

74

您需要@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;
  }

  // ...
}

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


更清楚地说:在您的场景中,您将连接两个类,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
    }
}

更新:如果您需要几个MyConstructorClass具有不同值的不同实例,您应该使用 Qualifier 注释

于 2011-07-18T21:20:51.240 回答
19

好吧,我不时遇到同样的问题。据我所知,当想要向构造函数添加动态参数时,不能这样做。但是,工厂模式可能会有所帮助。

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它本身不是春豆,但它已经足够接近了。MyBean依赖注入有效,尽管我注入的是工厂而不是 bean 本身——如果想要替换它,就必须在他自己的新实现之上注入一个新工厂。

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

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

于 2015-12-03T19:48:11.703 回答
9

在此示例中,如何MyBeanService@Autowire注释中指定“constrArg”的值?有没有办法做到这一点?

不,不是你说的那样。表示的 beanMyConstructorClass必须是可配置的,而不需要它的任何客户端 bean,因此MyBeanService对于如何MyConstructorClass配置没有发言权。

这不是自动装配问题,这里的问题是 Spring 如何实例化MyConstructorClass,因为它MyConstructorClass是 a @Component(并且您正在使用组件扫描,因此没有MyConstructorClass在配置中明确指定 a )。

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

于 2011-07-19T06:51:15.540 回答
7

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

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


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

使用Bean注解,您是在告诉 Spring 将返回的 bean 注册到BeanFactory.

因此,您可以根据需要自动装配它。

于 2017-01-22T16:40:29.517 回答
0

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

  ....
}
于 2016-03-06T14:22:58.970 回答
0

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

@Autowired 
private AutowireCapableBeanFactory autowireCapableBeanFactory;

public void doStuff() {
   YourObject obj = new YourObject("Value X", "etc");
   autowireCapableBeanFactory.autowireBean(obj);
}
于 2018-06-25T22:28:20.343 回答
0

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

所以知道答案是:

  • 不提供真正的 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构造函数(或者如果需要,可以使用构造函数)。

于 2016-10-31T09:55:20.587 回答
0

我喜欢 Zakaria 的回答,但是如果您在一个项目中,您的团队不想使用这种方法,并且您被困在尝试从属性文件中构建具有字符串、整数、浮点数或原始类型的东西到构造函数,那么你可以@Value在构造函数的参数上使用 Spring 的注解。

例如,我有一个问题,我试图将一个字符串属性拉入我的构造函数中,用于一个用@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 将无法找到 string envProperty。因此,这是获得该值的一种方式:

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

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

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

于 2020-12-11T16:57:22.677 回答
-1

您需要使用@Autowired 和@Value。有关此主题的更多信息,请参阅此帖子

于 2014-11-22T06:31:23.830 回答