基於配置的自訂的簡單struts(瞭解struts原理),struts原理
struts的基於配置的action跳轉方式使用起來特別方便,對此我也模仿struts的action,實現一個自己定義的通過設定檔配置action進行簡單的操作:
首先,可以知道sturts是通過過濾器來攔截瀏覽器發送的請求,再在過濾器裡進行操作,實現這個流程。
那麼,我們也定義一個filter在我們的程式中:
public class StrutsFilter implements Filter{ public void doFilter(ServletRequest arg0, ServletResponse arg1,FilterChain arg2) throws IOException, ServletException { { }}
同樣,對所有請求進行攔截,在web.xml中進行配置:
<?xml version="1.0" encoding="UTF-8"?><web-app id="WebApp_9" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <display-name>Struts Blank</display-name> <filter> <filter-name>struts2</filter-name> <filter-class>com.yc.frame.filter.StrutsFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>*</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list></web-app>
當然,struts中最重要的就是struts.xml的這個設定檔,在這裡分析設定檔可以知道,struts必然是先解析出xml,然後將解析的xml以一個資料結構儲存,利用反射找到相應的Action子類,再用反射啟用裡面的配置的方法或者預設的execute方法,既然邏輯出來了,那麼要如何設定資料結構對xml解析出來的檔案進行儲存呢?
struts.xml如下:
<?xml version="1.0" encoding="UTF-8" ?><struts> <!-- 自訂的Struts --> <package name="default" namespace="/" > <action name="user.action" class="com.yc.frame.actions.UserAction" method="add"> <result name="success" type="direct">a.jsp</result> <result name="fail" type="direct">a.jsp</result> </action> <!--多個action--> </package></struts>
在這裡,每個節點對應一個類, 由於xml中有對應的層級關係,所以在設計這些類時,也要將這些類的層級關係體現出來,才能準確的對資料進行處理和操作:
//package節點對應的節點類,它應該有一個actioin節點類的集合<pre name="code" class="java">自然要提供getset方法(get,set省略)
public class PackageWrapper implements Serializable{/** * */private static final long serialVersionUID = -7700027765680530207L;private String namespace;private Map<String,ActionWrapper> actionMap=new HashMap<String,ActionWrapper>();
}
//action節點類,同時也要有result類的節點集合,
<pre name="code" class="java">(get,set省略)
public class ActionWrapper implements Serializable{
/**
*
*/
private static final long serialVersionUID = -4176005261524671229L;
private String className;
private String methodName;
private Map<String,ResultWrapper> resultMap=new HashMap<String,ResultWrapper>();
}
package com.yc.frame.wrapper;
//result節點的節點類
public class ResultWrapper implements Serializable {
private String type;
private String value;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
那麼,自然要先將他們解析存好,再進行操作,所以,在filter的init方法裡進行初始化存好。這裡用的是dom4j對他解析:(解析就省略了,有需要的請留言給源碼哦!)
最核心的環節就是將資料讀取出來後取出action的實體類,利用反射找到對應的方法,執行action的方法得到返回值,最後判斷result的type,執行跳轉頁面,是不是清晰了一點?
//1.解析請求HttpServletRequest request=(HttpServletRequest)arg0;HttpServletResponse response=(HttpServletResponse)arg1;String servletPath=request.getServletPath();//這個是請求的資源路徑名//判斷請求的路徑名是否actionif(servletPath.lastIndexOf(".action")==-1){arg2.doFilter(request, response);}else{try {//地址格式為:cccc/user.action /user.actionint lastSlashIndex = servletPath.lastIndexOf("/");String requestActionName = servletPath.substring(lastSlashIndex + 1);String nameSpaceName = servletPath.substring(0,lastSlashIndex);//這個是總項目後面那個檔案夾地址//2.到map中找到這個actionPackageWrapper packageWrapper = null;//TODO://這裡為什麼要用這個entry?for (Map.Entry<String, PackageWrapper> entry : packagesMap.entrySet()) {PackageWrapper pw = entry.getValue();pw.getNamespace().equals(nameSpaceName);packageWrapper = pw;break;}//3.反射產生action的對象//4.啟用這個action的execute方法//5.得到這個execute返回的StringActionWrapper actionWrapper = packageWrapper.getActionMap().get(requestActionName);String actionClassName = actionWrapper.getClassName();Class c = Class.forName(actionClassName);Object obj1=c.newInstance();//TODO://這裡通過packageWrapper獲得攔截器的集合List<Interceptor> interceptors=new ArrayList<Interceptor>();List<InterceptorWrapper> iw=packageWrapper.getInterceptorList();if(iw.size()>0){for(int i=0;i<iw.size();i++){InterceptorWrapper interceptorWrapper=iw.get(i);String inname=interceptorWrapper.getName();String inclassName=interceptorWrapper.getClassName();if(inclassName.toLowerCase().endsWith(inname.toLowerCase()+"interceptor")){Class interceptor = Class.forName(inclassName);Object obj=interceptor.newInstance();interceptors.add(((Interceptor)obj));}}actionInvokation=new ActionInvokation(interceptors,((Action)obj1));}String result = invokeActionMethod(request,c, requestActionName,actionWrapper);//6.到map中找到這個String對應的result;ResultWrapper resultWrapper=actionWrapper.getResultMap().get(result);//7.轉寄到這個result上String dispatcherType=resultWrapper.getType();String page=resultWrapper.getValue();if(dispatcherType!=null&&!"".equals(dispatcherType)){if("direct".equals(dispatcherType)){String path = request.getContextPath();String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";response.sendRedirect(basePath+page);}}else{request.getRequestDispatcher(page).forward(request, response);}} catch (Exception e) {e.printStackTrace();request.getSession().setAttribute("ycexeption", e.getMessage());}}}/** * 這個方法是對result中的方法進行啟用,通過傳過來的反射執行個體和action的名字以及拿到的method名字 * @param c * @param requestActionName * @param actionWrapper * @return * @throws SecurityException * @throws NoSuchMethodException */private String invokeActionMethod(HttpServletRequest request,Class c,String requestActionName,ActionWrapper actionWrapper) throws Exception{String requestResult="";Object obj=c.newInstance();//這裡完成注入參數的工作injectParameterToAction(request, obj, c);//如果methodname為空白,則執行action中的execute方法if(actionWrapper.getMethodName()==null||"".equals(actionWrapper.getMethodName())){requestResult=((Action)obj).execute();}else{ //否則執行action中的配置的方法String methodName=actionWrapper.getMethodName();Method m=c.getMethod(methodName, null);requestResult=(String) m.invoke(obj, null);}return requestResult;}//注入參數private void injectParameterToAction(HttpServletRequest request,Object obj,Class c) throws Exception, IllegalAccessException, IllegalArgumentException, InvocationTargetException{//基本類型的注入//1.從request中取出所有的健值對,轉成Map setName Map<String,String[]> map=parseRequest(request);//2.從c中找出各個要注入的方法 getMthod方法名Method [] ms=c.getMethods();//3.反向啟用方法,注入參數for(Method m:ms){String methodName=m.getName();if(map.containsKey(methodName)){String[] values=map.get(methodName);if(values.length==1){String s=values[0];String type=m.getParameterTypes()[0].getName();if (m.getParameterTypes()[0].getName().equals("java.lang.Integer")|| m.getParameterTypes()[0].getName().equals("int")) {// 啟用方法,傳進去值m.invoke(obj, Integer.parseInt(s));continue;} else if (m.getParameterTypes()[0].getName().equals("java.lang.Double")|| m.getParameterTypes()[0].getName().equals("double")) {m.invoke(obj, Double.parseDouble(s));continue;} else if (m.getParameterTypes()[0].getName().equals("java.lang.Float")|| m.getParameterTypes()[0].getName().equals("float")) {m.invoke(obj, Float.parseFloat(s));continue;} else if (m.getParameterTypes()[0].getName().equals("java.lang.String")|| m.getParameterTypes()[0].getName().equals("String")) {m.invoke(obj, s);continue;} }}}}private Map<String, String[]> parseRequest(HttpServletRequest request) {Map<String,String[]> map=new HashMap<String,String[]>();Enumeration<String> enu=request.getParameterNames();while(enu.hasMoreElements()){String name=enu.nextElement();String methodName="set"+name.substring(0,1).toUpperCase()+name.substring(1);map.put(methodName, request.getParameterValues(name));}return map;}public void init(FilterConfig config) throws ServletException {//讀取設定檔 Struts.xml =》Maprealpath=config.getServletContext().getRealPath("/");packagesMap=parseStrutsXml();}解析xml檔案省略了,這是我自己實現的一個最簡單的struts,當然強大的struts我只是模仿了皮毛中的皮毛,歡迎來討論和相互學習。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。