[2023 동계 모각코] 5회차 회고

춤추는 머쉬룸·2024년 1월 26일
0

2023-24 동계 모각코

목록 보기
10/12
post-thumbnail

목표

Java Servlet 프로그래밍

  • 학생 관리 프로그램 Controller Factory 와 ServletContainerInitializer 를 이용하는 것으로 수정하기

저번 주차 학생 관리 프로그램
바로가기


결과

Domain 은 이전 주차와 동일함

Todo

  • Repository 를 json 파일을 사용하는 것으로 변경
  • Servlet 에서 Command 를 implement 하는 Controller 로 변경
  • ServletContainerInitializer 를 이용해 초기화
// Repository

public JsonStudentRepository() {
        objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JavaTimeModule());
        deleteExistsJsonFile();
        file = new File(JSON_FILE_PATH);
    }

    private void deleteExistsJsonFile() {
        File jsonFile = new File(JSON_FILE_PATH);

        if (jsonFile.exists()) {
            try {
                boolean deleted = jsonFile.delete();

                if (deleted) {
                    log.info("기존 json 파일 삭제 완료");
                } else {
                    log.error("기존 json 파일 삭제 실패");
                }
            } catch (RuntimeException e) {
                throw new RuntimeException();
            }
        } else {
            log.info("기존에 존재하는 json 파일 없음");
        }
    }

    private synchronized List<Student> readJsonFile() {
        if (!file.exists()) {
            // JSON 파일이 존재하지 않다면 비어있는 List<Student> 반환
            return new ArrayList<>();
        }

        //json read & 역직렬화 ( json string -> Object )
        try(FileInputStream fileInputStream = new FileInputStream(file);
            InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, StandardCharsets.UTF_8);
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
            List<Student> students = objectMapper.readValue(bufferedReader, new TypeReference<List<Student>>(){});
            return students;
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private synchronized void writeJsonFile(List<Student> studentList){
        // List<Student> 객체를 -> json 파일로 저장 : 직렬화
        File file = new File(JSON_FILE_PATH);
        try(FileWriter fileWriter = new FileWriter(file);
            BufferedWriter bufferedWriter = new BufferedWriter(fileWriter)) {
            objectMapper.writeValue(bufferedWriter,studentList);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

직렬화와 역직렬화가 Point!
단점 : json 파일을 다 읽어서 그중에 필요한 것을 찾아야 함


// Webinitializer

@Slf4j
@HandlesTypes(value = {com.nhnacademy.student.Command.class})
public class WebInitializer implements ServletContainerInitializer {
    @Override
    public void onStartup(Set<Class<?>> c, ServletContext ctx) {

        ControllerFactory controllerFactory = new ControllerFactory();
        log.info(c.toString());
        controllerFactory.init(c);
        ctx.setAttribute("controllerFactory", controllerFactory);
        log.info("web initializer 거침");

    }
}

HandlesTypes 로 어떤 class 를 읽어들일 것인지를 설정할 수 있는 것이 Listener 과의 차이점!


// ControllerFactory

@Slf4j
public class ControllerFactory {
    private final ConcurrentMap<String, Object> beanMap = new ConcurrentHashMap<>();

    public void init(Set<Class<?>> c) {
        //todo beanMap에 key = method + servletPath, value = Controller instance (controller 인스턴스 생성)
        for (Class<?> clazz : c) {
            try {
                // 기본 생성자로 인스턴스 생성
                Object controllerInstance = clazz.getDeclaredConstructor().newInstance();

                // 어노테이션으로 method 와 path 가져오기
                RequestMapping annotation = clazz.getDeclaredAnnotation(RequestMapping.class);
                RequestMapping.Method method = annotation.method();
                String servletPath = annotation.value();

                String key = method + servletPath;
                log.info("beanMap key : " + key);

                // beanMap 에 저장
                beanMap.put(key, controllerInstance);

            } catch (InvocationTargetException | InstantiationException | IllegalAccessException |
                     NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        }

    }

    public Object getBean(RequestMapping.Method method, String path) {
        //todo beanMap 에서 method+servletPath을 key로 이용하여 Controller instance를 반환합니다.
        log.info("getBean : " + method+path);
        try {
            return beanMap.get(method + path);
        } catch (NullPointerException e) {
            throw new RuntimeException();
        }
    }
}

java 의 Reflection 을 이용해 어떤 클래스가 들어오든 상관 없음!


소감

Webinitializer 를 계속 인식하지 못하는게 문제였는데
resources/META-INF/services 아래에 javax.servlet.ServletContainerInitializer 파일을 만들어 해당 클래스를 등록하기 해결되었다.

이제 스프링 써야겠다...

0개의 댓글