web-dev-qa-db-ja.com

Spring @autowiredは、シングルトン以外のコンテナ向けではありませんか?

MyTaskを実装するRunnableクラスがあり、そのようなオブジェクトはいつでもインスタンス化できます。 MyTaskクラスに自動配線したい特定のプロパティがあります。

しかし、私は思考MyTask@Componentでマークすると、それはスプリング管理されたシングルトンになりますか?それは私が望んでいることではありません。TaskExecutorによって実行されるこのクラスの多くの独立したインスタンスが必要です。

だから私の質問:

  • a)@Componentアノテーションの理解が根本的に間違っていますか? MyTaskをスプリング管理されたシングルトンにしませんか?
  • b)springが@Autowiredを検出してプロパティを注入するために使用する必要のある他の注釈はありますか?
  • c)春の自動配線は、MyTaskのようなシングルトン以外のコンテナ/クラス向けではありませんか?

アップデート#1-これらは機能しません:

public class MyTask implements Runnable { // I want this class to be non-singleton
    @Autowired
    public SomeSpecialSpringConfiguredConnectionClass blah; // this is the singleton bean that should be injected
    @Override
    public void run() {
        // BLAH IS NULL, this shouldn't be NULL, that is not what I want
        // which makes sense considering Spring never knew it had to work
        // on this class
    }
}

@Component
public class MyTask implements Runnable { // I want this class to be non-singleton
    @Autowired
    public SomeSpecialSpringConfiguredConnectionClass blah; // this is the singleton bean that should be injected
    @Override
    public void run() {
        // this works BUT now MyTask is singleton :(
    }
}

@Component
@Scope("prototype")
public class MyTask implements Runnable { // I want this class to be non-singleton
    @Autowired
    public SomeSpecialSpringConfiguredConnectionClass blah; // this is the singleton bean that should be injected
    @Override
    public void run() {
        // BLAH IS NULL, again ... this shouldn't be NULL, that is not what I want
    }
}

更新#2-簡単な方法についてのいくつかの提案を待っている間、私は代わりとしてUsing AspectJ to dependency inject domain objects with Springを探しています。

16
pulkitsinghal

最初に、@ Componentで宣言され、スプリングコンポーネントスキャンで取得されたBeanは、スプリング管理デフォルトではシングルトンになります。

MyTaskの使用方法はわかりませんが、状況でAspectJを使用するのはやり過ぎであり、MyTaskをスプリング管理Beanとして宣言するのはあまり意味がありません。これを行う別の方法は次のとおりです。

  1. myTaskをplain Java classとして定義し、依存関係を初期化するコンストラクターを追加しますblah

  2. autowire blahMyTaskを使用する場所で、次のようにタスクを実行するたびにMyTaskオブジェクトをインスタンス化します。

    //autowire the dependency of MyTask in another spring bean with default singleton scope
    @Autowired private SomeSpecialSpringConfiguredConnectionClass blah
    //create task and wire the blah yourself
    executor.submit(new MyTask(blah))
    
8
Septem

@Componentアノテーションにより、context:component scanを使用したクラスパススキャン中に自動検出が可能になります。 @Serviceと@Componentの間に細かい線があり、この場合はとにかく影響しません。

春の自動配線は、プロトタイプとシングルトンスコープに対して実行できます。プロトタイプのスコープの場合、Beanの破壊のためのライフサイクルコールバックは呼び出されません。

Springのドキュメントページで非常によく説明されています。 http://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch04s04.html

あなたが言及したものが働くべきではない理由はわかりません。

彼は、私がそれをより良く説明するためにやろうとしたことの実例です。

public class SpringContainerStartClass {

   public static void main(final String[] args) {
      final ClassPathXmlApplicationContext bf = new ClassPathXmlApplicationContext("beans.xml");
      final MainApplication1 bean = (MainApplication1) bf.getBean("mainApplication1");
      bean.getMyTask().printSomething();

   }
}

これがアプリの出発点です。

ここにあなたのmyTaskクラスがあります

@Component(value = "myTask")
@Scope(value = "prototype")
public class MyTask
      implements Runnable {

   @Autowired
   private SomeSpecialSpringConfiguredConnectionClass someSpringObject;

   @Override
   public void run() {
      System.out.println("running now");

   }

   public void printSomething() {
      System.out.println(someSpringObject.getValue());
   }

   public SomeSpecialSpringConfiguredConnectionClass getSomeSpringObject() {
      return someSpringObject;
   }

   public void setSomeSpringObject(final SomeSpecialSpringConfiguredConnectionClass someSpringObject) {
      this.someSpringObject = someSpringObject;
   }

}

プロトタイプスコープがどのように機能するかを示す他の2つのクラス

@Component
public class MainApplication1 {

   @Autowired
   private MyTask myTask;

   public MyTask getMyTask() {
      return myTask;
   }

   public void setMyTask(final MyTask myTask) {
      this.myTask = myTask;
   }

}

@Component
public class MainApplication2 {

   @Autowired
   private MyTask myTask;

   public MyTask getMyTask() {
      return myTask;
   }

   public void setMyTask(final MyTask myTask) {
      this.myTask = myTask;
   }

}

オブジェクトの作成方法を示すBeanPostprocessor

public class InstantiationTracingBeanPostProcessor
      implements BeanPostProcessor {

   @Override
   public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
      return bean;
   }

   @Override
   public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
      System.out.println("Bean '" + beanName + "' created : " + bean.toString());
      return bean;
   }
}

