0
点赞
收藏
分享

微信扫一扫

Java MVC开发原理

b91bff6ffdb5 2022-12-04 阅读 28


学习廖雪峰老师的 ​​MVC开发原理​​​ 在这里记录一下。
MVC:指的是model、view和controller,controller是处理业务逻辑返回view(视图)和model(数据)
代码最主要的类Dispatcher:作用是

  • 1.通过反射初始化保存​​GetDispatcher​​​的​​getMappings​​​和​​GetDispatcher​​​的​​postMappings​​​,用于后面根据​​path​​​取到对应的​​controller​​​和​​method​
  • 2.process方法负责调用dispatcher(继承多态)的invoke函数执行具体方法和参数的反射生成​​ModelandView​​,最后通过模板引擎进行渲染。

@WebServlet(urlPatterns = "/")
public class DispatcherServlet extends HttpServlet {

private final Logger logger = LoggerFactory.getLogger(getClass());

private Map<String, GetDispatcher> getMappings = new HashMap<>();

private Map<String, PostDispatcher> postMappings = new HashMap<>();

// TODO: 可指定package并自动扫描:
private List<Class<?>>
// 通过静态变量class获取Class实例,每个Class实例指向一个数据类型,包含该class的所有完整信息
controllers = List.of(IndexController.class, UserController.class);
private ViewEngine viewEngine;

/**
* 当Servlet容器创建当前Servlet实例后,会自动调用init(ServletConfig)方法
*/
// 初始化函数
@Override
public void init() throws ServletException {
logger.info("init {}...", getClass().getSimpleName());
// 把对象转换成为一个json字符串返回到前端
ObjectMapper objectMapper = new ObjectMapper();
// //反序列化的时候如果多了其他属性,不抛出异常
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 依次处理每个Controller:
for (Class<?> controllerClass : controllers) {
try {
// getConstructor()获取构造方法,newInstance()创建类
Object controllerInstance = controllerClass.getConstructor().newInstance();
// 依次处理每个Method:
for (Method method : controllerClass.getMethods()) {
if (method.getAnnotation(GetMapping.class) != null) {
// 处理@Get: 检查返回值类型
if (method.getReturnType() != ModelAndView.class && method.getReturnType() != void.class) {
throw new UnsupportedOperationException(
"Unsupported return type: " + method.getReturnType() + " for method: " + method);
}
// 检查参数类型是否支持
for (Class<?> parameterClass : method.getParameterTypes()) {
if (!supportedGetParameterTypes.contains(parameterClass)) {
throw new UnsupportedOperationException(
"Unsupported parameter type: " + parameterClass + " for method: " + method);
}
}
String[] parameterNames = Arrays.stream(method.getParameters()).map(p -> p.getName())
.toArray(String[]::new);
String path = method.getAnnotation(GetMapping.class).value();
logger.info("Found GET: {} => {}", path, method);
this.getMappings.put(path, new GetDispatcher(controllerInstance, method, parameterNames,
method.getParameterTypes()));
} else if (method.getAnnotation(PostMapping.class) != null) {
// 处理@Post:
if (method.getReturnType() != ModelAndView.class && method.getReturnType() != void.class) {
throw new UnsupportedOperationException(
"Unsupported return type: " + method.getReturnType() + " for method: " + method);
}
Class<?> requestBodyClass = null;
for (Class<?> parameterClass : method.getParameterTypes()) {
if (!supportedPostParameterTypes.contains(parameterClass)) {
if (requestBodyClass == null) {
requestBodyClass = parameterClass;
} else {
throw new UnsupportedOperationException("Unsupported duplicate request body type: "
+ parameterClass + " for method: " + method);
}
}
}
String path = method.getAnnotation(PostMapping.class).value();
logger.info("Found POST: {} => {}", path, method);
this.postMappings.put(path, new PostDispatcher(controllerInstance, method,
method.getParameterTypes(), objectMapper));
}
}
} catch (ReflectiveOperationException e) {
throw new ServletException(e);
}
}
// 创建ViewEngine:
this.viewEngine = new ViewEngine(getServletContext());
}

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
process(req, resp, this.getMappings);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
process(req, resp, this.postMappings);
}

private void process(HttpServletRequest req, HttpServletResponse resp,
Map<String, ? extends AbstractDispatcher> dispatcherMap) throws ServletException, IOException {
resp.setContentType("text/html");
resp.setCharacterEncoding("UTF-8");
String path = req.getRequestURI().substring(req.getContextPath().length());
AbstractDispatcher dispatcher = dispatcherMap.get(path);
if (dispatcher == null) {
resp.sendError(404);
return;
}
ModelAndView mv = null;
try {
mv = dispatcher.invoke(req, resp);
} catch (ReflectiveOperationException e) {
throw new ServletException(e);
}
if (mv == null) {
return;
}
if (mv.view.startsWith("redirect:")) {
resp.sendRedirect(mv.view.substring(9));
return;
}
PrintWriter pw = resp.getWriter();
this.viewEngine.render(mv, pw);
pw.flush();
}

