From 9892844df77ebf5a322122421509ee1b995c59b9 Mon Sep 17 00:00:00 2001 From: yuntasha Date: Thu, 30 Apr 2026 16:12:47 +0900 Subject: [PATCH 01/22] =?UTF-8?q?:heavy=5Fplus=5Fsign:=20=EB=A6=AC?= =?UTF-8?q?=ED=94=8C=EB=A0=89=EC=85=98=20cnrk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index 6b35215f..3018bb32 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -24,6 +24,9 @@ dependencies { implementation(group = "org.json", name = "json", version = "20160810") + // aop + implementation("org.reflections:reflections:0.10.2") + // 톰캣 implementation("org.apache.tomcat.embed:tomcat-embed-core:8.5.42") implementation("org.apache.tomcat.embed:tomcat-embed-jasper:8.5.42") From ce97712f4a6541f18594fdab9f7497c56fa2d2fc Mon Sep 17 00:00:00 2001 From: yuntasha Date: Thu, 30 Apr 2026 16:13:00 +0900 Subject: [PATCH 02/22] =?UTF-8?q?:sparkles:=20Car=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/study/reflection/Car.java | 29 +++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/test/java/study/reflection/Car.java diff --git a/src/test/java/study/reflection/Car.java b/src/test/java/study/reflection/Car.java new file mode 100644 index 00000000..3f659983 --- /dev/null +++ b/src/test/java/study/reflection/Car.java @@ -0,0 +1,29 @@ +package study.reflection; + +public class Car { + + private String name; + private int price; + + public Car() { + + } + + public Car(String name, int price) { + this.name = name; + this.price = price; + } + + @PrintView + public void printView() { + System.out.println("자동차 정보를 출력 합니다."); + } + + public String testGetName() { + return "test : " + name; + } + + public String testGetPrice() { + return "test : " + price; + } +} From dc5f9a4a178d8a3ee6e0505254883c4ad0ddf360 Mon Sep 17 00:00:00 2001 From: yuntasha Date: Thu, 30 Apr 2026 16:13:13 +0900 Subject: [PATCH 03/22] =?UTF-8?q?:sparkles:=20=EC=96=B4=EB=85=B8=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EC=B6=94=EA=B0=80=20(=EC=BB=B4=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=9D=B4=EC=8A=88)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/study/reflection/PrintView.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/test/java/study/reflection/PrintView.java diff --git a/src/test/java/study/reflection/PrintView.java b/src/test/java/study/reflection/PrintView.java new file mode 100644 index 00000000..bbba5b57 --- /dev/null +++ b/src/test/java/study/reflection/PrintView.java @@ -0,0 +1,8 @@ +package study.reflection; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface PrintView { +} From 0eea9eb1f1f3760a9bbd81c7c44007e8ee7f7dba Mon Sep 17 00:00:00 2001 From: yuntasha Date: Thu, 30 Apr 2026 16:13:45 +0900 Subject: [PATCH 04/22] =?UTF-8?q?:sparkles:=20=EC=9A=94=EA=B5=AC=EC=82=AC?= =?UTF-8?q?=ED=95=AD=203=EA=B9=8C=EC=A7=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/study/reflection/ReflectionTest.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/test/java/study/reflection/ReflectionTest.java diff --git a/src/test/java/study/reflection/ReflectionTest.java b/src/test/java/study/reflection/ReflectionTest.java new file mode 100644 index 00000000..8a44be68 --- /dev/null +++ b/src/test/java/study/reflection/ReflectionTest.java @@ -0,0 +1,55 @@ +package study.reflection; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; + +public class ReflectionTest { + + @Test + @DisplayName("Car 객체 정보 가져오기") + void showClass() { + Class carClass = Car.class; + System.out.println(carClass.getName()); + } + + @Test + @DisplayName("test로 시작하는 메소드 실행하기") + void executeMethodStartWithTest() throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + Class clazz = Car.class; + Car car = clazz.getDeclaredConstructor(String.class, int.class).newInstance("차 이름", 100000); + + Method[] methods = clazz.getDeclaredMethods(); + Arrays.stream(methods) + .filter(m -> m.getName().startsWith("test")) + .peek(m -> System.out.println(m.getName())) + .forEach(m -> { + try { + System.out.println(m.invoke(car)); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }); + } + + @Test + @DisplayName("@PrintView 애노테이션 메소드 실행하기") + void executeMethodHaveAnnotation() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { + Class clazz = Car.class; + Car car = clazz.getDeclaredConstructor(String.class, int.class).newInstance("차 이름", 100000); + + Method[] methods = clazz.getMethods(); + Arrays.stream(methods) + .filter(method -> method.isAnnotationPresent(PrintView.class)) + .forEach(m -> { + try { + m.invoke(car); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }); + } +} From 026cfaecc87f48bcba69d4a18bb0c0cb61a0c823 Mon Sep 17 00:00:00 2001 From: yuntasha Date: Thu, 30 Apr 2026 16:15:46 +0900 Subject: [PATCH 05/22] =?UTF-8?q?:recycle:=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/study/reflection/ReflectionTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/study/reflection/ReflectionTest.java b/src/test/java/study/reflection/ReflectionTest.java index 8a44be68..99981b2e 100644 --- a/src/test/java/study/reflection/ReflectionTest.java +++ b/src/test/java/study/reflection/ReflectionTest.java @@ -10,14 +10,14 @@ public class ReflectionTest { @Test - @DisplayName("Car 객체 정보 가져오기") + @DisplayName("요구사항 1 : Car 객체 정보 가져오기") void showClass() { Class carClass = Car.class; System.out.println(carClass.getName()); } @Test - @DisplayName("test로 시작하는 메소드 실행하기") + @DisplayName("요구사항 2 : test로 시작하는 메소드 실행하기") void executeMethodStartWithTest() throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { Class clazz = Car.class; Car car = clazz.getDeclaredConstructor(String.class, int.class).newInstance("차 이름", 100000); @@ -36,7 +36,7 @@ void executeMethodStartWithTest() throws InstantiationException, IllegalAccessEx } @Test - @DisplayName("@PrintView 애노테이션 메소드 실행하기") + @DisplayName("요구사항 3 : @PrintView 애노테이션 메소드 실행하기") void executeMethodHaveAnnotation() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { Class clazz = Car.class; Car car = clazz.getDeclaredConstructor(String.class, int.class).newInstance("차 이름", 100000); From fc518f24d5a69cc10528296d2ef302322b44bf40 Mon Sep 17 00:00:00 2001 From: yuntasha Date: Thu, 30 Apr 2026 16:50:17 +0900 Subject: [PATCH 06/22] =?UTF-8?q?:heavy=5Fplus=5Fsign:=20assertThat?= =?UTF-8?q?=EA=B0=80=EC=A0=B8=EC=98=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index 3018bb32..64599184 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,5 @@ +import org.gradle.internal.impldep.org.junit.Assert.assertThat + plugins { id("java") kotlin("jvm") @@ -32,6 +34,8 @@ dependencies { implementation("org.apache.tomcat.embed:tomcat-embed-jasper:8.5.42") implementation(kotlin("stdlib-jdk8")) + + implementation("org.assertj:assertj-core:3.24.2") } tasks.test { From 670a24d4e3a4a1e6ce3e1ef3c6e9c7cd59cb2eaa Mon Sep 17 00:00:00 2001 From: yuntasha Date: Thu, 30 Apr 2026 16:50:58 +0900 Subject: [PATCH 07/22] =?UTF-8?q?:sparkles:=20getter=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/study/reflection/Car.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/test/java/study/reflection/Car.java b/src/test/java/study/reflection/Car.java index 3f659983..8977a9fe 100644 --- a/src/test/java/study/reflection/Car.java +++ b/src/test/java/study/reflection/Car.java @@ -5,6 +5,14 @@ public class Car { private String name; private int price; + public String getName() { + return name; + } + + public int getPrice() { + return price; + } + public Car() { } From a68fe598727eacf01fedd3b6f77461fe20aea253 Mon Sep 17 00:00:00 2001 From: yuntasha Date: Thu, 30 Apr 2026 16:54:05 +0900 Subject: [PATCH 08/22] =?UTF-8?q?:sparkles:=20=EC=9A=94=EA=B5=AC=EC=82=AC?= =?UTF-8?q?=ED=95=AD=204=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/study/reflection/ReflectionTest.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/test/java/study/reflection/ReflectionTest.java b/src/test/java/study/reflection/ReflectionTest.java index 99981b2e..c6822558 100644 --- a/src/test/java/study/reflection/ReflectionTest.java +++ b/src/test/java/study/reflection/ReflectionTest.java @@ -3,10 +3,13 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; +import static org.assertj.core.api.Assertions.assertThat; + public class ReflectionTest { @Test @@ -52,4 +55,29 @@ void executeMethodHaveAnnotation() throws NoSuchMethodException, InvocationTarge } }); } + + @Test + @DisplayName("요구사항 4 : private field에 값 할당하기") + void setPirvateField() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException { + Class clazz = Car.class; + Car car = clazz.getDeclaredConstructor().newInstance(); + + Field name = clazz.getDeclaredField("name"); + name.setAccessible(true); + Field price = clazz.getDeclaredField("price"); + price.setAccessible(true); + + try { + String nameValue = "이름"; + int priceValue = 1000000; + name.set(car, nameValue); + price.set(car, priceValue); + + assertThat(car.getName()).isEqualTo(nameValue); + assertThat(car.getPrice()).isEqualTo(priceValue); + } finally { + name.setAccessible(false); + price.setAccessible(false); + } + } } From 4402540ad15937914a61d3c9e56f6c5fd57cad4a Mon Sep 17 00:00:00 2001 From: yuntasha Date: Thu, 30 Apr 2026 17:01:48 +0900 Subject: [PATCH 09/22] =?UTF-8?q?:sparkles:=20=EC=9A=94=EA=B5=AC=EC=82=AC?= =?UTF-8?q?=ED=95=AD=205=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/study/reflection/ReflectionTest.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/test/java/study/reflection/ReflectionTest.java b/src/test/java/study/reflection/ReflectionTest.java index c6822558..3814a843 100644 --- a/src/test/java/study/reflection/ReflectionTest.java +++ b/src/test/java/study/reflection/ReflectionTest.java @@ -80,4 +80,17 @@ void setPirvateField() throws NoSuchMethodException, InvocationTargetException, price.setAccessible(false); } } + + @Test + @DisplayName("요구사항 5 : 인자가 존재하는 인스턴스 생성") + public void constructerWithParameter() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { + String nameValue = "이름"; + int priceValue = 1000000; + + Class clazz = Car.class; + Car car = clazz.getDeclaredConstructor(String.class, int.class).newInstance(nameValue, priceValue); + + assertThat(car.getName()).isEqualTo(nameValue); + assertThat(car.getPrice()).isEqualTo(priceValue); + } } From d69fdfceaceebabab38ccf66cf758862d9cd779d Mon Sep 17 00:00:00 2001 From: yuntasha Date: Thu, 30 Apr 2026 17:24:36 +0900 Subject: [PATCH 10/22] =?UTF-8?q?:sparkles:=20Lecture=20=EC=9A=94=EA=B5=AC?= =?UTF-8?q?=EC=82=AC=ED=95=AD=201,=202=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../study/reflection/lecture/Lecture.java | 34 +++++++++++++++ .../lecture/LectureReflectionTest.java | 43 +++++++++++++++++++ .../study/reflection/lecture/MethodOrder.java | 12 ++++++ 3 files changed, 89 insertions(+) create mode 100644 src/test/java/study/reflection/lecture/Lecture.java create mode 100644 src/test/java/study/reflection/lecture/LectureReflectionTest.java create mode 100644 src/test/java/study/reflection/lecture/MethodOrder.java diff --git a/src/test/java/study/reflection/lecture/Lecture.java b/src/test/java/study/reflection/lecture/Lecture.java new file mode 100644 index 00000000..dc7095bd --- /dev/null +++ b/src/test/java/study/reflection/lecture/Lecture.java @@ -0,0 +1,34 @@ +package study.reflection.lecture; + + +public class Lecture { + private final String name; + private final int price; + private boolean visible; + + public Lecture(final String name, final int price) { + this.name = name; + this.price = price; + this.visible = false; + } + + public Lecture(final String name, final int price, final boolean visible) { + this.name = name; + this.price = price; + this.visible = visible; + } + + private void changeVisible() { + this.visible = true; + } + + @MethodOrder(1) + public String getName() { + return this.name; + } + + @MethodOrder(2) + public int getPrice() { + return this.price; + } +} diff --git a/src/test/java/study/reflection/lecture/LectureReflectionTest.java b/src/test/java/study/reflection/lecture/LectureReflectionTest.java new file mode 100644 index 00000000..4b2b4847 --- /dev/null +++ b/src/test/java/study/reflection/lecture/LectureReflectionTest.java @@ -0,0 +1,43 @@ +package study.reflection.lecture; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThat; + +public class LectureReflectionTest { + @Test + @DisplayName("요구사항 1 : Lecture 생성자 찾기") + void findConstructor() { + Constructor[] constructors = Lecture.class.getDeclaredConstructors(); + Arrays.stream(constructors) + .forEach(System.out::println); + } + + @Test + @DisplayName("요구사항 2 : Lecture 인스턴스 동적 생성1") + void createConstructor1() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { + final String name = "테스트 이름"; + final int price = 10000; + Lecture lecture = Lecture.class.getDeclaredConstructor(String.class, int.class).newInstance(name, price); + + assertThat(lecture.getName()).isEqualTo(name); + assertThat(lecture.getPrice()).isEqualTo(price); + } + + @Test + @DisplayName("요구사항 2 : Lecture 인스턴스 동적 생성2") + void createConstructor2() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { + final String name = "테스트 이름"; + final int price = 10000; + final boolean visible = false; + Lecture lecture = Lecture.class.getDeclaredConstructor(String.class, int.class, boolean.class).newInstance(name, price, visible); + + assertThat(lecture.getName()).isEqualTo(name); + assertThat(lecture.getPrice()).isEqualTo(price); + } +} diff --git a/src/test/java/study/reflection/lecture/MethodOrder.java b/src/test/java/study/reflection/lecture/MethodOrder.java new file mode 100644 index 00000000..239421be --- /dev/null +++ b/src/test/java/study/reflection/lecture/MethodOrder.java @@ -0,0 +1,12 @@ +package study.reflection.lecture; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface MethodOrder { + int value(); +} From 9104d4eba69ca9c9153544bb71eaeb36b0a3b485 Mon Sep 17 00:00:00 2001 From: yuntasha Date: Thu, 30 Apr 2026 17:29:22 +0900 Subject: [PATCH 11/22] =?UTF-8?q?:sparkles:=20Lecture=20=EC=9A=94=EA=B5=AC?= =?UTF-8?q?=EC=82=AC=ED=95=AD=203=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../study/reflection/lecture/LectureReflectionTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/test/java/study/reflection/lecture/LectureReflectionTest.java b/src/test/java/study/reflection/lecture/LectureReflectionTest.java index 4b2b4847..683369b4 100644 --- a/src/test/java/study/reflection/lecture/LectureReflectionTest.java +++ b/src/test/java/study/reflection/lecture/LectureReflectionTest.java @@ -3,6 +3,7 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import java.lang.reflect.AccessFlag; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; @@ -40,4 +41,11 @@ void createConstructor2() throws NoSuchMethodException, InvocationTargetExceptio assertThat(lecture.getName()).isEqualTo(name); assertThat(lecture.getPrice()).isEqualTo(price); } + + @Test + @DisplayName("요구사항 3 : private 메서드 찾기") + void findPrivateMethod() { + Class clazz = Lecture.class; + Arrays.stream(clazz.getDeclaredMethods()).filter(method -> method.accessFlags().contains(AccessFlag.PRIVATE)).forEach(System.out::println); + } } From 081b01b43b0797f66dc79484ba0f9b8e55d92d1b Mon Sep 17 00:00:00 2001 From: yuntasha Date: Thu, 30 Apr 2026 17:36:02 +0900 Subject: [PATCH 12/22] =?UTF-8?q?:sparkles:=20Lecture=20=EC=9A=94=EA=B5=AC?= =?UTF-8?q?=EC=82=AC=ED=95=AD=204=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lecture/LectureReflectionTest.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/test/java/study/reflection/lecture/LectureReflectionTest.java b/src/test/java/study/reflection/lecture/LectureReflectionTest.java index 683369b4..5c171cb8 100644 --- a/src/test/java/study/reflection/lecture/LectureReflectionTest.java +++ b/src/test/java/study/reflection/lecture/LectureReflectionTest.java @@ -5,6 +5,7 @@ import java.lang.reflect.AccessFlag; import java.lang.reflect.Constructor; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; @@ -48,4 +49,39 @@ void findPrivateMethod() { Class clazz = Lecture.class; Arrays.stream(clazz.getDeclaredMethods()).filter(method -> method.accessFlags().contains(AccessFlag.PRIVATE)).forEach(System.out::println); } + + @Test + @DisplayName("요구사항 4 : private 메서드 호출") + void executePrivateMethod() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException { + Class clazz = Lecture.class; + + final String name = "테스트 이름"; + final int price = 10000; + final boolean visible = false; + Lecture lecture = clazz.getDeclaredConstructor(String.class, int.class, boolean.class).newInstance(name, price, visible); + + Arrays.stream(clazz.getDeclaredMethods()).filter(method -> method.accessFlags().contains(AccessFlag.PRIVATE)) + .forEach(method -> { + try { + method.setAccessible(true); + method.invoke(lecture); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } finally { + method.setAccessible(false); + } + }); + + Field field = clazz.getDeclaredField("visible"); + field.setAccessible(true); + try { + assertThat(field.getBoolean(lecture)).isEqualTo(true); + } finally { + field.setAccessible(false); + } + } + + } From ba30f341357e0ce8eedd270dbf11b7419ddc270f Mon Sep 17 00:00:00 2001 From: yuntasha Date: Thu, 30 Apr 2026 17:42:47 +0900 Subject: [PATCH 13/22] =?UTF-8?q?:sparkles:=20Lecture=20=EC=9A=94=EA=B5=AC?= =?UTF-8?q?=EC=82=AC=ED=95=AD=205=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lecture/LectureReflectionTest.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/test/java/study/reflection/lecture/LectureReflectionTest.java b/src/test/java/study/reflection/lecture/LectureReflectionTest.java index 5c171cb8..ccf850ca 100644 --- a/src/test/java/study/reflection/lecture/LectureReflectionTest.java +++ b/src/test/java/study/reflection/lecture/LectureReflectionTest.java @@ -3,10 +3,7 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import java.lang.reflect.AccessFlag; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.*; import java.util.Arrays; import static org.assertj.core.api.Assertions.assertThat; @@ -60,7 +57,8 @@ void executePrivateMethod() throws NoSuchMethodException, InvocationTargetExcept final boolean visible = false; Lecture lecture = clazz.getDeclaredConstructor(String.class, int.class, boolean.class).newInstance(name, price, visible); - Arrays.stream(clazz.getDeclaredMethods()).filter(method -> method.accessFlags().contains(AccessFlag.PRIVATE)) + Arrays.stream(clazz.getDeclaredMethods()) + .filter(method -> method.accessFlags().contains(AccessFlag.PRIVATE)) .forEach(method -> { try { method.setAccessible(true); @@ -83,5 +81,13 @@ void executePrivateMethod() throws NoSuchMethodException, InvocationTargetExcept } } + @Test + @DisplayName("요구사항 5 : 애너테이션으로 메서드 찾기") + void findMethodByAnnotation() { + Method[] methods = Lecture.class.getMethods(); + Arrays.stream(methods) + .filter(method -> method.isAnnotationPresent(MethodOrder.class)) + .forEach(System.out::println); + } } From 8b136aeb18530e116fc27555aa3b89416b179387 Mon Sep 17 00:00:00 2001 From: yuntasha Date: Thu, 30 Apr 2026 17:46:30 +0900 Subject: [PATCH 14/22] =?UTF-8?q?:sparkles:=20Lecture=20=EC=9A=94=EA=B5=AC?= =?UTF-8?q?=EC=82=AC=ED=95=AD=206=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reflection/lecture/LectureReflectionTest.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/test/java/study/reflection/lecture/LectureReflectionTest.java b/src/test/java/study/reflection/lecture/LectureReflectionTest.java index ccf850ca..8c97dbae 100644 --- a/src/test/java/study/reflection/lecture/LectureReflectionTest.java +++ b/src/test/java/study/reflection/lecture/LectureReflectionTest.java @@ -90,4 +90,15 @@ void findMethodByAnnotation() { .forEach(System.out::println); } + @Test + @DisplayName("요구사항 6 : @MethodOrder 애너테이션 정보 조회") + void findMethodParameterByAnnotation() { + Method[] methods = Lecture.class.getMethods(); + Arrays.stream(methods) + .filter(method -> method.isAnnotationPresent(MethodOrder.class)) + .forEach(method -> { + System.out.println(method + " : " + method.getAnnotation(MethodOrder.class).value()); + }); + } + } From c482f0267f4144d95d7ac894bb5eab7b07c5ed64 Mon Sep 17 00:00:00 2001 From: yuntasha Date: Thu, 30 Apr 2026 18:09:03 +0900 Subject: [PATCH 15/22] =?UTF-8?q?:sparkles:=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/diy/app/infra/annotations/Component.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/main/java/com/diy/app/infra/annotations/Component.java diff --git a/src/main/java/com/diy/app/infra/annotations/Component.java b/src/main/java/com/diy/app/infra/annotations/Component.java new file mode 100644 index 00000000..906d630c --- /dev/null +++ b/src/main/java/com/diy/app/infra/annotations/Component.java @@ -0,0 +1,11 @@ +package com.diy.app.infra.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface Component { +} From aeacaa9050609076c22aa8f54c73225ce620a377 Mon Sep 17 00:00:00 2001 From: yuntasha Date: Fri, 1 May 2026 14:31:09 +0900 Subject: [PATCH 16/22] =?UTF-8?q?:sparkles:=20Component=20=EC=96=B4?= =?UTF-8?q?=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{app/infra => framework/beans}/annotations/Component.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/main/java/com/diy/{app/infra => framework/beans}/annotations/Component.java (85%) diff --git a/src/main/java/com/diy/app/infra/annotations/Component.java b/src/main/java/com/diy/framework/beans/annotations/Component.java similarity index 85% rename from src/main/java/com/diy/app/infra/annotations/Component.java rename to src/main/java/com/diy/framework/beans/annotations/Component.java index 906d630c..25fe4d20 100644 --- a/src/main/java/com/diy/app/infra/annotations/Component.java +++ b/src/main/java/com/diy/framework/beans/annotations/Component.java @@ -1,4 +1,4 @@ -package com.diy.app.infra.annotations; +package com.diy.framework.beans.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; From 08b15a46c2629b3f350a298853ee524fedacc727 Mon Sep 17 00:00:00 2001 From: yuntasha Date: Fri, 1 May 2026 14:34:05 +0900 Subject: [PATCH 17/22] =?UTF-8?q?:sparkles:=20=EC=8A=A4=EC=BA=90=EB=84=88?= =?UTF-8?q?=EA=B0=80=20=EC=8B=A4=ED=96=89=EB=90=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/diy/app/Main.java | 2 ++ .../repository/LectureRepository.java | 2 ++ .../framework/beans/factory/BeanFactory.java | 13 +++++++++++ .../framework/beans/factory/BeanScanner.java | 23 +++++++++++++++++++ 4 files changed, 40 insertions(+) create mode 100644 src/main/java/com/diy/framework/beans/factory/BeanFactory.java create mode 100644 src/main/java/com/diy/framework/beans/factory/BeanScanner.java diff --git a/src/main/java/com/diy/app/Main.java b/src/main/java/com/diy/app/Main.java index 36237ecf..ddb1ccac 100644 --- a/src/main/java/com/diy/app/Main.java +++ b/src/main/java/com/diy/app/Main.java @@ -1,9 +1,11 @@ package com.diy.app; +import com.diy.framework.beans.factory.BeanFactory; import com.diy.framework.web.server.TomcatWebServer; public class Main { public static void main(String[] args) { + new BeanFactory(); TomcatWebServer tomcatWebServer = new TomcatWebServer(); tomcatWebServer.start(); } diff --git a/src/main/java/com/diy/app/business/repository/LectureRepository.java b/src/main/java/com/diy/app/business/repository/LectureRepository.java index 91be4fad..47dd262a 100644 --- a/src/main/java/com/diy/app/business/repository/LectureRepository.java +++ b/src/main/java/com/diy/app/business/repository/LectureRepository.java @@ -1,6 +1,7 @@ package com.diy.app.business.repository; import com.diy.app.business.domain.Lecture; +import com.diy.framework.beans.annotations.Component; import java.util.List; import java.util.Map; @@ -8,6 +9,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; +@Component public class LectureRepository { static LectureRepository instance; diff --git a/src/main/java/com/diy/framework/beans/factory/BeanFactory.java b/src/main/java/com/diy/framework/beans/factory/BeanFactory.java new file mode 100644 index 00000000..e69fd1d8 --- /dev/null +++ b/src/main/java/com/diy/framework/beans/factory/BeanFactory.java @@ -0,0 +1,13 @@ +package com.diy.framework.beans.factory; + +import com.diy.framework.beans.annotations.Component; + +import java.util.Set; + +public class BeanFactory { + public BeanFactory() { + BeanScanner bc = new BeanScanner("com.diy.app"); + Set> classes = bc.scanClassesTypeAnnotatedWith(Component.class); + classes.forEach(System.out::println); + } +} diff --git a/src/main/java/com/diy/framework/beans/factory/BeanScanner.java b/src/main/java/com/diy/framework/beans/factory/BeanScanner.java new file mode 100644 index 00000000..fd2c6b2b --- /dev/null +++ b/src/main/java/com/diy/framework/beans/factory/BeanScanner.java @@ -0,0 +1,23 @@ +package com.diy.framework.beans.factory; + +import org.reflections.Reflections; + +import java.lang.annotation.Annotation; +import java.util.Set; +import java.util.stream.Collectors; + +public class BeanScanner { + + private final Reflections reflections; + + public BeanScanner(final String... basePackages) { + reflections = new Reflections(basePackages); + } + + public Set> scanClassesTypeAnnotatedWith(final Class annotation) { + return reflections.getTypesAnnotatedWith(annotation) + .stream() + .filter(type -> (!type.isAnnotation() && !type.isInterface())) + .collect(Collectors.toSet()); + } +} \ No newline at end of file From 209534799febebd83bb5f9779819bb7a3a81b232 Mon Sep 17 00:00:00 2001 From: yuntasha Date: Fri, 1 May 2026 14:35:03 +0900 Subject: [PATCH 18/22] =?UTF-8?q?:recycle:=20=EB=B9=88=20=EC=8A=A4?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=EC=A7=80=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/diy/app/Main.java | 4 ++-- .../beans/factory/{BeanFactory.java => BeanStorage.java} | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename src/main/java/com/diy/framework/beans/factory/{BeanFactory.java => BeanStorage.java} (85%) diff --git a/src/main/java/com/diy/app/Main.java b/src/main/java/com/diy/app/Main.java index ddb1ccac..dd765384 100644 --- a/src/main/java/com/diy/app/Main.java +++ b/src/main/java/com/diy/app/Main.java @@ -1,11 +1,11 @@ package com.diy.app; -import com.diy.framework.beans.factory.BeanFactory; +import com.diy.framework.beans.factory.BeanStorage; import com.diy.framework.web.server.TomcatWebServer; public class Main { public static void main(String[] args) { - new BeanFactory(); + BeanStorage beanStorage = new BeanStorage(); TomcatWebServer tomcatWebServer = new TomcatWebServer(); tomcatWebServer.start(); } diff --git a/src/main/java/com/diy/framework/beans/factory/BeanFactory.java b/src/main/java/com/diy/framework/beans/factory/BeanStorage.java similarity index 85% rename from src/main/java/com/diy/framework/beans/factory/BeanFactory.java rename to src/main/java/com/diy/framework/beans/factory/BeanStorage.java index e69fd1d8..35828123 100644 --- a/src/main/java/com/diy/framework/beans/factory/BeanFactory.java +++ b/src/main/java/com/diy/framework/beans/factory/BeanStorage.java @@ -4,8 +4,8 @@ import java.util.Set; -public class BeanFactory { - public BeanFactory() { +public class BeanStorage { + public BeanStorage() { BeanScanner bc = new BeanScanner("com.diy.app"); Set> classes = bc.scanClassesTypeAnnotatedWith(Component.class); classes.forEach(System.out::println); From da39f2e7e5f9d3a9823ff13b16f48f49caddc200 Mon Sep 17 00:00:00 2001 From: yuntasha Date: Fri, 1 May 2026 14:48:13 +0900 Subject: [PATCH 19/22] =?UTF-8?q?:sparkles:=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EB=A0=88=ED=8F=AC=EC=A7=80=ED=86=A0=EB=A6=AC=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/diy/app/Main.java | 2 ++ .../framework/beans/factory/BeanStorage.java | 28 ++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/diy/app/Main.java b/src/main/java/com/diy/app/Main.java index dd765384..14d60a7f 100644 --- a/src/main/java/com/diy/app/Main.java +++ b/src/main/java/com/diy/app/Main.java @@ -1,11 +1,13 @@ package com.diy.app; +import com.diy.app.business.repository.LectureRepository; import com.diy.framework.beans.factory.BeanStorage; import com.diy.framework.web.server.TomcatWebServer; public class Main { public static void main(String[] args) { BeanStorage beanStorage = new BeanStorage(); + beanStorage.getBeans(LectureRepository.class).forEach(System.out::println); TomcatWebServer tomcatWebServer = new TomcatWebServer(); tomcatWebServer.start(); } diff --git a/src/main/java/com/diy/framework/beans/factory/BeanStorage.java b/src/main/java/com/diy/framework/beans/factory/BeanStorage.java index 35828123..a51d4737 100644 --- a/src/main/java/com/diy/framework/beans/factory/BeanStorage.java +++ b/src/main/java/com/diy/framework/beans/factory/BeanStorage.java @@ -2,12 +2,38 @@ import com.diy.framework.beans.annotations.Component; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.List; import java.util.Set; public class BeanStorage { + + private final List beans; + public BeanStorage() { BeanScanner bc = new BeanScanner("com.diy.app"); Set> classes = bc.scanClassesTypeAnnotatedWith(Component.class); - classes.forEach(System.out::println); + beans = classes.stream().map(clazz -> { + Constructor constructor = null; + try { + constructor = clazz.getDeclaredConstructor(); + constructor.setAccessible(true); + return constructor.newInstance(); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } finally { + if (constructor != null) { + constructor.setAccessible(false); + } + } + }).toList(); + } + + public List getBeans(final Class classType) { + return beans.stream() + .filter(classType::isInstance) + .map(classType::cast) + .toList(); } } From 39351a74e5b31679057b9e1e1cc5c4c5127e350f Mon Sep 17 00:00:00 2001 From: yuntasha Date: Tue, 5 May 2026 22:49:35 +0900 Subject: [PATCH 20/22] =?UTF-8?q?:sparkles:=20autowired=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/diy/app/Main.java | 3 +- .../controller/LectureController.java | 16 +++-- .../repository/LectureRepository.java | 5 -- .../app/business/service/LectureService.java | 15 ++--- .../infra/servlet/UrlControllerMapper.java | 3 +- .../com/diy/app/infra/viewRender/JspView.java | 3 + .../beans/annotations/Autowired.java | 11 ++++ .../framework/beans/factory/BeanScanner.java | 9 +++ .../framework/beans/factory/BeanStorage.java | 62 +++++++++++++++++-- .../{lecture-list.html => lecture-list1.html} | 0 10 files changed, 99 insertions(+), 28 deletions(-) create mode 100644 src/main/java/com/diy/framework/beans/annotations/Autowired.java rename src/main/resources/{lecture-list.html => lecture-list1.html} (100%) diff --git a/src/main/java/com/diy/app/Main.java b/src/main/java/com/diy/app/Main.java index 14d60a7f..6832c6ab 100644 --- a/src/main/java/com/diy/app/Main.java +++ b/src/main/java/com/diy/app/Main.java @@ -6,8 +6,7 @@ public class Main { public static void main(String[] args) { - BeanStorage beanStorage = new BeanStorage(); - beanStorage.getBeans(LectureRepository.class).forEach(System.out::println); + BeanStorage beanStorage = BeanStorage.getInstance(); TomcatWebServer tomcatWebServer = new TomcatWebServer(); tomcatWebServer.start(); } diff --git a/src/main/java/com/diy/app/business/controller/LectureController.java b/src/main/java/com/diy/app/business/controller/LectureController.java index 67438c09..c61c3452 100644 --- a/src/main/java/com/diy/app/business/controller/LectureController.java +++ b/src/main/java/com/diy/app/business/controller/LectureController.java @@ -5,22 +5,30 @@ import com.diy.app.infra.dto.ModelAndView; import com.diy.app.infra.httpSpec.HttpMethod; import com.diy.app.infra.port.Controller; +import com.diy.framework.beans.annotations.Autowired; +import com.diy.framework.beans.annotations.Component; import com.fasterxml.jackson.databind.ObjectMapper; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Map; +@Component public class LectureController implements Controller { + @Autowired private LectureService service; - private ObjectMapper objectMapper; + private final ObjectMapper objectMapper; - public LectureController(LectureService service) { - this.service = service; - this.objectMapper = new ObjectMapper(); + public LectureController() { + objectMapper = new ObjectMapper(); } + // public LectureController(LectureService service) { +// this.service = service; +// this.objectMapper = new ObjectMapper(); +// } + @Override public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse resp) throws Exception { String uri = req.getRequestURI(); diff --git a/src/main/java/com/diy/app/business/repository/LectureRepository.java b/src/main/java/com/diy/app/business/repository/LectureRepository.java index 47dd262a..71f86fcd 100644 --- a/src/main/java/com/diy/app/business/repository/LectureRepository.java +++ b/src/main/java/com/diy/app/business/repository/LectureRepository.java @@ -11,7 +11,6 @@ @Component public class LectureRepository { - static LectureRepository instance; AtomicLong nextId; Map lectureDB; @@ -21,10 +20,6 @@ private LectureRepository() { lectureDB = new ConcurrentHashMap<>(); } - public static LectureRepository getInstance() { - return instance == null ? instance = new LectureRepository() : instance; - } - public List getAll() { return lectureDB.values().stream().toList(); } diff --git a/src/main/java/com/diy/app/business/service/LectureService.java b/src/main/java/com/diy/app/business/service/LectureService.java index 677fa505..d4263b36 100644 --- a/src/main/java/com/diy/app/business/service/LectureService.java +++ b/src/main/java/com/diy/app/business/service/LectureService.java @@ -2,21 +2,16 @@ import com.diy.app.business.domain.Lecture; import com.diy.app.business.repository.LectureRepository; +import com.diy.framework.beans.annotations.Autowired; +import com.diy.framework.beans.annotations.Component; import java.util.List; +@Component public class LectureService { - private static LectureService instance = null; - private static LectureRepository repository = null; - - private LectureService(LectureRepository repository) { - this.repository = repository; - } - - public static LectureService getInstance() { - return instance == null ? instance = new LectureService(LectureRepository.getInstance()) : instance; - } + @Autowired + private static LectureRepository repository; public List getAllLectures() { return repository.getAll(); diff --git a/src/main/java/com/diy/app/infra/servlet/UrlControllerMapper.java b/src/main/java/com/diy/app/infra/servlet/UrlControllerMapper.java index 36d6140d..afb6cfa3 100644 --- a/src/main/java/com/diy/app/infra/servlet/UrlControllerMapper.java +++ b/src/main/java/com/diy/app/infra/servlet/UrlControllerMapper.java @@ -4,6 +4,7 @@ import com.diy.app.business.service.LectureService; import com.diy.app.infra.port.Controller; import com.diy.app.infra.viewRender.ViewResolver; +import com.diy.framework.beans.factory.BeanStorage; import java.util.Comparator; import java.util.Map; @@ -14,7 +15,7 @@ public class UrlControllerMapper { private static UrlControllerMapper instance; public UrlControllerMapper() { - uriToController.put("/lectures", new LectureController(LectureService.getInstance())); + uriToController.put("/lectures", BeanStorage.getInstance().getBeans(LectureController.class).getFirst()); } public Controller findController(String uri) { diff --git a/src/main/java/com/diy/app/infra/viewRender/JspView.java b/src/main/java/com/diy/app/infra/viewRender/JspView.java index f5271d55..075fddc8 100644 --- a/src/main/java/com/diy/app/infra/viewRender/JspView.java +++ b/src/main/java/com/diy/app/infra/viewRender/JspView.java @@ -24,6 +24,9 @@ public void render(final HttpServletRequest req, final HttpServletResponse resp, .getFile().split("resources/")[1]; final RequestDispatcher requestDispatcher = req.getRequestDispatcher(filePath); + for (String key : modelAndView.getModel().keySet()) { + req.setAttribute(key, modelAndView.getModel().get(key)); + } requestDispatcher.forward(req, resp); } diff --git a/src/main/java/com/diy/framework/beans/annotations/Autowired.java b/src/main/java/com/diy/framework/beans/annotations/Autowired.java new file mode 100644 index 00000000..b52db5f8 --- /dev/null +++ b/src/main/java/com/diy/framework/beans/annotations/Autowired.java @@ -0,0 +1,11 @@ +package com.diy.framework.beans.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Autowired { +} diff --git a/src/main/java/com/diy/framework/beans/factory/BeanScanner.java b/src/main/java/com/diy/framework/beans/factory/BeanScanner.java index fd2c6b2b..937aae00 100644 --- a/src/main/java/com/diy/framework/beans/factory/BeanScanner.java +++ b/src/main/java/com/diy/framework/beans/factory/BeanScanner.java @@ -1,8 +1,13 @@ package com.diy.framework.beans.factory; +import com.diy.framework.beans.annotations.Autowired; import org.reflections.Reflections; import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -20,4 +25,8 @@ public Set> scanClassesTypeAnnotatedWith(final Class (!type.isAnnotation() && !type.isInterface())) .collect(Collectors.toSet()); } + + public Set scanField(final Class clazz) { + return Arrays.stream(clazz.getDeclaredFields()).filter(field -> Objects.nonNull(field.getDeclaredAnnotation(Autowired.class))).collect(Collectors.toSet()); + } } \ No newline at end of file diff --git a/src/main/java/com/diy/framework/beans/factory/BeanStorage.java b/src/main/java/com/diy/framework/beans/factory/BeanStorage.java index a51d4737..6d7600b6 100644 --- a/src/main/java/com/diy/framework/beans/factory/BeanStorage.java +++ b/src/main/java/com/diy/framework/beans/factory/BeanStorage.java @@ -3,24 +3,37 @@ import com.diy.framework.beans.annotations.Component; import java.lang.reflect.Constructor; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; -import java.util.List; -import java.util.Set; +import java.util.*; public class BeanStorage { + private static BeanStorage instance; + private final List beans; + private final BeanScanner bc = new BeanScanner("com.diy.app"); - public BeanStorage() { - BeanScanner bc = new BeanScanner("com.diy.app"); + private BeanStorage() { Set> classes = bc.scanClassesTypeAnnotatedWith(Component.class); - beans = classes.stream().map(clazz -> { + beans = getBeans(classes); + setFields(getFields()); + } + + public static BeanStorage getInstance() { + if (Objects.isNull(instance)) instance = new BeanStorage(); + return instance; + } + + private static List getBeans(Set> classes) { + return classes.stream().map(clazz -> { Constructor constructor = null; try { constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); return constructor.newInstance(); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + NoSuchMethodException e) { throw new RuntimeException(e); } finally { if (constructor != null) { @@ -36,4 +49,41 @@ public List getBeans(final Class classType) { .map(classType::cast) .toList(); } + + public Map, Set> getFields() { + Map, Set> classToField = new HashMap<>(); + for (Class clazz : beans.stream().map(Object::getClass).toList()) { + classToField.put(clazz, bc.scanField(clazz)); + } + return classToField; + } + + public void setFields(Map, Set> classToFields) { + for (Object bean : beans) { + for (Field field : classToFields.get(bean.getClass())) { + Object target = null; + for (Object targetBean : beans) { + if (!field.getType().isInstance(targetBean)) continue; + if (Objects.nonNull(target)) throw new IllegalArgumentException("2개 이상의 빈이 존재"); + target = targetBean; + } + if (Objects.isNull(target)) throw new IllegalArgumentException("존재하지 않는 빈" + field.getName()); + try { + field.setAccessible(true); + field.set(bean, target); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } finally { + field.setAccessible(false); + } + } + } + } + + // 그냥 자기 필드에 있는 애들 쭉 순회하면서 자기 자신으로 돌아오는지 확인 + // 그러면서 해당 인스턴스가 아닌 애들이 존재하면 탈락 + // 순환 참조 +// public boolean checkValidation(Map, Set> classMap) { +// Set> +// } } diff --git a/src/main/resources/lecture-list.html b/src/main/resources/lecture-list1.html similarity index 100% rename from src/main/resources/lecture-list.html rename to src/main/resources/lecture-list1.html From f1dcf506bb4525fcf7fefb0d8d3d516017ede79b Mon Sep 17 00:00:00 2001 From: yuntasha Date: Tue, 5 May 2026 23:29:36 +0900 Subject: [PATCH 21/22] =?UTF-8?q?:sparkles:=20=EC=9C=84=EC=83=81=EC=A0=95?= =?UTF-8?q?=EB=A0=AC=EC=9D=84=20=ED=86=B5=ED=95=9C=20=EB=A7=8C=EB=93=9C?= =?UTF-8?q?=EB=8A=94=20=EC=88=9C=EC=84=9C=20=EC=A1=B0=EC=A0=95=20=EB=B0=8F?= =?UTF-8?q?=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/beans/factory/BeanStorage.java | 98 +++++++++++++------ 1 file changed, 66 insertions(+), 32 deletions(-) diff --git a/src/main/java/com/diy/framework/beans/factory/BeanStorage.java b/src/main/java/com/diy/framework/beans/factory/BeanStorage.java index 6d7600b6..7dd300d4 100644 --- a/src/main/java/com/diy/framework/beans/factory/BeanStorage.java +++ b/src/main/java/com/diy/framework/beans/factory/BeanStorage.java @@ -11,13 +11,14 @@ public class BeanStorage { private static BeanStorage instance; - private final List beans; + private final List beans; private final BeanScanner bc = new BeanScanner("com.diy.app"); private BeanStorage() { Set> classes = bc.scanClassesTypeAnnotatedWith(Component.class); - beans = getBeans(classes); - setFields(getFields()); + Map, Set> classToFields = getFields(classes); + List> sortedClasses = topologicalSort(classToFields); + beans = makeBeans(sortedClasses, classToFields); } public static BeanStorage getInstance() { @@ -25,13 +26,17 @@ public static BeanStorage getInstance() { return instance; } - private static List getBeans(Set> classes) { - return classes.stream().map(clazz -> { + private static List makeBeans(List> classes, Map, Set> classToFields) { + List results = new ArrayList<>(); + + for (Class clazz : classes) { Constructor constructor = null; try { constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); - return constructor.newInstance(); + Object bean = constructor.newInstance(); + setFields(bean, results, classToFields); + results.add(bean); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { throw new RuntimeException(e); @@ -40,7 +45,9 @@ private static List getBeans(Set> classes) { constructor.setAccessible(false); } } - }).toList(); + } + + return results; } public List getBeans(final Class classType) { @@ -50,40 +57,67 @@ public List getBeans(final Class classType) { .toList(); } - public Map, Set> getFields() { + public Map, Set> getFields(Set> classes) { Map, Set> classToField = new HashMap<>(); - for (Class clazz : beans.stream().map(Object::getClass).toList()) { + for (Class clazz : classes) { classToField.put(clazz, bc.scanField(clazz)); } return classToField; } - public void setFields(Map, Set> classToFields) { - for (Object bean : beans) { - for (Field field : classToFields.get(bean.getClass())) { - Object target = null; - for (Object targetBean : beans) { - if (!field.getType().isInstance(targetBean)) continue; - if (Objects.nonNull(target)) throw new IllegalArgumentException("2개 이상의 빈이 존재"); - target = targetBean; - } - if (Objects.isNull(target)) throw new IllegalArgumentException("존재하지 않는 빈" + field.getName()); - try { - field.setAccessible(true); - field.set(bean, target); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } finally { - field.setAccessible(false); + public static void setFields(Object target, List nowBeanList, Map, Set> classToFields) { + for (Field field : classToFields.get(target.getClass())) { + Object fieldTarget = null; + for (Object targetBean : nowBeanList) { + if (!field.getType().isInstance(targetBean)) continue; + if (Objects.nonNull(fieldTarget)) throw new IllegalArgumentException("2개 이상의 빈이 존재 : " + field.getType().getName()); + fieldTarget = targetBean; + } + if (Objects.isNull(fieldTarget)) throw new IllegalArgumentException("존재하지 않는 빈 : " + field.getType().getName()); + try { + field.setAccessible(true); + field.set(target, fieldTarget); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } finally { + field.setAccessible(false); + } + } + } + + public List> topologicalSort(Map, Set> classMap) { + Map, Set>> map = copyMap(classMap); + + List> result = new ArrayList<>(); + Set> visited = new HashSet<>(); + + while (visited.size() < map.size()) { + List> canMake = new ArrayList<>(); + for (Map.Entry, Set>> entry : map.entrySet()) { + if (visited.contains(entry.getKey())) continue; + if (visited.containsAll(entry.getValue())) { + canMake.add(entry.getKey()); } } + if (canMake.isEmpty()) { + throw new IllegalArgumentException("순환 참조 혹은 존재하지 않는 빈을 필드로하는 것이 존재합니다"); + } + visited.addAll(canMake); + result.addAll(canMake); } + + return result; } - // 그냥 자기 필드에 있는 애들 쭉 순회하면서 자기 자신으로 돌아오는지 확인 - // 그러면서 해당 인스턴스가 아닌 애들이 존재하면 탈락 - // 순환 참조 -// public boolean checkValidation(Map, Set> classMap) { -// Set> -// } + private static Map, Set>> copyMap(Map, Set> classMap) { + Map, Set>> map = new HashMap<>(); + for (Map.Entry, Set> entry : classMap.entrySet()) { + Set> fieldTypes = new HashSet<>(); + for (Field field : entry.getValue()) { + fieldTypes.add(field.getType()); + } + map.put(entry.getKey(), fieldTypes); + } + return map; + } } From d0eacac30bb92d1c60c7b8d4776f4248cffc389f Mon Sep 17 00:00:00 2001 From: yuntasha Date: Sat, 16 May 2026 10:43:42 +0900 Subject: [PATCH 22/22] =?UTF-8?q?:recycle:=20Autowired=20=EC=9A=94?= =?UTF-8?q?=EA=B5=AC=EC=82=AC=ED=95=AD=EC=97=90=20=EB=A7=9E=EA=B2=8C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/LectureController.java | 5 +- .../app/business/service/LectureService.java | 6 +- .../infra/servlet/UrlControllerMapper.java | 2 +- .../beans/annotations/Autowired.java | 2 +- .../framework/beans/factory/BeanScanner.java | 17 ++- .../framework/beans/factory/BeanStorage.java | 120 +++++------------- 6 files changed, 52 insertions(+), 100 deletions(-) diff --git a/src/main/java/com/diy/app/business/controller/LectureController.java b/src/main/java/com/diy/app/business/controller/LectureController.java index c61c3452..494e45a9 100644 --- a/src/main/java/com/diy/app/business/controller/LectureController.java +++ b/src/main/java/com/diy/app/business/controller/LectureController.java @@ -16,12 +16,13 @@ @Component public class LectureController implements Controller { - @Autowired private LectureService service; private final ObjectMapper objectMapper; - public LectureController() { + @Autowired + public LectureController(LectureService service) { objectMapper = new ObjectMapper(); + this.service = service; } // public LectureController(LectureService service) { diff --git a/src/main/java/com/diy/app/business/service/LectureService.java b/src/main/java/com/diy/app/business/service/LectureService.java index d4263b36..c7281c0e 100644 --- a/src/main/java/com/diy/app/business/service/LectureService.java +++ b/src/main/java/com/diy/app/business/service/LectureService.java @@ -10,8 +10,12 @@ @Component public class LectureService { + private final LectureRepository repository; + @Autowired - private static LectureRepository repository; + public LectureService(LectureRepository repository) { + this.repository = repository; + } public List getAllLectures() { return repository.getAll(); diff --git a/src/main/java/com/diy/app/infra/servlet/UrlControllerMapper.java b/src/main/java/com/diy/app/infra/servlet/UrlControllerMapper.java index afb6cfa3..dd7b2b36 100644 --- a/src/main/java/com/diy/app/infra/servlet/UrlControllerMapper.java +++ b/src/main/java/com/diy/app/infra/servlet/UrlControllerMapper.java @@ -15,7 +15,7 @@ public class UrlControllerMapper { private static UrlControllerMapper instance; public UrlControllerMapper() { - uriToController.put("/lectures", BeanStorage.getInstance().getBeans(LectureController.class).getFirst()); + uriToController.put("/lectures", BeanStorage.getInstance().getBean(LectureController.class).get()); } public Controller findController(String uri) { diff --git a/src/main/java/com/diy/framework/beans/annotations/Autowired.java b/src/main/java/com/diy/framework/beans/annotations/Autowired.java index b52db5f8..3d093de8 100644 --- a/src/main/java/com/diy/framework/beans/annotations/Autowired.java +++ b/src/main/java/com/diy/framework/beans/annotations/Autowired.java @@ -5,7 +5,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@Target(ElementType.FIELD) +@Target(ElementType.CONSTRUCTOR) @Retention(RetentionPolicy.RUNTIME) public @interface Autowired { } diff --git a/src/main/java/com/diy/framework/beans/factory/BeanScanner.java b/src/main/java/com/diy/framework/beans/factory/BeanScanner.java index 937aae00..c4a20479 100644 --- a/src/main/java/com/diy/framework/beans/factory/BeanScanner.java +++ b/src/main/java/com/diy/framework/beans/factory/BeanScanner.java @@ -4,11 +4,8 @@ import org.reflections.Reflections; import java.lang.annotation.Annotation; -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.Set; +import java.lang.reflect.Constructor; +import java.util.*; import java.util.stream.Collectors; public class BeanScanner { @@ -26,7 +23,13 @@ public Set> scanClassesTypeAnnotatedWith(final Class scanField(final Class clazz) { - return Arrays.stream(clazz.getDeclaredFields()).filter(field -> Objects.nonNull(field.getDeclaredAnnotation(Autowired.class))).collect(Collectors.toSet()); + public Constructor scanConstructor(final Class clazz) throws NoSuchMethodException { + Optional> autowired = Arrays.stream(clazz.getDeclaredConstructors()) + .filter(constructor -> Objects.nonNull(constructor.getDeclaredAnnotation(Autowired.class))) + .findAny(); + if (autowired.isPresent()) { + return autowired.get(); + } + return clazz.getDeclaredConstructor(); } } \ No newline at end of file diff --git a/src/main/java/com/diy/framework/beans/factory/BeanStorage.java b/src/main/java/com/diy/framework/beans/factory/BeanStorage.java index 7dd300d4..90582de2 100644 --- a/src/main/java/com/diy/framework/beans/factory/BeanStorage.java +++ b/src/main/java/com/diy/framework/beans/factory/BeanStorage.java @@ -3,7 +3,6 @@ import com.diy.framework.beans.annotations.Component; import java.lang.reflect.Constructor; -import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.*; @@ -11,14 +10,12 @@ public class BeanStorage { private static BeanStorage instance; - private final List beans; + private final Map beans = new HashMap<>(); private final BeanScanner bc = new BeanScanner("com.diy.app"); private BeanStorage() { Set> classes = bc.scanClassesTypeAnnotatedWith(Component.class); - Map, Set> classToFields = getFields(classes); - List> sortedClasses = topologicalSort(classToFields); - beans = makeBeans(sortedClasses, classToFields); + makeBeans(classes); } public static BeanStorage getInstance() { @@ -26,98 +23,45 @@ public static BeanStorage getInstance() { return instance; } - private static List makeBeans(List> classes, Map, Set> classToFields) { - List results = new ArrayList<>(); - - for (Class clazz : classes) { - Constructor constructor = null; - try { - constructor = clazz.getDeclaredConstructor(); - constructor.setAccessible(true); - Object bean = constructor.newInstance(); - setFields(bean, results, classToFields); - results.add(bean); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException | - NoSuchMethodException e) { - throw new RuntimeException(e); - } finally { - if (constructor != null) { - constructor.setAccessible(false); - } - } - } - - return results; - } - - public List getBeans(final Class classType) { - return beans.stream() - .filter(classType::isInstance) - .map(classType::cast) - .toList(); - } - - public Map, Set> getFields(Set> classes) { - Map, Set> classToField = new HashMap<>(); + private void makeBeans(Set> classes) { for (Class clazz : classes) { - classToField.put(clazz, bc.scanField(clazz)); + makeBean(clazz); } - return classToField; } - public static void setFields(Object target, List nowBeanList, Map, Set> classToFields) { - for (Field field : classToFields.get(target.getClass())) { - Object fieldTarget = null; - for (Object targetBean : nowBeanList) { - if (!field.getType().isInstance(targetBean)) continue; - if (Objects.nonNull(fieldTarget)) throw new IllegalArgumentException("2개 이상의 빈이 존재 : " + field.getType().getName()); - fieldTarget = targetBean; - } - if (Objects.isNull(fieldTarget)) throw new IllegalArgumentException("존재하지 않는 빈 : " + field.getType().getName()); - try { - field.setAccessible(true); - field.set(target, fieldTarget); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } finally { - field.setAccessible(false); + private Object makeBean(Class clazz) { + Constructor constructor = null; + try { + constructor = bc.scanConstructor(clazz); + + Object[] params = Arrays.stream(constructor.getParameterTypes()) + .map(paramType -> findBean(paramType).orElseGet(() -> makeBean(paramType))) + .toArray(); + + constructor.setAccessible(true); + Object bean = constructor.newInstance(params); + beans.put(clazz.getName(), bean); + return bean; + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + NoSuchMethodException e) { + throw new RuntimeException(e); + } finally { + if (constructor != null) { + constructor.setAccessible(false); } } } - public List> topologicalSort(Map, Set> classMap) { - Map, Set>> map = copyMap(classMap); - - List> result = new ArrayList<>(); - Set> visited = new HashSet<>(); - - while (visited.size() < map.size()) { - List> canMake = new ArrayList<>(); - for (Map.Entry, Set>> entry : map.entrySet()) { - if (visited.contains(entry.getKey())) continue; - if (visited.containsAll(entry.getValue())) { - canMake.add(entry.getKey()); - } - } - if (canMake.isEmpty()) { - throw new IllegalArgumentException("순환 참조 혹은 존재하지 않는 빈을 필드로하는 것이 존재합니다"); - } - visited.addAll(canMake); - result.addAll(canMake); - } - - return result; + private Optional findBean(final Class classType) { + return beans.values().stream() + .filter(classType::isInstance) + .findAny(); } - private static Map, Set>> copyMap(Map, Set> classMap) { - Map, Set>> map = new HashMap<>(); - for (Map.Entry, Set> entry : classMap.entrySet()) { - Set> fieldTypes = new HashSet<>(); - for (Field field : entry.getValue()) { - fieldTypes.add(field.getType()); - } - map.put(entry.getKey(), fieldTypes); - } - return map; + public Optional getBean(final Class classType) { + return beans.values().stream() + .filter(classType::isInstance) + .map(classType::cast) + .findAny(); } }