Java Servlet 프로그래밍
저번 주차 학생 관리 프로그램
바로가기
Domain 은 이전 주차와 동일함
Todo
// 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 파일을 만들어 해당 클래스를 등록하기 해결되었다.
이제 스프링 써야겠다...