struts1原理
来源:互联网 发布:怎样利用淘宝赚钱 编辑:程序博客网 时间:2024/05/20 11:25
struts1属于MVC开发模式中的控制层框架,这种控制层框架的主要作用是将模型与视图分离(就是用户发送一个请求的时候,后台并不是直接在jsp页面里进行业务逻辑操作,把数据直接渲染到页面上返回给用户。而是先获取数据,再解析页面,再把数据和页面进行组合,最后返回给用户响应,达到一个解耦的作用。),而MVC这种开发模式的作用也是实现这个作用。
ps:在多年以前的开发过程中是没有像struts这样的控制层框架在模型和视图之间进行解耦的,而是直接在jsp页面上写业务逻辑,连接数据库,进行增删改查(这也就是为什么jstl标签库会提供那么多类似连接数据库等功能强大的标签的原因),显然这种开发方式不利于维护,不利于扩展,不利于复用,所以就出现了MVC这种设计模式,就有struts1,sruts2,springMVC这种控制层框架在模型和视图之间进行解耦。
下面进入正题:
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配置文件中的配置信息主要有两个:
- “请求名称” 与 “对应的处理该请求的业务控制类” 之间的映射关系(就是你发送的这个请求将由哪个业务控制类来处理)
- 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.响应客户端
最终响应给客户端
注:上述代码并非源码,仅供理解原理。
- struts1 原理
- struts1,原理
- Struts1原理
- struts1 原理
- struts1原理
- Struts1 的工作原理
- struts1的工作原理
- struts1的工作原理
- Struts1工作原理概述
- Struts1.x工作原理
- struts1.2原理
- struts1工作原理
- struts1工作原理
- struts1的工作原理
- struts1的工作原理
- struts1.2原理
- struts1工作原理
- struts1的工作原理
- 怎么样利用激光雷达检测车道线?这上面提供了4个方法---凯利讯半导体
- 语义化版本(SemVer)的范围
- 公共技术点之面向对象六大原则
- Android性能优化
- 第15章 面向对象与原型
- struts1原理
- halcon有三种模板匹配方法
- 怎么给从数据库得到list<>追加一条记录(下拉框添加全部)
- Java基础之IO流知识点总结三
- java中字符串为何设置为不可改变对象
- 设计模式总结
- Log4j日志体系结构
- SQL函数随笔
- 通用的表单验证,邮箱身份证号和电话