Java MVC开发原理

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

  • 1.通过反射初始化保存GetDispatchergetMappingsGetDispatcherpostMappings,用于后面根据path取到对应的controllermethod
  • 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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值