您好,登錄后才能下訂單哦!
title: 深入理解struts的運行機制
date: 2016-10-26 20:02
tags: java
categories: 編程
個人剖析,不喜勿噴
掃碼關注公眾號,不定期更新干活
在此申明本博文并非原創,原文:http://blog.csdn.net/lenotang/article/details/3336623,本文章是在此文章基礎上進行優化。也談不上優化,只是加上了點自己的想法
這里寫圖片描述
為什么會用到這兩個jar包呢,因為我需要通過這個jar來解析xml配置文件。
這里寫圖片描述
struts配置文件
<?xml?version="1.0"?encoding="UTF-8"?>
<struts>
<package>
????<action?name="login"?method="login"?class="org.zxh.action.LoginAction">
????????<result?name="success">/index.jsp</result>
????????<result?name="login">/WEB-INF/login.jsp</result>
????</action>
</package>
</struts>
熟悉struts的朋友都清楚struts.xml配置文件的重要性,這個配置文件名字是可以更改的,這里簡單解釋下這個配置文件的作用,首先我們找到action這個節點這個action的name是login,就是說前臺中請求這個login經過這個配置文件解析就會把這個請求交給action中的class屬性,也就是上面的
org.zxh.action.LoginAction
具體的是交由這個類的login(method)這個方法。這個方法會方法一個string類型的字符串,如果返回的是success就將頁面重定向到index.jsp如果是login就重定向到login.jsp。這個配置文件就是這樣的作用。因為是自己寫的,所以這里并不會想struts框架那樣封裝了很多東西,這里只是為了讓讀者更加深入的理解struts的運行機制。
剛入門的同志可能會疑問,寫一個配置文件就能處理前后臺交互了?答案當然是不能。這里給大家普及一下web基礎接觸filter的,每次交互需要filter(jsp就是特殊的servlet),所以想實現交互我們就得新建一個servlet,在這個servlet里我們去讀我們寫的struts.xml文件,通過讀到的信息決定下一步的操作。那么如何啟動一個filter呢?這個不多說,直接在web項目中的web.xml配置攔截器就會執行filter。
這個servlet就是struts的核心過濾器,需要先繼承過濾器。
```
public class FilterDispatcher implements Filter{
@Override
public void destroy() {
? ?// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1,
? ? ? ?FilterChain arg2) throws IOException, ServletException {
? ?// TODO Auto-generated method stub
}
@Override
public void init(FilterConfig arg0) throws ServletException {
? ?// TODO Auto-generated method stub
}
}
```
Filter中我們要在初始化函數(init)中對一些參數進行初始化,對那些數據初始化呢,對!當然是拿配置文件的信息啦。配置文件是.xml這里我用dom4j讀取.xml配置文件。 把struts.xml配置文件放在src下,(可以放在其他地方,這里的地址填的對應就行了)
//?獲得xml配置文件
????????String?webRootPath?=?getClass().getClassLoader()
????????????????.getResource("struts.xml").getPath();
拿到配置文件路徑之后開始讀取,這里我講讀到的數據封裝到一個map里面。在封裝在Map中我們仔細觀察一下配置文件
這里寫圖片描述
其實我們放在Map里面就是這四個屬性的值,有了這四個值我們就可以完成一次前后臺交互的映射了。所以為了方便這里封裝成javabean。
package?org.zxh.util;
import?java.util.HashMap;
import?java.util.Map;
/**
?*?將action屬性封裝成類
?*?@author?87077
?*
?*/
public?class?ActionConfig?{
????//action?給別人調用的名字
????private?String?name;
????//action對應程序中的action類
????private?String?clazzName;
????//action中的方法
????private?String?method;
????//返回結果不知一條?所以用Map
????private?Map<String,?String>?resultMap?=?new?HashMap<String,?String>();
????public?ActionConfig(){
????}
????public?ActionConfig(String?name?,?String?clazzName?,?String?method?,?Map<String,?String>?resultMap){
????????this.name=name;
????????this.clazzName=clazzName;
????????this.method=method;
????????this.resultMap=resultMap;
????}
????public?String?getName()?{
????????return?name;
????}
????public?String?getClazzName()?{
????????return?clazzName;
????}
????public?String?getMethod()?{
????????return?method;
????}
????public?Map<String,?String>?getResultMap()?{
????????return?resultMap;
????}
????public?void?setName(String?name)?{
????????this.name?=?name;
????}
????public?void?setClazzName(String?clazzName)?{
????????this.clazzName?=?clazzName;
????}
????public?void?setMethod(String?method)?{
????????this.method?=?method;
????}
????public?void?setResultMap(Map<String,?String>?resultMap)?{
????????this.resultMap?=?resultMap;
????}
}
有了javabean 我們開始解析xml文件
package?org.zxh.util;
import?java.io.File;
import?java.util.List;
import?java.util.Map;
import?org.dom4j.Document;
import?org.dom4j.DocumentException;
import?org.dom4j.Element;
import?org.dom4j.io.SAXReader;
/**
?*?采用dom4j解析xml配置文件
?*?
?*?@author?87077
?*?
?*/
public?class?ConfigUtil?{
????/**
?????*?@param?fileName
?????*????????????待解析的文件
?????*?@param?map
?????*????????????存放解析的數據
?????*/
????public?static?void?parseConfigFile(String?fileName,
????????????Map<String,?ActionConfig>?map)?{
????????SAXReader?reader?=?new?SAXReader();
????????try?{
????????????Document?doc?=?reader.read(new?File("D:\\zxh\\soft\\apache-tomcat-7.0.70\\apache-tomcat-7.0.70\\webapps\\MyStruts\\WEB-INF\\classes\\struts.xml"));
????????????Element?root?=?doc.getRootElement();
????????????List<Element>?list?=?root.selectNodes("package/action");
????????????for?(Element?element?:?list)?{
????????????????//?封裝成ActionConfig對象,保存在map中
????????????????ActionConfig?config?=?new?ActionConfig();
????????????????//?獲取action中的值
????????????????String?name?=?element.attributeValue("name");
????????????????String?clazzName?=?element.attributeValue("class");
????????????????String?method?=?element.attributeValue("method");
????????????????//?將值傳入javabean中
????????????????config.setName(name);
????????????????config.setClazzName(clazzName);
????????????????//?如果沒有設置執行method?執行默認的
????????????????if?(method?==?null?||?"".equals(method))?{
????????????????????method?=?"execute";
????????????????}
????????????????config.setMethod(method);
????????????????//?繼續向下獲取action中的返回方法
????????????????List<Element>?resultList?=?element.selectNodes("result");
????????????????for?(Element?resultElement?:?resultList)?{
????????????????????String?resultName?=?resultElement.attributeValue("name");
????????????????????String?urlPath?=?resultElement.getTextTrim();
????????????????????if?(resultName?==?null?||?"".equals(resultName))?{
????????????????????????resultName?=?"success";
????????????????????}
????????????????????config.getResultMap().put(resultName,?urlPath);
????????????????}
????????????????map.put(name,?config);
????????????}
????????}?catch?(DocumentException?e)?{
????????????//?TODO?Auto-generated?catch?block
????????????e.printStackTrace();
????????}
????}
}
現在我們在回到過濾器上,上面兩個類就是為了解析xml的。所以在Filter中的init方法里我們就可以將解析的數據放到我們的全局Map中
@Override
????public?void?init(FilterConfig?arg0)?throws?ServletException?{
????????//?TODO?Auto-generated?method?stub?過濾器的初始化過程
????????//?獲得xml配置文件
????????String?webRootPath?=?getClass().getClassLoader()
????????????????.getResource("struts.xml").getPath();
????????//?將xml配置文件解析裝在到map中
????????ConfigUtil.parseConfigFile(webRootPath,?map);
????}
過濾器真正執行是在doFilter方法開始時。
public?void?doFilter(ServletRequest?arg0,?ServletResponse?arg1,
????????????FilterChain?arg2)
doFilter()方法類似于Servlet接口的service()方法。當客戶端請求目標資源的時候,容器就會調用與這個目標資源相關聯的過濾器的 doFilter()方法。其中參數 request, response 為 web 容器或 Filter 鏈的上一個 Filter 傳遞過來的請求和相應對象;參數 chain 為代表當前 Filter 鏈的對象,在特定的操作完成后,可以調用 FilterChain 對象的 chain.doFilter(request,response)方法將請求交付給 Filter 鏈中的下一個 Filter 或者目標 Servlet 程序去處理,也可以直接向客戶端返回響應信息,或者利用RequestDispatcher的forward()和include()方法,以及 HttpServletResponse的sendRedirect()方法將請求轉向到其他資源。這個方法的請求和響應參數的類型是 ServletRequest和ServletResponse,也就是說,過濾器的使用并不依賴于具體的協議。
獲取請求域和響應域還有Filter鏈,并設置編碼防止亂碼
//針對http請求,將請求和響應的類型還原為HTTP類型
????????HttpServletRequest?request?=?(HttpServletRequest)?arg0;
????????HttpServletResponse?response?=?(HttpServletResponse)?arg1;
????????//設置請求和響應的編碼問題
????????request.setCharacterEncoding("UTF-8");
????????response.setCharacterEncoding("UTF-8");
獲取請求地址
//獲取請求路徑
String?url?=?request.getServletPath();
通過請求去判斷知否攔截過濾這個地址的請求,本文默認過濾所有以.action結尾的請求
//請求地址過濾,如果不是以.action結尾的
????????if(!url.endsWith(".action")){
????????????//不是.action的放行
????????????arg2.doFilter(request,?response);
????????????return?;
????????}
看我之前將xml文件中數據放入到Map的格式可以看出我是講整個javabean放入Map中名字是action的name。所以下面我就要去那個name(就是請求中的login)
//解析request路徑
????????int??start?=?url.indexOf("/");
????????int?end?=?url.lastIndexOf(".");
????????String?path=url.substring(start+1,end);
????????//通過path去匹配到對應的ActionConfig類。在這里已經解析到了所有的action的信息
????????ActionConfig?config?=?map.get(path);
????????//匹配不成功就返回找不到頁面錯誤信息
????????if(config==null){
????????????response.setStatus(response.SC_NOT_FOUND);
????????????return?;
????????}
獲取了ActionConfig類了,action的所有信息都存儲在這個javabean類中了,下面的事情就好辦了。下面的只是會用到反射的知識。我們拿到真正action類的名稱后就需要根據名字獲取到這個action的實體類。
//通過ActionConfig獲取完成的類名字
????????String?clazzName=config.getClazzName();
????????//實例化Action對象,不存在的話就提示錯誤信息?
????????Object?action?=?getAction(clazzName);
????????if(action==null){
????????????//說明這個action是錯誤的,在配置文件中沒有占到對應的action類
????????????response.setStatus(response.SC_NOT_FOUND);
????????????return?;
????????}
執行action的方法前很定需要先將request中的參數獲取到,進行賦值,這部才是真正的意義上的交互。
public?static?void?requestToAction(HttpServletRequest?request?,?Object?action?)
將傳進來的action對象進行class話并獲取action實體下的屬性
Class<??extends?Object>?clazzAction?=?action.getClass();
????????//獲取aciton中所有屬性,從前臺獲取的值很多,只有action屬性中有的才會進行反射賦值
????????Field[]?fields?=?action.getClass().getDeclaredFields();
拿到request傳過來的值并進行遍歷
//獲取請求中的名字屬性值
????????Enumeration<String>?names=request.getParameterNames();
String?name=names.nextElement();
????????????boolean?flag=false;
????????????//需要判斷action屬性中沒有的而請求中有的我們不需要進行反射處理
????????????for?(Field?field?:?fields)?{
????????????????if(name.equals(field.getName())){
????????????????????flag=true;
????????????????}
????????????}
????????????if(!flag){
????????????????return;
????????????}
????????????String[]?value=request.getParameterValues(name);
通過request中的name并且在action中有這個屬性之后我們就需要獲取action這個字段的屬性。
Class<Object>?fieldType=(Class<Object>)?clazzAction.getDeclaredField(name).getType();
獲取action的改name字段屬性的set方法
//通過反射調用該屬性的set方法
????????????????????String?setName="set"+name.substring(0,1).toUpperCase()+name.substring(1);
????????????????????Method?method=clazzAction.getMethod(setName,?new?Class[]{fieldType});
下面我們就需要將獲取的value按類型
private?static?Object[]?transfer(Class<Object>?fieldType?,?String[]?value){
????????Object[]?os?=?null;
????????//fieldType?是[]這種類型的,需要將[]去掉
????????String?type=fieldType.getSimpleName().replace("[]",?"");
????????if("String".equals(type)){
????????????os=value;
????????}else?if("int".equals(type)||"Integer".equals(type)){
????????????os?=?new?Integer[value.length];
????????????for?(int?i?=?0;?i?<?os.length;?i++)?{
????????????????os[i]?=?Integer.parseInt(value[i]);
????????????}
????????}else?if("float".equals(type)||"Float".equals(type)){
????????????os=new?Float[value.length];
????????????for?(int?i?=?0;?i?<?os.length;?i++)?{
????????????????os[i]=Float.parseFloat(value[i]);
????????????}
????????}else?if("double".equals(type)||"Double".equals(type)){
????????????os=new?Double[value.length];
????????????for?(int?i?=?0;?i?<?os.length;?i++)?{
????????????????os[i]=Double.parseDouble(value[i]);
????????????}
????????}
????????return?os;
????}
獲取object數據之后就是講這個object數據通過反射付給action對應的屬性
//判斷是否是數組屬性
????????????????????if(fieldType.isArray()){
????????????????????????method.invoke(action,?new?Object[]{object});
????????????????????}else?{
????????????????????????method.invoke(action,?new?Object[]{object[0]});
????????????????????}
這說一下 method.invoke是將action類中method方法這個方法需要的參數就是object詳解
有了這個方法我們在回到Filter就可以了
//前置攔截,獲取request里面的參數,調用action的set方法給屬性設置值 ????????BeanUtil.requestToAction(request,?action);
屬性賦值完成就開始執行action中的method了
private?String?executeAction(ActionConfig?config,?Object?action)?{ ????????String?method?=?config.getMethod(); ????????String?result?=?null; ????????try?{ ????????????Method?callMethod?=?action.getClass().getMethod(method,String.class); ????????????result?=?(String)?callMethod.invoke(action,?new?Object[]?{}); ????????}?catch?(Exception?e)?{ ????????????//?TODO?Auto-generated?catch?block ????????????e.printStackTrace(); ????????} ????????return?config.getResultMap().get(result); ????}
到這里你已經獲取了配置文件中前臺映射后應該的result了,那么就簡單了,直接重定向就可以了,到這里就實現了struts的前后臺交互。
request.getRequestDispatcher(result).forward(request,?response);
下面就在前臺jsp中form表單將數據傳遞給我們的login ?action看看會不會去執行指定的方法
有了這個方法我們在回到Filter就可以了
//前置攔截,獲取request里面的參數,調用action的set方法給屬性設置值 ????????BeanUtil.requestToAction(request,?action);
屬性賦值完成就開始執行action中的method了
private?String?executeAction(ActionConfig?config,?Object?action)?{ ????????String?method?=?config.getMethod(); ????????String?result?=?null; ????????try?{ ????????????Method?callMethod?=?action.getClass().getMethod(method,String.class); ????????????result?=?(String)?callMethod.invoke(action,?new?Object[]?{}); ????????}?catch?(Exception?e)?{ ????????????//?TODO?Auto-generated?catch?block ????????????e.printStackTrace(); ????????} ????????return?config.getResultMap().get(result); ????}
到這里你已經獲取了配置文件中前臺映射后應該的result了,那么就簡單了,直接重定向就可以了,到這里就實現了struts的前后臺交互。
request.getRequestDispatcher(result).forward(request,?response);
掃碼關注公眾號,不定期更新干活
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。