zoukankan      html  css  js  c++  java
  • Struts2开山篇【引入Struts、自定义MyStruts框架】

    前言

    这是Strtus的开山篇,主要是引入struts框架…为什么要引入struts,引入struts的好处是什么….

    为什么要引入struts?

    首先,在讲解struts之前,我们来看看我们以前写的Servlet,下面我就随便截几张图来说明问题了…

    这里写图片描述

    这里写图片描述

    这里写图片描述

    Servlet作为MVC的Controller,无非就是三个步骤

    • 得到web层的数据、封装到JavaBean
    • 调用Service的逻辑代码
    • 跳转到相对应的JSP页面

    当我们写Servlet的时候,一般都离不开这三个步骤,也可以说,这是Servlet的“固定写法”

    那我们这样写代码,有啥不好的地方呢??再重新看回上图,我们发现几个弊端

    • 我们写一个项目需要非常非常多的Servlet,这就造成十分冗余..为了职责分明,我们却不得不这么做…
    • 跳转到JSP页面的路径被我们写死了【一旦有别的需求,就需要改源代码】

    为了解决上边的弊端,struts就应运而生了


    自定义struts

    在正式讲解struts之前,我们来看一下,以我们现在的水平,能够怎么优化它。。

    以用户的登陆注册案例来进行说明

    传统的用户登陆注册

    • dao
    public class UserDao {
    
        public User login(User user) {
    
            if ("aaa".equals(user.getUsername()) && "123".equals(user.getPsd())) {
    
                System.out.println("登陆成功!");
                return user;
    
            } else {
                System.out.println("登陆失败!");
                return null;
            }
        }
    
        public void register(User user) {
    
            System.out.println("注册成功!" + user.getUsername());
        }
    
    
    }
    • service
    
    public class UserService {
    
        private UserDao userDao = new UserDao();
    
        public User longin(User user) {
            return userDao.login(user);
        }
    
        public void register(User user) {
            userDao.register(user);
        }
    
    }
    • loginServlet
    
    @javax.servlet.annotation.WebServlet(name = "LoginServlet",urlPatterns = "/LoginServlet")
    public class LoginServlet extends javax.servlet.http.HttpServlet {
        protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
    
            //得到用户带过来的数据,封装到Bean对象中
            String username = request.getParameter("username");
            String psd = request.getParameter("psd");
    
            User user = new User();
            user.setPsd(psd);
            user.setUsername(username);
    
            try {
                //调用Service方法
                UserService userService = new UserService();
                userService.longin(user);
    
                //登陆成功跳转到首页
                request.getRequestDispatcher("/index.jsp").forward(request, response);
    
            } catch (Exception e) {
                e.printStackTrace();
    
                //登陆失败,跳转到相关的提示页面
                request.setAttribute("message","登陆失败了!!!");
                request.getRequestDispatcher("/message.jsp").forward(request, response);
            }
    
        }
    
        protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
    
            this.doPost(request, response);
    
        }
    }
    
    • registerServlet
    
    @javax.servlet.annotation.WebServlet(name = "RegisterServlet",urlPatterns = "/RegisterServlet")
    public class RegisterServlet extends javax.servlet.http.HttpServlet {
        protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
    
            //得到用户带过来的数据,封装到Bean对象中
            String username = request.getParameter("username");
            String psd = request.getParameter("psd");
    
            User user = new User();
            user.setPsd(psd);
            user.setUsername(username);
    
            try {
                //调用Service方法
                UserService userService = new UserService();
                userService.register(user);
    
                //注册成功跳转到登陆界面
                request.getRequestDispatcher("/login.jsp").forward(request, response);
    
                //注册成功,我也可以跳转到首页
                //request.getRequestDispatcher("/index.jsp").forward(request, response);
    
            } catch (Exception e) {
                e.printStackTrace();
    
                //注册失败,跳转到相关的提示页面
                request.setAttribute("message","注册失败了!!!");
                request.getRequestDispatcher("/message.jsp").forward(request, response);
            }
    
        }
    
        protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
    
            this.doPost(request, response);
    
        }
    }
    • login.jsp
    
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>$Title$</title>
      </head>
      <body>
    
    
      <form action="${pageContext.request.contextPath}/LoginServlet" method="post">
    
        用户名:<input type="text " name="username">
        密码:<input type="password " name="psd">
        <input type="submit" value="登陆">
      </form>
      </body>
    </html>
    
    • register.jsp
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>$Title$</title>
      </head>
      <body>
    
    
      <form action="${pageContext.request.contextPath}/RegisterServlet" method="post">
    
        用户名:<input type="text " name="username">
        密码:<input type="password " name="psd">
        <input type="submit" value="注册">
      </form>
      </body>
    </html>

    上面的代码已经经过了测试,是可以跑起来的。


    ①:跳转页面的路径是写死的。我在注册成功了以后,我可以跳转到首页上,也可以跳转到登陆的界面上。如果我要选择其中的一个,就必须修改源代码

    ②:一个功能对应一个Servlet,太麻烦了…写了LoginServlet,还要写RegisterServlet….


    新型的用户登陆注册

    我们会发现,无论什么Servlet上最终还是跳转到相对应的JSP页面的...也就是说,第一和第二步骤【封装数据、调用Service】我们可以封装起来…只要返回uri给Servlet跳转到JSP页面就好了


    LoginAction

    返回的uri分两种情况:

    • 如果是转发,那么返回的是RequestDispatcher对象
    • 如果是重定向,那么返回的是字符串
    
    /**
     * Created by ozc on 2017/4/26.
     * <p>
     * 一个Action对应一个Servlet,Action负责处理具体的请求
     */
    public class LoginAction {
    
    
        public Object login(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
    
            Object uri ;
    
            //得到用户带过来的数据,封装到Bean对象中
            String username = request.getParameter("username");
            String psd = request.getParameter("psd");
    
            User user = new User();
            user.setPsd(psd);
            user.setUsername(username);
    
            try {
                //调用Service方法
                UserService userService = new UserService();
                userService.longin(user);
    
                //登陆成功跳转到首页
                request.getSession().setAttribute("user", user);
    
                //跳转到首页的时候需要重定向
                //response.sendRedirect(request.getContextPath() + "/index.jsp");
    
                //如果是重定向,那么返回的是字符串
                uri = "/index.jsp";
                return uri;
    
            } catch (Exception e) {
                e.printStackTrace();
    
                //登陆失败,跳转到相关的提示页面
                request.setAttribute("message","登陆失败了!!!");
                //request.getRequestDispatcher("/message.jsp").forward(request, response);
    
                //如果是转发,那么返回的是RequestDispatcher对象
                uri = request.getRequestDispatcher("/message.jsp");
                return uri;
            }
        }
    }
    
    • LoginServlet就可以写成这样了:
    
        protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
    
            //得到LoginAction对象
            LoginAction loginAction = new LoginAction();
            Object uri = loginAction.login(request, response);
    
    
            //是重定向
            if (uri instanceof String) {
                response.sendRedirect(request.getContextPath() + uri);
            } else {
    
                //是转发,强转成是RequestDispatcher对象
                ((RequestDispatcher) uri).forward(request, response);
            }
        }
    

    RegisterAction

    • RegisterAction
    
    
    /**
     * Created by ozc on 2017/4/26.
     * 
     * 一个Action对应一个Servlet,Action负责处理具体的请求
     */
    public class RegisterAction {
    
    
        public Object register(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
    
            Object uri ;
    
            //得到用户带过来的数据,封装到Bean对象中
            String username = request.getParameter("username");
            String psd = request.getParameter("psd");
    
            User user = new User();
            user.setPsd(psd);
            user.setUsername(username);
    
            try {
                //调用Service方法
                UserService userService = new UserService();
                userService.register(user);
    
                //登陆成功跳转到登陆页面
                uri = request.getRequestDispatcher("/login.jsp");
                return uri;
    
            } catch (Exception e) {
                e.printStackTrace();
    
                //注册失败,跳转到相关的提示页面
                request.setAttribute("message","注册失败了!!!");
                //request.getRequestDispatcher("/message.jsp").forward(request, response);
    
                uri = request.getRequestDispatcher("/message.jsp");
                return uri;
            }
        }
    }
    
    • RegisterServlet
    
        protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
    
            //得到RegisterAction
            RegisterAction registerAction = new RegisterAction();
    
            Object uri = registerAction.register(request, response);
    
            //是重定向
            if (uri instanceof String) {
                response.sendRedirect(request.getContextPath() + uri);
            } else {
    
                //是转发,强转成是RequestDispatcher对象
                ((RequestDispatcher) uri).forward(request, response);
            }
        }
    

    思考

    到目前为止,我们搞了两个Action类来封装Servlet的逻辑代码,我们再次看回Servlet的代码。

    这里写图片描述

    这里写图片描述

    可以很清楚地发现:两个实现不同功能的Servlet仅仅是调用的Action不同….如果是仅仅调用的Action不同【通过反射来调用不同的Action】,那么我们应该想到使用一个Servlet来管理整个项目,也就是说:整个web项目只有一个核心的控制器

    问题:

    ①:我们在之前是直接指明Servlet的映射路径了,现在要ActionServlet处理所有的请求,我们只要定一个规则:只要后缀为.action的,那么都交由核心控制器ActionServlet来控制….

    ②:现在全部的请求已经交由ActionServlet控制,那怎么知道调用的是哪个Action???我们可以通过请求的uri,比如:http://localhost:8080/login.action,其中login就代表的是调用LoginAction..也就是说login=LoginAction,我们可以通过properties文件来配置..

    ③:现在我们已经知道了调用的是哪个Action了,但是Action可能不仅仅只有一个方法,我们还要在调用的时候,指定的方法名是什么.这很简单,一般我们都是职责分工明确的,method=login….并且,调用的Action和具体的方法也是有关系的,不可能是孤立存在的。因此,我们的配置文件是不能使用properties的,需要使用XML

    ④:在调用方法的时候,是返回一个Object的uri的,uri的类型可能是String、也可以能是RequestDispatcher、并且返回的结果可能有几种情况的【可能跳转到首页,也可能跳转到登陆界面】

    ⑤:Action调用的方法和返回的uri也是是有关系的!…..不同的Action调用不同的方法,返回的uri也是不同的….

    ⑥:要跳转到哪个页面上,可以通过标识量来识别….比如:success表示成功执行,如果要重定向那么多加个type类型,如果不重定向就没有type类型..路径使用path来表示..因此,在具体的Action中,就不需要返回具体的uri,只要返回一个标识量即可


    画一张图来梳理一下思路:

    这里写图片描述


    XML配置

    我们可以写出这样的XML配置,当ActionServlet初始化的时候,读取XML配置文件,就知道调用的是什么Action,Action中的什么方法,以及跳转到哪个页面上了

    <?xml version="1.0" encoding="UTF-8" ?>
    <mystruts>
        <package>
    
            <action name="login" className="zhongfucheng.servlet.LoginServlet" method="login">
                <!--是否存在type属性,存在则是重定向,不存在则是转发-->
                <!--result的值表示的就是跳转的路径-->
                <result name="success" type="redirect">/index.jsp</result>
                <result name="fail">/message.jsp</result>
    
            </action>
            <action name="register" className="zhongfucheng.servlet.RegisterServlet" method="register">
                <!--是否存在type属性,存在则是重定向,不存在则是转发-->
                <!--result的值表示的就是跳转的路径-->
                <result name="success">/message.jsp</result>
                <result name="fail">/message.jsp</result>
            </action>
        </package>
    
    </mystruts>

    为了更好地管理这些信息,我们应该使用JavaBean来对它们封装

    • ActionMappingManager——-管理全部的Action
    /**
     * Created by ozc on 2017/4/26.
     * 
     * 该类管理着全部的Action
     *
     * 要管理全部的Action,就需要用一个容器来装载这些Action
     *
     * 选择Map集合是最合适的,可以通过key来得到Action,key就是<action name=><action/>中的name属性
     *
     */
    public class ActionMappingManager {
    
        private Map<String, ActionMapping> map = new HashMap<>();
    
        //注意:外界都是通过name来得到对应的Action的,并不会获取得到整个Manager
        public ActionMapping getActionMapping(String name) {
            return map.get(name);
        }
    
    }
    
    • ActionMapping—-表示单个的Action
    
    public class ActionMapping {
    
        //所有的results
        private Map<String, Results> results;
    
        //关键字name
        private String name;
    
        //要调用的Action路径
        private String className;
    
        //Action中的方法
        private String method;
    
        public Map<String, Results> getResults() {
            return results;
        }
    
        public void setResults(Map<String, Results> results) {
            this.results = results;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getClassName() {
            return className;
        }
    
        public void setClassName(String className) {
            this.className = className;
        }
    
        public String getMethod() {
            return method;
        }
    
        public void setMethod(String method) {
            this.method = method;
        }
    }
    
    • Results—表示的是结果视图
    
    
    /**
     * Created by ozc on 2017/4/26.
     *
     * 该类表示的是结果视图
     *
     *
     *
     */
    public class Results {
    
        //方法返回的标识
        private String name;
    
        //要跳转的方式
        private String type;
    
        //要跳转的页面
        private String page;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getType() {
            return type;
        }
    
        public void setType(String type) {
            this.type = type;
        }
    
        public String getPage() {
            return page;
        }
    
        public void setPage(String page) {
            this.page = page;
        }
    }
    

    ActionMappingManager读取配置文件

    在ActionMappingManager中,应该读取配置文件,然后把信息全部封装到里边去…

    
    /**
     * Created by ozc on 2017/4/26.
     *
     * 该类管理着全部的Action
     *
     * 要管理全部的Action,就需要用一个容器来装载这些Action
     *
     * 选择Map集合是最合适的,可以通过key来得到Action,key就是<action name=><action/>中的name属性
     *
     */
    public class ActionMappingManager {
        private Map<String, ActionMapping> allAction ;
    
        public ActionMappingManager() {
            this.allAction = new HashMap<>();
    
            //读取配置文件信息
            init();
    
        }
    
    
        public void init() {
            /********通过DOM4J读取配置文件信息*********/
    
            try {
                //得到解析器
                SAXReader saxReader = new SAXReader();
    
                //读取在类目录下的mystruts.xml文件
                InputStream stream = ActionMappingManager.class.getClassLoader().getResourceAsStream("mystruts.xml");
    
                //得到代表XML文件的Document对象
                Document document = saxReader.read(stream);
    
                //通过XPATH直接得到所有的Action节点
                List list = document.selectNodes("//action");
    
                //得到每个Action节点
                for (int i = 0; i < list.size(); i++) {
                    Element action = (Element) list.get(i);
    
                    //把得到每个Action的节点信息封装到ActionMapping中
                    ActionMapping actionMapping = new ActionMapping();
    
                    String name = action.attributeValue("name");
                    String method = action.attributeValue("method");
                    String className = action.attributeValue("className");
                    actionMapping.setName(name);
                    actionMapping.setMethod(method);
                    actionMapping.setClassName(className);
    
                    //得到action节点下的所有result节点
                    List results = action.elements("result");
    
                    //得到每一个result节点
                    for (int j = 0; j < results.size(); j++) {
                        Element result = (Element) results.get(j);
    
                        //把得到每个result节点的信息封装到Results中
                        Results results1 = new Results();
    
                        //得到节点的信息
                        String name1 = result.attributeValue("name");
                        String type = result.attributeValue("type");
                        String page = result.getText();
    
                        results1.setName(name1);
                        results1.setType(type);
                        results1.setPage(page);
    
                        //把result节点添加到ActionMapping的集合中
                        actionMapping.getResults().put(name1, results1);
                    }
    
                    //最后把得到每个ActionMapping的信息添加到ActionMappingManager中
                    allAction.put(name, actionMapping);
    
                }
    
    
    
            } catch (DocumentException e) {
    
                new RuntimeException("初始化的时候出错了!“" + e);
            }
        }
    
        //注意:外界都是通过name来得到对应的Action的,并不会获取得到整个Manager
        public ActionMapping getActionMapping(String name) {
            return allAction.get(name);
        }
    }
    

    ActionServlet

    使用init()方法只加载创建一个ActionManagerMapping对象,并设置在Web容器启动了该Servlet就启动

    
    /**
     * Created by ozc on 2017/4/26.
     *
     *
     * Web容器一启动的时候,该类就应该加载了,在web.xml文件中配置onloadStart
     */
    
    public class ActionServlet extends HttpServlet {
    
    
        //该对象封装了所有的XML信息
        ActionMappingManager actionMappingManager ;
        @Override
        public void init() throws ServletException {
    
            //让ActionMappingManager对象只有一个!
            actionMappingManager = new ActionMappingManager();
        }
    
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
            try {
                //得到用户的uri
                String uri = request.getRequestURI();
    
                //截取uri的关键部分-----截完应该是login
                uri = uri.substring(uri.lastIndexOf("/") + 1, uri.lastIndexOf("."));
    
                //通过uri得到配置文件中的action信息
                ActionMapping actionMapping = actionMappingManager.getActionMapping(uri);
    
                //得到action的类名,方法名
                String className = actionMapping.getClassName();
                String method = actionMapping.getMethod();
    
                //通过反射创建出Action的对象,调用对应的方法
                Class t = Class.forName(className);
                Object o = t.newInstance();
    
                //注意:这里的参数是接口的class,不是单纯的request的class,单纯的class是实现类
                Method m = t.getMethod(method, HttpServletRequest.class, HttpServletResponse.class);
    
                //调用方法,得到标记
                String returnFlag = (String) m.invoke(o, request, response);
    
    
                //通过标记得到result的具体信息
                Results result = actionMapping.getResults().get(returnFlag);
                String type = result.getType();
                String page = result.getPage();
    
                //判断是重定向还是转发,为空就是转发,反则是重定向
                if (type == null) {
                    response.sendRedirect(page);
                } else {
                    request.getRequestDispatcher(request.getContextPath() + page).forward(request, response);
                }
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
            this.doPost(request, response);
    
        }
    }

    具体的Action的方法只要返回一个标识量即可,我们通过标识量来得到具体的跳转页面url和跳转的方法的。。。


    效果:

    这里写图片描述


    总结:

    由于传统web的Controller模块存在弊端:

    • 需要创建非常多的Servlet
    • 跳转的页面写死了。改变需求的时候需要更改源代码

    因此struts就应运而生了,本博文主要模拟Struts的开发流程

    使用一个ActionServlet核心控制器来管理全部的Web请求,写XML配置文件,读取配置文件。通过uri来判断要调用具体的Action,Action中的方法。得到返回值,再根据XML文件的配置信息来确定跳转方法、跳转的url

    如果您觉得这篇文章帮助到了您,可以给作者一点鼓励



  • 相关阅读:
    jquery跑马灯效果(ajax调取数据)
    IE6下双倍边距和关于IE6 7display:inline无效的问题
    js 利用ajax将前台数据传到后台(json格式)
    js 利用ajax将前台数据传到后台(1)
    js 点击某一块就显示某一块
    点击进行复制的JS代码
    jq利用ajax调用后台方法
    每一个程序员需要了解的10个Linux命令
    101个MySQL的调节和优化技巧
    JavaScript Math和Number对象
  • 原文地址:https://www.cnblogs.com/zhong-fucheng/p/7202993.html
Copyright © 2011-2022 走看看