복붙노트

[SPRING] Spring @autowired는 non-singleton 컨테이너가 아닌가요?

SPRING

Spring @autowired는 non-singleton 컨테이너가 아닌가요?

Runnable을 구현하는 MyTask 클래스가 있고 주어진 순간에 인스턴스화 된 많은 객체가있을 수 있습니다. MyTask 클래스에 autowire하고 싶은 특정 속성이 있습니다.

하지만 MyTask에 @Component라고 표시하면 스프링 관리 싱글 톤이 될 것이라고 생각합니다. 그게 내가 원하는 건 아니에요, 나는 TaskExecutor에 의해 실행되도록이 클래스의 독립적 인 인스턴스가 많이 필요합니다.

그래서 내 질문 (들) :

업데이트 # 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
    }
}

Update # 2 - 쉬운 방법으로하는 방법에 대한 더 많은 제안을 기다리는 동안, AspectJ를 사용하여 의존성을 도메인 객체에 Spring을 대안으로 주입하자.

해결법

  1. ==============================

    1.먼저, @Component로 선언되고 스프링 구성 요소 검사에 의해 선택되는 bean은 기본적으로 스프링 관리 싱글 톤이됩니다.

    먼저, @Component로 선언되고 스프링 구성 요소 검사에 의해 선택되는 bean은 기본적으로 스프링 관리 싱글 톤이됩니다.

    어떻게 MyTask를 사용하는지 모르겠지만 상황에 따라 AspectJ를 사용하는 것은 과도하며 MyTask를 스프링 관리 빈으로 선언하는 것은별로 의미가 없다. 이 작업을 수행하는 또 다른 방법은 다음과 같습니다.

  2. ==============================

    2.@Component 주석은 컨텍스트 (component scan)를 사용하여 클래스 경로를 검색하는 동안 자동 detetion을 허용합니다 그것이하는 일입니다. @Service와 @Component 사이에는 미세한 선이 있습니다.이 경우 어쨌든 영향을 미치지 않습니다.

    @Component 주석은 컨텍스트 (component scan)를 사용하여 클래스 경로를 검색하는 동안 자동 detetion을 허용합니다 그것이하는 일입니다. @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;
       }
    
    }
    

    프로토 타입 범위가 작동하는 방법을 보여주는 두 개의 다른 클래스

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

    YourSpringConfig 클래스

    @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
    

    carefulyy를 발견하면 myTask의 두 객체에 서로 다른 해시 코드가 있습니다.

    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"에 대해 하나의 객체가 생성됩니다.

    이게 도움이 되나요?

  3. ==============================

    3.일반적으로 @Scope ( "prototype")을 추가하면 autowired blah bean에 널 오류가 발생해서는 안되므로 MyTask bean을 인스턴스화하는 방법을 확인해야합니다. 문제는 MyTask를 수동으로 인스턴스화하는 것입니다.

    일반적으로 @Scope ( "prototype")을 추가하면 autowired blah bean에 널 오류가 발생해서는 안되므로 MyTask bean을 인스턴스화하는 방법을 확인해야합니다. 문제는 MyTask를 수동으로 인스턴스화하는 것입니다.

       MyTask task = new MyTask();
    

    따라서 Spring의 제어에서 벗어나서, 의존성, blah bean이 null 인 경우, 수동 인스턴스화 대신에 autowire하고 Spring이 의존성을 처리하도록하고 blah가 null이되지 않도록해야합니다. 그러나 또 다른 문제가 있습니다. 프로토 타입 빈을 Autowiring하는 MyTask는 또 다른 싱글 톤 객체가 잘못되었다. 스프링 컨테이너는 싱글 톤 빈을 한 번만 생성하므로 프로토 타입 빈을 한 번만 설정하면 프로토 타입 범위가 작동하지 않게됩니다. 아래처럼 Myactivity는 MyTask를 자동 실행하는 싱글 톤이며, 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 프록시를 기반으로 @Scope ( "프로토 타입")을 @Scope (proxyMode = ScopedProxyMode.TARGET_CLASS, value = "prototype")으로 변경했습니다. 따라서 범위가 지정된 프록시는 MyActivity bean이 호출 될 때마다 MyTask의 새 인스턴스를 주입합니다.

    @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
    
  4. ==============================

    4.@Autowire 대신 @Inject를 사용하여 마법을 봅니다. 나는 Validator 클래스가 Java Singleton 클래스이고 Spring Scoped Bean이 아닌 동일한 상황을 가지고 있습니다. 다른 팀이 제공 한 UAA Client 스프링 빈을 주입해야합니다. @Autowire는 작동하지 않았지만 @Inject가 작동했습니다.

    @Autowire 대신 @Inject를 사용하여 마법을 봅니다. 나는 Validator 클래스가 Java Singleton 클래스이고 Spring Scoped Bean이 아닌 동일한 상황을 가지고 있습니다. 다른 팀이 제공 한 UAA Client 스프링 빈을 주입해야합니다. @Autowire는 작동하지 않았지만 @Inject가 작동했습니다.

  5. from https://stackoverflow.com/questions/20873404/is-spring-autowired-not-meant-for-non-singleton-containers by cc-by-sa and MIT license