@Autowired 및 정적 메서드


101

나는이 @Autowired정적 메서드 내에서 사용하는 서비스입니다. 나는 이것이 틀렸다는 것을 알고 있지만 많은 작업이 필요하기 때문에 현재 디자인을 변경할 수 없으므로 간단한 해킹이 필요합니다. randomMethod()비 정적으로 변경할 수 없으며이 자동 연결 빈을 사용해야합니다. 그 방법에 대한 단서가 있습니까?

@Service
public class Foo {
    public int doStuff() {
        return 1;
    }
}

public class Boo {
    @Autowired
    Foo foo;

    public static void randomMethod() {
         foo.doStuff();
    }
}

4
정적 메서드는 비 정적 / 인스턴스 필드를 참조 할 수 없습니다.
Sotirios Delimanolis 2007

18
이것이 제가이 스레드를 만든 이유입니다. 정적 메서드 내에서 Autowired 인스턴스에 액세스 할 수있는 방법이 있습니까?
Taks

정적 메서드에서 @Autowired를 사용하는 것이 잘못된 이유는 무엇입니까?
user59290

답변:


152

솔루션 중 하나에 따라이를 수행 할 수 있습니다.

@Autowired 생성자 사용

이 접근 방식은 생성자 매개 변수로 일부 빈이 필요한 빈을 생성합니다. 생성자 코드 내에서 생성자 실행을위한 매개 변수로 얻은 값으로 정적 필드를 설정합니다. 견본:

@Component
public class Boo {

    private static Foo foo;

    @Autowired
    public Boo(Foo foo) {
        Boo.foo = foo;
    }

    public static void randomMethod() {
         foo.doStuff();
    }
}

@PostConstruct를 사용하여 값을 정적 필드에 전달

여기서 아이디어는 bean이 spring에 의해 구성된 후 정적 필드에 bean을 넘겨주는 것입니다.

@Component
public class Boo {

    private static Foo foo;
    @Autowired
    private Foo tFoo;

    @PostConstruct
    public void init() {
        Boo.foo = tFoo;
    }

    public static void randomMethod() {
         foo.doStuff();
    }
}

3
이것은 안전한 해결책입니까?
Taks 2013-07-15

2
나는 첫 번째 솔루션을 사용했고 그것은 매력처럼 작동했습니다, 감사합니다!
victorleduc

1
첫 번째 솔루션은 @Qualifier 사용을 지원하지 않습니다. 여러 저장소를 사용하는 경우 문제가 남아 있습니다.
user1767316

16
정적 메서드에 액세스하기 전에 생성자가 호출된다는 것을 보장하는 것은 무엇입니까?
David Dombrowsky

2
init 메소드는 비 정적 메소드가 정적 필드를 수정하기 때문에 SonarQube 버그를 유발합니다.
jDub9

45

정적 애플리케이션 컨텍스트 접근 자 접근 방식을 통해이 문제를 해결해야합니다.

@Component
public class StaticContextAccessor {

    private static StaticContextAccessor instance;

    @Autowired
    private ApplicationContext applicationContext;

    @PostConstruct
    public void registerInstance() {
        instance = this;
    }

    public static <T> T getBean(Class<T> clazz) {
        return instance.applicationContext.getBean(clazz);
    }

}

그런 다음 정적 방식으로 Bean 인스턴스에 액세스 할 수 있습니다.

public class Boo {

    public static void randomMethod() {
         StaticContextAccessor.getBean(Foo.class).doStuff();
    }

}

나는 그것을 완전히 이해하지는 못하지만 실제로이 솔루션을 좋아합니다 .. 나는 봄에 머리를 긁고 코드의 일부를 빠르게 리팩토링해야합니다. 이것은 정적과 자동 배선을 혼합하는 문제입니다.이 솔루션은 얼마나 안전합니까?
TAKS

2
정적 호출이 제어되는 경우 상당히 안전합니다. 가장 명백한 부정적인 측면은 getBean컨텍스트가 초기화되기 전 (NPE) 또는 빈이있는 컨텍스트가 파괴 된 후에 호출 할 수 있다는 것입니다 . 이 접근 방식은 "추악한"정적 컨텍스트 액세스 가 하나의 메서드 / 클래스에 포함 된다는 이점이 있습니다.
Pavel Horal 2013

1
이것은 내 생명을 구했습니다. 다른 접근 방식에 비해 매우 유용합니다.
phoenix

