struts1原理

来源:互联网 发布:怎样利用淘宝赚钱 编辑:程序博客网 时间:2024/05/20 11:25

struts1属于MVC开发模式中的控制层框架,这种控制层框架的主要作用是将模型与视图分离(就是用户发送一个请求的时候,后台并不是直接在jsp页面里进行业务逻辑操作,把数据直接渲染到页面上返回给用户。而是先获取数据,再解析页面,再把数据和页面进行组合,最后返回给用户响应,达到一个解耦的作用。),而MVC这种开发模式的作用也是实现这个作用。

ps:在多年以前的开发过程中是没有像struts这样的控制层框架在模型和视图之间进行解耦的,而是直接在jsp页面上写业务逻辑,连接数据库,进行增删改查(这也就是为什么jstl标签库会提供那么多类似连接数据库等功能强大的标签的原因),显然这种开发方式不利于维护,不利于扩展,不利于复用,所以就出现了MVC这种设计模式,就有struts1,sruts2,springMVC这种控制层框架在模型和视图之间进行解耦。

下面进入正题:

struts1

1.初始化ActionServlet

struts1通过一个前端控制器ActionServlet来匹配所有后缀为”.do”的请求,这个servlet在web容器(Tomcat)中配置成一启动就加载并初始化的servlet。

<?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns="http://java.sun.com/xml/ns/javaee"    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"    id="WebApp_ID" version="3.0">    <display-name>struts1Login</display-name>    <welcome-file-list>        <welcome-file>login.jsp</welcome-file>    </welcome-file-list>    <!-- 配置struts1的ActionServlet匹配所有.do请求,在系统启动时就初始化 -->    <servlet>        <description>ActionServlet</description>        <servlet-name>ActionServlet</servlet-name>        <servlet-class>com.tz.jspstudy.framework.struts.servlet.ActionServlet</servlet-class>        <!--             值设为大于等于0的正数,web容器(Tomcat)在启动的时候就加载这个servlet             值小于0或者没有配置该选项的时候,用户在请求该servlet的时候才会加载初始化这个servlet            值必须为整数,并且正数的值越小优先级越高,这里把他的优先级设为0,表示最高的优先级            如果两个servlet的值相同,容器会自行选择哪一个servlet被先加载。        -->        <load-on-startup>0</load-on-startup>    </servlet>    <servlet-mapping>        <servlet-name>ActionServlet</servlet-name>        <url-pattern>*.do</url-pattern>    </servlet-mapping></web-app>

ActionServlet在初始化的时候调用init方法,使用DOM4J解析struts1的配置文件struts-config.xml,并将配置信息存储到ActionMapping中然后将ActionMapping放到分发器DispatchProcessor中,这个分发器的作用是将ActionServlet所匹配到的请求分发给对应的业务控制类Action来处理

