본문 바로가기
백엔드

[트러블 슈팅] Reflection 객체 내 @Autowired NullPointerException 발생

by BeforB 2022. 10. 8.
728x90

 

1. 이슈

Reflection으로 동적 생성한 객체 내 @Autowired된 변수 접근 시 NPE(NullPointerException) 발생

 

 

 

2. 상세 내용

Reflection을 이용하여 객체를 동적으로 생성.

-> method.invoke를 이용하여 객체 내 메서드를 실행

-> InvocationTargetException 발생.

-> 원인을 확인해보니 실행된 메서드 내에서 @Autowired된 변수에 접근 시 NPE 발생

 

 

문제가 된 코드

Class targetClass = Class.forName(className);
Object instance = targetClass.getDeclaredConstructor().newInstance();
Method method = targetClass.getDeclaredMethod(methodName, String.class);
method.invoke(instance, 파라미터); // (instance, parameter)

 

처음에는 InvocationTargetException 발생.

-> 원인을 확인하기 위해 e.getCause()로 원인을 확인하였더니, NullPointerException이 발생된 것을 확인하였음.

[java] java.lang.reflect.InvocationTargetException
...
Caused by: java.lang.NullPointerException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
...

 

 

 

3. 원인

 

스프링에서 DI는 런타임 시점에 의존성 주입(DI)이 이루어지는데, 위 코드에서 나는 Reflection을 이용해서 인스턴스를 직접 생성(?) 했기 때문에 DI가 되지 않은 객체로, 내부에서 @Autowired된 객체를 사용할 경우 Injection되지 않은채 null이 반환된거였음. 따라서 객체.메서드 를 아무리 해도 null에 접근하였기 때문에 NullPointerException 에러가 발생한 것….

 

 

 

 

4. 접근

  • 구글링했을 때 new로 생성된 객체도 마찬가지로 동일한 이슈가 발생한다는 것을 확인.
  • Reflection도 마찬가지로 DI가 되지 않았기 때문에 동일함.
  • 동적으로 생성된 객체 내에서 DI된 Bean으로 사용하기 위해서는 applicationContext에서 이미 등록된 Bean을 직접 가져와야 함.
// ApplicationContextServe.java
@Component
public class ApplicationContextServe implements ApplicationContextAware {
	private static ApplicationContext applicationContext;

	@Override
	public void setApplicationContext(ApplicationContext context) throws BeansException {
		applicationContext = context;
	}
	public static ApplicationContext getApplicationContext() {
		return applicationContext;
	}
}

// NewBeanUtils.java
public class NewBeanUtils {
	public static Object getBean(String beanName) {
		ApplicationContext context = ApplicationContextServe.getApplicationContet();
		return context.getBean(beanName);
	}
  public static <T> T getBean(Class<T> classType) {
		ApplicationContext context = ApplicationContextServe.getApplicationContet();
		return context.getBean(classType);
	}
}
Class targetClass = Class.forName(className);
Object apiService = NewBeanUtils.getBean(targetClass); // 직접 꺼내씀

Method method = apiService.getClass().getDeclaredMethod(methodName, String.class);
method.invoke(apiService, brodNo);

 

 

 

728x90

댓글