Java 基于web的应用程序的设计模式

Java 基于web的应用程序的设计模式,java,design-patterns,jsp,servlets,Java,Design Patterns,Jsp,Servlets,我正在设计一个简单的基于web的应用程序。我不熟悉这个基于web的领域,我需要你对设计模式的建议,比如如何在Servlet之间分配责任,创建新Servlet的标准,等等 事实上,我的主页上几乎没有实体,对应于每个实体,我们几乎没有添加、编辑和删除等选项。早些时候,我对每个选项使用了一个Servlet,比如Servlet1用于添加entity1,Servlet2用于编辑entity1等等,这样我们就拥有了大量的Servlet 现在我们正在改变我们的设计。我的问题是如何准确地选择servlet的职责

我正在设计一个简单的基于web的应用程序。我不熟悉这个基于web的领域,我需要你对设计模式的建议,比如如何在Servlet之间分配责任,创建新Servlet的标准,等等

事实上,我的主页上几乎没有实体,对应于每个实体,我们几乎没有添加、编辑和删除等选项。早些时候,我对每个选项使用了一个Servlet,比如Servlet1用于添加entity1,Servlet2用于编辑entity1等等,这样我们就拥有了大量的Servlet


现在我们正在改变我们的设计。我的问题是如何准确地选择servlet的职责。每个实体应该有一个Servlet,它将处理所有选项并将请求转发到服务层。或者我们应该为整个页面提供一个servlet,它将处理整个页面请求,然后将其转发到相应的服务层?另外,请求对象是否应该转发到服务层。

IMHO,如果从责任分配的角度来看,web应用程序的情况没有太大区别。但是,请在图层中保持清晰。在表示层中保留任何纯粹用于表示目的的内容,例如特定于web控件的控件和代码。只需将实体保留在业务层中,将所有功能(如添加、编辑、删除)等保留在业务层中即可。但是,将它们渲染到浏览器上,以便在表示层中进行处理。对于.Net,ASP.Net MVC模式在保持层之间的分离方面非常好。查看MVC模式。

在破旧的MVC模式中,Servlet是“C”-控制器

它的主要工作是执行初始请求评估,然后根据初始评估将处理分派给特定的工作人员。工作人员的职责之一可能是设置一些表示层bean,并将请求转发到JSP页面以呈现HTML。因此,仅出于这个原因,您就需要将请求对象传递给服务层


不过,我不会开始编写原始的
Servlet
类。他们所做的工作是非常可预测和样板的,框架做得非常好。幸运的是,有许多可用的、经过时间考验的候选程序(按字母顺序排列):,举几个例子。

一个有点体面的web应用程序由多种设计模式组成。我只提最重要的几点


您希望使用的核心(架构)设计模式是。控制器将由一个Servlet表示,该Servlet(in)根据请求直接创建/使用特定的模型和视图。该模型将由Javabean类表示。这通常在包含动作(行为)的业务模型和包含数据(信息)的数据模型中进一步划分。视图由JSP文件表示,这些文件可以通过EL(表达式语言)直接访问(数据)模型

然后,会根据操作和事件的处理方式而有所不同。受欢迎的有:

  • 基于请求(操作)的MVC:这是最简单的实现。(业务)模型直接用于
    HttpServletRequest
    HttpServletResponse
    对象。您必须自己收集、转换和验证请求参数(大部分)。该视图可以用普通的HTML/CSS/JS表示,并且不在请求之间维护状态。这就是其中之一,并且是有效的

  • 基于组件的MVC:这更难实现。但是您最终得到了一个更简单的模型和视图,其中所有“原始”Servlet API都被完全抽象掉了。您不需要自己收集、转换和验证请求参数。控制器执行此任务,并在模型中设置收集、转换和验证的请求参数。您所需要做的就是定义直接与模型属性一起工作的操作方法。视图由JSP标记库或XML元素风格的“组件”表示,这些元素反过来生成HTML/CSS/JS。后续请求的视图状态在会话中保持。这对于服务器端转换、验证和值更改事件特别有用。这就是其中之一,并且是有效的

顺便说一句,使用一个自主开发的MVC框架是一个非常好的学习练习,我建议您将其保留下来,以供个人/私人使用。但一旦你变得专业,强烈建议你选择一个现有的框架,而不是重新设计你自己的框架。与自己开发和维护一个健壮的框架相比,学习一个现有的、开发良好的框架所需的时间要短得多

在下面的详细解释中,我将限制自己使用基于请求的MVC,因为它更容易实现


() 首先,控制器部分应该实现(这是一种特殊类型的)。它应该只包含一个servlet,它为所有请求提供一个集中的入口点。它应该根据请求提供的信息创建模型,例如pathinfo或servletpath、方法和/或特定参数。在下面的示例中,业务模型称为
Action

执行操作应该返回一些标识符来定位视图。最简单的方法是将其用作JSP的文件名。将此servlet映射到
web.xml
中的特定
url模式
,例如
/pages/*
*.do
,甚至只是
*.html

在前缀模式的情况下,例如
/pages/*
,您可以调用URL等,并提供
/WEB-INF/register.jsp
/WEB-INF/login.jsp
,以及相应的GET和POST操作。如上文所述,
注册
登录
等部分可通过
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);
    }
}
public interface Action {
    public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
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.
        }
    }

}
public static Action getAction(HttpServletRequest request) {
    return actions.get(request.getMethod() + request.getPathInfo());
}
actions.put("POST/register", new RegisterAction());
actions.put("POST/login", new LoginAction());
actions.put("GET/logout", new LogoutAction());
// ...
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());
    }
}