一个像样的web应用程序包含了多种设计模式。我只提最重要的几个。
模型-视图-控制器模式
The core (architectural) design pattern you'd like to use is the Model-View-Controller pattern. The Controller is to be represented by a Servlet which (in)directly creates/uses a specific Model and View based on the request. The Model is to be represented by Javabean classes. This is often further dividable in Business Model which contains the actions (behaviour) and Data Model which contains the data (information). The View is to be represented by JSP files which have direct access to the (Data) Model by EL (Expression Language).
然后,根据操作和事件的处理方式有一些变化。比较流行的有:
Request (action) based MVC: this is the simplest to implement. The (Business) Model works directly with HttpServletRequest and HttpServletResponse objects. You have to gather, convert and validate the request parameters (mostly) yourself. The View can be represented by plain vanilla HTML/CSS/JS and it does not maintain state across requests. This is how among others Spring MVC, Struts and Stripes works.
Component based MVC: this is harder to implement. But you end up with a simpler model and view wherein all the "raw" Servlet API is abstracted completely away. You shouldn't have the need to gather, convert and validate the request parameters yourself. The Controller does this task and sets the gathered, converted and validated request parameters in the Model. All you need to do is to define action methods which works directly with the model properties. The View is represented by "components" in flavor of JSP taglibs or XML elements which in turn generates HTML/CSS/JS. The state of the View for the subsequent requests is maintained in the session. This is particularly helpful for server-side conversion, validation and value change events. This is how among others JSF, Wicket and Play! works.
顺便说一句,对自己开发的MVC框架感兴趣是一种很好的学习练习,只要你出于个人/私人目的使用它,我强烈建议你使用它。但是一旦你变得专业,那么强烈建议你选择一个现有的框架,而不是重新发明你自己的框架。从长远来看,学习一个现有的、开发良好的框架所花费的时间比自己开发和维护一个健壮的框架要少。
在下面的详细解释中,我将限制自己使用基于请求的MVC,因为这更容易实现。
前端控制器模式(中介模式)
首先,控制器部分应该实现Front Controller模式(这是一种专门的中介模式)。它应该只包含一个servlet,该servlet提供所有请求的集中入口点。它应该基于请求提供的信息来创建Model,比如路径信息或servletpath、方法和/或特定的参数。在下面的HttpServlet示例中,业务模型称为Action。
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
Action action = ActionFactory.getAction(request);
String view = action.execute(request, response);
if (view.equals(request.getPathInfo().substring(1)) {
request.getRequestDispatcher("/WEB-INF/" + view + ".jsp").forward(request, response);
}
else {
response.sendRedirect(view); // We'd like to fire redirect in case of a view change as result of the action (PRG pattern).
}
}
catch (Exception e) {
throw new ServletException("Executing action failed.", e);
}
}
执行该操作应该返回一些标识符来定位视图。最简单的方法是将其用作JSP的文件名。将这个servlet映射到web.xml中的特定url模式上,例如/pages/*, *。做,甚至只是*.html。
对于前缀模式,例如/pages/*,您可以调用URL,例如http://example.com/pages/register, http://example.com/pages/login等,并提供/WEB-INF/register.jsp, /WEB-INF/login.jsp与适当的GET和POST操作。零件注册,登录等,然后可以通过request.getPathInfo(),如上例所示。
当你使用后缀模式,如*。do, *.html等,然后你可以调用URL像http://example.com/register.do, http://example.com/login.do等,你应该改变在这个答案(也是ActionFactory)的代码示例提取注册和登录部分由request.getServletPath()代替。
策略模式
行动应该遵循战略模式。它需要定义为一个抽象/接口类型,它应该基于抽象方法的传入参数来完成工作(这是与命令模式的区别,其中抽象/接口类型应该基于在创建实现期间传入的参数来完成工作)。
public interface Action {
public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
您可能想要使用自定义异常(如ActionException)使异常更加特定。这只是一个基本的开始示例,其余的都取决于您。
这里有一个LoginAction的例子,它(顾名思义)登录用户。用户本身又是一个数据模型。视图知道用户的存在。
public class LoginAction implements Action {
public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
String username = request.getParameter("username");
String password = request.getParameter("password");
User user = userDAO.find(username, password);
if (user != null) {
request.getSession().setAttribute("user", user); // Login user.
return "home"; // Redirect to home page.
}
else {
request.setAttribute("error", "Unknown username/password. Please retry."); // Store error message in request scope.
return "login"; // Go back to redisplay login form with error.
}
}
}
工厂方法模式
ActionFactory应该遵循Factory方法模式。基本上,它应该提供一个创建方法,返回抽象/接口类型的具体实现。在这种情况下,它应该根据请求提供的信息返回Action接口的实现。例如,方法和pathinfo (pathinfo是请求URL中上下文和servlet路径之后的部分,不包括查询字符串)。
public static Action getAction(HttpServletRequest request) {
return actions.get(request.getMethod() + request.getPathInfo());
}
动作应该是一些静态的/应用范围的Map<String, Action>,它包含所有已知的动作。如何填满这张地图取决于你。硬编码:
actions.put("POST/register", new RegisterAction());
actions.put("POST/login", new LoginAction());
actions.put("GET/logout", new LogoutAction());
// ...
或者根据类路径中的属性/XML配置文件进行配置:(pseudo)
for (Entry entry : configuration) {
actions.put(entry.getKey(), Class.forName(entry.getValue()).newInstance());
}
或者动态地基于类路径扫描实现特定接口和/或注释的类:(伪)
for (ClassFile classFile : classpath) {
if (classFile.isInstanceOf(Action.class)) {
actions.put(classFile.getAnnotation("mapping"), classFile.newInstance());
}
}
请记住,在没有映射的情况下创建“不做任何事”操作。例如,让它直接返回request.getPathInfo().substring(1)。
其他模式
这些是目前为止最重要的模式。
To get a step further, you could use the Facade pattern to create a Context class which in turn wraps the request and response objects and offers several convenience methods delegating to the request and response objects and pass that as argument into the Action#execute() method instead. This adds an extra abstract layer to hide the raw Servlet API away. You should then basically end up with zero import javax.servlet.* declarations in every Action implementation. In JSF terms, this is what the FacesContext and ExternalContext classes are doing. You can find a concrete example in this answer.
然后是State模式,您希望添加一个额外的抽象层来拆分收集请求参数、转换它们、验证它们、更新模型值和执行操作的任务。在JSF术语中,这就是生命周期所做的事情。
然后是Composite模式,您想要创建一个基于组件的视图,该视图可以附加到模型中,并且其行为取决于基于请求的生命周期的状态。在JSF术语中,这就是UIComponent所表示的。
通过这种方式,您可以一点一点地向基于组件的框架发展。
参见:
Java核心库中的GoF设计模式示例
请求MVC和组件MVC的区别
在JSP页面使用MVC和DAO模式在HTML中显示JDBC ResultSet
JSF MVC框架中的MVC组件是什么?
JSF控制器、服务和DAO