溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

深入理解struts的運行機制

發(fā)布時間:2020-06-30 01:50:11 來源:網(wǎng)絡 閱讀:2465 作者:煙花散盡1 欄目:軟件技術

title: 深入理解struts的運行機制
date: 2016-10-26 20:02
tags: java
categories: 編程

permalink: zxh

個人剖析,不喜勿噴

掃碼關注公眾號,不定期更新干活

深入理解struts的運行機制這里寫圖片描述

在此申明本博文并非原創(chuàng),原文:http://blog.csdn.net/lenotang/article/details/3336623,本文章是在此文章基礎上進行優(yōu)化。也談不上優(yōu)化,只是加上了點自己的想法

jar包準備

  • 深入理解struts的運行機制這里寫圖片描述

  • 為什么會用到這兩個jar包呢,因為我需要通過這個jar來解析xml配置文件。

新建項目

  • 深入理解struts的運行機制這里寫圖片描述

流程梳理

  • 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這個節(jié)點這個action的name是login,就是說前臺中請求這個login經(jīng)過這個配置文件解析就會把這個請求交給action中的class屬性,也就是上面的

    org.zxh.action.LoginAction

    具體的是交由這個類的login(method)這個方法。這個方法會方法一個string類型的字符串,如果返回的是success就將頁面重定向到index.jsp如果是login就重定向到login.jsp。這個配置文件就是這樣的作用。因為是自己寫的,所以這里并不會想struts框架那樣封裝了很多東西,這里只是為了讓讀者更加深入的理解struts的運行機制。


如何將我們寫的struts.xml文件在程序中啟動呢?

  • 剛?cè)腴T的同志可能會疑問,寫一個配置文件就能處理前后臺交互了?答案當然是不能。這里給大家普及一下web基礎接觸filter的,每次交互需要filter(jsp就是特殊的servlet),所以想實現(xiàn)交互我們就得新建一個servlet,在這個servlet里我們?nèi)プx我們寫的struts.xml文件,通過讀到的信息決定下一步的操作。那么如何啟動一個filter呢?這個不多說,直接在web項目中的web.xml配置攔截器就會執(zhí)行filter。

新建filter(FilterDispatcher)

  • 這個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中我們要在初始化函數(shù)(init)中對一些參數(shù)進行初始化,對那些數(shù)據(jù)初始化呢,對!當然是拿配置文件的信息啦。配置文件是.xml這里我用dom4j讀取.xml配置文件。 把struts.xml配置文件放在src下,(可以放在其他地方,這里的地址填的對應就行了)

//?獲得xml配置文件
????????String?webRootPath?=?getClass().getClassLoader()
????????????????.getResource("struts.xml").getPath();
  • 拿到配置文件路徑之后開始讀取,這里我講讀到的數(shù)據(jù)封裝到一個map里面。在封裝在Map中我們仔細觀察一下配置文件

    深入理解struts的運行機制這里寫圖片描述

  • 其實我們放在Map里面就是這四個屬性的值,有了這四個值我們就可以完成一次前后臺交互的映射了。所以為了方便這里封裝成javabean。

package?org.zxh.util;

import?java.util.HashMap;
import?java.util.Map;

/**
?*?將action屬性封裝成類
?*?@author?87077
?*
?*/

public?class?ActionConfig?{

????//action?給別人調(diào)用的名字
????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
?????*????????????存放解析的數(shù)據(jù)
?????*/

????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);
????????????????//?如果沒有設置執(zhí)行method?執(zhí)行默認的
????????????????if?(method?==?null?||?"".equals(method))?{
????????????????????method?=?"execute";
????????????????}
????????????????config.setMethod(method);
????????????????//?繼續(xù)向下獲取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();
????????}
????}
}
  • 現(xiàn)在我們在回到過濾器上,上面兩個類就是為了解析xml的。所以在Filter中的init方法里我們就可以將解析的數(shù)據(jù)放到我們的全局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);
????}

過濾器的執(zhí)行

  • 過濾器真正執(zhí)行是在doFilter方法開始時。

