A Guide to the Front Controller Pattern in Java
1.概述
在本教程中,我们将更深入地研究前端控制器模式,这是Martin Fowler的书"企业应用程序体系结构的模式"中定义的企业模式的一部分。
Front Controller定义为"处理网站请求的控制器"。它位于Web应用程序的前面,并将请求委派给后续资源。它还为常见行为(例如安全性,国际化)提供界面,并向某些用户提供特定视图。
这使应用程序可以在运行时更改其行为。此外,它还通过防止代码重复来帮助阅读和维护应用程序。
前控制器通过通过单个处理程序对象引导请求来合并所有请求处理。
2.它如何运作?
前控制器模式主要分为两部分。单个调度控制器和命令层次结构。以下UML描述了通用Front Controller实现的类关系:
该单个控制器将请求分派给命令,以触发与请求关联的行为。
为了演示其实现,我们将在FrontControllerServlet中实现控制器,并将命令作为从抽象FrontCommand继承的类实现。
3.设定
3.1。 Maven依赖
首先,我们将使用javax.servlet-api设置一个新的Maven WAR项目:
1 2 3 4 5 6 | <dependency> <groupId>javax.servlet</groupId> javax.servlet-api</artifactId> <version>4.0.0-b01</version> <scope>provided</scope> </dependency> |
以及jetty-maven-plugin:
1 2 3 4 5 6 7 8 9 10 | <plugin> <groupId>org.eclipse.jetty</groupId> jetty-maven-plugin</artifactId> <version>9.4.0.M1</version> <configuration> <webApp> <contextPath>/front-controller</contextPath> </webApp> </configuration> </plugin> |
3.2。模型
接下来,我们将定义一个Model类和一个模型存储库。我们将使用以下Book类作为模型:
1 2 3 4 5 6 7 |
这将是存储库,您可以查找用于具体实现的源代码,也可以自己提供一个:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public interface Bookshelf { default void init() { add(new Book("Wilson, Robert Anton & Shea, Robert", "Illuminati", 9.99)); add(new Book("Fowler, Martin", "Patterns of Enterprise Application Architecture", 27.88)); } Bookshelf getInstance(); <E extends Book> boolean add(E book); Book findByTitle(String title); } |
3.3。 FrontControllerServlet
Servlet本身的实现非常简单。我们从请求中提取命令名称,动态创建命令类的新实例并执行它。
这使我们可以添加新命令,而无需更改前端控制器的代码库。
另一个选择是使用静态条件逻辑来实现Servlet。这具有编译时错误检查的优点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public class FrontControllerServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) { FrontCommand command = getCommand(request); command.init(getServletContext(), request, response); command.process(); } private FrontCommand getCommand(HttpServletRequest request) { try { Class type = Class.forName(String.format( "com.baeldung.enterprise.patterns.front." +"controller.commands.%sCommand", request.getParameter("command"))); return (FrontCommand) type .asSubclass(FrontCommand.class) .newInstance(); } catch (Exception e) { return new UnknownCommand(); } } } |
3.4。 FrontCommand
让我们实现一个称为FrontCommand的抽象类,该类保留所有命令的共同行为。
此类可以访问ServletContext及其请求和响应对象。此外,它将处理视图分辨率:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public abstract class FrontCommand { protected ServletContext context; protected HttpServletRequest request; protected HttpServletResponse response; public void init( ServletContext servletContext, HttpServletRequest servletRequest, HttpServletResponse servletResponse) { this.context = servletContext; this.request = servletRequest; this.response = servletResponse; } public abstract void process() throws ServletException, IOException; protected void forward(String target) throws ServletException, IOException { target = String.format("/WEB-INF/jsp/%s.jsp", target); RequestDispatcher dispatcher = context.getRequestDispatcher(target); dispatcher.forward(request, response); } } |
此抽象FrontCommand的具体实现是SearchCommand。这将包括在发现书或书丢失的情况下的条件逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class SearchCommand extends FrontCommand { @Override public void process() throws ServletException, IOException { Book book = new BookshelfImpl().getInstance() .findByTitle(request.getParameter("title")); if (book != null) { request.setAttribute("book", book); forward("book-found"); } else { forward("book-notfound"); } } } |
如果应用程序正在运行,则可以通过将浏览器指向http:// localhost:8080 / front-controller /?command = Search&title = patterns来访问此命令。
SearchCommand解析为两个视图,可以使用以下请求http:// localhost:8080 / front-controller /?command = Search&title = any-title来测试第二个视图。
为了完善我们的方案,我们将实现第二个命令,该命令在所有情况下都将作为回退触发,Servlet未知命令请求:
1 2 3 4 5 6 | public class UnknownCommand extends FrontCommand { @Override public void process() throws ServletException, IOException { forward("unknown"); } } |
可以通过http:// localhost:8080 / front-controller /?command = Order&title = any-title或完全省略URL参数来访问此视图。
4.部署
因为我们决定创建一个WAR文件项目,所以我们需要一个Web部署描述符。使用此web.xml,我们可以在任何Servlet容器中运行Web应用程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <servlet> <servlet-name>front-controller</servlet-name> <servlet-class> com.baeldung.enterprise.patterns.front.controller.FrontControllerServlet </servlet-class> </servlet> <servlet-mapping> <servlet-name>front-controller</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app> |
作为最后一步,我们将运行'mvn install jetty:run'并在浏览器中检查视图。
5.结论
到目前为止,我们已经熟悉前端控制器模式及其作为Servlet和命令层次结构的实现。
和往常一样,您可以在GitHub上找到源。