package com.tz.jspstudy.framework.struts.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.tz.jspstudy.framework.struts.bean.ActionMapping;public class ActionServlet extends HttpServlet {    private static final long serialVersionUID = 1L;    private ActionMapping mapping = null;    private DispatchProcessor dispatchProcessor = new DispatchProcessor();    public ActionServlet() {        super();    }    public void destroy() {    }    //处理请求    protected void service(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {              //请求传递给RquestProcessor处理        System.out.println("已经进入ActionServlet sevice方法-准备分发请求");        dispatchProcessor.processor(request, response);     }    //系统启动的时候 就会初始化ActionSevelet     public void init() throws ServletException {        System.out.println("初始化ActionServlet(所有的请求入口-准备加载struts-config配置文件)");        //获得配置文件所在的文件路径(这里将它放在WEB-INF目录下)        String realPath = this.getServletContext().getRealPath("/WEB-INF");             //使用DOM4J解析struts-config.xml配置文件中的配置信息,封装成一个ActionMapping对象        mapping = new XMLParser().parseXML(realPath);        dispatchProcessor.setMapping(mapping);        System.out.println("初始化ActionServlet(所有的请求入口-加载struts-config配置文件完毕)");    }}

struts-config.xml配置文件中的配置信息主要有两个:

  1. “请求名称” 与 “对应的处理该请求的业务控制类” 之间的映射关系(就是你发送的这个请求将由哪个业务控制类来处理)
  2. form-name与所对应的表单类之间的映射关系(就是与你这个请求相关联的表单对象的名称,在这个表单对象里存储着你请求里面的参数信息)
<?xml version="1.0" encoding="UTF-8"?><struts-config>    <!-- 用来配置form-name与所对应的表单类之间的映射关系 -->    <form-beans>        <!-- form-bean标签有多个 -->        <form-bean name="loginForm" type="com.tz.jspstudy.sysmanage.formbean.LoginForm"/>    </form-beans>    <!-- 用来配置  "请求名path" 与   "对应的处理该请求的业务控制类type" 之间的映射关系 -->    <action-mappings>        <!--             action元素:配置业务Action类            path : 请求的路径            type : 请求对应的业务Action类的类路径            -->        <!-- action标签有多个 -->        <action path="/login" type="com.tz.jspstudy.sysmanage.action.LoginAction" name="loginForm"/>    </action-mappings></struts-config>

使用DOM4J解析xml文件的代码如下:

package com.tz.jspstudy.framework.struts.servlet;import java.util.List;import org.dom4j.Document;import org.dom4j.DocumentException;import org.dom4j.Element;import org.dom4j.io.SAXReader;import com.tz.jspstudy.framework.struts.bean.ActionConfig;import com.tz.jspstudy.framework.struts.bean.ActionMapping;import com.tz.jspstudy.framework.struts.bean.FormBeanConfig;public class XMLParser {    public ActionMapping parseXML(String realPath) {        ActionMapping mapping = new ActionMapping();        //构造SAX解析器        SAXReader reader = new SAXReader();        try {            //获得文档对象和根元素            Document doc = reader.read(realPath+"/struts-config.xml");            Element root = doc.getRootElement();            //读取form-beans标签            Element formBeans = root.element("form-beans");            List<Element> formBeanList = formBeans.elements();            //循环封装对象            for (Element formBean : formBeanList) {                String name = formBean.attributeValue("name");                String type = formBean.attributeValue("type");                              FormBeanConfig config = new FormBeanConfig();                config.setName(name);                config.setType(type);                               //数据存入ActionMapping                mapping.setFormBeanConfig(config);            }            //读取action-mappings            Element actions = root.element("action-mappings");            List<Element> actionList = actions.elements();            for (Element action : actionList) {                String name = action.attributeValue("name");                String path = action.attributeValue("path");                String type = action.attributeValue("type");                ActionConfig config = new ActionConfig();                config.setName(name);                config.setPath(path);                config.setType(type);                //数据封装到mapping中                mapping.setActionConfig(config);            }        } catch (DocumentException e) {            e.printStackTrace();        }        return mapping;    }}

ActionMapping类的成员如下:

package com.tz.jspstudy.framework.struts.bean;import java.util.HashMap;import java.util.Map;public class ActionMapping {    //key是name 存储表单名与表单对象之间的映射关系    private Map<String,FormBeanConfig> formMap = new HashMap<String,FormBeanConfig>();    //key是path 存储请求名称与处理该请求的业务控制类之间的映射关系    private Map<String,ActionConfig> actionMap = new HashMap<String,ActionConfig>();    public void setFormBeanConfig(FormBeanConfig config) {        formMap.put(config.getName(), config);    }    public void setActionConfig(ActionConfig config) {        actionMap.put(config.getPath(), config);    }    public FormBeanConfig getFormBeanConfig(String name) {        return formMap.get(name);    }    public ActionConfig getActionConfig(String path) {        return actionMap.get(path);    }}

2.客户端发送请求

用户发送一个请求,该请求会被ActionServlet拦截然后进入service方法里

3.分发器分发请求

在该方法里,ActionServlet会调用分发器(DispatchProcessor)的processor方法来分发request请求。

protected void service(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {              //请求传递给RquestProcessor处理        System.out.println("已经进入ActionServlet sevice方法-准备分发请求");        dispatchProcessor.processor(request, response);     }

在分发器内部持有上文提到的ActionMapping对象的引用,以及一个actionMap(存储Action的缓存,就是一个map集合)

进入processor方法,根据url地址截取请求名称path

String path = null;String uri = request.getRequestURI(); // 得到/工程名/xxx.doString contenPath = request.getContextPath(); // 得到 /工程名uri = uri.substring(contenPath.length()); // 去掉工程名得到 /xxx.dopath = uri.substring(0, uri.length() - 3); // 去掉.do 获得/xxxSystem.out.println("进入请求分发器DispatchProcessor--解析完请求方法,请求的路径为"+path);

创建ActionConfig对象

然后在ActionMapping中根据请求的名称查找该请求的相关信息,然后创建一个ActionConfig对象(该对象其实是对struts-config.xml文件中action配置信息的封装)
如果创建的ActionConfig对象为空说明ActionMpping中没有与该请求名对应的配置信息,就会抛出异常

// 根据路径获取ActionConfig对象ActionConfig config = mapping.getActionConfig(path);// config为空则说明没有配置这个路径if (config == null) {    throw new ServletException("struts-config.xml没有配置" + path + "路径");}

ActionConfig的类信息如下:

package com.tz.jspstudy.framework.struts.bean;public class ActionConfig {    // 请求的名称    private String path;    // 请求对应的业务控制类    private String type;    // 与该请求相关的表单名form-name    private String name;    public String getPath() {        return path;    }    public void setPath(String path) {        this.path = path;    }    public String getType() {        return type;    }    public void setType(String type) {        this.type = type;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}

查找Action

如果ActionConfig不为空,则会到ActionMap缓存中根据path(键)去查找对应的Action,如果没有查找到该Action说明客户端第一次发送该请求,那么分发器就会通过反射创建一个该Action的实例,并且放入缓存里,查找到了就直接赋值给引用。

Action action = actionMap.get(path);// 用路径在缓存中找action的实例,没找到说明是第一次用户请求这个Action,则反射创建缓存if (action == null) {    // 反射的代码    action = this.createAction(config);    // 缓存    actionMap.put(path, action);}

填充Form

接下来会到刚才封装的ActionConfig对象中取出form的name值到FormBeanConfig(ActionServlet在初始化的时候创建的一个form-name到form之间的映射)中查找是否有相对应的表单对象,如果没有就说明配置文件中action配置中的form的name属性配置错误,抛出异常;如果有就会根据FormBeanConfig对象创建一个ActionForm对象并从request中获取参数赋值给ActionForm。

// 获得ActionForm设置参数ActionForm form = null;// 获得ActionConfig的name,如果有就设置参数if (config.getName() != null) {    // 用name查找出对应的form-bean标签    FormBeanConfig cfg = mapping.getFormBeanConfig(config.getName());    if (cfg == null) {        throw new ServletException(                "action标签的name必须匹配一个form-bean标签的name");    }    // 反射创建ActionForm的实例    form = this.createActionForm(cfg);    // 反射设置好参数    this.setParameter(form, request);}

FormBeanConfig类的信息:

package com.tz.jspstudy.framework.struts.bean;public class FormBeanConfig {    // form-name    private String name;    // 对应的form类    private String type;    public String getType() {        return type;    }    public void setType(String type) {        this.type = type;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}

6.执行Action的execute方法

在执行Action的execute方法的时候会把上一步封装的ActionForm对象一并传入方法内,处理完请求后返回一个ActionForward对象,ActionForward对象中其实就是封装了一个标记位(用来判断是转发还是重定向)和一个路径(用于转发或重定向的路径)

// 调用Action的execute获得ActionForward转发/重定向try {    System.out.println("进入请求分发器--准备调用对应的业务处理Action");    ActionForward forward = action.execute(form, request, response);    if (forward.isForward()) {              request.getRequestDispatcher(forward.getPath()).forward(request, response);    } else {        response.sendRedirect(forward.getPath());    }} catch (Exception e) {    e.printStackTrace();}

ActionForward类的代码如下:

package com.tz.jspstudy.framework.struts.servlet;public class ActionForward {    private boolean isForward = true;   //默认是转发    private String path;    public ActionForward(String path) {        this.path = path;    }    public ActionForward(String path,boolean isForward) {        this.path = path;        this.isForward = isForward; //传递false,则是做重定向    }    public boolean isForward() {        return isForward;    }    public void setForward(boolean isForward) {        this.isForward = isForward;    }    public String getPath() {        return path;    }    public void setPath(String path) {        this.path = path;    }}

Action的代码如下:

package com.tz.jspstudy.framework.struts.servlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class Action {    public ActionForward execute(ActionForm form,HttpServletRequest request,HttpServletResponse response) throws Exception {        return null;    }   }

完整的分发器类以及它的processor方法如下:

package com.tz.jspstudy.framework.struts.servlet;import java.io.IOException;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.Enumeration;import java.util.HashMap;import java.util.Map;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import com.tz.jspstudy.framework.struts.bean.ActionConfig;import com.tz.jspstudy.framework.struts.bean.ActionMapping;import com.tz.jspstudy.framework.struts.bean.FormBeanConfig;public class DispatchProcessor {    private ActionMapping mapping = null;    // 这个MAP是缓存所有的Action的实例的,因为Action写成单例模式    private Map<String, Action> actionMap = new HashMap<String, Action>();    public DispatchProcessor() {    }    public void setMapping(ActionMapping mapping) {        this.mapping = mapping;    }    public void processor(HttpServletRequest request,            HttpServletResponse response) throws ServletException, IOException {        //http://localhost:8080/studyJsp/login.do        //login        // 获得Action        // 获得请求的路径地址        System.out.println("进入请求分发器DispatchProcessor--解析请求方法,去ActionConfig对象匹配");        String path = null;        String uri = request.getRequestURI(); // 得到/工程名/xxx.do        String contenPath = request.getContextPath(); // 得到 /工程名        uri = uri.substring(contenPath.length()); // 去掉工程名得到 /xxx.do        path = uri.substring(0, uri.length() - 3); // 去掉.do 获得/xxx        System.out.println("进入请求分发器DispatchProcessor--解析完请求方法,请求的路径为"+path);        // 根据路径获取ActionConfig对象        ActionConfig config = mapping.getActionConfig(path);        // config为空则说明没有配置这个路径        if (config == null) {            throw new ServletException("struts-config.xml没有配置" + path + "路径");        }        Action action = actionMap.get(path);        // 用路径在缓存中找action的实例,没找到说明是第一次用户请求这个Action,则反射创建缓存        if (action == null) {            // 反射的代码            action = this.createAction(config);            // 缓存            actionMap.put(path, action);        }        // 获得ActionForm设置参数        ActionForm form = null;        // 获得ActionConfig的name,如果有就设置参数        if (config.getName() != null) {            // 用name查找出对应的form-bean标签            FormBeanConfig cfg = mapping.getFormBeanConfig(config.getName());            if (cfg == null) {                throw new ServletException(                        "action标签的name必须匹配一个form-bean标签的name");            }            // 反射创建ActionForm的实例            form = this.createActionForm(cfg);            // 反射设置好参数            this.setParameter(form, request);        }        // 调用Action的execute获得ActionForward转发/重定向        try {            System.out.println("进入请求分发器--准备调用对应的业务处理Action");            ActionForward forward = action.execute(form, request, response);            if (forward.isForward()) {                request.getRequestDispatcher(forward.getPath()).forward(request, response);            } else {                response.sendRedirect(forward.getPath());            }        } catch (Exception e) {            e.printStackTrace();        }    }    private void setParameter(ActionForm form, HttpServletRequest request) {        // 得到所有的参数名称        Enumeration<String> enu = request.getParameterNames();        // 迭代所有名称        while (enu.hasMoreElements()) {            String name = enu.nextElement();            //过滤掉method参数            if ("method".equals(name)) {                continue;            }            // 根据名称拼接set方法            String setMethodName = "set" + name.substring(0, 1).toUpperCase()                    + name.substring(1);            // 得到参数的值,反射调用form的set方法传递值            String[] array = request.getParameterValues(name);            Class clz = form.getClass();            Method method = null;            try {                if (array.length > 1) {                    method = clz.getMethod(setMethodName, String[].class);                    method.invoke(form, array);                } else {                    method = clz.getMethod(setMethodName, String.class);                    method.invoke(form, array[0]);                }            } catch (SecurityException e) {                e.printStackTrace();            } catch (NoSuchMethodException e) {                e.printStackTrace();            } catch (IllegalArgumentException e) {                e.printStackTrace();            } catch (IllegalAccessException e) {                e.printStackTrace();            } catch (InvocationTargetException e) {                e.printStackTrace();            }        }    }    private ActionForm createActionForm(FormBeanConfig config) {        String type = config.getType();        ActionForm form = null;        try {            form = (ActionForm) Class.forName(type).newInstance();        } catch (InstantiationException e) {            e.printStackTrace();        } catch (IllegalAccessException e) {            e.printStackTrace();        } catch (ClassNotFoundException e) {            e.printStackTrace();        }        return form;    }    private Action createAction(ActionConfig config) {        String type = config.getType();        Action action = null;        try {            action = (Action) Class.forName(type).newInstance();        } catch (InstantiationException e) {            e.printStackTrace();        } catch (IllegalAccessException e) {            e.printStackTrace();        } catch (ClassNotFoundException e) {            e.printStackTrace();        }        return action;    }}

8.响应客户端

最终响应给客户端

注:上述代码并非源码,仅供理解原理。

原创粉丝点击