diff --git a/app/src/main/java/com/techcourse/ControllerInterfaceHandlerExecutor.java b/app/src/main/java/com/techcourse/ControllerInterfaceHandlerExecutor.java deleted file mode 100644 index c214d890b8..0000000000 --- a/app/src/main/java/com/techcourse/ControllerInterfaceHandlerExecutor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.techcourse; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import webmvc.org.springframework.web.servlet.ModelAndView; -import webmvc.org.springframework.web.servlet.mvc.asis.Controller; -import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerExecutor; -import webmvc.org.springframework.web.servlet.view.JspView; - -public class ControllerInterfaceHandlerExecutor implements HandlerExecutor { - - @Override - public boolean executable(Object handler) { - return handler instanceof Controller; - } - - @Override - public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) - throws Exception { - String viewName = ((Controller) handler).execute(request, response); - return new ModelAndView(new JspView(viewName)); - } -} diff --git a/app/src/main/java/com/techcourse/DispatcherServlet.java b/app/src/main/java/com/techcourse/DispatcherServlet.java index 6cae4bb8ad..5fe797b849 100644 --- a/app/src/main/java/com/techcourse/DispatcherServlet.java +++ b/app/src/main/java/com/techcourse/DispatcherServlet.java @@ -7,10 +7,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import webmvc.org.springframework.web.servlet.ModelAndView; -import webmvc.org.springframework.web.servlet.mvc.asis.Controller; +import webmvc.org.springframework.web.servlet.View; import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerExecutorComposite; import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerMappingComposite; -import webmvc.org.springframework.web.servlet.view.JspView; public class DispatcherServlet extends HttpServlet { @@ -21,8 +20,8 @@ public class DispatcherServlet extends HttpServlet { private final HandlerExecutorComposite handlerExecutorComposite; public DispatcherServlet() { - this.handlerMappingComposite = new HandlerMappingComposite(new ManualHandlerMapping()); - this.handlerExecutorComposite = new HandlerExecutorComposite(new ControllerInterfaceHandlerExecutor()); + this.handlerMappingComposite = new HandlerMappingComposite(); + this.handlerExecutorComposite = new HandlerExecutorComposite(); } @Override @@ -39,22 +38,11 @@ protected void service(final HttpServletRequest request, final HttpServletRespon try { final var handler = handlerMappingComposite.getHandler(request); ModelAndView modelAndView = handlerExecutorComposite.handle(request, response, handler); - final var viewName = modelAndView.getView().viewName(); - move(viewName, request, response); + View view = modelAndView.getView(); + view.render(modelAndView.getModel(), request, response); } catch (Throwable e) { log.error("Exception : {}", e.getMessage(), e); throw new ServletException(e.getMessage()); } } - - private void move(final String viewName, final HttpServletRequest request, final HttpServletResponse response) - throws Exception { - if (viewName.startsWith(JspView.REDIRECT_PREFIX)) { - response.sendRedirect(viewName.substring(JspView.REDIRECT_PREFIX.length())); - return; - } - - final var requestDispatcher = request.getRequestDispatcher(viewName); - requestDispatcher.forward(request, response); - } } diff --git a/app/src/main/java/com/techcourse/ManualHandlerMapping.java b/app/src/main/java/com/techcourse/ManualHandlerMapping.java deleted file mode 100644 index b4fbc2c886..0000000000 --- a/app/src/main/java/com/techcourse/ManualHandlerMapping.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.techcourse; - -import com.techcourse.controller.LoginController; -import com.techcourse.controller.LoginViewController; -import com.techcourse.controller.LogoutController; -import jakarta.servlet.http.HttpServletRequest; -import java.util.HashMap; -import java.util.Map; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import webmvc.org.springframework.web.servlet.mvc.asis.Controller; -import webmvc.org.springframework.web.servlet.mvc.asis.ForwardController; -import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerMapping; - -public class ManualHandlerMapping implements HandlerMapping { - - private static final Logger log = LoggerFactory.getLogger(ManualHandlerMapping.class); - - private static final Map controllers = new HashMap<>(); - - public void initialize() { - controllers.put("/", new ForwardController("/index.jsp")); - controllers.put("/login", new LoginController()); - controllers.put("/login/view", new LoginViewController()); - controllers.put("/logout", new LogoutController()); - - log.info("Initialized Handler Mapping!"); - controllers.keySet() - .forEach(path -> log.info("Path : {}, Controller : {}", path, controllers.get(path).getClass())); - } - - @Override - public Object getHandler(HttpServletRequest request) { - String requestURI = request.getRequestURI(); - log.debug("Request Mapping Uri : {}", requestURI); - return controllers.get(requestURI); - } -} diff --git a/app/src/main/java/com/techcourse/controller/IndexController.java b/app/src/main/java/com/techcourse/controller/IndexController.java new file mode 100644 index 0000000000..306d1226ee --- /dev/null +++ b/app/src/main/java/com/techcourse/controller/IndexController.java @@ -0,0 +1,18 @@ +package com.techcourse.controller; + +import context.org.springframework.stereotype.Controller; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import web.org.springframework.web.bind.annotation.RequestMapping; +import web.org.springframework.web.bind.annotation.RequestMethod; +import webmvc.org.springframework.web.servlet.ModelAndView; +import webmvc.org.springframework.web.servlet.view.JspView; + +@Controller +public class IndexController { + + @RequestMapping(value = "/", method = RequestMethod.GET) + public ModelAndView index(final HttpServletRequest req, final HttpServletResponse res) { + return new ModelAndView(new JspView("/index.jsp")); + } +} diff --git a/app/src/main/java/com/techcourse/controller/LoginController.java b/app/src/main/java/com/techcourse/controller/LoginController.java index 0428fe109e..970f828f3e 100644 --- a/app/src/main/java/com/techcourse/controller/LoginController.java +++ b/app/src/main/java/com/techcourse/controller/LoginController.java @@ -2,28 +2,34 @@ import com.techcourse.domain.User; import com.techcourse.repository.InMemoryUserRepository; +import context.org.springframework.stereotype.Controller; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import webmvc.org.springframework.web.servlet.mvc.asis.Controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import web.org.springframework.web.bind.annotation.RequestMapping; +import web.org.springframework.web.bind.annotation.RequestMethod; +import webmvc.org.springframework.web.servlet.ModelAndView; +import webmvc.org.springframework.web.servlet.view.JspView; -public class LoginController implements Controller { +@Controller +public class LoginController { private static final Logger log = LoggerFactory.getLogger(LoginController.class); - @Override - public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception { + @RequestMapping(value = "/login", method = RequestMethod.POST) + public ModelAndView execute(final HttpServletRequest req, final HttpServletResponse res) { if (UserSession.isLoggedIn(req.getSession())) { - return "redirect:/index.jsp"; + return new ModelAndView(new JspView("redirect:/index.jsp")); } - return InMemoryUserRepository.findByAccount(req.getParameter("account")) + String view = InMemoryUserRepository.findByAccount(req.getParameter("account")) .map(user -> { log.info("User : {}", user); return login(req, user); }) .orElse("redirect:/401.jsp"); + return new ModelAndView(new JspView(view)); } private String login(final HttpServletRequest request, final User user) { diff --git a/app/src/main/java/com/techcourse/controller/LoginViewController.java b/app/src/main/java/com/techcourse/controller/LoginViewController.java index 86ec26cdce..e8beeb14e8 100644 --- a/app/src/main/java/com/techcourse/controller/LoginViewController.java +++ b/app/src/main/java/com/techcourse/controller/LoginViewController.java @@ -1,22 +1,28 @@ package com.techcourse.controller; +import context.org.springframework.stereotype.Controller; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import webmvc.org.springframework.web.servlet.mvc.asis.Controller; +import web.org.springframework.web.bind.annotation.RequestMapping; +import web.org.springframework.web.bind.annotation.RequestMethod; +import webmvc.org.springframework.web.servlet.ModelAndView; +import webmvc.org.springframework.web.servlet.view.JspView; -public class LoginViewController implements Controller { +@Controller +public class LoginViewController { private static final Logger log = LoggerFactory.getLogger(LoginViewController.class); - @Override - public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception { - return UserSession.getUserFrom(req.getSession()) + @RequestMapping(value = "/login/view", method = RequestMethod.GET) + public ModelAndView execute(final HttpServletRequest req, final HttpServletResponse res) { + String view = UserSession.getUserFrom(req.getSession()) .map(user -> { log.info("logged in {}", user.getAccount()); return "redirect:/index.jsp"; }) .orElse("/login.jsp"); + return new ModelAndView(new JspView(view)); } } diff --git a/app/src/main/java/com/techcourse/controller/LogoutController.java b/app/src/main/java/com/techcourse/controller/LogoutController.java index 4642fd9450..3bd23ef885 100644 --- a/app/src/main/java/com/techcourse/controller/LogoutController.java +++ b/app/src/main/java/com/techcourse/controller/LogoutController.java @@ -1,15 +1,20 @@ package com.techcourse.controller; +import context.org.springframework.stereotype.Controller; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import webmvc.org.springframework.web.servlet.mvc.asis.Controller; +import web.org.springframework.web.bind.annotation.RequestMapping; +import web.org.springframework.web.bind.annotation.RequestMethod; +import webmvc.org.springframework.web.servlet.ModelAndView; +import webmvc.org.springframework.web.servlet.view.JspView; -public class LogoutController implements Controller { +@Controller +public class LogoutController { - @Override - public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception { + @RequestMapping(value = "/logout", method = RequestMethod.POST) + public ModelAndView execute(final HttpServletRequest req, final HttpServletResponse res) { final var session = req.getSession(); session.removeAttribute(UserSession.SESSION_KEY); - return "redirect:/"; + return new ModelAndView(new JspView("redirect:/")); } } diff --git a/app/src/main/java/com/techcourse/controller/RegisterController.java b/app/src/main/java/com/techcourse/controller/RegisterController.java deleted file mode 100644 index c5362a4247..0000000000 --- a/app/src/main/java/com/techcourse/controller/RegisterController.java +++ /dev/null @@ -1,21 +0,0 @@ -//package com.techcourse.controller; -// -//import com.techcourse.domain.User; -//import com.techcourse.repository.InMemoryUserRepository; -//import jakarta.servlet.http.HttpServletRequest; -//import jakarta.servlet.http.HttpServletResponse; -//import webmvc.org.springframework.web.servlet.mvc.asis.Controller; -// -//public class RegisterController implements Controller { -// -// @Override -// public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception { -// final var user = new User(2, -// req.getParameter("account"), -// req.getParameter("password"), -// req.getParameter("email")); -// InMemoryUserRepository.save(user); -// -// return "redirect:/index.jsp"; -// } -//} diff --git a/app/src/main/java/com/techcourse/controller/RegisterViewController.java b/app/src/main/java/com/techcourse/controller/RegisterViewController.java deleted file mode 100644 index 6616a81fc5..0000000000 --- a/app/src/main/java/com/techcourse/controller/RegisterViewController.java +++ /dev/null @@ -1,13 +0,0 @@ -//package com.techcourse.controller; -// -//import jakarta.servlet.http.HttpServletRequest; -//import jakarta.servlet.http.HttpServletResponse; -//import webmvc.org.springframework.web.servlet.mvc.asis.Controller; -// -//public class RegisterViewController implements Controller { -// -// @Override -// public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception { -// return "/register.jsp"; -// } -//} diff --git a/app/src/main/java/com/techcourse/controller/UserController.java b/app/src/main/java/com/techcourse/controller/UserController.java new file mode 100644 index 0000000000..c5cee6b200 --- /dev/null +++ b/app/src/main/java/com/techcourse/controller/UserController.java @@ -0,0 +1,32 @@ +package com.techcourse.controller; + +import com.techcourse.domain.User; +import com.techcourse.repository.InMemoryUserRepository; +import context.org.springframework.stereotype.Controller; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import web.org.springframework.web.bind.annotation.RequestMapping; +import web.org.springframework.web.bind.annotation.RequestMethod; +import webmvc.org.springframework.web.servlet.ModelAndView; +import webmvc.org.springframework.web.servlet.view.JsonView; + +@Controller +public class UserController { + + private static final Logger log = LoggerFactory.getLogger(LoginViewController.class); + + @RequestMapping(value = "/api/user", method = RequestMethod.GET) + public ModelAndView show(HttpServletRequest request, HttpServletResponse response) { + String account = request.getParameter("account"); + log.debug("user id : {}", account); + + ModelAndView modelAndView = new ModelAndView(new JsonView()); + User user = InMemoryUserRepository.findByAccount(account) + .orElseThrow(); + + modelAndView.addObject("user", user); + return modelAndView; + } +} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/asis/Controller.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/asis/Controller.java deleted file mode 100644 index bdd1fde780..0000000000 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/asis/Controller.java +++ /dev/null @@ -1,8 +0,0 @@ -package webmvc.org.springframework.web.servlet.mvc.asis; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - -public interface Controller { - String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception; -} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/asis/ForwardController.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/asis/ForwardController.java deleted file mode 100644 index cd8f1ef371..0000000000 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/asis/ForwardController.java +++ /dev/null @@ -1,20 +0,0 @@ -package webmvc.org.springframework.web.servlet.mvc.asis; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - -import java.util.Objects; - -public class ForwardController implements Controller { - - private final String path; - - public ForwardController(final String path) { - this.path = Objects.requireNonNull(path); - } - - @Override - public String execute(final HttpServletRequest request, final HttpServletResponse response) { - return path; - } -} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JsonView.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JsonView.java index 2e9f71515f..a6916f73db 100644 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JsonView.java +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JsonView.java @@ -1,15 +1,29 @@ package webmvc.org.springframework.web.servlet.view; +import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import webmvc.org.springframework.web.servlet.View; - +import java.io.PrintWriter; import java.util.Map; +import web.org.springframework.http.MediaType; +import webmvc.org.springframework.web.servlet.View; public class JsonView implements View { + private static final ObjectMapper objectMapper = new ObjectMapper(); + @Override - public void render(final Map model, final HttpServletRequest request, HttpServletResponse response) throws Exception { + public void render(final Map model, final HttpServletRequest request, HttpServletResponse response) + throws Exception { + response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); + String value; + if (model.size() == 1) { + value = String.valueOf(model.values().toArray()[0]); + } else { + value = objectMapper.writeValueAsString(model); + } + PrintWriter writer = response.getWriter(); + writer.write(value); } @Override diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JspView.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JspView.java index 140761d517..271fb03395 100644 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JspView.java +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JspView.java @@ -22,14 +22,16 @@ public JspView(final String viewName) { @Override public void render(final Map model, final HttpServletRequest request, final HttpServletResponse response) throws Exception { - // todo - model.keySet().forEach(key -> { log.debug("attribute name : {}, value : {}", key, model.get(key)); request.setAttribute(key, model.get(key)); }); - - // todo + if (viewName.startsWith(JspView.REDIRECT_PREFIX)) { + response.sendRedirect(viewName.substring(JspView.REDIRECT_PREFIX.length())); + return; + } + final var requestDispatcher = request.getRequestDispatcher(viewName); + requestDispatcher.forward(request, response); } @Override diff --git a/study/src/test/java/di/stage3/context/DIContainer.java b/study/src/test/java/di/stage3/context/DIContainer.java index b62feb1ed3..ecc8950af4 100644 --- a/study/src/test/java/di/stage3/context/DIContainer.java +++ b/study/src/test/java/di/stage3/context/DIContainer.java @@ -1,20 +1,96 @@ package di.stage3.context; +import java.lang.reflect.Constructor; +import java.lang.reflect.Parameter; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; import java.util.Set; +import org.reflections.Reflections; /** * 스프링의 BeanFactory, ApplicationContext에 해당되는 클래스 */ class DIContainer { - private final Set beans; + private final Set beans = new HashSet<>(); + private final Map, Object> beanBySuperTypeMap = new HashMap<>(); public DIContainer(final Set> classes) { - this.beans = Set.of(); + for (Class aClass : classes) { + createBean(aClass); + } + } + + private void createBean(Class aClass) { + if (beanBySuperTypeMap.containsKey(aClass)) { + return; + } + if (aClass.isInterface()) { + createInterfaceBean(aClass); + return; + } + Constructor declaredConstructor = aClass.getDeclaredConstructors()[0]; + if (isDefaultConstructor(declaredConstructor)) { + createBeanWithDefaultCtor(declaredConstructor); + } else { + createBeanWithCtorParameters(declaredConstructor); + } + } + + private void createInterfaceBean(Class aClass) { + Set> subTypes = findAllSubTypes(aClass); + for (Class subType : subTypes) { + createBean(subType); + return; + } + } + + private Set> findAllSubTypes(Class aClass) { + return (Set>) + new Reflections(aClass.getPackage().getName()).getSubTypesOf(aClass); + } + + private boolean isDefaultConstructor(Constructor declaredConstructor) { + Parameter[] parameters = declaredConstructor.getParameters(); + return parameters.length == 0; + } + + private void createBeanWithDefaultCtor(Constructor declaredConstructor) { + try { + Object o = declaredConstructor.newInstance(); + storeBean(o); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private void storeBean(Object o) { + beanBySuperTypeMap.put(o.getClass(), o); + if (o.getClass().getInterfaces().length != 0) { + beanBySuperTypeMap.put(o.getClass().getInterfaces()[0], o); + } + beans.add(o); + } + + private void createBeanWithCtorParameters(Constructor declaredConstructor) { + Parameter[] parameters = declaredConstructor.getParameters(); + Object[] parameterBeans = new Object[parameters.length]; + for (int i = 0; i < parameters.length; i++) { + Class type = parameters[i].getType(); + createBean(type); + parameterBeans[i] = getBean(type); + } + try { + Object o = declaredConstructor.newInstance(parameterBeans); + storeBean(o); + } catch (Exception e) { + throw new RuntimeException(e); + } } @SuppressWarnings("unchecked") public T getBean(final Class aClass) { - return null; + return (T) beanBySuperTypeMap.get(aClass); } } diff --git a/study/src/test/java/di/stage3/context/Stage3Test.java b/study/src/test/java/di/stage3/context/Stage3Test.java index 2363e8666f..89b8cb35de 100644 --- a/study/src/test/java/di/stage3/context/Stage3Test.java +++ b/study/src/test/java/di/stage3/context/Stage3Test.java @@ -30,10 +30,14 @@ void stage3() { * DIContainer가 객체를 생성하고 관계를 설정하도록 구현해보자. */ final var userService = diContainer.getBean(UserService.class); + final var interfaceBean = diContainer.getBean(UserDao.class); + final var concreateBean = diContainer.getBean(InMemoryUserDao.class); final var actual = userService.join(user); assertThat(actual.getAccount()).isEqualTo("gugu"); + assertThat(interfaceBean).isNotNull(); + assertThat(interfaceBean).isEqualTo(concreateBean); } /** diff --git a/study/src/test/java/di/stage4/annotations/ClassPathScanner.java b/study/src/test/java/di/stage4/annotations/ClassPathScanner.java index 9dab1fd9c4..306664fa00 100644 --- a/study/src/test/java/di/stage4/annotations/ClassPathScanner.java +++ b/study/src/test/java/di/stage4/annotations/ClassPathScanner.java @@ -1,10 +1,29 @@ package di.stage4.annotations; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; import java.util.Set; +import java.util.stream.Collectors; public class ClassPathScanner { public static Set> getAllClassesInPackage(final String packageName) { - return null; + InputStream stream = ClassLoader.getSystemClassLoader() + .getResourceAsStream(packageName.replaceAll("[.]", "/")); + BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + return reader.lines() + .filter(line -> line.endsWith(".class")) + .map(line -> getClass(line, packageName)) + .collect(Collectors.toSet()); + } + + private static Class getClass(String className, String packageName) { + try { + return Class.forName(packageName + "." + + className.substring(0, className.lastIndexOf('.'))); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } } } diff --git a/study/src/test/java/di/stage4/annotations/DIContainer.java b/study/src/test/java/di/stage4/annotations/DIContainer.java index 9248ecad7e..a8ce629f64 100644 --- a/study/src/test/java/di/stage4/annotations/DIContainer.java +++ b/study/src/test/java/di/stage4/annotations/DIContainer.java @@ -1,24 +1,131 @@ package di.stage4.annotations; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Parameter; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; +import org.reflections.Reflections; /** * 스프링의 BeanFactory, ApplicationContext에 해당되는 클래스 */ class DIContainer { - private final Set beans; + + private final Set beans = new HashSet<>(); + private final Map, Object> beanBySuperTypeMap = new HashMap<>(); public DIContainer(final Set> classes) { - this.beans = Set.of(); + for (Class aClass : classes) { + createBean(aClass); + } + for (Object bean : beans) { + injectBean(bean); + } } public static DIContainer createContainerForPackage(final String rootPackageName) { - return null; + Set> allClassesInPackage = ClassPathScanner.getAllClassesInPackage(rootPackageName); + Set> beanCandidate = findBeanCandidate(allClassesInPackage); + return new DIContainer(beanCandidate); + } + + private static Set> findBeanCandidate(Set> allClassesInPackage) { + return allClassesInPackage.stream() + .filter(it -> it.isAnnotationPresent(Repository.class) + || it.isAnnotationPresent(Service.class)) + .collect(Collectors.toSet()); + } + + private void createBean(Class aClass) { + if (beanBySuperTypeMap.containsKey(aClass)) { + return; + } + if (aClass.isInterface()) { + createInterfaceBean(aClass); + return; + } + Constructor declaredConstructor = aClass.getDeclaredConstructors()[0]; + declaredConstructor.setAccessible(true); + if (isDefaultConstructor(declaredConstructor)) { + createBeanWithDefaultCtor(declaredConstructor); + } else { + createBeanWithCtorParameters(declaredConstructor); + } + } + + private void createInterfaceBean(Class aClass) { + Set> subTypes = findAllSubTypes(aClass); + for (Class subType : subTypes) { + createBean(subType); + return; + } + } + + private Set> findAllSubTypes(Class aClass) { + return (Set>) + new Reflections(aClass.getPackage().getName()).getSubTypesOf(aClass); + } + + private boolean isDefaultConstructor(Constructor declaredConstructor) { + Parameter[] parameters = declaredConstructor.getParameters(); + return parameters.length == 0; + } + + private void createBeanWithDefaultCtor(Constructor declaredConstructor) { + try { + Object o = declaredConstructor.newInstance(); + storeBean(o); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private void storeBean(Object o) { + beanBySuperTypeMap.put(o.getClass(), o); + if (o.getClass().getInterfaces().length != 0) { + beanBySuperTypeMap.put(o.getClass().getInterfaces()[0], o); + } + beans.add(o); + } + + private void createBeanWithCtorParameters(Constructor declaredConstructor) { + Parameter[] parameters = declaredConstructor.getParameters(); + Object[] parameterBeans = new Object[parameters.length]; + for (int i = 0; i < parameters.length; i++) { + Class type = parameters[i].getType(); + createBean(type); + parameterBeans[i] = getBean(type); + } + try { + Object o = declaredConstructor.newInstance(parameterBeans); + storeBean(o); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private void injectBean(Object bean) { + for (Field field : bean.getClass().getDeclaredFields()) { + field.setAccessible(true); + if (!field.isAnnotationPresent(Inject.class)) { + continue; + } + try { + field.set(bean, getBean(field.getType())); + } catch (IllegalAccessException e) { + System.out.println("Inject 오류!"); + throw new RuntimeException(e); + } + } } @SuppressWarnings("unchecked") public T getBean(final Class aClass) { - return null; + return (T) beanBySuperTypeMap.get(aClass); } }