深入理解Struts2原理之自己实现Struts2框架
来源:互联网 发布:知乎 招聘 编辑:程序博客网 时间:2024/05/28 09:32
作为struts2的初学者,有时候理解struts框架的原理,网上看教程会一头雾水,所以要是能自己体会实现struts框架的整个过程,对理解struts思路会更加清晰一些,下面就来尝试自己实现这个过程。
先看一下struts2的官方框架结构图
上面那个图好复杂,看不懂没关系,我们先简化一下过程方便理解,因为作为初学者不能一下子就实现了整个框架的所有功能,只能实现他的核心功能,如下图:
这样图就好看很多了,大概意思是我们发送的request请求先经过过滤器筛选,然后到达ActionInvocation,通过这个类加载拦截器,再去找到对应的action,再找到action要返回的jsp界面,然后响应(response)返回显示数据。
官方的图里有Actionproxy,这是个action代理对象,通过他找到ConfigManager读取配置文件然后发送需要的数据到ActionInvocation中,本文是自己实现struts框架,没必要弄的这么复杂,就把这部分融合到ActionInvocation中去了,其他部分就不介绍了,因为我也不懂是啥
下面实现过程,每一个类希望大家参考这个图边看边实现,这样才好理解
接下来要说的就是需要具备的技术:
- XML解析,Xpath表达式.(dom4j)
- Servlet技术
- java内省(BeanUtils)(参数拦截器)
- ThreadLocal线程本地化类
- 递归调用
需要用到的jar包:
(jar其他版本的自己找了)
commons-beanutils-1.8.3.jar
commons-logging-1.1.1.jar
dom4j-1.6.1.jar
jaxen-1.1-beta-6.jar
步骤1:
创建web项目,然后导入jar包
步骤2:
创建struts.xml文件,放在src目录下
<?xml version="1.0" encoding="UTF-8" ?><struts> <interceptor class="com.test.interceptor.ParamInterceptor" /> <constant name="struts.action.extension" value="action" /> <action name="HelloAction" method="regist" class="com.test.action.HelloAction" > <result name="success" >/index.jsp</result> </action> <action name="registAction" method="regist" class="com.test.action.RegistAction" > <result name="regist" >/home.jsp</result> </action></struts>
步骤3:
创建读取XML配置文件的解析类(ConfigurationManager)
public class ConfigurationManager { /** * 读取Interceptor * @return */ public static List<String> getInterceptors(){ List<String> interceptors = null; //1创建解析器 SAXReader reader = new SAXReader(); //2.加载配置文件=>document //获得配置文件流 InputStream is = ConfigurationManager.class.getResourceAsStream("/struts.xml"); Document doc = null; try{ doc = reader.read(is); }catch(DocumentException e){ e.printStackTrace(); throw new RuntimeException("配置文件加载失败!"); } //3.书写xpath String xpath = "//interceptor"; //4.根据xpath获得拦截器配置 List<Element> list = doc.selectNodes(xpath); //5.将配置信息封装到list集合中 if(list != null && list.size()>0){ interceptors = new ArrayList<String>(); for(Element ele : list){ String className = ele.attributeValue("class"); interceptors.add(className); } } //返回 return interceptors; } /** * 读取action * @return */ public static Map<String,ActionConfig> getActionConfig() { Map<String, ActionConfig> actionMap; Document doc = getDocument(); String xpath = "//action"; List<Element> list = doc.selectNodes(xpath); if(list == null || list.size() ==0){ return null; } actionMap = new HashMap<String,ActionConfig>(); for(Element e : list){ ActionConfig action = new ActionConfig(); action.setName(e.attributeValue("name")); action.setClassName(e.attributeValue("class")); String method = e.attributeValue("method"); action.setMethod(method==null||method.trim().equals("")?"execute":method); List<Element> results = e.elements("result"); for(Element result : results){ action.getResult().put(result.attributeValue("name"), result.getText()); } actionMap.put(action.getName(),action); } return actionMap; } private static Document getDocument() { Map<String,ActionConfig> actionMap =null; //1创建解析器 SAXReader reader = new SAXReader(); //2.加载配置文件=>document //获得配置文件流 InputStream is = ConfigurationManager.class.getResourceAsStream("/struts.xml"); Document doc; try { doc = reader.read(is); return doc; } catch (DocumentException e) { e.printStackTrace(); throw new RuntimeException("加载配置文件失败!"); } } /** * 读取constant * @param key * @return */ public static String getConstant(String key) { Document doc = getDocument(); String path = "//constant[@name='"+key+"']"; Element constant = (Element) doc.selectSingleNode(path); if(constant!=null){ return constant.attributeValue("value"); }else{ return null; } }}
创建需要的ActionConfig类:
public class ActionConfig { /** * <action name="" method="" class="" > <result name="success" >/index.jsp</result> </action> */ private String name;//对应的是action中的name private String method;//对应的是action中的method private String className;//对应的是action中的class private Map<String,String> result = new HashMap<String, String>();//对应的是action中的result public String getName() { return name; } public void setName(String name) { this.name = name; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } public Map<String, String> getResult() { return result; } public void setResult(Map<String, String> result) { this.result = result; }}
步骤4:
创建Struts2的数据中心(ActionContext)
public class ActionContext implements Serializable{ private static final long serialVersionUID = 1294441883362417229L; //为了保证能获得ActionContext public static ThreadLocal<ActionContext> tl = new ThreadLocal<ActionContext>(); ///用于存放各个域 private Map<String, Object> context; public ActionContext(Map<String, Object> context) { this.context = context; } public ActionContext(HttpServletRequest request, HttpServletResponse response, Object action) { // 准备域 context = new HashMap<String, Object>(); // 1.request context.put(Constant.REQUEST, request); // 2. response context.put(Constant.RESPONSE, response); // 3. param context.put(Constant.PARAM, request.getParameterMap()); // 4.session context.put(Constant.SESSION, request.getSession()); // 5.application context.put(Constant.APPLICATION, request.getSession() .getServletContext()); // --------------------------------------------------- // 6.valuestack 值栈 ValueStack vs = new ValueStack(); // 将action压入栈顶 vs.push(action); // 将值栈放入request域 request.setAttribute(Constant.VALUE_STACK, vs); // 将值栈放入数据中心 context.put(Constant.VALUE_STACK, vs); // ----------------------------------------------------------------- tl.set(this); } //下面提供域的获取方法 public HttpServletRequest getRequest() { return (HttpServletRequest) context.get(Constant.REQUEST); } public HttpServletResponse getResponse() { return (HttpServletResponse) context.get(Constant.RESPONSE); } public HttpSession getSession() { return (HttpSession) context.get(Constant.SESSION); } public ServletContext getApplication() { return (ServletContext) context.get(Constant.APPLICATION); } public Map<String, String[]> getParam() { return (Map<String, String[]>) context.get(Constant.PARAM); } public ValueStack getStack() { return (ValueStack) context.get(Constant.VALUE_STACK); } public static ActionContext getActionContext(){ return tl.get(); } }
需要用到的类(Constant ):
public class Constant { /** * request域 */ public static final String REQUEST = "com.test.request"; /** * response域 */ public static final String RESPONSE = "com.test.response"; /** * session域 */ public static final String SESSION = "com.test.session"; /** * application域 */ public static final String APPLICATION = "com.test.application"; /** * param域 */ public static final String PARAM = "com.test.param"; /** * value_stack域 */ public static final String VALUE_STACK = "com.test.stack";}
需要用到的类(值栈):
public class ValueStack { private List<Object> list = new ArrayList<Object>(); //弹栈 public Object pop(){ return list.remove(0); } //压栈 public void push(Object o){ list.add(0, o); } //取出顶部对象 public Object seek(){ return list.get(0); }}
需要用到的接口(Interceptor):
public interface Interceptor extends Serializable{ /** * 初始化 */ public void init(); /** * 拦截 * @param invocation * @return */ public String interceptor(ActionInvocation invocation); /** * 销毁 */ public void destory();}
步骤5:
创建ActionInvocation
ActionInvocation负责完成拦截器链状调用以及action调用,以及数据中心(ActionContext)的提供
public class ActionInvocation { //过滤器链 private Iterator<Interceptor> interceptors; //即将调用的action实例 private Object action; //action配置信息 private ActionConfig config; //数据中心 private ActionContext ac; public ActionInvocation(List<String> InterceptorClassNames,ActionConfig config,HttpServletRequest request,HttpServletResponse response) { //1 准备Interceptor链 List<Interceptor> interceptorList = null; if(InterceptorClassNames!=null && InterceptorClassNames.size()>0){ interceptorList = new ArrayList<Interceptor>(); for(String className : InterceptorClassNames){ Interceptor interceptor; try { //获取实例 interceptor = (Interceptor) Class.forName(className).newInstance(); interceptor.init();//初始化 } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("创建Interceptor失败!!"+className); } interceptorList.add(interceptor); } this.interceptors = interceptorList.iterator(); } //2 准备action实例 this.config = config; try { action = Class.forName(config.getClassName()).newInstance();//获取action对象 } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("action创建失败!"+config.getClassName()); } //3 准备数据中心actionContext ac = new ActionContext(request, response, action); } public ActionContext getActionContext() { return ac; } /** * 递归调用拦截器链 * @param invocation * @return */ public String invoke(ActionInvocation invocation){ //1 准备一个变量接受action运行结果的路由串 String result = null; //2 判断拦截器链中是否有下一个拦截器&&变量是否被赋值 if(interceptors!= null && interceptors.hasNext() && result==null ){ //有=>调用下一个拦截的拦截方法 Interceptor it = interceptors.next(); result = it.interceptor(invocation); }else{ //没有=> 调用action实例的处理方法 //获得将要调用的action方法名称 String methodName = config.getMethod(); // execute //根据action对象和方法名称获得方法对应的Method对象 try { Method executeMethod = action.getClass().getMethod(methodName); //调用目标方法 result = (String) executeMethod.invoke(action); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("您配置的action方法不存在!"); } } //3将action的结果路由串返回 return result; }
步骤6:
创建StrutsPrepareAndExecuteFilter 过滤器类:
public class StrutsPrepareAndExcuteFilter implements Filter{ //配置文件中的过滤器配置信息 private List<String> InterceptorList; //struts处理的action后缀 private String extension; // 配置文件中action配置信息 private Map<String, ActionConfig> actionConfigs; /** * 初始化 */ public void init(FilterConfig arg0) throws ServletException { //1> 准备过滤器链配置 InterceptorList = ConfigurationManager.getInterceptors(); //2> 准备constant配置=> 访问后缀的配置信息 extension = ConfigurationManager.getConstant("struts.action.extension"); //3> 加载action配置 actionConfigs = ConfigurationManager.getActionConfig(); } /** * 过滤器处理 */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //0 强转request和response为 HttpServletRequest 和 HttpServletResponse HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; //1 获得请求路径 // http://localhost:8080/MyStruts/HellAction.action String path = req.getServletPath(); // /HellAction.action //2 判断请求是否需要访问action if(!path.endsWith(extension)){ // 后缀不以".action"结尾 => 不需要访问action资源 => chain.doFIlter()放行 chain.doFilter(request, response); return; }else{ // 后缀以".action"结尾 => 需要访问action //3 获得需要访问的action名称=>提取需要访问的action名称 path = path.substring(1);// HellAction.action path = path.replace("."+extension, "");// HellAction //4 查找action对应的配置信息 ActionConfig config = actionConfigs.get(path); if(config == null){ //未找到配置信息 => 抛出异常提示访问的action不存在 throw new RuntimeException("访问的action不存在!"); } //找到配置信息 => 获得到配置信息=>继续 //5 创建actionInvocation实例,完成对拦截器器链以及action的方法 ActionInvocation invocation = new ActionInvocation(InterceptorList,config,req,resp); //6 获得结果串 String result = invocation.invoke(invocation); //success //7 从配置信息找到结果串对应的路径 String dispatcherPath = config.getResult().get(result); //找不到结果路径=> 抛出异常提示返回的路径找不到对应页面 if(dispatcherPath ==null || "".equals(dispatcherPath)){ throw new RuntimeException("您要访问的结果没有找到配置!"); } //8 将请求转发到配置的路径 req.getRequestDispatcher(dispatcherPath).forward(req, resp); //释放资源 ActionContext.tl.remove(); } } /** * 销毁 */ public void destroy() { // TODO Auto-generated method stub }}
步骤7:
将Filter配置到web.xml中
步骤8:
struts经典之参数封装拦截器创建:
以后自己写的每一个拦截器操作步骤都是这样的
public class ParamInterceptor implements Interceptor{ public void init() { } public String interceptor(ActionInvocation invocation) { //1 获得参数 //2 获得action对象 //ActionContext ac = ActionContext.getActionContext().getStack();//第一种获得ActionContext对象 ActionContext ac = invocation.getActionContext();//第二种获得ActionContext对象 ValueStack vs = ac.getStack(); Object action = vs.seek(); //3 封装 try { BeanUtils.populate(action, ac.getRequest().getParameterMap()); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } //4 放行 return invocation.invoke(invocation); } public void destory() { }}
然后在struts.xml里加上:
<interceptor class="com.test.interceptor.ParamInterceptor" />
测试一:
新建一个action:
地址栏测试get请求
public class HelloAction { private String name; private String password; public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String execute(){ System.out.println("hello world!"+name+"==>"+password); return "success"; }}
然后在struts.xml里配置对应的action:
<action name="HelloAction" method="execute" class="com.test.action.HelloAction" > <result name="success" >/index.jsp</result> </action>
把项目部署到tomcat运行起来,然后在浏览器地址栏输入
http://localhost:8080/MyStruts2/HelloAction.action?name=marry&password=123456
会看到控制台输出
测试二:
表单(post)提交数据返回界面
新建一个RegistAction:
public class RegistAction implements Serializable{ private static final long serialVersionUID = -793621223468025885L; private String name; private String password; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String regist(){ System.out.println("注册的用户名:" + name+" ==> 密码:"+password); ActionContext.getActionContext().getRequest().setAttribute("name", name); return "regist"; } }
然后在struts.xml里配置对于的action:
<action name="registAction" method="regist" class="com.test.action.RegistAction" > <result name="regist" >/home.jsp</result> </action>
再创建一个regist.jsp,下面是body的代码:
<body> <form action="registAction.action" method="post"> <table> <tr> <td>用户名:</td> <td><input type="text" id="name" name="name"></td> </tr> <tr> <td>密码:</td> <td><input type="password" id="password" name="password"></td> </tr> <tr> <td colspan="2"><input type="submit" value="提交"></td> </tr> </table> </form> </body>
然后创建一个home.jsp,下面是body的代码:
<body> 欢迎您:${requestScope.name} </body>
最后部署到tomcat运行起来
在浏览器访问地址:
输入参数后,运行结果如下:
到此说明已经成功实现了
下面是整个项目的结构图:
本文的源码地址:
http://download.csdn.net/detail/u014204541/9842176
写在最后,本文可能存在不足之处,但愿大神看到后有不足之处可以提出,方便后期学习,如果实现过程看不懂,建议多看图,边看边实现这个过程。
- 深入理解Struts2原理之自己实现Struts2框架
- Struts2实现原理(附上自己理解)
- struts2原理-深入理解(转)
- Struts2原理分析,自己实现一个struts2
- struts2 (一)自己实现struts2框架
- struts2之实现原理
- 深入理解PHP原理之实现自己的PHP语法
- Java程序员从笨鸟到菜鸟之(三十五)细谈struts2(一)自己实现struts2框架
- Java程序员从笨鸟到菜鸟之(三十五)细谈struts2(一)自己实现struts2框架
- 细谈Struts2框架(一) Struts2框架之原理
- Struts2第(一)篇《自己实现struts2框架》
- 细谈struts2(一)自己实现struts2框架
- struts2.0深入理解
- Struts2深入理解
- 深入理解Struts2
- Struts2 深入理解
- Struts2深入理解
- Struts2拦截器实现原理的理解
- 初识opencv人脸检测
- 调用远程API接口,获取服务器数据
- 【AQS框架】戏(细)说Executor框架线程池任务执行全过程(下)
- RHEL7.3下Mariadb安装及部署
- 私活,永远解救不了自己屌丝的人生!(转载)
- 深入理解Struts2原理之自己实现Struts2框架
- Web service
- Linux用户名 不在 sudoers文件中,此事将被报告。
- 数据库——设计过程
- rtsp 客户端请求视频的时候顺便填写输入用户名和密码的格式
- HDU 5901 Count primes(求1e11内素数个数模板)
- mysql基础日期处理(六)
- Android存储系统之源码篇
- 最大和连续子数组