Skip to content

Commit

Permalink
[MVC 구현하기 - 1단계] 비버(전인표) 미션 제출합니다. (#337)
Browse files Browse the repository at this point in the history
* test: ReflectionTest 작성

* test: ServletTest 작성

* feat: 패키지에 있는 클래스 handlerExecutions변수에 초기화

* refactor: 예외 처리를 위한 getDeclaredConstructor()메소드 추가

* refactor: 가독성을 위한 forEach문 제거
  • Loading branch information
ingpyo authored Sep 14, 2023
1 parent e7c21ab commit 44f207d
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
package webmvc.org.springframework.web.servlet.mvc.tobe;

import context.org.springframework.stereotype.Controller;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.reflections.Reflections;
import web.org.springframework.web.bind.annotation.RequestMapping;
import web.org.springframework.web.bind.annotation.RequestMethod;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class AnnotationHandlerMapping {

private static final Logger log = LoggerFactory.getLogger(AnnotationHandlerMapping.class);

private final Object[] basePackage;
private final Map<HandlerKey, HandlerExecution> handlerExecutions;

Expand All @@ -19,11 +21,40 @@ public AnnotationHandlerMapping(final Object... basePackage) {
this.handlerExecutions = new HashMap<>();
}


public void initialize() {
log.info("Initialized AnnotationHandlerMapping!");
getAnnotatedClasses().forEach(this::registerMethods);
}

private Set<Class<?>> getAnnotatedClasses() {
Reflections reflections = new Reflections(basePackage);
return reflections.getTypesAnnotatedWith(Controller.class);
}

private void registerMethods(final Class<?> clazz) {
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(RequestMapping.class)) {
registerMethod(clazz, method);
}
}
}

private void registerMethod(final Class<?> clazz, final Method method) {
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
final String value = requestMapping.value();
for (RequestMethod requestMethod : requestMapping.method()) {
handlerExecutions.put(
new HandlerKey(value, requestMethod),
new HandlerExecution(clazz, method));
}
}

public Object getHandler(final HttpServletRequest request) {
return null;
final HandlerKey handlerKey = new HandlerKey(request.getRequestURI(), RequestMethod.valueOf(request.getMethod()));
return handlerExecutions.keySet().stream()
.filter(handlerKey::equals)
.map(handlerExecutions::get)
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("존재하지 않은 요청입니다."));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,19 @@
import jakarta.servlet.http.HttpServletResponse;
import webmvc.org.springframework.web.servlet.ModelAndView;

import java.lang.reflect.Method;

