-
Notifications
You must be signed in to change notification settings - Fork 43
[3주차] 관심사 분리 1 : 리플렉션 및 빈 주입 구현 #82
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
Open
MINJOOOONG
wants to merge
23
commits into
Loopers-play-dev-lab:MINJOOOOONG
Choose a base branch
from
MINJOOOONG:week-3
base: MINJOOOOONG
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
560f9e8
init
jude-loopers 5a4f362
1주차 자바 웹 개발 기초
jude-loopers 3cd3989
2주차 자바 웹 요청 추상화
jude-loopers 87473be
Merge pull request #65 from Loopers-play-dev-lab/template-main
jude-loopers a15d543
2주차 자바 웹 요청 추상화
jude-loopers 7fb5780
Lecture 리플렉션 실습 추가
MINJOOOONG 2facee0
MethodOrder 애너테이션 추가
MINJOOOONG 8c50963
애너테이션으로 메서드 찾기 실습 추가
MINJOOOONG 3f14fb7
MethodOrder 애너테이션 값 조회 실습 추가
MINJOOOONG bdbf604
Component 애너테이션 추가
MINJOOOONG f70c8e0
Reflections 의존성 추가
MINJOOOONG 789a421
Component 붙은 클래스 스캔 실습 추가
MINJOOOONG 77d3f47
Component 클래스로 빈 객체 생성 실습 추가
MINJOOOONG d198014
BeanFactory 빈 저장 조회 실습 추가
MINJOOOONG 4c345bf
BeanFactory 빈 초기화 실습 추가
MINJOOOONG cc8d235
Autowired 애너테이션 추가
MINJOOOONG 18b574c
Autowired 생성자 찾기 실습 추가
MINJOOOONG 9a2ffd9
Autowired 생성자로 빈 주입 실습 추가
MINJOOOONG 36f092f
LectureRepository 추가
MINJOOOONG 8b21496
LectureService에 Repository 주입 적용
MINJOOOONG bdb6a69
LectureController에 Service 주입 적용
MINJOOOONG 1c8c0f9
Lecture를 빈 대상에서 제외
MINJOOOONG ed81986
Bean 생성 순서 문제 수정
MINJOOOONG File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -42,3 +42,6 @@ bin/ | |
|
|
||
| ### Mac OS ### | ||
| .DS_Store\ntomcat.8080/ | ||
|
|
||
| ## tomcat | ||
| **/tomcat.8080 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package com.diy.app; | ||
|
|
||
| import com.diy.framework.web.annotation.MethodOrder; | ||
|
|
||
| import java.math.BigDecimal; | ||
|
|
||
|
|
||
| public class Lecture { | ||
|
|
||
| private Long id; | ||
| private String name; | ||
| private BigDecimal price; | ||
|
|
||
| public Lecture() { | ||
| } | ||
|
|
||
| public Long getId() { | ||
| return id; | ||
| } | ||
|
|
||
| public String getName() { | ||
| return name; | ||
| } | ||
|
|
||
| public BigDecimal getPrice() { | ||
| return price; | ||
| } | ||
|
|
||
| public void setId(Long id) { | ||
| this.id = id; | ||
| } | ||
|
|
||
| @MethodOrder(1) | ||
| private String getDisplayName() { | ||
| return "lecture"; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| package com.diy.app; | ||
|
|
||
| import com.diy.framework.web.annotation.Component; | ||
| import com.diy.framework.web.beans.factory.BeanFactory; | ||
| import com.diy.framework.web.beans.factory.BeanScanner; | ||
| import com.diy.framework.web.mvc.Controller; | ||
| import com.diy.framework.web.server.TomcatWebServer; | ||
| import com.diy.framework.web.servlet.DispatcherServlet; | ||
|
|
||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
| import java.util.Set; | ||
|
|
||
| public class LectureApplication { | ||
|
|
||
| public static void main(String[] args) throws Exception { | ||
| final BeanScanner beanScanner = new BeanScanner("com.diy.app"); | ||
| final Set<Class<?>> beanClasses = beanScanner.scanClassesTypeAnnotatedWith(Component.class); | ||
|
|
||
| final BeanFactory beanFactory = new BeanFactory(); | ||
| beanFactory.initialize(beanClasses); | ||
|
|
||
| final LectureService lectureService = (LectureService) beanFactory.getBean(LectureService.class); | ||
|
|
||
| final Map<String, Controller> controllerMapping = new HashMap<>(); | ||
| controllerMapping.put("/lectures", new LectureController(lectureService)); | ||
|
|
||
| final DispatcherServlet servlet = new DispatcherServlet(controllerMapping); | ||
| final TomcatWebServer tomcatWebServer = new TomcatWebServer(servlet); | ||
| tomcatWebServer.start(); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| package com.diy.app; | ||
|
|
||
| import com.diy.framework.web.mvc.Controller; | ||
| import com.diy.framework.web.mvc.view.ModelAndView; | ||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
|
|
||
| import javax.servlet.ServletException; | ||
| import javax.servlet.http.HttpServletRequest; | ||
| import javax.servlet.http.HttpServletResponse; | ||
| import java.io.IOException; | ||
| import java.nio.charset.StandardCharsets; | ||
| import java.util.Collection; | ||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
|
|
||
| public class LectureController implements Controller { | ||
|
|
||
| private final LectureService lectureService; | ||
|
|
||
| public LectureController(final LectureService lectureService) { | ||
| this.lectureService = lectureService; | ||
| } | ||
|
|
||
| @Override | ||
| public ModelAndView handleRequest(final HttpServletRequest request, final HttpServletResponse response) throws Exception { | ||
| if ("POST".equals(request.getMethod())) { | ||
| return doPost(request, response); | ||
| } else if ("GET".equals(request.getMethod())) { | ||
| return doGet(request, response); | ||
| } | ||
|
|
||
| throw new RuntimeException("404 Not Found"); | ||
| } | ||
|
|
||
| private ModelAndView doPost(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { | ||
| final byte[] bodyBytes = req.getInputStream().readAllBytes(); | ||
| final String body = new String(bodyBytes, StandardCharsets.UTF_8); | ||
|
|
||
| final Lecture lecture = new ObjectMapper().readValue(body, Lecture.class); | ||
| lectureService.save(lecture); | ||
|
|
||
| return new ModelAndView("redirect:/lectures"); | ||
| } | ||
|
|
||
| private ModelAndView doGet(final HttpServletRequest req, final HttpServletResponse resp) throws Exception { | ||
| final Collection<Lecture> lectures = lectureService.findAll(); | ||
| final Map<String, Object> model = new HashMap<>(); | ||
| final Object lectureModels = lectures.stream() | ||
| .map(lecture -> Map.of("id", lecture.getId(), "name", lecture.getName(), "price", lecture.getPrice())) | ||
| .toList(); | ||
| model.put("lectures", lectureModels); | ||
|
|
||
| return new ModelAndView("lecture-list", model); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package com.diy.app; | ||
|
|
||
| import com.diy.framework.web.annotation.Component; | ||
|
|
||
| import java.util.Collection; | ||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
|
|
||
| @Component | ||
| public class LectureRepository { | ||
|
|
||
| // 메모리에 Lecture 데이터를 저장하는 임시 저장소 | ||
| private final Map<Long, Lecture> lectures = new HashMap<>(); | ||
|
|
||
| public Lecture save(final Lecture lecture) { | ||
| final long id = lectures.size(); | ||
| lectures.put(id, lecture); | ||
| lecture.setId(id); | ||
| return lecture; | ||
| } | ||
|
|
||
| public Collection<Lecture> findAll() { | ||
| return lectures.values(); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package com.diy.app; | ||
|
|
||
| import com.diy.framework.web.annotation.Autowired; | ||
| import com.diy.framework.web.annotation.Component; | ||
|
|
||
| import java.util.Collection; | ||
|
|
||
| @Component | ||
| public class LectureService { | ||
|
|
||
| private final LectureRepository lectureRepository; | ||
|
|
||
| @Autowired | ||
| public LectureService(final LectureRepository lectureRepository) { | ||
| this.lectureRepository = lectureRepository; | ||
| } | ||
|
|
||
| public Lecture save(final Lecture lecture) { | ||
| return lectureRepository.save(lecture); | ||
| } | ||
|
|
||
| public Collection<Lecture> findAll() { | ||
| return lectureRepository.findAll(); | ||
| } | ||
| } |
This file was deleted.
Oops, something went wrong.
9 changes: 9 additions & 0 deletions
9
src/main/java/com/diy/framework/web/annotation/Autowired.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| package com.diy.framework.web.annotation; | ||
|
|
||
| import java.lang.annotation.Retention; | ||
| import java.lang.annotation.RetentionPolicy; | ||
|
|
||
| // 의존성 주입에 사용할 생성자를 표시하는 애너테이션 | ||
| @Retention(RetentionPolicy.RUNTIME) | ||
| public @interface Autowired { | ||
| } |
9 changes: 9 additions & 0 deletions
9
src/main/java/com/diy/framework/web/annotation/Component.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| package com.diy.framework.web.annotation; | ||
|
|
||
| import java.lang.annotation.Retention; | ||
| import java.lang.annotation.RetentionPolicy; | ||
|
|
||
| // 런타임에 bean으로 등록할 클래스를 구분하기 위한 애너테이션 | ||
| @Retention(RetentionPolicy.RUNTIME) | ||
| public @interface Component { | ||
| } |
12 changes: 12 additions & 0 deletions
12
src/main/java/com/diy/framework/web/annotation/MethodOrder.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package com.diy.framework.web.annotation; | ||
|
|
||
| import java.lang.annotation.Retention; | ||
| import java.lang.annotation.RetentionPolicy; | ||
|
|
||
| // 실행 중에도 리플렉션으로 읽을 수 있게 설정 | ||
| @Retention(RetentionPolicy.RUNTIME) | ||
| public @interface MethodOrder { | ||
|
|
||
| // @MethodOrder(1) 처럼 순서 값을 넣기 위한 값 | ||
| int value(); | ||
| } |
85 changes: 85 additions & 0 deletions
85
src/main/java/com/diy/framework/web/beans/factory/BeanFactory.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| package com.diy.framework.web.beans.factory; | ||
|
|
||
| import com.diy.framework.web.annotation.Autowired; | ||
|
|
||
| import java.lang.reflect.Constructor; | ||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
| import java.util.Set; | ||
|
|
||
| public class BeanFactory { | ||
|
|
||
| private final Map<Class<?>, Object> beans = new HashMap<>(); | ||
| private Set<Class<?>> beanClasses; | ||
|
|
||
| public void addBean(final Class<?> clazz, final Object bean) { | ||
| // 생성한 빈 저장 | ||
| beans.put(clazz, bean); | ||
| } | ||
|
|
||
| public void initialize(final Set<Class<?>> beanClasses) throws Exception { | ||
| this.beanClasses = beanClasses; | ||
|
|
||
| // 빈 클래스들로 객체 생성 후 저장 | ||
| for (Class<?> beanClass : beanClasses) { | ||
| if (getBean(beanClass) == null) { | ||
| Object bean = createBean(beanClass); | ||
| addBean(beanClass, bean); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| public Object getBean(final Class<?> clazz) { | ||
| // 저장된 빈 조회 | ||
| return beans.get(clazz); | ||
| } | ||
|
|
||
| private Object createBean(final Class<?> beanClass) throws Exception { | ||
| // 사용할 생성자 찾기 | ||
| Constructor<?> constructor = findConstructor(beanClass); | ||
|
|
||
| // 생성자 파라미터 타입 가져오기 | ||
| Class<?>[] parameterTypes = constructor.getParameterTypes(); | ||
|
|
||
| // 파라미터 없는 생성자면 바로 객체 생성 | ||
| if (parameterTypes.length == 0) { | ||
| return constructor.newInstance(); | ||
| } | ||
|
|
||
| // 생성자 파라미터에 넣을 빈들 찾기 | ||
| Object[] parameters = new Object[parameterTypes.length]; | ||
| for (int i = 0; i < parameterTypes.length; i++) { | ||
| parameters[i] = getOrCreateBean(parameterTypes[i]); | ||
| } | ||
|
|
||
| // 찾은 빈들을 생성자에 넣어서 객체 생성 | ||
| return constructor.newInstance(parameters); | ||
| } | ||
|
|
||
| private Object getOrCreateBean(final Class<?> beanClass) throws Exception { | ||
| Object bean = getBean(beanClass); | ||
| if (bean != null) { | ||
| return bean; | ||
| } | ||
|
|
||
| if (!beanClasses.contains(beanClass)) { | ||
| throw new IllegalArgumentException("빈 클래스를 찾을 수 없습니다: " + beanClass.getName()); | ||
| } | ||
|
|
||
| bean = createBean(beanClass); | ||
| addBean(beanClass, bean); | ||
| return bean; | ||
| } | ||
|
|
||
| private Constructor<?> findConstructor(final Class<?> beanClass) throws NoSuchMethodException { | ||
| // Autowired 붙은 생성자 찾기 | ||
| for (Constructor<?> constructor : beanClass.getDeclaredConstructors()) { | ||
| if (constructor.isAnnotationPresent(Autowired.class)) { | ||
| return constructor; | ||
| } | ||
| } | ||
|
|
||
| // 없으면 기본 생성자 사용 | ||
| return beanClass.getDeclaredConstructor(); | ||
| } | ||
| } | ||
25 changes: 25 additions & 0 deletions
25
src/main/java/com/diy/framework/web/beans/factory/BeanScanner.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package com.diy.framework.web.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<Class<?>> scanClassesTypeAnnotatedWith(final Class<? extends Annotation> annotation) { | ||
| // 특정 애너테이션이 붙은 클래스 찾기 | ||
| return reflections.getTypesAnnotatedWith(annotation) | ||
| .stream() | ||
| .filter(type -> (!type.isAnnotation() && !type.isInterface())) | ||
| .collect(Collectors.toSet()); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package com.diy.framework.web.mvc; | ||
|
|
||
| import com.diy.framework.web.mvc.view.ModelAndView; | ||
|
|
||
| import javax.servlet.http.HttpServletRequest; | ||
| import javax.servlet.http.HttpServletResponse; | ||
|
|
||
| @FunctionalInterface | ||
| public interface Controller { | ||
| ModelAndView handleRequest(final HttpServletRequest request, final HttpServletResponse response) throws Exception; | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
오 빈 생성 대상인지 검증! 👍🏻