java中struts 框架的實現(xiàn)
該文章主要簡單粗暴的實現(xiàn)了struts的請求轉(zhuǎn)發(fā)功能。 其他的功能后續(xù)會慢慢補上。
最近在學習javassist的內(nèi)容,看到一篇文章 大家一起寫mvc 主要簡單的描述了mvc的工作流程,同時實現(xiàn)了簡單的struts2功能。
這里仿照的寫了個簡單的struts2框架,同時加上了自己的一些理解。
該文章主要簡單粗暴的實現(xiàn)了struts的請求轉(zhuǎn)發(fā)功能。 其他的功能后續(xù)會慢慢補上。
首先,在struts2框架中,請求的實現(xiàn)、跳轉(zhuǎn)主要是通過在struts.xml進行相關(guān)配置。 一個<action>標簽表示一個請求的定義,action中包含了①請求的名稱“name”;②請求對應的實現(xiàn)類“class” ;③同時還可通過“method”屬性自定義執(zhí)行的方法,若沒配置默認執(zhí)行execute0方法。<result》標簽定義了①結(jié)果的類型“name”,包括'SUCCESS'、'NONE'、'LOGIN'、'INPUT'、'ERROR';②請求的類型“type”,包括'dispatcher(默認)'、'chain'、'redirect'、'redirectAction'、'stream';③結(jié)果的跳轉(zhuǎn)。 在配置完struts.xml后,界面中的表單就可以通過action屬性與action定義的name屬性值相匹配找到對應的action標簽,從而找到對應的class以及執(zhí)行的方法。再根據(jù)執(zhí)行方法返回的string字符串同result標簽中的name相匹配,根據(jù)定義的type類型,進行下一步請求操作。
好了,在了解了struts2是怎么將界面請求同程序功能相連接后,我們通過自己的代碼來實現(xiàn)這部分的功能。
那么,我們該如何下手了?
我們將需要實現(xiàn)的功能簡單的分為兩部分 ①action部分 ②result部分
action部分
①我們需要根據(jù)界面的請求找到對應的類以及執(zhí)行的方法
result部分
①我們需要根據(jù)方法執(zhí)行的邏輯返回'SUCCESS'、'NONE'、'LOGIN'、'INPUT'、'ERROR'這類型的字符串
②需要對不同的返回類型,指定不同的下一步請求地址
③需要定義請求的類型,包括'dispatcher(默認)'、'chain'、'redirect'、'redirectAction'、'stream'
在本文章中,result的返回類型只實現(xiàn)了'SUCCESS'、'LOGIN'兩種,并且暫不考慮請求類型,實現(xiàn)的是默認的dispatcher請求轉(zhuǎn)發(fā)類型。完善的功能后期會再補充。
那么,下面我們來通過代碼看怎么實現(xiàn)如上功能。
首先定義了ActionAnnotation和ResultAnnotation 兩個自定義注解來請求需要對應的方法以及方法返回的字符串對應的跳轉(zhuǎn)請求
/**
* action注解:ActionName相當于web.xml配置中的url-pattern
* @author linling
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ActionAnnotation
{
String ActionName() default "";
ResultAnnotation[] results() default {};
}
/**
* 返回注解對象:name相當于struts配置中的result的name,包括'SUCCESS'、'NONE'、'ERROR'、'INPUT'、'LOGIN';value相當于struts配置中對應返回跳轉(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";
}
然后我們定義一個ActionContext類,來保存一個請求所需要的內(nèi)容
/**
* 實現(xiàn)模擬struts根據(jù)配置文件跳轉(zhuǎn)至action執(zhí)行相應方法中需要的內(nèi)容
* @author linling
*
*/
public class ActionContext
{
/**
* 相當于web.xml中url-pattern,唯一的
*/
private String Url;
/**
* ActionAnnotation注解對應方法,也就是action中要執(zhí)行的方法
*/
private String method;
/**
* ActionAnnotation中的Result,對應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ù)一一對應
* 可以理解為是struts中action中的屬性
*/
private String[] actionParamsName;
/**
* 本次請求的HttpServletRequest
*/
private HttpServletRequest request;
/**
* 本次請求的HttpServletResponse
*/
private HttpServletResponse response;
analysePackage是在組裝ActionContext需要的方法
/**
* 遍歷scan_package包下的class文件,將使用了ActionAnnotation注解的方法進行解析,組裝成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;
}
好了,接下來。我們可以來實現(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;
}
}
接下來,我們需要做的是讓程序在啟動的時候去遍歷工作目錄下所有類的方法,將使用了ActionAnnotation的方法找出來組裝成ActionContext,這就是我們請求需要執(zhí)行的方法。這樣在請求到了的時候我們就可以根據(jù)請求的地址找到對應的ActionContext,并通過反射的機制進行方法的調(diào)用。
我們定了兩個Servlet。一個用于執(zhí)行初始化程序。一個用來過濾所有的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實現(xiàn)了對所用action請求的過濾,并使之執(zhí)行對應的action方法,以及進行下一步的跳轉(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)實現(xiàn)了最簡單的strut2框架的請求轉(zhuǎn)發(fā)的功能。功能寫得很粗糙,很多情況都還未考慮進來,希望大家多多指點~
以上所述就是本文的全部內(nèi)容了,希望大家能夠喜歡。
相關(guān)文章
Maven配置文件修改及導入第三方j(luò)ar包的實現(xiàn)
本文主要介紹了Maven配置文件修改及導入第三方j(luò)ar包的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-08-08
使用注解+RequestBodyAdvice實現(xiàn)http請求內(nèi)容加解密方式
這篇文章主要介紹了使用注解+RequestBodyAdvice實現(xiàn)http請求內(nèi)容加解密方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06
JAVA編程實現(xiàn)TCP網(wǎng)絡(luò)通訊的方法示例
這篇文章主要介紹了JAVA編程實現(xiàn)TCP網(wǎng)絡(luò)通訊的方法,簡單說明了TCP通訊的原理并結(jié)合具體實例形式分析了java實現(xiàn)TCP通訊的步驟與相關(guān)操作技巧,需要的朋友可以參考下2017-08-08
Spring中如何使用@Value注解實現(xiàn)給Bean屬性賦值
這篇文章主要介紹了Spring中如何使用@Value注解實現(xiàn)給Bean屬性賦值的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08
詳解SpringBoot緩存的實例代碼(EhCache 2.x 篇)
這篇文章主要介紹了詳解SpringBoot緩存的實例代碼(EhCache 2.x 篇),具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-07-07
Java inputstream和outputstream使用詳解
這篇文章主要介紹了Java inputstream和outputstream使用詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-08-08