public class HandlerExecution {
private final Class<?> clazz;
private final Method method;

public HandlerExecution(Class<?> clazz, final Method method) {
this.clazz = clazz;
this.method = method;
}

public ModelAndView handle(final HttpServletRequest request, final HttpServletResponse response) throws Exception {
return null;
final Object instance = clazz.getDeclaredConstructor().newInstance();
return (ModelAndView) method.invoke(instance, request, response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public class CharacterEncodingFilter implements Filter {

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
request.getServletContext().log("doFilter() 호출");
chain.doFilter(request, response);
}
Expand Down
8 changes: 7 additions & 1 deletion study/src/test/java/di/stage3/context/DIContainer.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package di.stage3.context;

import java.util.HashSet;
import java.util.Set;

/**
Expand All @@ -10,11 +11,16 @@ class DIContainer {
private final Set<Object> beans;

public DIContainer(final Set<Class<?>> classes) {
this.beans = Set.of();
this.beans = Set.of(classes.toArray());
}

@SuppressWarnings("unchecked")
public <T> T getBean(final Class<T> aClass) {
for (Object bean : beans) {
if (aClass.isAssignableFrom(bean.getClass())) {
return (T) bean;
}
}
return null;
}
}
13 changes: 12 additions & 1 deletion study/src/test/java/reflection/Junit3TestRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,23 @@

import org.junit.jupiter.api.Test;

import java.lang.reflect.Method;

class Junit3TestRunner {

@Test
void run() throws Exception {
Class<Junit3Test> clazz = Junit3Test.class;

// TODO Junit3Test에서 test로 시작하는 메소드 실행
// Junit3Test 클래스의 모든 메소드를 가져옵니다.
Method[] methods = clazz.getDeclaredMethods();

for (Method method : methods) {
String methodName = method.getName();
// 메소드 이름이 "test"로 시작하는 경우 실행합니다.
if (methodName.startsWith("test")) {
method.invoke(clazz.newInstance());
}
}
}
}
9 changes: 9 additions & 0 deletions study/src/test/java/reflection/Junit4TestRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,21 @@

import org.junit.jupiter.api.Test;

import java.lang.reflect.Method;

class Junit4TestRunner {

@Test
void run() throws Exception {
Class<Junit4Test> clazz = Junit4Test.class;

// TODO Junit4Test에서 @MyTest 애노테이션이 있는 메소드 실행
final Method[] declaredMethods = clazz.getDeclaredMethods();

for (final Method declaredMethod : declaredMethods) {
if (declaredMethod.isAnnotationPresent(MyTest.class)) {
declaredMethod.invoke(clazz.newInstance());
}
}
}
}
59 changes: 36 additions & 23 deletions study/src/test/java/reflection/ReflectionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.InstanceOfAssertFactories.DATE;

class ReflectionTest {

Expand All @@ -18,35 +22,39 @@ class ReflectionTest {
@Test
void givenObject_whenGetsClassName_thenCorrect() {
final Class<Question> clazz = Question.class;

assertThat(clazz.getSimpleName()).isEqualTo("");
assertThat(clazz.getName()).isEqualTo("");
assertThat(clazz.getCanonicalName()).isEqualTo("");
System.out.println(clazz.getCanonicalName());
assertThat(clazz.getSimpleName()).isEqualTo("Question");
assertThat(clazz.getName()).isEqualTo("reflection.Question");
assertThat(clazz.getCanonicalName()).isEqualTo("reflection.Question");
}

@Test
void givenClassName_whenCreatesObject_thenCorrect() throws ClassNotFoundException {
final Class<?> clazz = Class.forName("reflection.Question");

assertThat(clazz.getSimpleName()).isEqualTo("");
assertThat(clazz.getName()).isEqualTo("");
assertThat(clazz.getCanonicalName()).isEqualTo("");
assertThat(clazz.getSimpleName()).isEqualTo("Question");
assertThat(clazz.getName()).isEqualTo("reflection.Question");
assertThat(clazz.getCanonicalName()).isEqualTo("reflection.Question");
}

@Test
void givenObject_whenGetsFieldNamesAtRuntime_thenCorrect() {
final Object student = new Student();
final Field[] fields = null;
final List<String> actualFieldNames = null;
final Field[] fields = student.getClass().getDeclaredFields();
final List<String> actualFieldNames = Arrays.stream(fields)
.map(Field::getName)
.collect(Collectors.toList());

assertThat(actualFieldNames).contains("name", "age");
}

@Test
void givenClass_whenGetsMethods_thenCorrect() {
final Class<?> animalClass = Student.class;
final Method[] methods = null;
final List<String> actualMethods = null;
final Method[] methods = animalClass.getDeclaredMethods();
final List<String> actualMethods = Arrays.stream(methods)
.map(Method::getName)
.collect(Collectors.toList());

assertThat(actualMethods)
.hasSize(3)
Expand All @@ -56,20 +64,24 @@ void givenClass_whenGetsMethods_thenCorrect() {
@Test
void givenClass_whenGetsAllConstructors_thenCorrect() {
final Class<?> questionClass = Question.class;
final Constructor<?>[] constructors = null;
final Constructor<?>[] constructors = questionClass.getDeclaredConstructors();

assertThat(constructors).hasSize(2);
}


@Test
void givenClass_whenInstantiatesObjectsAtRuntime_thenCorrect() throws Exception {
final Class<?> questionClass = Question.class;

final Constructor<?> firstConstructor = null;
final Constructor<?> secondConstructor = null;
final Constructor<?>[] constructors = questionClass.getDeclaredConstructors();

final Constructor<?> firstConstructor = constructors[0];
final Constructor<?> secondConstructor = constructors[1];

final Question firstQuestion = (Question) firstConstructor.newInstance("gugu", "제목1", "내용1");

final Question firstQuestion = null;
final Question secondQuestion = null;
final Question secondQuestion = (Question) secondConstructor.newInstance(1L, "gugu", "제목2", "내용2", new Date(), 3);

assertThat(firstQuestion.getWriter()).isEqualTo("gugu");
assertThat(firstQuestion.getTitle()).isEqualTo("제목1");
Expand All @@ -82,15 +94,15 @@ void givenClass_whenInstantiatesObjectsAtRuntime_thenCorrect() throws Exception
@Test
void givenClass_whenGetsPublicFields_thenCorrect() {
final Class<?> questionClass = Question.class;
final Field[] fields = null;
final Field[] fields = questionClass.getFields();

assertThat(fields).hasSize(0);
}

@Test
void givenClass_whenGetsDeclaredFields_thenCorrect() {
final Class<?> questionClass = Question.class;
final Field[] fields = null;
final Field[] fields = questionClass.getDeclaredFields();

assertThat(fields).hasSize(6);
assertThat(fields[0].getName()).isEqualTo("questionId");
Expand All @@ -99,31 +111,32 @@ void givenClass_whenGetsDeclaredFields_thenCorrect() {
@Test
void givenClass_whenGetsFieldsByName_thenCorrect() throws Exception {
final Class<?> questionClass = Question.class;
final Field field = null;
final Field field = questionClass.getDeclaredField("questionId");

assertThat(field.getName()).isEqualTo("questionId");
}

@Test
void givenClassField_whenGetsType_thenCorrect() throws Exception {
final Field field = Question.class.getDeclaredField("questionId");
final Class<?> fieldClass = null;
final Class<?> fieldClass = field.getType();

assertThat(fieldClass.getSimpleName()).isEqualTo("long");
}

@Test
void givenClassField_whenSetsAndGetsValue_thenCorrect() throws Exception {
final Class<?> studentClass = Student.class;
final Student student = null;
final Field field = null;
final Student student = new Student();
final Field field = studentClass.getDeclaredField("age");

// todo field에 접근 할 수 있도록 만든다.
field.setAccessible(true);

assertThat(field.getInt(student)).isZero();
assertThat(student.getAge()).isZero();

field.set(null, null);
field.setInt(student, 99);

assertThat(field.getInt(student)).isEqualTo(99);
assertThat(student.getAge()).isEqualTo(99);
Expand Down
12 changes: 12 additions & 0 deletions study/src/test/java/reflection/ReflectionsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reflection.annotation.Controller;
import reflection.annotation.Repository;
import reflection.annotation.Service;

import java.util.Set;

class ReflectionsTest {

Expand All @@ -14,5 +19,12 @@ void showAnnotationClass() throws Exception {
Reflections reflections = new Reflections("reflection.examples");

// TODO 클래스 레벨에 @Controller, @Service, @Repository 애노테이션이 설정되어 모든 클래스 찾아 로그로 출력한다.
Set<Class<?>> annotatedClasses = reflections.getTypesAnnotatedWith(Controller.class, false);
annotatedClasses.addAll(reflections.getTypesAnnotatedWith(Service.class, false));
annotatedClasses.addAll(reflections.getTypesAnnotatedWith(Repository.class, false));

for (Class<?> annotatedClass : annotatedClasses) {
log.info("Annotated class: {}", annotatedClass.getName());
}
}
}
4 changes: 2 additions & 2 deletions study/src/test/java/servlet/com/example/ServletTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ void testSharedCounter() {

// expected를 0이 아닌 올바른 값으로 바꿔보자.
// 예상한 결과가 나왔는가? 왜 이런 결과가 나왔을까?
assertThat(Integer.parseInt(response.body())).isEqualTo(0);
assertThat(Integer.parseInt(response.body())).isEqualTo(3);
}

@Test
Expand All @@ -50,6 +50,6 @@ void testLocalCounter() {

// expected를 0이 아닌 올바른 값으로 바꿔보자.
// 예상한 결과가 나왔는가? 왜 이런 결과가 나왔을까?
assertThat(Integer.parseInt(response.body())).isEqualTo(0);
assertThat(Integer.parseInt(response.body())).isEqualTo(1);
}
}

0 comments on commit 44f207d

Please sign in to comment.