6

할 수있는 것은 @Autowiredsetter 메서드이며 새 정적 필드를 설정하도록하는 것입니다.

public class Boo {
    @Autowired
    Foo foo;

    static Foo staticFoo;   

    @Autowired
    public void setStaticFoo(Foo foo) {
        Boo.staticFoo = foo;
    }

    public static void randomMethod() {
         staticFoo.doStuff();
    }
}

빈이 처리되면 Spring은 Foo인스턴스 필드에 구현 인스턴스를 주입 합니다 foo. 그런 다음 정적 필드를 설정하는 데 사용되는 인수 목록에 동일한 Foo인스턴스를 삽입합니다 setStaticFoo().

이것은 끔찍한 해결 방법이며 randomMethod()Spring이 인스턴스를 처리하기 전에 사용하려고하면 실패합니다 Boo.


@PostConstruct 도움말을 사용 하시겠습니까?
Taks 2013-07-15

@Taks 물론, 그것도 작동합니다. 에 setStaticFoo()그없이입니다 Foo매개 변수입니다.
Sotirios Delimanolis 2007

문제는 그것이 더 안전
할까요

1
@Taks 보여준 방식은 작동하지 않습니다 (의사 코드를 보여주지 않는 한). 그 방법에 대한 단서가 있습니까? 당신이 얻은 여러 답변은 해결 방법이지만 Spring이 클래스를 처리 할 때까지 정적 필드를 사용할 수 없다는 동일한 문제가 있습니다 (실제로 부작용이있는 하나의 인스턴스를 처리). 그런 의미에서 안전하지 않습니다.
Sotirios Delimanolis 2007

3

짜증나지만 ApplicationContextAware인터페이스 를 사용하여 빈을 얻을 수 있습니다 . 다음과 같은 것 :

public class Boo implements ApplicationContextAware {

    private static ApplicationContext appContext;

    @Autowired
    Foo foo;

    public static void randomMethod() {
         Foo fooInstance = appContext.getBean(Foo.class);
         fooInstance.doStuff();
    }

    @Override
    public void setApplicationContext(ApplicationContext appContext) {
        Boo.appContext = appContext;
    }
}

0

이것은 정적 getBean 메소드에서 액세스 할 때 Spring 컨텍스트가 초기화되지 않을 가능성을 해결하기 위해 @Pavel의 답변을 기반으로합니다 .

@Component
public class Spring {
  private static final Logger LOG = LoggerFactory.getLogger (Spring.class);

  private static Spring spring;

  @Autowired
  private ApplicationContext context;

  @PostConstruct
  public void registerInstance () {
    spring = this;
  }

  private Spring (ApplicationContext context) {
    this.context = context;
  }

  private static synchronized void initContext () {
    if (spring == null) {
      LOG.info ("Initializing Spring Context...");
      ApplicationContext context = new AnnotationConfigApplicationContext (io.zeniq.spring.BaseConfig.class);
      spring = new Spring (context);
    }
  }

  public static <T> T getBean(String name, Class<T> className) throws BeansException {
    initContext();
    return spring.context.getBean(name, className);
  }

  public static <T> T getBean(Class<T> className) throws BeansException {
    initContext();
    return spring.context.getBean(className);
  }

  public static AutowireCapableBeanFactory getBeanFactory() throws IllegalStateException {
    initContext();
    return spring.context.getAutowireCapableBeanFactory ();
  }
}

여기서 중요한 부분은 initContext방법입니다. 컨텍스트가 항상 초기화되도록합니다. 그러나 initContext동기화되면 코드에서 경합 지점이 될 것입니다. 애플리케이션이 과도하게 병렬화 된 경우 (예 : 트래픽이 많은 사이트의 백엔드) 이것은 좋은 솔루션이 아닐 수 있습니다.


-2

AppContext를 사용하십시오. 컨텍스트 파일에 Bean을 작성했는지 확인하십시오.

private final static Foo foo = AppContext.getApplicationContext().getBean(Foo.class);

public static void randomMethod() {
     foo.doStuff();
}

이게 뭐야 ?? 무엇이 @Autowired와의 getBean 차이
madhairsilence의

클래스를 정규 스프링 @Component로 전환 할 수없는 경우는 일반적이며 레거시 코드에서 많이 발생합니다.
carpinchosaurio
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.