Skip to content
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

[MVC 구현 - 3단계] 베로(김은솔) 미션 제출합니다. #553

Merged
merged 13 commits into from
Sep 29, 2023
Merged
16 changes: 0 additions & 16 deletions app/src/main/java/com/techcourse/HandlerAdapterFactory.java

This file was deleted.

15 changes: 0 additions & 15 deletions app/src/main/java/com/techcourse/HandlerMappingFactory.java

This file was deleted.

42 changes: 0 additions & 42 deletions app/src/main/java/com/techcourse/ManualHandlerMapping.java

This file was deleted.

18 changes: 18 additions & 0 deletions app/src/main/java/com/techcourse/controller/ForwardController.java
Original file line number Diff line number Diff line change
@@ -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.view.ModelAndView;
import webmvc.org.springframework.web.servlet.view.JspView;

@Controller
public class ForwardController {

@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView showIndex(final HttpServletRequest request, final HttpServletResponse response) {
return new ModelAndView(new JspView("index.jsp"));
}
}
35 changes: 20 additions & 15 deletions app/src/main/java/com/techcourse/controller/LoginController.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,41 @@

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.view.ModelAndView;
import webmvc.org.springframework.web.servlet.view.RedirectView;

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 {
if (UserSession.isLoggedIn(req.getSession())) {
return "redirect:/index.jsp";
@RequestMapping(value = "/login", method = RequestMethod.POST)
public ModelAndView login(final HttpServletRequest request, final HttpServletResponse response) {
if (UserSession.isLoggedIn(request.getSession())) {
return new ModelAndView(new RedirectView("/index.jsp"));
}

return InMemoryUserRepository.findByAccount(req.getParameter("account"))
.map(user -> {
log.info("User : {}", user);
return login(req, user);
})
.orElse("redirect:/401.jsp");
return InMemoryUserRepository.findByAccount(request.getParameter("account"))
.map(user -> {
log.info("User : {}", user);
return checkPassword(request, user);
})
.orElse(new ModelAndView(new RedirectView("/401.jsp")));
}

private String login(final HttpServletRequest request, final User user) {
private ModelAndView checkPassword(final HttpServletRequest request, final User user) {
if (user.checkPassword(request.getParameter("password"))) {
final var session = request.getSession();
session.setAttribute(UserSession.SESSION_KEY, user);
return "redirect:/index.jsp";
return new ModelAndView(new RedirectView("/index.jsp"));
}
return "redirect:/401.jsp";
return new ModelAndView(new RedirectView("/401.jsp"));
}
}
Original file line number Diff line number Diff line change
@@ -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.view.ModelAndView;
import webmvc.org.springframework.web.servlet.view.JspView;
import webmvc.org.springframework.web.servlet.view.RedirectView;

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())
.map(user -> {
log.info("logged in {}", user.getAccount());
return "redirect:/index.jsp";
})
.orElse("/login.jsp");
@RequestMapping(value = "/login/view", method = RequestMethod.GET)
public ModelAndView show(final HttpServletRequest request, final HttpServletResponse response) {
return UserSession.getUserFrom(request.getSession())
.map(user -> {
log.info("logged in {}", user.getAccount());
return new ModelAndView(new RedirectView("/index.jsp"));
})
.orElse(new ModelAndView(new JspView("/login.jsp")));
}
}
17 changes: 11 additions & 6 deletions app/src/main/java/com/techcourse/controller/LogoutController.java
Original file line number Diff line number Diff line change
@@ -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.view.ModelAndView;
import webmvc.org.springframework.web.servlet.view.RedirectView;

