web-dev-qa-db-ja.com

コンストラクター引数を必要とするBeanを@Autowireする方法はありますか?

私はSpring 3.0.5を使用しており、可能な限りクラスメンバーに@Autowireアノテーションを使用しています。自動配線する必要があるBeanの1つは、コンストラクターへの引数が必要です。私は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」の値を指定するにはどうすればよいですか?これを行う方法はありますか?

おかげで、

エリック

97
Eric B.

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

  // ...
}

参照:式言語>注釈設定


より明確にするために、シナリオでは、次のような2つのクラス、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のいくつかの異なるインスタンスが必要な場合は、 修飾子​​アノテーションを使用

55

まあ、時々同じ質問に出くわします。私の知る限り、動的パラメーターをコンストラクターに追加したい場合、それはできません。ただし、ファクトリパターンが役立つ場合があります。

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が非常に適切である。

10
Ivan Ketler

この例では、MyBeanServiceの「constrArg」の値を@Autowireアノテーションで指定するにはどうすればよいですか?これを行う方法はありますか?

いいえ、あなたの言う意味ではありません。 MyConstructorClassを表すBeanは、クライアントBeanを必要とせずに構成可能でなければならないため、MyBeanServiceMyConstructorClassの構成方法については発言しません。

これは自動配線の問題ではありません。ここでの問題は、MyConstructorClass@Componentである場合に、SpringがMyConstructorClassをインスタンス化する方法です。 MyConstructorClass構成内で明示的に)。

@Seanが言ったように、ここでの答えの1つは、コンストラクターパラメーターで@Valueを使用することです。これにより、Springはシステムプロパティまたはプロパティファイルからコンストラクター値を取得します。別の方法は、MyBeanServiceMyConstructorClassを直接インスタンス化することですが、それを行うと、MyConstructorClassはSpring Beanではなくなります。

9
skaffman

次のようにコンポーネントを構成することもできます。

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


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

Beanアノテーションを使用すると、返されたBeanをBeanFactoryに登録するようにSpringに指示します。

したがって、必要に応じて自動配線できます。

5
Zakaria

@Autowiredと@Valueを使用する必要があります。このトピックの詳細については、これを参照してください post .

1
Fahim Farook

もう1つの方法として、オブジェクトのインスタンスが既に作成されていて、それを@autowired依存関係として追加して、すべての内部@autowired変数を初期化する場合、次のようになります。

@Autowired 
private AutowireCapableBeanFactory autowireCapableBeanFactory;

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

別の方法は、コンストラクターにパラメーターを渡す代わりに、それらをゲッターおよびセッターとして使用し、@ 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;

  ....
}
0
george

ほとんどの答えはかなり古いため、当時は不可能だったかもしれませんが、実際には、考えられるすべてのユースケースを満たすソリューションがあります。

答えは次のとおりです。

  • 実際の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コンストラクター)に引数を取らない(そして空にすることができる)コンストラクターを持たなければならないということです。

0
AntoineB