目录:
【SSH进阶之路】Struts基本原理 + 实现简单登录(二)
【SSH进阶之路】一步步重构MVC实现Struts框架——从一个简单MVC开始(三)
【SSH进阶之路】一步步重构MVC实现Struts框架——封装业务逻辑和跳转路径(四)
【SSH进阶之路】一步步重构MVC实现Struts框架——彻底去掉逻辑判断(五)
【SSH进阶之路】一步步重构MVC实现Struts框架——完善转向页面,大功告成(六)
Struts的第二篇博客【SSH进阶之路】Struts基本原理 + 实现简单登录(二),我们介绍了MVC模型和Struts的基本理论,对比学习了他们之间的联系和区别。从第三篇博客【SSH进阶之路】一步步重构MVC实现Struts框架——从一个简单MVC开始(三)开始,我们实现了一个简单MVC模型,提出了三个重构的问题。
上篇博客【SSH进阶之路】一步步重构MVC实现Struts框架——封装业务逻辑和跳转路径(四),我们解决了第一个问题:封装业务逻辑和跳转路径。这篇博客我们解决第二个问题:彻底去掉Servlet中的逻辑判断。
我们先回顾一下上篇博客中的TestServlet的代码:
- package com.liang.servlet;
-
- import java.io.IOException;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
-
- import com.liang.action.Action;
- import com.liang.action.AddUserAction;
- import com.liang.action.DelUserAction;
- import com.liang.action.ModifyUserAction;
- import com.liang.action.QueryUserAction;
-
-
-
-
-
- public class TestServlet extends HttpServlet {
-
- @Override
- protected void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
-
- String reqeuestURI = request.getRequestURI();
- System.out.println(reqeuestURI);
-
- String path = reqeuestURI.substring(reqeuestURI.indexOf("/",1), reqeuestURI.indexOf("."));
- System.out.println(path);
-
- Action action = null;
-
- if ("/servlet/addUser".equals(path)) {
- action = new AddUserAction();
-
- }else if ("/servlet/delUser".equals(path)) {
- action = new DelUserAction();
-
- }else if ("/servlet/modifyUser".equals(path)) {
- action = new ModifyUserAction();
-
- }else if ("/servlet/queryUser".equals(path)) {
- action = new QueryUserAction();
- }else {
- throw new RuntimeException("请求失败");
- }
- String forward = null;
-
- try {
- forward = action.execute(request, response);
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- request.getRequestDispatcher(forward).forward(request, response);
- }
-
- @Override
- protected void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- doGet(request,response);
- }
-
- }
我们来分析一下上面TestServlet中的代码,我们发现了多个逻辑判断IF..ELSE,系统不灵活,需求改变时,违背了开发封闭原则,不能满足需求。我们知道我们配置连接数据库信息时,使数据库更加灵活,使用了配置文件。
下面我们也打算把所有的字符串和业务逻辑的实现类都配置到配置文件中,最后通过实现类的方法来返回跳转路径,最终跳转到相应的页面。 需求改变时,修改配置文件即可。
所以配置文件不仅要配上Servlet中出现的字符串,还要配置相应的Action接口的实现类(我们可以利用反射来动态的实例化该类的对象,进而使用多态的机制动态这个类的所有属性和方法),另外返回跳转路径字符串。其实此时和Struts的配置文件的道理是一致,我们也只需要自己实现一个配置文件。
web.xml,我们将struts-config.xml配置成tomacat启动时就初始化
- <?xml version="1.0" encoding="UTF-8"?>
- <web-app version="2.5"
- xmlns="http://java.sun.com/xml/ns/javaee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
- http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
- <welcome-file-list>
- <welcome-file>index.jsp</welcome-file>
- </welcome-file-list>
-
- <servlet>
- <servlet-name>TestServlet</servlet-name>
-
- <servlet-class>com.liang.servlet.TestServlet</servlet-class>
-
- <init-param>
- <param-name>config</param-name>
- <param-value>/WEB-INF/struts-config.xml</param-value>
- </init-param>
- <load-on-startup>0</load-on-startup>
- </servlet>
-
-
- <servlet-mapping>
- <servlet-name>TestServlet</servlet-name>
-
- <url-pattern>*.do</url-pattern>
- </servlet-mapping>
-
- </web-app>
com.liang.servlet.TestServlet,就是我们的核心Servlet。
struts-config.xml,将path路径和Action的实现类,配置成map结构
- <?xml version="1.0" encoding="UTF-8"?>
-
- <action-config>
- <action-mappings>
-
- <action path="/servlet/delUser" type="com.liang.action.DelUserAction"></action>
- <action path="/servlet/addUser" type="com.liang.action.AddUserAction"></action>
- <action path="/servlet/modifyUser" type="com.liang.action.ModifyUserAction"></action>
- <action path="/servlet/queryUser" type="com.liang.action.QueryUserAction"></action>
- </action-mappings>
- </action-config>
下面我们只需要读取配置文件,实现相应的功能。读取xml,非常流行的一种方式就是使用dom4j,我们曾经也实现过了dom4j读取xml的例子,但是使用dom4j读取配置文件之后,我们需要将信息放置到哪,毋庸置疑,我们需要一个map结构的类,用于存放路径信息和action实现类(type)的信息,里面最少需要两个参数,如下:
ActionMapping
- package com.liang.action;
-
- public class ActionMapping {
-
-
- private String path;
-
- private String type;
- public String getType() {
- return type;
- }
- public void setType(String type) {
- this.type = type;
- }
- public String getPath() {
- return path;
- }
- public void setPath(String path) {
- this.path = path;
- }
-
- }
配置信息,我们已经放到了一个map中,此时一个请求过来,我们需要跟上面TestServlet中的IF语句一样,根据截取的URL请求,到Map中取得本次请求对应的Action拿出来,但是Action有多个, 我们还需要一个map结构,用来区分此时的URL请求对应哪一个Action。所以此时我们还需要一个存储Action的map结构。(如果没有看懂,没事,下面的代码中有注释,可以帮助你)
Mappings
- package com.liang.action;
-
- import java.util.HashMap;
- import java.util.Map;
-
- public class Mappings {
- public static Map actions = new HashMap();
- }
此时,我们的准备工作已经做完了,我们需要使用dom4j读取配置文件,存储到相应的map结构中。
ConfigInit
struts-config.xml解析器。把解析后的结点保存到ActionMapping里,MappingAction是我们上面封装的一个类。如何解析看代码:
- package com.liang.servlet;
-
- import java.io.File;
- import java.util.Iterator;
-
- import org.dom4j.Document;
- import org.dom4j.Element;
- import org.dom4j.io.SAXReader;
-
- import com.liang.action.*;
-
- public class ConfigInit {
-
- public static void init(String config) {
-
- SAXReader reader = new SAXReader();
- File f = new File(config);
- try {
-
- Document doc = reader.read(f);
-
- Element root = doc.getRootElement();
- Element actionmappings = (Element) root.element("action-mappings");
-
- for (Iterator j = actionmappings.elementIterator("action"); j
- .hasNext();) {
- Element am = (Element) j.next();
- ActionMapping actionMapping = new ActionMapping();
-
-
- actionMapping.setPath(am.attributeValue("path"));
- actionMapping.setType(am.attributeValue("type"));
-
-
-
-
-
-
-
- Mappings.actions.put((String) am.attributeValue("path"),
- actionMapping);
-
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
最后,万事具备就差东风了,我们看一下最关键的类TestServlet。
其实它就是普通的Servlet。通过上面web.xml的映射,只要是.do的访问都会先能过这个Servlet过滤到想要访问的Action里。因为这个Servlet的load-on-startup设置为0,所以Tomcat启动时,会执行init方法。ConfigInit类就是我的struts-config.xml解析器。
TestServlet
- package com.liang.servlet;
-
- import java.io.IOException;
-
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import com.liang.action.Action;
- import com.liang.action.ActionMapping;
- import com.liang.action.Mappings;
-
-
-
-
-
-
- public class TestServlet extends HttpServlet {
-
-
- protected static String config = "/WEB-INF/struts-config.xml";
-
- public void init() throws ServletException {
-
-
-
- config = getServletContext().getRealPath("/")+ getInitParameter("config");
-
- ConfigInit.init(config);
- }
-
-
-
-
-
-
-
-
-
-
- @Override
- protected void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
-
-
- String reqeuestURI = request.getRequestURI();
-
- String path = reqeuestURI.substring(reqeuestURI.indexOf("/",1), reqeuestURI.indexOf("."));
-
- Mappings mapings = new Mappings();
-
- ActionMapping actionMapping = (ActionMapping)mapings.actions.get(path);
-
- String type = actionMapping.getType();
-
- try {
- Action action = (Action)Class.forName(type).newInstance();
-
- String forward = action.execute(request, response);
-
-
- request.getRequestDispatcher(forward).forward(request, response);
- } catch (InstantiationException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- }
-
- @Override
- protected void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- doGet(request,response);
- }
-
- }
Action实现类的代码,我只贴一个添加用户的,其他类都没有改变,代码就不再重复了。
AddUserAction
- package com.liang.action;
-
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
-
- import com.liang.servlet.UserManager;
-
- public class AddUserAction implements Action {
-
- @Override
- public String execute(HttpServletRequest req, HttpServletResponse resp)
- throws Exception {
-
- String username = req.getParameter("username");
-
- UserManager userManager = new UserManager();
-
- userManager.add(username);
-
- return "/add_success.jsp";
- }
- }
现在我们发现TestServlet中所有的IF...ELSE语句都消失了,而且没有了业务逻辑的实现类,更加面向接口开发,此时我们的系统已经非常灵活了,但是我们为了更好的实现Struts框架的雏形,我们提出了第三个问题,如上面代码中显示的一样,所有的转向页面都写死了,我们需要显示和控制分离,如果想换一个视图,要修改Action实现类的代码。