public class LogoutController implements Controller {
@Controller
public class LogoutController {

@Override
public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception {
final var session = req.getSession();
@RequestMapping(value = "/logout", method = RequestMethod.GET)
public ModelAndView logout(final HttpServletRequest request, final HttpServletResponse response) {
final var session = request.getSession();
session.removeAttribute(UserSession.SESSION_KEY);
return "redirect:/";
return new ModelAndView(new RedirectView("/index.jsp"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,25 @@

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 web.org.springframework.web.bind.annotation.RequestMapping;
import web.org.springframework.web.bind.annotation.RequestMethod;
import webmvc.org.springframework.web.servlet.view.ModelAndView;
import webmvc.org.springframework.web.servlet.view.RedirectView;

public class RegisterController implements Controller {
@Controller
public class RegisterController {

@Override
public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception {
@RequestMapping(value = "/register", method = RequestMethod.POST)
public ModelAndView register(final HttpServletRequest request, final HttpServletResponse response) {
final var user = new User(2,
req.getParameter("account"),
req.getParameter("password"),
req.getParameter("email"));
request.getParameter("account"),
request.getParameter("password"),
request.getParameter("email"));
InMemoryUserRepository.save(user);

return "redirect:/index.jsp";
return new ModelAndView(new RedirectView("/index.jsp"));
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
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.view.ModelAndView;
import webmvc.org.springframework.web.servlet.view.JspView;

public class RegisterViewController implements Controller {
@Controller
public class RegisterViewController {

@Override
public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception {
return "/register.jsp";
@RequestMapping(value = "/register/view", method = RequestMethod.GET)
public ModelAndView show(final HttpServletRequest request, final HttpServletResponse response) {
return new ModelAndView(new JspView("/register.jsp"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package context.org.springframework.context;

import context.org.springframework.stereotype.Controller;
import core.org.springframework.util.ReflectionUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.reflections.Reflections;
import webmvc.org.springframework.web.servlet.mvc.handler.adapter.HandlerAdapter;
import webmvc.org.springframework.web.servlet.mvc.handler.mapping.HandlerMapping;

public class ApplicationContext {

private static final String INTERNAL_BASE_PACKAGE = "webmvc.org.springframework.web.servlet";
private static final List<Class<?>> beanClasses = List.of(
HandlerMapping.class,
HandlerAdapter.class,
ApplicationContextAware.class
);
private static final List<Class<? extends Annotation>> beanAnnotations = List.of(
Controller.class
);

private final Map<Class<?>, Object> beans;

public ApplicationContext(final String externalPackage) {
this.beans = new HashMap<>();
registerBeans(INTERNAL_BASE_PACKAGE);
registerBeans(externalPackage);
initialize();
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public으로 registerBeans 메서드와 initalilize 메서드를 열어두었더라고요!
그래서인지 생성자의 파라미터를 가변인자로 받아도 좋을거같다는 생각이 들었네요!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분은 내부 패키지와 외부 패키지를 분리하려고 가변 인자로 받지 않고 각각 받았습니다!
그런데 스프링 코드를 보니 실제로는 가변인자로 가져오고 있네요 🧐

AnnotationConfigServletWebApplicationContext 를 보면, scan 이라는 함수가 있습니다.

	@Override
	public final void scan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		this.basePackages = basePackages;
	}

이런 것처럼 basePackages 들을 가변인자로 받는 걸 보면, 가변 인자로 받아도 상관 없을 것 같네요!!


public void registerBeans(final String basePackage) {
final Reflections reflections = new Reflections(basePackage);
beanClasses.stream()
.map(reflections::getSubTypesOf)
.forEach(this::registerBeans);
beanAnnotations.stream()
.map(reflections::getTypesAnnotatedWith)
.forEach(this::registerBeans);
}

private <T> void registerBeans(final Set<Class<? extends T>> objectClasses) {
objectClasses.stream()
.filter(clazz -> !clazz.isInterface())
.forEach(this::registerBean);
}

private void registerBean(final Class<?> clazz) {
try {
final Constructor<?> constructor = ReflectionUtils.accessibleConstructor(clazz);
final Object bean = constructor.newInstance();
beans.put(clazz, bean);
} catch (NoSuchMethodException e) {
throw new RuntimeException(clazz.getName() + " 기본 생성자가 존재하지 않습니다.");
} catch (InvocationTargetException e) {
throw new RuntimeException("기본 생성자를 호출할 수 없습니다.");
} catch (InstantiationException e) {
throw new RuntimeException("인스턴스를 생성할 수 없습니다.");
} catch (IllegalAccessException e) {
throw new RuntimeException("기본 생성자에 접근할 수 없습니다.");
}
}

public void initialize() {
setFields();
setApplicationContext();
}

private void setFields() {
for (final Class<?> clazz : beans.keySet()) {
final Field[] fields = clazz.getDeclaredFields();
for (final Field field : fields) {
setField(clazz, field);
}
}
}

private void setField(final Class<?> clazz, final Field field) {
final Class<?> type = field.getType();
final Object bean = getBean(type);
if (bean != null) {
try {
field.setAccessible(true);
field.set(beans.get(clazz), bean);
} catch (IllegalAccessException e) {
throw new RuntimeException("필드에 접근할 수 없습니다.");
}
}
}

public void setApplicationContext() {
beans.values().stream()
.filter(bean -> bean instanceof ApplicationContextAware)
.forEach(bean -> ((ApplicationContextAware) bean).setApplicationContext(this));
}

public Object getBean(final Class<?> classType) {
return beans.get(classType);
}

public List<Object> getBeansOfAnnotationType(final Class<? extends Annotation> annotationType) {
return beans.keySet().stream()
.filter(clazz -> clazz.isAnnotationPresent(annotationType))
.map(beans::get)
.collect(Collectors.toList());
}

public <T> List<? extends T> getBeansOfType(final Class<T> classType) {
return beans.keySet().stream()
.filter(classType::isAssignableFrom)
.map(beans::get)
.map(classType::cast)
.collect(Collectors.toList());
}
}
Loading