public?void?doFilter(ServletRequest?arg0,?ServletResponse?arg1,
????????????FilterChain?arg2)

doFilter()方法類似于Servlet接口的service()方法。當客戶端請求目標資源的時候,容器就會調(diào)用與這個目標資源相關聯(lián)的過濾器的 doFilter()方法。其中參數(shù) request, response 為 web 容器或 Filter 鏈的上一個 Filter 傳遞過來的請求和相應對象;參數(shù) chain 為代表當前 Filter 鏈的對象,在特定的操作完成后,可以調(diào)用 FilterChain 對象的 chain.doFilter(request,response)方法將請求交付給 Filter 鏈中的下一個 Filter 或者目標 Servlet 程序去處理,也可以直接向客戶端返回響應信息,或者利用RequestDispatcher的forward()和include()方法,以及 HttpServletResponse的sendRedirect()方法將請求轉(zhuǎn)向到其他資源。這個方法的請求和響應參數(shù)的類型是 ServletRequest和ServletResponse,也就是說,過濾器的使用并不依賴于具體的協(xié)議。

  • 獲取請求域和響應域還有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文件中數(shù)據(jù)放入到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類。在這里已經(jīng)解析到了所有的action的信息
????????ActionConfig?config?=?map.get(path);
????????//匹配不成功就返回找不到頁面錯誤信息
????????if(config==null){
????????????response.setStatus(response.SC_NOT_FOUND);
????????????return?;
????????}
  • 獲取了ActionConfig類了,action的所有信息都存儲在這個javabean類中了,下面的事情就好辦了。下面的只是會用到反射的知識。我們拿到真正action類的名稱后就需要根據(jù)名字獲取到這個action的實體類。

//通過ActionConfig獲取完成的類名字
????????String?clazzName=config.getClazzName();
????????//實例化Action對象,不存在的話就提示錯誤信息?
????????Object?action?=?getAction(clazzName);
????????if(action==null){
????????????//說明這個action是錯誤的,在配置文件中沒有占到對應的action類
????????????response.setStatus(response.SC_NOT_FOUND);
????????????return?;
????????}

request參數(shù)獲取并賦值給action

  • 執(zhí)行action的方法前很定需要先將request中的參數(shù)獲取到,進行賦值,這部才是真正的意義上的交互。

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方法

//通過反射調(diào)用該屬性的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數(shù)據(jù)之后就是講這個object數(shù)據(jù)通過反射付給action對應的屬性

//判斷是否是數(shù)組屬性
????????????????????if(fieldType.isArray()){
????????????????????????method.invoke(action,?new?Object[]{object});
????????????????????}else?{
????????????????????????method.invoke(action,?new?Object[]{object[0]});
????????????????????}

這說一下 method.invoke是將action類中method方法這個方法需要的參數(shù)就是object詳解

  • 有了這個方法我們在回到Filter就可以了

//前置攔截,獲取request里面的參數(shù),調(diào)用action的set方法給屬性設置值
????????BeanUtil.requestToAction(request,?action);
  • 屬性賦值完成就開始執(zhí)行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);
????}
  • 到這里你已經(jīng)獲取了配置文件中前臺映射后應該的result了,那么就簡單了,直接重定向就可以了,到這里就實現(xiàn)了struts的前后臺交互。

request.getRequestDispatcher(result).forward(request,?response);

  • 下面就在前臺jsp中form表單將數(shù)據(jù)傳遞給我們的login ?action看看會不會去執(zhí)行指定的方法

詳解
  • 有了這個方法我們在回到Filter就可以了

//前置攔截,獲取request里面的參數(shù),調(diào)用action的set方法給屬性設置值
????????BeanUtil.requestToAction(request,?action);
  • 屬性賦值完成就開始執(zhí)行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);
????}
  • 到這里你已經(jīng)獲取了配置文件中前臺映射后應該的result了,那么就簡單了,直接重定向就可以了,到這里就實現(xiàn)了struts的前后臺交互。

request.getRequestDispatcher(result).forward(request,?response);

上訴原理的×××

掃碼關注公眾號,不定期更新干活

深入理解struts的運行機制這里寫圖片描述


向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI