Skip to content

Commit 0155bad

Browse files
authored
[MVC 구현하기 - 1단계] 케빈(박진홍) 미션 제출합니다. (#17)
* test: 학습 테스트 완성 * feat: Annotation 기반 및 레거시 인터페이스 기반의 컨트롤러 처리 mapping & adapter 구현 * style: 코드 포맷팅 * chore: jacoco 및 sonarcube 설정 추가 * refactor: requestMapping 예외 수정 * refactor: 마갸 피드백 반영
1 parent 2dc0c39 commit 0155bad

File tree

17 files changed

+385
-84
lines changed

17 files changed

+385
-84
lines changed

app/src/main/java/com/techcourse/AppWebApplicationInitializer.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,25 @@
33
import jakarta.servlet.ServletContext;
44
import jakarta.servlet.ServletRegistration;
55
import nextstep.mvc.DispatcherServlet;
6+
import nextstep.mvc.controller.tobe.AnnotationHandlerAdapter;
7+
import nextstep.mvc.controller.tobe.AnnotationHandlerMapping;
8+
import nextstep.mvc.controller.tobe.ManualHandlerAdapter;
69
import nextstep.web.WebApplicationInitializer;
710
import org.slf4j.Logger;
811
import org.slf4j.LoggerFactory;
912

1013
public class AppWebApplicationInitializer implements WebApplicationInitializer {
1114

1215
private static final Logger log = LoggerFactory.getLogger(AppWebApplicationInitializer.class);
16+
private static final String BASE_PACKAGE = "com.techcourse";
1317

1418
@Override
1519
public void onStartup(ServletContext servletContext) {
1620
final DispatcherServlet dispatcherServlet = new DispatcherServlet();
1721
dispatcherServlet.addHandlerMapping(new ManualHandlerMapping());
18-
22+
dispatcherServlet.addHandlerMapping(new AnnotationHandlerMapping(BASE_PACKAGE));
23+
dispatcherServlet.addHandlerAdapter(new ManualHandlerAdapter());
24+
dispatcherServlet.addHandlerAdapter(new AnnotationHandlerAdapter());
1925
final ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", dispatcherServlet);
2026
dispatcher.setLoadOnStartup(1);
2127
dispatcher.addMapping("/");
Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,35 @@
11
package reflection;
22

3+
import java.lang.reflect.Constructor;
4+
import java.lang.reflect.InvocationTargetException;
5+
import java.lang.reflect.Method;
6+
import java.util.Arrays;
7+
import java.util.function.Consumer;
38
import org.junit.jupiter.api.Test;
49

510
class Junit3TestRunner {
611

712
@Test
813
void run() throws Exception {
14+
// given
915
Class<Junit3Test> clazz = Junit3Test.class;
16+
Method[] methods = clazz.getDeclaredMethods();
17+
Constructor<Junit3Test> constructor = clazz.getConstructor();
18+
Junit3Test junit3Test = constructor.newInstance();
1019

11-
// TODO Junit3Test에서 test로 시작하는 메소드 실행
20+
// when, then
21+
Arrays.stream(methods)
22+
.forEach(execute(junit3Test));
23+
}
24+
25+
private Consumer<Method> execute(Junit3Test junit3Test) {
26+
return method -> {
27+
method.setAccessible(true);
28+
try {
29+
method.invoke(junit3Test);
30+
} catch (IllegalAccessException | InvocationTargetException exception) {
31+
exception.printStackTrace();
32+
}
33+
};
1234
}
1335
}
Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,33 @@
11
package reflection;
22

3+
import java.lang.reflect.Constructor;
4+
import java.lang.reflect.InvocationTargetException;
5+
import java.lang.reflect.Method;
36
import org.junit.jupiter.api.Test;
47

58
class Junit4TestRunner {
69

710
@Test
811
void run() throws Exception {
12+
// given
913
Class<Junit4Test> clazz = Junit4Test.class;
14+
Method[] methods = clazz.getDeclaredMethods();
15+
Constructor<Junit4Test> constructor = clazz.getDeclaredConstructor();
16+
Junit4Test junit4Test = constructor.newInstance();
1017

11-
// TODO Junit4Test에서 @MyTest 애노테이션이 있는 메소드 실행
18+
// when, then
19+
for (Method method : methods) {
20+
if (method.isAnnotationPresent(MyTest.class)) {
21+
execute(method, junit4Test);
22+
}
23+
}
24+
}
25+
26+
private void execute(Method method, Junit4Test junit4Test) {
27+
try {
28+
method.invoke(junit4Test);
29+
} catch (IllegalAccessException | InvocationTargetException exception) {
30+
exception.printStackTrace();
31+
}
1232
}
1333
}

learning/src/test/java/reflection/ReflectionTest.java

Lines changed: 87 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,111 @@
11
package reflection;
22

3-
import org.junit.jupiter.api.Test;
4-
import org.slf4j.Logger;
5-
import org.slf4j.LoggerFactory;
3+
import static org.assertj.core.api.Assertions.assertThat;
64

75
import java.lang.reflect.Constructor;
86
import java.lang.reflect.Field;
97
import java.lang.reflect.Method;
8+
import java.util.Arrays;
9+
import java.util.Date;
1010
import java.util.List;
11-
12-
import static org.assertj.core.api.Assertions.assertThat;
11+
import java.util.stream.Collectors;
12+
import org.junit.jupiter.api.Test;
13+
import org.slf4j.Logger;
14+
import org.slf4j.LoggerFactory;
1315

1416
class ReflectionTest {
1517

1618
private static final Logger log = LoggerFactory.getLogger(ReflectionTest.class);
1719

1820
@Test
1921
void givenObject_whenGetsClassName_thenCorrect() {
22+
// given
2023
final Class<Question> clazz = Question.class;
2124

22-
assertThat(clazz.getSimpleName()).isEqualTo("");
23-
assertThat(clazz.getName()).isEqualTo("");
24-
assertThat(clazz.getCanonicalName()).isEqualTo("");
25+
// when, then
26+
assertThat(clazz.getSimpleName()).isEqualTo("Question");
27+
assertThat(clazz.getName()).isEqualTo("reflection.Question");
28+
assertThat(clazz.getCanonicalName()).isEqualTo("reflection.Question");
2529
}
2630

2731
@Test
2832
void givenClassName_whenCreatesObject_thenCorrect() throws ClassNotFoundException {
33+
// given
2934
final Class<?> clazz = Class.forName("reflection.Question");
3035

31-
assertThat(clazz.getSimpleName()).isEqualTo("");
32-
assertThat(clazz.getName()).isEqualTo("");
33-
assertThat(clazz.getCanonicalName()).isEqualTo("");
36+
// when, then
37+
assertThat(clazz.getSimpleName()).isEqualTo("Question");
38+
assertThat(clazz.getName()).isEqualTo("reflection.Question");
39+
assertThat(clazz.getCanonicalName()).isEqualTo("reflection.Question");
3440
}
3541

3642
@Test
3743
void givenObject_whenGetsFieldNamesAtRuntime_thenCorrect() {
44+
// given
3845
final Object student = new Student();
39-
final Field[] fields = null;
40-
final List<String> actualFieldNames = null;
46+
final Field[] fields = student.getClass().getDeclaredFields();
4147

48+
// when
49+
final List<String> actualFieldNames = Arrays.stream(fields)
50+
.map(Field::getName)
51+
.collect(Collectors.toList());
52+
53+
// then
4254
assertThat(actualFieldNames).contains("name", "age");
4355
}
4456

4557
@Test
4658
void givenClass_whenGetsMethods_thenCorrect() {
59+
// given
4760
final Class<?> animalClass = Student.class;
48-
final Method[] methods = null;
49-
final List<String> actualMethods = null;
61+
final Method[] methods = animalClass.getDeclaredMethods();
62+
63+
// when
64+
final List<String> actualMethods = Arrays.stream(methods)
65+
.map(Method::getName)
66+
.collect(Collectors.toList());
5067

68+
// then
5169
assertThat(actualMethods)
52-
.hasSize(3)
53-
.contains("getAge", "toString", "getName");
70+
.hasSize(3)
71+
.contains("getAge", "toString", "getName");
5472
}
5573

5674
@Test
5775
void givenClass_whenGetsAllConstructors_thenCorrect() {
76+
// given
5877
final Class<?> questionClass = Question.class;
59-
final Constructor<?>[] constructors = null;
6078

79+
// when
80+
final Constructor<?>[] constructors = questionClass.getDeclaredConstructors();
81+
82+
// then
6183
assertThat(constructors).hasSize(2);
6284
}
6385

6486
@Test
6587
void givenClass_whenInstantiatesObjectsAtRuntime_thenCorrect() throws Exception {
88+
// given
6689
final Class<?> questionClass = Question.class;
67-
68-
final Constructor<?> firstConstructor = null;
69-
final Constructor<?> secondConstructor = null;
70-
71-
final Question firstQuestion = null;
72-
final Question secondQuestion = null;
73-
90+
final Constructor<?> firstConstructor =
91+
questionClass.getDeclaredConstructor(String.class, String.class, String.class);
92+
final Constructor<?> secondConstructor =
93+
questionClass.getDeclaredConstructor(
94+
long.class,
95+
String.class,
96+
String.class,
97+
String.class,
98+
Date.class,
99+
int.class
100+
);
101+
102+
// when
103+
final Question firstQuestion =
104+
(Question) firstConstructor.newInstance("gugu", "제목1", "내용1");
105+
final Question secondQuestion =
106+
(Question) secondConstructor.newInstance(1L, "gugu", "제목2", "내용2", new Date(), 0);
107+
108+
// then
74109
assertThat(firstQuestion.getWriter()).isEqualTo("gugu");
75110
assertThat(firstQuestion.getTitle()).isEqualTo("제목1");
76111
assertThat(firstQuestion.getContents()).isEqualTo("내용1");
@@ -81,50 +116,66 @@ void givenClass_whenInstantiatesObjectsAtRuntime_thenCorrect() throws Exception
81116

82117
@Test
83118
void givenClass_whenGetsPublicFields_thenCorrect() {
119+
// given
84120
final Class<?> questionClass = Question.class;
85-
final Field[] fields = null;
86121

87-
assertThat(fields).hasSize(0);
122+
// when
123+
final Field[] fields = questionClass.getFields();
124+
125+
// then
126+
assertThat(fields).isEmpty();
88127
}
89128

90129
@Test
91130
void givenClass_whenGetsDeclaredFields_thenCorrect() {
131+
// given
92132
final Class<?> questionClass = Question.class;
93-
final Field[] fields = null;
94133

134+
// when
135+
final Field[] fields = questionClass.getDeclaredFields();
136+
137+
// then
95138
assertThat(fields).hasSize(6);
96139
assertThat(fields[0].getName()).isEqualTo("questionId");
97140
}
98141

99142
@Test
100143
void givenClass_whenGetsFieldsByName_thenCorrect() throws Exception {
144+
// given
101145
final Class<?> questionClass = Question.class;
102-
final Field field = null;
103146

147+
// when
148+
final Field field = questionClass.getDeclaredField("questionId");
149+
150+
// then
104151
assertThat(field.getName()).isEqualTo("questionId");
105152
}
106153

107154
@Test
108155
void givenClassField_whenGetsType_thenCorrect() throws Exception {
156+
// given
109157
final Field field = Question.class.getDeclaredField("questionId");
110-
final Class<?> fieldClass = null;
111158

159+
// when
160+
final Class<?> fieldClass = field.getType();
161+
162+
// then
112163
assertThat(fieldClass.getSimpleName()).isEqualTo("long");
113164
}
114165

115166
@Test
116167
void givenClassField_whenSetsAndGetsValue_thenCorrect() throws Exception {
168+
// given
117169
final Class<?> studentClass = Student.class;
118-
final Student student = null;
119-
final Field field = null;
120-
121-
// todo field에 접근 할 수 있도록 만든다.
170+
final Student student = (Student) studentClass.getDeclaredConstructor().newInstance();
171+
final Field field = studentClass.getDeclaredField("age");
122172

173+
// when, then
174+
field.setAccessible(true);
123175
assertThat(field.getInt(student)).isZero();
124176
assertThat(student.getAge()).isZero();
125177

126-
field.set(null, null);
127-
178+
field.set(student, 99);
128179
assertThat(field.getInt(student)).isEqualTo(99);
129180
assertThat(student.getAge()).isEqualTo(99);
130181
}

learning/src/test/java/reflection/ReflectionsTest.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package reflection;
22

3+
import annotation.Controller;
4+
import annotation.Repository;
5+
import annotation.Service;
6+
import java.util.Set;
37
import org.junit.jupiter.api.Test;
48
import org.reflections.Reflections;
59
import org.slf4j.Logger;
@@ -11,8 +15,21 @@ class ReflectionsTest {
1115

1216
@Test
1317
void showAnnotationClass() throws Exception {
18+
// given
1419
Reflections reflections = new Reflections("examples");
20+
Set<Class<?>> controllers = reflections.getTypesAnnotatedWith(Controller.class);
21+
Set<Class<?>> services = reflections.getTypesAnnotatedWith(Service.class);
22+
Set<Class<?>> repositories = reflections.getTypesAnnotatedWith(Repository.class);
1523

16-
// TODO 클래스 레벨에 @Controller, @Service, @Repository 애노테이션이 설정되어 모든 클래스 찾아 로그로 출력한다.
24+
// when, then
25+
for (Class<?> controller : controllers) {
26+
log.info("name : {}", controller.getSimpleName());
27+
}
28+
for (Class<?> service : services) {
29+
log.info("name : {}", service.getSimpleName());
30+
}
31+
for (Class<?> repository : repositories) {
32+
log.info("name : {}", repository.getSimpleName());
33+
}
1734
}
1835
}

mvc/build.gradle

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
plugins {
22
id 'java'
3+
id 'org.sonarqube' version '3.3'
4+
id 'jacoco'
35
}
46

57
repositories {
@@ -27,4 +29,26 @@ dependencies {
2729

2830
tasks.named('test') {
2931
useJUnitPlatform()
32+
finalizedBy(jacocoTestReport)
33+
}
34+
35+
sonarqube {
36+
properties {
37+
property "sonar.sourceEncoding", "UTF-8"
38+
property "sonar.projectKey", "woowacourse_be-jwp-http-server"
39+
property "sonar.organization", "woowacourse"
40+
property "sonar.host.url", "https://sonarcloud.io"
41+
property "sonar.coverage.jacoco.xmlReportPaths", "jacoco/jacoco.xml"
42+
}
43+
}
44+
45+
jacoco {
46+
toolVersion = "0.8.5"
47+
}
48+
49+
jacocoTestReport {
50+
reports {
51+
xml.enabled true
52+
xml.destination(new File("jacoco/jacoco.xml"))
53+
}
3054
}

mvc/jacoco/jacoco.xml

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)