someSpringConfigクラス

@Service
public class SomeSpecialSpringConfiguredConnectionClass {

   private String value = "someValue";

   public String getValue() {
      return value;
   }

   public void setValue(final String value) {
      this.value = value;
   }

}

このサンプルを実行すると、コンソールの出力が次のようになります。

INFO: Loading XML bean definitions from class path resource [beans.xml]
Jan 02, 2014 12:07:15 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@26efabf8: defining beans [mainApplication1,mainApplication2,myTask,someSpecialSpringConfiguredConnectionClass,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,com.stackoverflow.DIQuestion.InstantiationTracingBeanPostProcessor#0,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy
Bean 'someSpecialSpringConfiguredConnectionClass' created : com.stackoverflow.DIQuestion.SomeSpecialSpringConfiguredConnectionClass@1e20d04b
Bean 'myTask' created : com.stackoverflow.DIQuestion.MyTask@175d6331
Bean 'mainApplication1' created : com.stackoverflow.DIQuestion.MainApplication1@741b31f2
Bean 'myTask' created : com.stackoverflow.DIQuestion.MyTask@2c2815d3
Bean 'mainApplication2' created : com.stackoverflow.DIQuestion.MainApplication2@7bb0e64a

気づいたら、異なるハッシュコードを持つmyTaskの2つのオブジェクトがあります。

MyTaskのスコープを「Singleton」に変更すると、ここに出力されます。

INFO: Loading XML bean definitions from class path resource [beans.xml]
Jan 02, 2014 12:08:35 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@26efabf8: defining beans [mainApplication1,mainApplication2,myTask,someSpecialSpringConfiguredConnectionClass,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,com.stackoverflow.DIQuestion.InstantiationTracingBeanPostProcessor#0,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy
Bean 'someSpecialSpringConfiguredConnectionClass' created : com.stackoverflow.DIQuestion.SomeSpecialSpringConfiguredConnectionClass@1e20d04b
Bean 'myTask' created : com.stackoverflow.DIQuestion.MyTask@175d6331
Bean 'mainApplication1' created : com.stackoverflow.DIQuestion.MainApplication1@741b31f2
Bean 'mainApplication2' created : com.stackoverflow.DIQuestion.MainApplication2@2c2815d3

この場合、「myTask」用に作成されたオブジェクトが1つあります

これは役立ちますか?

8
Hrishikesh

通常、@ Scope( "prototype")を追加しても、自動配線されたblah beanに対してnullエラーは発生しません。MyTaskbeanのインスタンス化方法を確認する必要があります。問題は、次のようにMyTaskを手動でインスタンス化することだと思います。

   MyTask task = new MyTask();

そのため、Springの制御から外れるため、依存関係であるblah beanがnullであるため、手動でインスタンス化するのではなく、自動ワイヤリングし、Springで依存関係を処理する必要がありますが、blahはnullにはなりません。しかし、別の問題があります。プロトタイプBean MyTaskを自動配線しますが、別のシングルトンオブジェクトは間違っています。 SpringコンテナはシングルトンBeanを一度だけ作成するため、プロトタイプBeanを一度だけ設定するため、プロトタイプスコープが機能しなくなります。以下のように、MyactivityはMyTaskを自動配線するシングルトンです。 MyTaskの新しいインスタンスが作成されます。以下の場合、一度だけ印刷されるため、プロトタイプは機能しません。

@Component
@Scope("prototype")
public class MyTask implements Runnable {

  @Autowired
  public SomeSpecialSpringConfiguredConnectionClass blah;

  public MyTask(){
    System.out.println("New Instance of MyTask");
  }

  @Override
  public void run() {
    assert(blah != null);
  }
}


@Component
public class MyActivity {

  @Autowired
  private MyTask task;

  public MyTask start() {
    return task;
  }
}

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = {MyActivity.class, MyTask.class, 
   SomeSpecialSpringConfiguredConnectionClass.class})
public class MyTaskTest {

  @Autowired
  private MyActivity activity;

  @Test
  public void testActivity() {
    for (int i = 0; i < 5; i++) {
        MyTask task = activity.start();
        task.run();
    }
  }
 }

Spring AOP Scoped proxy に基づいて@Scope( "prototype")を@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS、value = "prototype")に変更したため、スコープ付きプロキシは毎回MyTaskの新しいインスタンスを注入します。シングルトンMyActivity Beanが呼び出されます。

@Component
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS, value = "prototype")
public class MyTask implements Runnable {

  @Autowired
  public SomeSpecialSpringConfiguredConnectionClass blah;

  public MyTask(){
    System.out.println("New Instance of MyTask");
  }

  @Override
  public void run() {
    assert(blah != null);
  }
}

そして今、プロトタイプはうまく機能しており、これはコンソールに出力された結果でした:

New Instance of MyTask
New Instance of MyTask
New Instance of MyTask
New Instance of MyTask
New Instance of MyTask
1
ShayneR

@Autowireの代わりに、@ Injectを使用して魔法を見てください。同じ状況で、ValidatorクラスがJava Singletonクラスであり、SpringスコープのBeanではありません。別のチームが提供するUAAクライアントSpring Beanを注入する必要があります。動作しますが、@ Injectは動作しました。

0
Yoga Gowda