-
Notifications
You must be signed in to change notification settings - Fork 43
[3주차] 관심사 분리 1 - 신재희 #74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: yuntasha
Are you sure you want to change the base?
Changes from 22 commits
9892844
ce97712
dc5f9a4
0eea9eb
026cfae
fc518f2
670a24d
a68fe59
4402540
d69fdfc
9104d4e
081b01b
ba30f34
8b136ae
c482f02
aeacaa9
08b15a4
2095347
da39f2e
39351a7
f1dcf50
cc636ac
d0eacac
7bb520a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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.TYPE) | ||
| @Retention(RetentionPolicy.RUNTIME) | ||
| public @interface Component { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| 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; | ||
|
|
||
| public class BeanScanner { | ||
|
|
||
| private final Reflections reflections; | ||
|
|
||
| public BeanScanner(final String... basePackages) { | ||
| reflections = new Reflections(basePackages); | ||
| } | ||
|
|
||
| public Set<Class<?>> scanClassesTypeAnnotatedWith(final Class<? extends Annotation> annotation) { | ||
| return reflections.getTypesAnnotatedWith(annotation) | ||
| .stream() | ||
| .filter(type -> (!type.isAnnotation() && !type.isInterface())) | ||
| .collect(Collectors.toSet()); | ||
| } | ||
|
|
||
| public Set<Field> scanField(final Class<?> clazz) { | ||
| return Arrays.stream(clazz.getDeclaredFields()).filter(field -> Objects.nonNull(field.getDeclaredAnnotation(Autowired.class))).collect(Collectors.toSet()); | ||
| } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 메서드가 |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| package com.diy.framework.beans.factory; | ||
|
|
||
| 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.*; | ||
|
|
||
| public class BeanStorage { | ||
|
|
||
| private static BeanStorage instance; | ||
|
|
||
| private final List<Object> beans; | ||
| private final BeanScanner bc = new BeanScanner("com.diy.app"); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 음.. 이걸 하드코딩 해도 괜찮을까요? |
||
|
|
||
| private BeanStorage() { | ||
| Set<Class<?>> classes = bc.scanClassesTypeAnnotatedWith(Component.class); | ||
| Map<Class<?>, Set<Field>> classToFields = getFields(classes); | ||
| List<Class<?>> sortedClasses = topologicalSort(classToFields); | ||
| beans = makeBeans(sortedClasses, classToFields); | ||
| } | ||
|
|
||
| public static BeanStorage getInstance() { | ||
| if (Objects.isNull(instance)) instance = new BeanStorage(); | ||
| return instance; | ||
| } | ||
|
|
||
| private static List<Object> makeBeans(List<Class<?>> classes, Map<Class<?>, Set<Field>> classToFields) { | ||
| List<Object> 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 <T> List<T> getBeans(final Class<T> classType) { | ||
| return beans.stream() | ||
| .filter(classType::isInstance) | ||
| .map(classType::cast) | ||
| .toList(); | ||
| } | ||
|
|
||
| public Map<Class<?>, Set<Field>> getFields(Set<Class<?>> classes) { | ||
| Map<Class<?>, Set<Field>> classToField = new HashMap<>(); | ||
| for (Class<?> clazz : classes) { | ||
| classToField.put(clazz, bc.scanField(clazz)); | ||
| } | ||
| return classToField; | ||
| } | ||
|
|
||
| public static void setFields(Object target, List<Object> nowBeanList, Map<Class<?>, Set<Field>> 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); | ||
| } | ||
| } | ||
| } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 음 이 메서드에 대해서는 궁금한게 많아요. |
||
|
|
||
| public List<Class<?>> topologicalSort(Map<Class<?>, Set<Field>> classMap) { | ||
| Map<Class<?>, Set<Class<?>>> map = copyMap(classMap); | ||
|
|
||
| List<Class<?>> result = new ArrayList<>(); | ||
| Set<Class<?>> visited = new HashSet<>(); | ||
|
|
||
| while (visited.size() < map.size()) { | ||
| List<Class<?>> canMake = new ArrayList<>(); | ||
| for (Map.Entry<Class<?>, Set<Class<?>>> 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 static Map<Class<?>, Set<Class<?>>> copyMap(Map<Class<?>, Set<Field>> classMap) { | ||
| Map<Class<?>, Set<Class<?>>> map = new HashMap<>(); | ||
| for (Map.Entry<Class<?>, Set<Field>> entry : classMap.entrySet()) { | ||
| Set<Class<?>> fieldTypes = new HashSet<>(); | ||
| for (Field field : entry.getValue()) { | ||
| fieldTypes.add(field.getType()); | ||
| } | ||
| map.put(entry.getKey(), fieldTypes); | ||
| } | ||
| return map; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package study.reflection; | ||
|
|
||
| public class Car { | ||
|
|
||
| private String name; | ||
| private int price; | ||
|
|
||
| public String getName() { | ||
| return name; | ||
| } | ||
|
|
||
| public int getPrice() { | ||
| return 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; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package study.reflection; | ||
|
|
||
| import java.lang.annotation.Retention; | ||
| import java.lang.annotation.RetentionPolicy; | ||
|
|
||
| @Retention(RetentionPolicy.RUNTIME) | ||
| public @interface PrintView { | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오호 BeanStorage 를 싱글톤으로 활용해서 controller 매핑을 하는군요 ㅎㅎ