Web/Spring

[Spring] static 필드에 @Autowired(의존성 주입) 사용하기

메바동 2021. 9. 29. 21:34
728x90

겉으로 보기에는 이상이 없는 코드였는데 (물론 이건 내가 실력이 없고 무지했기에 아무런 이상이 없는 코드처럼 보였던 것이다.) NullPointerException이 뜨며 에러가 뜨는 상황이 발생했다.

 

그래서 디버그 모드로 들어가 하나씩 살펴보던 중 이상한 부분을 발견했다.

 

바로 @Autowired를 사용한 service 혹은 dao가 null을 저장하고 있었다.

 

null을 저장하고 있던 필드는 static으로 선언된 정적 필드였는데, 이 상황에서 내가 생각했던 것은 정적 필드는 @Autowired를 사용하지 못한다는 생각이었다.

찾아보니 역시나 정적 필드는 @Autowired를 사용할 수 없었고, 이 부분을 모두 바꾸려면 일정을 맞추지 못할 것 같기에 static 필드에 @Autowired를 사용하는 방법을 찾아야 했다.

 

우선 static 필드에 @Autowired를 사용하지 못하는 이유는 정적 필드가 클래스 로더에 의해 인스턴스화 될 때 아직 Spring Context는 로드되지 않았기 때문에 Spring 프레임워크에 의해 관리되는 @Autowired가 동작하지 않는 것이었다.

 

이 방법을 해결하기 위한 방법은 두 가지가 있었다.

 

1. @PostConstruct 사용하기

@Autowired
private FooObject nonStaticObj;

private static FooObject staticObj;

@PostConstruct
private void initStatic() {
  staticObj = this.nonStaticObj;
}

 

위 코드와 같이 @PostConsturct 어노테이션을 사용해주면 된다. 

 

나는 Java와 Spring에 대한 지식이 그렇게 깊지 않기 때문에 @PostConstruct의 기능이 무엇인지 알지 못했었고, 일반적인 생성자와 같은 기능을 하는 줄 알았다.

하지만 찾아보니 @PostConstruct는 Spring에 의존적이지 않은 Java의 기능으로, 의존성 주입이 끝난 이후의 작업이 필요할 경우 @PostConsturct를 사용해야 한다고 한다.

지금까지 그런 상황을 보지 않아 몰랐었는데 생성자가 동작하는 시점에는 의존성 주입이 되지 않은 상태라고 한다.

또한, @PostConstruct가 적용된 메서드는 Bean LifeCycle에서 한 번만 수행되는 것이 보장되므로 생성자의 호출이 여러 번 발생하는 것을 원하지 않을 경우 사용하기 유용한 것 같다.

 

다만, 나는 Java 8 환경에서 작업을 했기 때문에 상관이 없었지만 @PostConstruct는 Java 9부터 Deprecated 되었다고 한다.

 

Java 9 이상의 환경에서는 InitializingBean의 afterPropertiesSet()을 구현하여 사용하면 될 것 같다.

 

class TestObject implements InitializingBean {
  @Override
  public void afterPropertiesSet() {
    ...
  }
}

 

실행 순서는 @PostConstruct가 우선 실행되고 afterPropertiesSet()이 실행된다고 하니 알아두면 좋을 것 같다.

 

 

 

2. @Autowired 생성자 사용하기

@PostConsturct 말고 @Autowired 생성자를 사용해서도 정적 필드에 의존성을 주입시킬 수 있다.

 

@Component
public class TestObject {
  private static FooObject fooObj;
  
  @Autowired
  private TestObject(FooObject fooObj) {
    this.fooObj = fooObj;
  }
  
  // 혹은 @Autowired setter를 이용할 수도 있다.
  @Autowired
  public void setFooObj(FooObject fooObj) {
    this.fooObj = fooObj;
  } 
}

 

나는 1번의 방법으로 사용하였지만 위와 같은 코드로 사용하여도 static 필드에 의존성 주입을 사용할 수 있다고 한다.

 

 

 

 

 

물론 나는 기존 코드를 수정해야 하는 상황이었고, 전체적으로 수정하기에는 일정이 촉박하여 이렇게 static 필드에 의존성 주입을 사용하는 방법을 찾아 해결하였지만, 찾아보니 Spring의 제어를 벗어난 방식이기 때문에 가급적 사용을 자제하는 것이 좋다고 한다.

728x90