明确MVC
包含Model
(数据模型)、View
(视图)和Controller
(控制器)。Model
为业务数据模型,在Java
中表现为JavaBean
;View用于展现数据模型,可以基于JSP
实现;Cotroller
则用于处理不同的Web
请求,可以使用Servlet
实现。
整体思路是,定义一个DispatcherServlet
的HttpServlet
,它用于接收处理所有Web
请求(get
、post
),内部实现Web
请求路径和Controller
的映射关系,将处理请求转发至Controller
处理,接收处理结果ModelAndView
类,将ModelAndView
类输入模板引擎进行渲染。
DispatcherServlet
中的路径-Controller映射
使用注解和类反射技术。注解用于说明Controller
中每个方法(mehod
)处理的内容,类反射技术则记录调用每个方法需要输入的参数数量及其类型。两者的映射关系使用Map(String, Object)
保存,其中的Object详细记录Controller
实例、method
、参数信息。DispatcherServlet
需要在初始化阶段完成所有Controller
的扫描及其与路径映射关系的建立。
ModelAndView
类是Controller
的方法处理的结果,也是模板引擎的输入参数。它其实记录的是JSP
模板的路径和输入的数据模型。
模板引擎可以使用第三方成熟插件如Pebble
等实现。
// ModelAndView.java
package framework;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class ModelAndView {
Map<String, Object> model;
String view;
/**
* 无数据模型的模板
* @param view
*/
public ModelAndView(String view) {
this.view = view;
this.model = new HashMap<String, Object>();
// this.model = Map.of();
}
/**
* 数据模型为对象的模板
* @param view
* @param name
* @param value
*/
public ModelAndView(String view, String name, Object value) {
this.view = view;
this.model = new HashMap<>();
this.model.put(name, value);
}
/**
* 以Map为数据模型的模板
* @param view
* @param model
*/
public ModelAndView(String view, Map<String, Object> model) {
this.view = view;
this.model = new HashMap<>(model);
}
}
// DispatcherServelet.java
DispatcherServlet extends HttpServlet{
@Override
public void init() throws ServletException{}
@Override
protected void doGet(){}
@Override
protected void doPost(){}
private void process(){}
}
abstract class AbstractDispatcher{}
class GetDispatcher extends AbstractDispatcher{
public GetDispatcher(){}
@Override
public ModelAndView invoke(){}
}
class PostDispatcher extends AbstractDispatcher{
public PostDispatcher (){}
@Override
public ModelAndView invoke(){}
}
// GetMapping.java
package framework;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Retention(RUNTIME)
@Target(METHOD)
public @interface GetMapping {
String value();
}
// ViewEngine.java
package framework;
import com.mitchellbosecke.pebble.PebbleEngine;
import com.mitchellbosecke.pebble.error.PebbleException;
import com.mitchellbosecke.pebble.loader.ServletLoader;
import com.mitchellbosecke.pebble.template.PebbleTemplate;
import javax.servlet.ServletContext;
import java.io.IOException;
import java.io.Writer;
public class ViewEngine {
private final PebbleEngine engine;
public ViewEngine(ServletContext servletContext) {
// 定义一个ServletLoader用于加载模板:
ServletLoader loader = new ServletLoader(servletContext);
// 模板编码:
loader.setCharset("UTF-8");
// 模板前缀,这里默认模板必须放在`/WEB-INF/templates`目录:
loader.setPrefix("/WEB-INF/templates");
// 模板后缀:
loader.setSuffix("");
// 创建Pebble实例:
this.engine = new PebbleEngine.Builder()
.autoEscaping(true) // 默认打开HTML字符转义,防止XSS攻击
.cacheActive(false) // 禁用缓存使得每次修改模板可以立刻看到效果
.loader(loader).build();
}
public void render(ModelAndView mv, Writer writer) throws IOException, PebbleException {
// 查找模板:
PebbleTemplate template = this.engine.getTemplate(mv.view);
// 渲染:
template.evaluate(writer, mv.model);
}
}
// User.java
package bean;
public class User {
public String email;
public String password;
public String name;
public String description;
public User() {
}
public User(String email, String password, String name, String description) {
this.email = email;
this.password = password;
this.name = name;
this.description = description;
}
}
// IndexController.java
package controller;
import bean.User;
import framework.GetMapping;
import framework.ModelAndView;
import javax.servlet.http.HttpSession;
public class IndexController {
@GetMapping("/")
public ModelAndView index(HttpSession session) {
User user = (User) session.getAttribute("user");
return new ModelAndView("/index.html", "user", user);
}
@GetMapping("/hello")
public ModelAndView hello(String name) {
if (name == null) {
name = "World";
}
return new ModelAndView("/hello.html", "name", name);
}
}
{% extends "_base.html" %}
{% block main %}
{% if user == null %}
<h3>Welcome!h3>
{% else %}
<h3>Welcome {{ user.name }}!h3>
{% endif %}
{% endblock %}