devise模式基于Web的应用程序
我正在devise一个简单的基于Web的应用程序。 我是这个基于Web的领域的新手。我需要你的devise模式的build议,比如在Servlet之间如何分配责任,制定新的Servlet的标准等。
实际上,我的主页上没有几个实体,对应于其中的每一个实体,我们只有很less的选项,比如添加,编辑和删除。 早些时候,我使用了一个Servlet,像Servlet1,用于添加entity1,Servlet2用于编辑entity1等,这样我们就得到了大量的servlet。
现在我们正在改变我们的devise。 我的问题是如何正确select你如何select一个servlet的责任。 每个实体应该有一个Servlet,它将处理所有的选项,并将请求转发给服务层。 还是应该为整个页面有一个servlet,它将处理整个页面请求,然后将其转发到相应的服务层? 此外,请求对象是否转发到服务层。
一个体面的Web应用程序由混合的devise模式组成。 我只会提到最重要的。
模型视图控制器模式
您要使用的核心(架构)devise模式是模型 – 视图 – 控制器模式 。 控制器将由一个Servlet来表示,该Servlet根据请求直接创build/使用特定的模型和视图 。 该模型将由Javabean类来表示。 这在业务模型中经常可以进一步分解,其中包含了包含数据(信息)的动作(行为)和数据模型 。 视图将由可通过EL(expression式语言)直接访问( 数据 ) 模型的JSP文件表示。
然后,根据如何处理行动和事件,有各种各样的变化。 stream行的是:
-
基于请求(action)的MVC :这是最简单的实现。 ( 业务 ) 模型直接与
HttpServletRequest
和HttpServletResponse
对象一起工作。 您必须自己收集,转换和validation请求参数(主要是)。 该视图可以用简单的香草HTML / CSS / JS来表示,并且不会在请求之间维护状态。 这是Spring MVC , Struts和Stripes之间的工作原理。 -
基于组件的MVC :这很难实现。 但是最终你会看到一个更简单的模型和视图,其中所有“原始”的Servlet API都被完全抽象出来了。 您不需要自己收集,转换和validation请求参数。 控制器执行此任务,并在模型中设置收集,转换和validation的请求参数。 所有你需要做的就是定义直接与模型属性一起工作的动作方法。 该视图由JSP标签库或XML元素的风格中的“组件”表示,后者又生成HTML / CSS / JS。 后续请求的查看状态保留在会话中。 这对服务器端转换,validation和值更改事件特别有用。 这是其他JSF , Wicket和Play之间的! 作品。
作为一个侧面说明,在本土MVC框架中游荡是一个非常好的学习练习,而且只要您为了个人/私人目的而保留它,我就会推荐它。 但是,一旦你去专业,那么强烈build议select一个现有的框架,而不是重新创build自己的。 学习现有的和发展良好的框架比自己开发和维护一个强大的框架要花费更多的时间。
在下面的详细解释中,我将自己限制为基于MVC的请求,因为这更容易实现。
前端控制器模式 ( 中介模式 )
首先, 控制器部分应该实现前端控制器模式 (这是一种特殊的中介模式 )。 它应该只包含一个servlet,它提供了所有请求的集中入口点。 它应该根据请求可用的信息(如pathinfo或servletpath,方法和/或特定参数)创buildModel 。 在下面的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-pattern
,例如/pages/*
, *.do
甚至只是*.html
。
在前缀模式的情况下,例如/pages/*
您可以调用URL http://example.com/pages/register,http://example.com/pages/login等,并提供;/WEB-INF/register.jsp
以及相应的GET和POST操作。 然后可以通过request.getPathInfo()
来获得部件register
, login
等等,如上例所示。
当您使用*.do
, *.html
等后缀模式时,则可以调用URL http://example.com/register.do,http://example.com/login.do ,等等,你应该改变这个答案(也是ActionFactory
)中的代码示例,通过request.getServletPath()
来提取register
和login
部分。
战略模式
Action
应该遵循战略模式 。 它需要被定义为抽象/接口types,它应该基于抽象方法的传入参数来完成工作(这是与Command模式的区别,其中抽象/接口types应该基于在创build过程中被传入的参数)。
public interface Action { public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception; }
您可能希望使用ActionException
类的自定义exception来使Exception
更具体。 这只是一个基本的开球的例子,其余的全部取决于你。
下面是LoginAction
的一个例子(正如其名称所示)login到用户。 User
本身又是一个数据模型 。 该视图知道User
的存在。
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方法模式 。 基本上,它应该提供一个创build方法,它返回一个抽象/接口types的具体实现。 在这种情况下,它应该根据请求提供的信息返回一个Action
接口的实现。 例如, 方法和pathinfo (pathinfo是请求URL中上下文和servletpath之后的部分,不包括查询string)。
public static Action getAction(HttpServletRequest request) { return actions.get(request.getMethod() + request.getPathInfo()); }
这些actions
反过来应该是一些静态/应用范围的Map<String, Action>
,它包含所有已知的动作。 这取决于你如何填写这张地图。 硬编码:
actions.put("POST/register", new RegisterAction()); actions.put("POST/login", new LoginAction()); actions.put("GET/logout", new LogoutAction()); // ...
或者可以根据类path中的属性/ XMLconfiguration文件进行configuration:(伪)
for (Entry entry : configuration) { actions.put(entry.getKey(), Class.forName(entry.getValue()).newInstance()); }
或者dynamic地基于类path中的扫描来实现特定的接口和/或注释:(伪)
for (ClassFile classFile : classpath) { if (classFile.isInstanceOf(Action.class)) { actions.put(classFile.getAnnotation("mapping"), classFile.newInstance()); } }
请记住为没有映射的情况创build一个“什么也不做”的Action
。 比如让它直接返回request.getPathInfo().substring(1)
。
其他模式
这些是迄今为止的重要模式。
为了更进一步,您可以使用Facade模式创build一个Context
类,该类继而包装请求和响应对象,并提供一些委托给请求和响应对象的便利方法,并将其作为parameter passing给Action#execute()
方法。 这增加了一个额外的抽象层来隐藏原始的Servlet API。 那么你应该基本上在每个Action
实现中都以零 import javax.servlet.*
声明结束。 在JSF中,这就是FacesContext
和ExternalContext
类正在做的事情。 你可以在这个答案中find具体的例子。
然后是状态模式 ,您希望添加一个额外的抽象层来分割收集请求参数,转换它们,validation它们,更新模型值并执行操作的任务。 用JSF来说,这就是LifeCycle
所做的。
然后有一个复合模式的情况下,你想创build一个基于组件的视图,可以附加模型,其行为取决于基于请求的生命周期的状态。 在JSF中,这是UIComponent
代表的。
这样你就可以逐渐向基于组件的框架发展。
也可以看看:
- Java核心库中的GoFdevise模式示例
- 请求MVC和组件MVC之间的区别
- 在JSP页面中使用MVC和DAO模式在HTML中显示JDBC ResultSet
- JSF MVC框架中的MVC是什么组件?
- JSF控制器,服务和DAO
在被殴打的MVC模式中,Servlet是“C” – 控制器。
它的主要工作是进行初始请求评估,然后将基于初始评估的处理分派给特定的工作人员。 工作人员的职责之一可能是设置一些表示层bean,并将请求转发到JSP页面以呈现HTML。 所以,仅仅因为这个原因,你需要将请求对象传递给服务层。
我不会,但是,开始编写原始的Servlet
类。 他们所做的工作是非常可预测的,并且是模板化的,框架非常好。 幸运的是,有许多可用的,经过时间考验的候选人(按字母顺序排列): Apache Wicket , Java Server Faces , Spring等等。
恕我直言,如果你从责任分配的angular度来看,在web应用的情况下没有太大的区别。 但是,保持图层的清晰度。 在表示层保留任何纯粹用于演示目的的东西,比如特定于Web控件的控件和代码。 只需将业务层中的实体和业务层中的所有function(如添加,编辑,删除等)保持一致即可。 但是,将它们呈现到浏览器中将在表示层中进行处理。 对于.Net,ASP.NET MVC模式在保持图层分离方面非常出色。 看看MVC模式。
我已经使用了struts框架,发现它很容易学习。 在使用struts框架的时候,你网站的每个页面都会包含以下内容。
1)每次刷新HTML页面时都会调用所使用的动作。 该操作应在页面首次加载时填充表单中的数据,并处理Web UI和业务层之间的交互。 如果使用jsp页面来修改一个可变的java对象,则java对象的副本应该存储在表单中,而不是原始数据,这样除非用户保存页面,否则原始数据不会被修改。
2)用于在action和jsp页面之间传输数据的表单。 这个对象应该由一组getter和setter组成,这些属性需要被jsp文件访问。 表单也有一个方法来validation数据,在它被持续之前。
3)用于呈现页面的最终HTML的jsp页面。 jsp页面是HTML和特殊struts标签的混合体,用于访问和操作表单中的数据。 尽pipestruts允许用户将Java代码插入到jsp文件中,但您应该非常谨慎,因为这会使代码更难以阅读。 jsp文件里的Java代码很难debugging,不能unit testing。 如果你发现你自己在一个jsp文件中写了超过4-5行的java代码,代码应该被移动到action中。
BalusC优秀的答案涵盖了Web应用程序的大部分模式。
某些应用程序可能需要链接责任模式
在面向对象devise中, 责任链模式是由命令对象源和一系列处理对象组成的devise模式。 每个处理对象都包含用于定义可处理的命令对象types的逻辑; 其余的被传递给链中的下一个处理对象。
用例使用这种模式:
处理请求(命令)的处理程序是未知的,并且可以将此请求发送到多个对象。 一般来说,你设置对象的后继者 。 如果当前对象不能处理请求或部分处理请求,并将相同的请求转发给后继对象。
有用的SE问题/文章:
为什么我会用装饰者的责任链?
共同责任链的用法?
oodesign 的责任链模式
源头的责任