java中struts 框架的實(shí)現(xiàn)
該文章主要簡單粗暴的實(shí)現(xiàn)了struts的請求轉(zhuǎn)發(fā)功能。 其他的功能后續(xù)會慢慢補(bǔ)上。
最近在學(xué)習(xí)javassist的內(nèi)容,看到一篇文章 大家一起寫mvc 主要簡單的描述了mvc的工作流程,同時(shí)實(shí)現(xiàn)了簡單的struts2功能。
這里仿照的寫了個(gè)簡單的struts2框架,同時(shí)加上了自己的一些理解。
該文章主要簡單粗暴的實(shí)現(xiàn)了struts的請求轉(zhuǎn)發(fā)功能。 其他的功能后續(xù)會慢慢補(bǔ)上。
首先,在struts2框架中,請求的實(shí)現(xiàn)、跳轉(zhuǎn)主要是通過在struts.xml進(jìn)行相關(guān)配置。 一個(gè)<action>標(biāo)簽表示一個(gè)請求的定義,action中包含了①請求的名稱“name”;②請求對應(yīng)的實(shí)現(xiàn)類“class” ;③同時(shí)還可通過“method”屬性自定義執(zhí)行的方法,若沒配置默認(rèn)執(zhí)行execute0方法。<result》標(biāo)簽定義了①結(jié)果的類型“name”,包括'SUCCESS'、'NONE'、'LOGIN'、'INPUT'、'ERROR';②請求的類型“type”,包括'dispatcher(默認(rèn))'、'chain'、'redirect'、'redirectAction'、'stream';③結(jié)果的跳轉(zhuǎn)。 在配置完struts.xml后,界面中的表單就可以通過action屬性與action定義的name屬性值相匹配找到對應(yīng)的action標(biāo)簽,從而找到對應(yīng)的class以及執(zhí)行的方法。再根據(jù)執(zhí)行方法返回的string字符串同result標(biāo)簽中的name相匹配,根據(jù)定義的type類型,進(jìn)行下一步請求操作。
好了,在了解了struts2是怎么將界面請求同程序功能相連接后,我們通過自己的代碼來實(shí)現(xiàn)這部分的功能。
那么,我們該如何下手了?
我們將需要實(shí)現(xiàn)的功能簡單的分為兩部分 ①action部分 ②result部分
action部分
①我們需要根據(jù)界面的請求找到對應(yīng)的類以及執(zhí)行的方法
result部分
①我們需要根據(jù)方法執(zhí)行的邏輯返回'SUCCESS'、'NONE'、'LOGIN'、'INPUT'、'ERROR'這類型的字符串
②需要對不同的返回類型,指定不同的下一步請求地址
③需要定義請求的類型,包括'dispatcher(默認(rèn))'、'chain'、'redirect'、'redirectAction'、'stream'
在本文章中,result的返回類型只實(shí)現(xiàn)了'SUCCESS'、'LOGIN'兩種,并且暫不考慮請求類型,實(shí)現(xiàn)的是默認(rèn)的dispatcher請求轉(zhuǎn)發(fā)類型。完善的功能后期會再補(bǔ)充。
那么,下面我們來通過代碼看怎么實(shí)現(xiàn)如上功能。
首先定義了ActionAnnotation和ResultAnnotation 兩個(gè)自定義注解來請求需要對應(yīng)的方法以及方法返回的字符串對應(yīng)的跳轉(zhuǎn)請求
/** * action注解:ActionName相當(dāng)于web.xml配置中的url-pattern * @author linling * */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface ActionAnnotation { String ActionName() default ""; ResultAnnotation[] results() default {}; } /** * 返回注解對象:name相當(dāng)于struts配置中的result的name,包括'SUCCESS'、'NONE'、'ERROR'、'INPUT'、'LOGIN';value相當(dāng)于struts配置中對應(yīng)返回跳轉(zhuǎn)內(nèi)容 * @author linling * */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface ResultAnnotation { ResultType name() default ResultType.SUCCESS; String value() default "index.jsp"; }
然后我們定義一個(gè)ActionContext類,來保存一個(gè)請求所需要的內(nèi)容
/** * 實(shí)現(xiàn)模擬struts根據(jù)配置文件跳轉(zhuǎn)至action執(zhí)行相應(yīng)方法中需要的內(nèi)容 * @author linling * */ public class ActionContext { /** * 相當(dāng)于web.xml中url-pattern,唯一的 */ private String Url; /** * ActionAnnotation注解對應(yīng)方法,也就是action中要執(zhí)行的方法 */ private String method; /** * ActionAnnotation中的Result,對應(yīng)action方法返回的類型。例如:key:'SUCCESS';value:'index.jsp' */ private Map<ResultType, String> results; /** * action的類 */ private Class<?> classType; /** * action的對象 */ private Object action; /** * 方法參數(shù)類型 */ private Class<?>[] paramsType; /** * 方法參數(shù)的名稱,注意這里方法名稱需要和上面paramType參數(shù)一一對應(yīng) * 可以理解為是struts中action中的屬性 */ private String[] actionParamsName; /** * 本次請求的HttpServletRequest */ private HttpServletRequest request; /** * 本次請求的HttpServletResponse */ private HttpServletResponse response;
analysePackage是在組裝ActionContext需要的方法
/** * 遍歷scan_package包下的class文件,將使用了ActionAnnotation注解的方法進(jìn)行解析,組裝成ActionContext對象 并放入urlMap中 * @param real_path * @param scan_package * @throws ClassNotFoundException * @throws InstantiationException * @throws IllegalAccessException * @throws NotFoundException */ public static void analysePackage(String real_path, String scan_package) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NotFoundException { File file = new File(real_path); if(file.isDirectory()) { File[] files = file.listFiles(); for(File f : files) { analysePackage(f.getAbsolutePath(),scan_package); } } else { String str = real_path.replaceAll("/", "."); if (str.indexOf("classes." + scan_package) <= 0 || !str.endsWith(".class")) { return; } String fileName = str.substring(str.indexOf(scan_package),str.lastIndexOf(".class")); Class<?> classType = Class.forName(fileName); Method[] methods = classType.getMethods(); for(Method method : methods) { if(method.isAnnotationPresent(ActionAnnotation.class)) { ActionContext actionContext = new ActionContext(); ActionAnnotation actionAnnotation = (ActionAnnotation)method.getAnnotation(ActionAnnotation.class); String url = actionAnnotation.ActionName(); ResultAnnotation[] results = actionAnnotation.results(); if(url.isEmpty() || results.length < 1) { throw new RuntimeException("method annotation error! method:" + method + " , ActionName:" + url + " , result.length:" + results.length); } actionContext.setUrl(url); actionContext.setMethod(method.getName()); Map<ResultType, String> map = new HashMap<ResultType, String>(); for(ResultAnnotation result : results) { String value = result.value(); if(value.isEmpty()) { throw new RuntimeException("Result name() is null"); } map.put(result.name(), value); } actionContext.setResults(map); actionContext.setClassType(classType); actionContext.setAction(classType.newInstance()); actionContext.setParamsType(method.getParameterTypes()); actionContext.setActionParamsName(getActionParamsName(classType, method.getName())); urlMap.put(url, actionContext); } } } }
getParams是根據(jù)httpServletRequest請求中的請求內(nèi)容獲得請求參數(shù)數(shù)組,該參數(shù)數(shù)組為調(diào)用方法體的參數(shù)內(nèi)容
/** * 根據(jù) 參數(shù)類型parasType 和 參數(shù)名actinParamsName 來解析請求request 構(gòu)建參數(shù)object[] * @param request * @param paramsType * @param actionParamsName * @return * @throws InstantiationException * @throws IllegalAccessException * @throws IllegalArgumentException * @throws InvocationTargetException * @throws NoSuchMethodException * @throws SecurityException */ public static Object[] getParams(HttpServletRequest request, Class<?>[] paramsType, String[] actionParamsName) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { Object[] objects = new Object[paramsType.length]; for(int i = 0; i < paramsType.length; i++) { Object object = null; if(ParamsUtils.isBasicType(paramsType[i])) { objects[i] = ParamsUtils.getParam(request, paramsType[i], actionParamsName[i]); } else { Class<?> classType = paramsType[i]; object = classType.newInstance(); Field[] fields = classType.getDeclaredFields(); for(Field field : fields) { Map<String, String[]> map = request.getParameterMap(); for(Iterator<String> iterator = map.keySet().iterator(); iterator.hasNext();) { String key = iterator.next(); if(key.indexOf(".") <= 0) { continue; } String[] strs = key.split("\\."); if(strs.length != 2) { continue; } if(!actionParamsName[i].equals(strs[0])) { continue; } if(!field.getName().equals(strs[1])) { continue; } String value = map.get(key)[0]; classType.getMethod(convertoFieldToSetMethod(field.getName()), field.getType()).invoke(object, value); break; } } objects[i] = object; } } return objects; }
好了,接下來。我們可以來實(shí)現(xiàn)action方法了
public class LoginAction { @ActionAnnotation(ActionName="login.action",results={@ResultAnnotation(name=ResultType.SUCCESS,value="index.jsp"),@ResultAnnotation(name=ResultType.LOGIN,value="login.jsp")}) public ResultType login(String name, String password) { if("hello".equals(name) && "world".equals(password)) { return ResultType.SUCCESS; } return ResultType.LOGIN; } @ActionAnnotation(ActionName="loginForUser.action",results={@ResultAnnotation(name=ResultType.SUCCESS,value="index.jsp"),@ResultAnnotation(name=ResultType.LOGIN,value="login.jsp")}) public ResultType loginForUser(int number, LoginPojo loginPojo) { if("hello".equals(loginPojo.getUsername()) && "world".equals(loginPojo.getPassword())) { return ResultType.SUCCESS; } return ResultType.LOGIN; } }
接下來,我們需要做的是讓程序在啟動的時(shí)候去遍歷工作目錄下所有類的方法,將使用了ActionAnnotation的方法找出來組裝成ActionContext,這就是我們請求需要執(zhí)行的方法。這樣在請求到了的時(shí)候我們就可以根據(jù)請求的地址找到對應(yīng)的ActionContext,并通過反射的機(jī)制進(jìn)行方法的調(diào)用。
我們定了兩個(gè)Servlet。一個(gè)用于執(zhí)行初始化程序。一個(gè)用來過濾所有的action請求
<servlet> <servlet-name>StrutsInitServlet</servlet-name> <servlet-class>com.bayern.struts.one.servlet.StrutsInitServlet</servlet-class> <init-param> <param-name>scan_package</param-name> <param-value>com.bayern.struts.one</param-value> </init-param> <load-on-startup>10</load-on-startup> </servlet> <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>com.bayern.struts.one.servlet.DispatcherServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>*.action</url-pattern> </servlet-mapping>
DispatcherServlet實(shí)現(xiàn)了對所用action請求的過濾,并使之執(zhí)行對應(yīng)的action方法,以及進(jìn)行下一步的跳轉(zhuǎn)
ublic void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); String url = request.getServletPath().substring(1); ActionContext actionContext = DispatcherServletUtil.urlMap.get(url); if(actionContext != null) { actionContext.setRequest(request); actionContext.setResponse(response); try { Object[] params = DispatcherServletUtil.getParams(request, actionContext.getParamsType(), actionContext.getActionParamsName()); Class<?> classType = actionContext.getClassType(); Method method = classType.getMethod(actionContext.getMethod(), actionContext.getParamsType()); ResultType result = (ResultType)method.invoke(actionContext.getAction(), params); Map<ResultType,String> results = actionContext.getResults(); if(results.containsKey(result)) { String toUrl = results.get(result); request.getRequestDispatcher(toUrl).forward(request, response); } else { throw new RuntimeException("result is error! result:" + result); } }
好了,現(xiàn)在我們已經(jīng)實(shí)現(xiàn)了最簡單的strut2框架的請求轉(zhuǎn)發(fā)的功能。功能寫得很粗糙,很多情況都還未考慮進(jìn)來,希望大家多多指點(diǎn)~
以上所述就是本文的全部內(nèi)容了,希望大家能夠喜歡。
相關(guān)文章
Maven配置文件修改及導(dǎo)入第三方j(luò)ar包的實(shí)現(xiàn)
本文主要介紹了Maven配置文件修改及導(dǎo)入第三方j(luò)ar包的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-08-08使用注解+RequestBodyAdvice實(shí)現(xiàn)http請求內(nèi)容加解密方式
這篇文章主要介紹了使用注解+RequestBodyAdvice實(shí)現(xiàn)http請求內(nèi)容加解密方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06JAVA編程實(shí)現(xiàn)TCP網(wǎng)絡(luò)通訊的方法示例
這篇文章主要介紹了JAVA編程實(shí)現(xiàn)TCP網(wǎng)絡(luò)通訊的方法,簡單說明了TCP通訊的原理并結(jié)合具體實(shí)例形式分析了java實(shí)現(xiàn)TCP通訊的步驟與相關(guān)操作技巧,需要的朋友可以參考下2017-08-08Spring中如何使用@Value注解實(shí)現(xiàn)給Bean屬性賦值
這篇文章主要介紹了Spring中如何使用@Value注解實(shí)現(xiàn)給Bean屬性賦值的操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08詳解SpringBoot緩存的實(shí)例代碼(EhCache 2.x 篇)
這篇文章主要介紹了詳解SpringBoot緩存的實(shí)例代碼(EhCache 2.x 篇),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07Java inputstream和outputstream使用詳解
這篇文章主要介紹了Java inputstream和outputstream使用詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08