private static final Set<Class<?>> supportedGetParameterTypes = Set.of(int.class, long.class, boolean.class,
String.class, HttpServletRequest.class, HttpServletResponse.class, HttpSession.class);

private static final Set<Class<?>> supportedPostParameterTypes = Set.of(HttpServletRequest.class,
HttpServletResponse.class, HttpSession.class);
}

具体的分发类

abstract class AbstractDispatcher {

public abstract ModelAndView invoke(HttpServletRequest request, HttpServletResponse response)
throws IOException, ReflectiveOperationException;
}

class GetDispatcher extends AbstractDispatcher {

final Object instance;
final Method method;
final String[] parameterNames;
final Class<?>[] parameterClasses;

public GetDispatcher(Object instance, Method method, String[] parameterNames, Class<?>[] parameterClasses) {
super();
this.instance = instance;
this.method = method;
this.parameterNames = parameterNames;
this.parameterClasses = parameterClasses;
}

@Override
public ModelAndView invoke(HttpServletRequest request, HttpServletResponse response)
throws IOException, ReflectiveOperationException {
Object[] arguments = new Object[parameterClasses.length];
for (int i = 0; i < parameterClasses.length; i++) {
String parameterName = parameterNames[i];
Class<?> parameterClass = parameterClasses[i];
if (parameterClass == HttpServletRequest.class) {
arguments[i] = request;
} else if (parameterClass == HttpServletResponse.class) {
arguments[i] = response;
} else if (parameterClass == HttpSession.class) {
arguments[i] = request.getSession();
} else if (parameterClass == int.class) {
arguments[i] = Integer.valueOf(getOrDefault(request, parameterName, "0"));
} else if (parameterClass == long.class) {
arguments[i] = Long.valueOf(getOrDefault(request, parameterName, "0"));
} else if (parameterClass == boolean.class) {
arguments[i] = Boolean.valueOf(getOrDefault(request, parameterName, "false"));
} else if (parameterClass == boolean.class) {
arguments[i] = Boolean.valueOf(getOrDefault(request, parameterName, "false"));
} else if (parameterClass == String.class) {
arguments[i] = getOrDefault(request, parameterName, "");
} else {
throw new RuntimeException("Missing handler for type: " + parameterClass);
}
}
return (ModelAndView) this.method.invoke(this.instance, arguments);
}

private String getOrDefault(HttpServletRequest request, String name, String defaultValue) {
String s = request.getParameter(name);
return s == null ? defaultValue : s;
}
}

class PostDispatcher extends AbstractDispatcher {

final Object instance;
final Method method;
final Class<?>[] parameterClasses;
final ObjectMapper objectMapper;

public PostDispatcher(Object instance, Method method, Class<?>[] parameterClasses, ObjectMapper objectMapper) {
this.instance = instance;
this.method = method;
this.parameterClasses = parameterClasses;
this.objectMapper = objectMapper;
}

@Override
public ModelAndView invoke(HttpServletRequest request, HttpServletResponse response)
throws IOException, ReflectiveOperationException {
Object[] arguments = new Object[parameterClasses.length];
for (int i = 0; i < parameterClasses.length; i++) {
Class<?> parameterClass = parameterClasses[i];
if (parameterClass == HttpServletRequest.class) {
arguments[i] = request;
} else if (parameterClass == HttpServletResponse.class) {
arguments[i] = response;
} else if (parameterClass == HttpSession.class) {
arguments[i] = request.getSession();
} else {
BufferedReader reader = request.getReader();
arguments[i] = this.objectMapper.readValue(reader, parameterClass);
}
}
return (ModelAndView) this.method.invoke(instance, arguments);
}
}

定义的一个controller样例

public class UserController {

private Map<String, User> userDatabase = new HashMap<>() {
{
List<User> users = List.of( //
new User("bob@example.com", "bob123", "Bob", "This is bob."),
new User("tom@example.com", "tomcat", "Tom", "This is tom."));
users.forEach(user -> {
put(user.email, user);
});
}
};

@GetMapping("/signin")
public ModelAndView signin() {
return new ModelAndView("/signin.html");
}

@PostMapping("/signin")
public ModelAndView doSignin(SignInBean bean, HttpServletResponse response, HttpSession session)
throws IOException {
User user = userDatabase.get(bean.email);
if (user == null || !user.password.equals(bean.password)) {
response.setContentType("application/json");
PrintWriter pw = response.getWriter();
pw.write("{\"error\":\"Bad email or password\"}");
pw.flush();
} else {
session.setAttribute("user", user);
response.setContentType("application/json");
PrintWriter pw = response.getWriter();
pw.write("{\"result\":true}");
pw.flush();
}
return null;
}

@GetMapping("/signout")
public ModelAndView signout(HttpSession session) {
session.removeAttribute("user");
return new ModelAndView("redirect:/");
}

@GetMapping("/user/profile")
public ModelAndView profile(HttpSession session) {
User user = (User) session.getAttribute("user");
if (user == null) {
return new ModelAndView("redirect:/signin");
}
return new ModelAndView("/profile.html", "user", user);
}
}


举报

相关推荐

0 条评论