Controller 내 gameService null값 에러 해결
기존 Reflection API를 써서 Custom하게 만든 Annotation 클래스를 가져와서 요청 메시지에 맞는 Method를 등록시켜두었습니다. 그래서 소켓 메시지로 어떠한 요청이 왔을 때 해당 key값을 찾고 그에 해당하는 Method를 호출하였습니다.
그런데 문제는 이 컨트롤러 안에 있는 GameService
를 주입시키려면 이 Controller도 빈이어야 한다는 것이죠. 물론 빈으로 등록하는 건 클래스 위에 @Component
만 설정해놓으면 되니 큰 문제가 아닙니다. 하지만 요청에 맞는 Method를 불러오고 그것을 invoke 할 때 빈으로 등록시켜놓은 객체를 들고와야 GameService
가 @Autowired
된 빈을 가져온다는 것이죠. 그렇기 때문에 다음과 같은 작업이 필요하였습니다.
빈 컨테이너에서 제가 커스텀으로 만든 어노테이션이 있는 클래스를 가져옵니다.
그리고 그 클래스의 메서드를 탐색해서 요청패스에 맞는 Method를 등록시켜둡니다.
근데 이젠 그 메서드만 등록시키는 것이 아니라 빈 객체도 같이 등록시켜야합니다!
- 매번 그냥 객체를 생성했는데
GameService
가 주입된 객체를 가져오려면 Method와 함께 Object도 같이 넣어두어야 합니다.
- 매번 그냥 객체를 생성했는데
@Component
public class GameControllerMappingHandler {
private static final Logger log = getLogger(GameControllerMappingHandler.class);
@Autowired
private ParameterBinder parameterBinder;
private final Map<MessageType, Pair> mapper = new HashMap<>();
@Autowired
public GameControllerMappingHandler(ApplicationContext context) {
init(context);
}
private void init(ApplicationContext context) {
Map<String, Object> controllers = context.getBeansWithAnnotation(SocketController.class);
for (String key : controllers.keySet()) {
Object object = controllers.get(key);
Class<?> clazz = object.getClass();
Method[] methods = clazz.getDeclaredMethods();
registerMethods(object, methods);
}
}
private void registerMethods(Object object, Method[] methods) {
Arrays.stream(methods)
.filter(method -> method.isAnnotationPresent(RequestMapping.class))
.forEach(method -> mapper.put(_toMessageType(method), new Pair(object, method)));
}
private MessageType _toMessageType(Method method) {
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
return new MessageType(requestMapping.value()[0], requestMapping.method()[0]);
}
public void invoke(MessageDto messageDto) throws Exception {
Pair pair = mapper.get(messageDto.getMessageType());
Object[] args = parameterBinder.bind(pair.method, messageDto);
pair.method.invoke(pair.object, args);
}
static class Pair {
private Object object;
private Method method;
public Pair(Object object, Method method) {
this.object = object;
this.method = method;
}
}
}
- 31 ~ 34줄 :
ApplicationContext
을 가져옵니다. - 37 ~ 42줄 : 커스텀 어노테이션 가진 객체를 찾습니다.
- 46 ~ 50줄 : 메서드를 객체와 함께
Pair
라는 클래스 형태로 저장해둡니다. - 57 ~ 60줄 :
Pair
객체를 이용하여 해당 객체 내의 메서드를 invoke합니다.
배운 점
이때까지 Reflection API를 써오면서 invoke할 때 첫번째 매개변수로 해당 Method의 객체를 넣는데 그 의미를 잘 몰랐습니다. 전 항상 그 메서드를 이용하여 그 메서드가 있는 클래스를 바로 객체로 만들었으니까요. 하지만 이번에 꼭 Bean 객체를 써야할 필요성을 느끼면서 첫번째 매개변수로써 객체를 넣는 이유를 조금이나마 이해하게 되었습니다. Method가 어떤 메서드를 호출해야 하는지 말하는거라면 Object는 어떤 객체를 말하는 것이었네요!
참고
Jackson 객체 만들때 ignore 할 부분에 대해 참고
- 이후에 row, col, commandNumber 까지 정보가 들어올 때 구조를 바꿔야해서 참고할 것 같네요.
'TIL' 카테고리의 다른 글
TIL(2019-12-16) (0) | 2019.12.16 |
---|---|
TIL(2019-12-15) (0) | 2019.12.16 |
Todays' Dev Notes(2018-03-16) (0) | 2019.03.16 |
Todays' Dev Notes(2018-03-15) (0) | 2019.03.16 |
Todays' Dev Notes(2018-03-11) (0) | 2019.03.12 |