java中struts 框架的实现
该文章主要简单粗暴的实现了struts的请求转发功能。其他的功能后续会慢慢补上。
最近在学习javassist的内容,看到一篇文章 大家一起写mvc 主要简单的描述了mvc的工作流程,同时实现了简单的struts2功能。
这里仿照的写了个简单的struts2框架,同时加上了自己的一些理解。
该文章主要简单粗暴的实现了struts的请求转发功能。其他的功能后续会慢慢补上。
首先,在struts2框架中,请求的实现、跳转主要是通过在struts.xml进行相关配置。一个<action>标签表示一个请求的定义,action中包含了①请求的名称“name”;②请求对应的实现类“class”;③同时还可通过“method”属性自定义执行的方法,若没配置默认执行execute0方法。<result》标签定义了①结果的类型“name”,包括'SUCCESS'、'NONE'、'LOGIN'、'INPUT'、'ERROR';②请求的类型“type”,包括'dispatcher(默认)'、'chain'、'redirect'、'redirectAction'、'stream';③结果的跳转。在配置完struts.xml后,界面中的表单就可以通过action属性与action定义的name属性值相匹配找到对应的action标签,从而找到对应的class以及执行的方法。再根据执行方法返回的string字符串同result标签中的name相匹配,根据定义的type类型,进行下一步请求操作。
好了,在了解了struts2是怎么将界面请求同程序功能相连接后,我们通过自己的代码来实现这部分的功能。
那么,我们该如何下手了?
我们将需要实现的功能简单的分为两部分①action部分②result部分
action部分
①我们需要根据界面的请求找到对应的类以及执行的方法
result部分
①我们需要根据方法执行的逻辑返回'SUCCESS'、'NONE'、'LOGIN'、'INPUT'、'ERROR'这类型的字符串
②需要对不同的返回类型,指定不同的下一步请求地址
③需要定义请求的类型,包括'dispatcher(默认)'、'chain'、'redirect'、'redirectAction'、'stream'
在本文章中,result的返回类型只实现了'SUCCESS'、'LOGIN'两种,并且暂不考虑请求类型,实现的是默认的dispatcher请求转发类型。完善的功能后期会再补充。
那么,下面我们来通过代码看怎么实现如上功能。
首先定义了ActionAnnotation和ResultAnnotation两个自定义注解来请求需要对应的方法以及方法返回的字符串对应的跳转请求
/**
*action注解:ActionName相当于web.xml配置中的url-pattern
*@authorlinling
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public@interfaceActionAnnotation
{
StringActionName()default"";
ResultAnnotation[]results()default{};
}
/**
*返回注解对象:name相当于struts配置中的result的name,包括'SUCCESS'、'NONE'、'ERROR'、'INPUT'、'LOGIN';value相当于struts配置中对应返回跳转内容
*@authorlinling
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public@interfaceResultAnnotation
{
ResultTypename()defaultResultType.SUCCESS;
Stringvalue()default"index.jsp";
}
然后我们定义一个ActionContext类,来保存一个请求所需要的内容
/**
*实现模拟struts根据配置文件跳转至action执行相应方法中需要的内容
*@authorlinling
*
*/
publicclassActionContext
{
/**
*相当于web.xml中url-pattern,唯一的
*/
privateStringUrl;
/**
*ActionAnnotation注解对应方法,也就是action中要执行的方法
*/
privateStringmethod;
/**
*ActionAnnotation中的Result,对应action方法返回的类型。例如:key:'SUCCESS';value:'index.jsp'
*/
privateMap<ResultType,String>results;
/**
*action的类
*/
privateClass<?>classType;
/**
*action的对象
*/
privateObjectaction;
/**
*方法参数类型
*/
privateClass<?>[]paramsType;
/**
*方法参数的名称,注意这里方法名称需要和上面paramType参数一一对应
*可以理解为是struts中action中的属性
*/
privateString[]actionParamsName;
/**
*本次请求的HttpServletRequest
*/
privateHttpServletRequestrequest;
/**
*本次请求的HttpServletResponse
*/
privateHttpServletResponseresponse;
analysePackage是在组装ActionContext需要的方法
/**
*遍历scan_package包下的class文件,将使用了ActionAnnotation注解的方法进行解析,组装成ActionContext对象并放入urlMap中
*@paramreal_path
*@paramscan_package
*@throwsClassNotFoundException
*@throwsInstantiationException
*@throwsIllegalAccessException
*@throwsNotFoundException
*/
publicstaticvoidanalysePackage(Stringreal_path,Stringscan_package)throwsClassNotFoundException,InstantiationException,IllegalAccessException,NotFoundException
{
Filefile=newFile(real_path);
if(file.isDirectory())
{
File[]files=file.listFiles();
for(Filef:files)
{
analysePackage(f.getAbsolutePath(),scan_package);
}
}
else
{
Stringstr=real_path.replaceAll("/",".");
if(str.indexOf("classes."+scan_package)<=0||!str.endsWith(".class"))
{
return;
}
StringfileName=str.substring(str.indexOf(scan_package),str.lastIndexOf(".class"));
Class<?>classType=Class.forName(fileName);
Method[]methods=classType.getMethods();
for(Methodmethod:methods)
{
if(method.isAnnotationPresent(ActionAnnotation.class))
{
ActionContextactionContext=newActionContext();
ActionAnnotationactionAnnotation=(ActionAnnotation)method.getAnnotation(ActionAnnotation.class);
Stringurl=actionAnnotation.ActionName();
ResultAnnotation[]results=actionAnnotation.results();
if(url.isEmpty()||results.length<1)
{
thrownewRuntimeException("methodannotationerror!method:"+method+",ActionName:"+url+",result.length:"+results.length);
}
actionContext.setUrl(url);
actionContext.setMethod(method.getName());
Map<ResultType,String>map=newHashMap<ResultType,String>();
for(ResultAnnotationresult:results)
{
Stringvalue=result.value();
if(value.isEmpty())
{
thrownewRuntimeException("Resultname()isnull");
}
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是根据httpServletRequest请求中的请求内容获得请求参数数组,该参数数组为调用方法体的参数内容
/**
*根据参数类型parasType和参数名actinParamsName来解析请求request构建参数object[]
*@paramrequest
*@paramparamsType
*@paramactionParamsName
*@return
*@throwsInstantiationException
*@throwsIllegalAccessException
*@throwsIllegalArgumentException
*@throwsInvocationTargetException
*@throwsNoSuchMethodException
*@throwsSecurityException
*/
publicstaticObject[]getParams(HttpServletRequestrequest,Class<?>[]paramsType,String[]actionParamsName)throwsInstantiationException,IllegalAccessException,IllegalArgumentException,InvocationTargetException,NoSuchMethodException,SecurityException
{
Object[]objects=newObject[paramsType.length];
for(inti=0;i<paramsType.length;i++)
{
Objectobject=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(Fieldfield:fields)
{
Map<String,String[]>map=request.getParameterMap();
for(Iterator<String>iterator=map.keySet().iterator();iterator.hasNext();)
{
Stringkey=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;
}
Stringvalue=map.get(key)[0];
classType.getMethod(convertoFieldToSetMethod(field.getName()),field.getType()).invoke(object,value);
break;
}
}
objects[i]=object;
}
}
returnobjects;
}
好了,接下来。我们可以来实现action方法了
publicclassLoginAction
{
@ActionAnnotation(ActionName="login.action",results={@ResultAnnotation(name=ResultType.SUCCESS,value="index.jsp"),@ResultAnnotation(name=ResultType.LOGIN,value="login.jsp")})
publicResultTypelogin(Stringname,Stringpassword)
{
if("hello".equals(name)&&"world".equals(password))
{
returnResultType.SUCCESS;
}
returnResultType.LOGIN;
}
@ActionAnnotation(ActionName="loginForUser.action",results={@ResultAnnotation(name=ResultType.SUCCESS,value="index.jsp"),@ResultAnnotation(name=ResultType.LOGIN,value="login.jsp")})
publicResultTypeloginForUser(intnumber,LoginPojologinPojo)
{
if("hello".equals(loginPojo.getUsername())&&"world".equals(loginPojo.getPassword()))
{
returnResultType.SUCCESS;
}
returnResultType.LOGIN;
}
}
接下来,我们需要做的是让程序在启动的时候去遍历工作目录下所有类的方法,将使用了ActionAnnotation的方法找出来组装成ActionContext,这就是我们请求需要执行的方法。这样在请求到了的时候我们就可以根据请求的地址找到对应的ActionContext,并通过反射的机制进行方法的调用。
我们定了两个Servlet。一个用于执行初始化程序。一个用来过滤所有的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实现了对所用action请求的过滤,并使之执行对应的action方法,以及进行下一步的跳转
ublicvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)
throwsServletException,IOException
{
request.setCharacterEncoding("utf-8");
Stringurl=request.getServletPath().substring(1);
ActionContextactionContext=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();
Methodmethod=classType.getMethod(actionContext.getMethod(),actionContext.getParamsType());
ResultTyperesult=(ResultType)method.invoke(actionContext.getAction(),params);
Map<ResultType,String>results=actionContext.getResults();
if(results.containsKey(result))
{
StringtoUrl=results.get(result);
request.getRequestDispatcher(toUrl).forward(request,response);
}
else
{
thrownewRuntimeException("resultiserror!result:"+result);
}
}
好了,现在我们已经实现了最简单的strut2框架的请求转发的功能。功能写得很粗糙,很多情况都还未考虑进来,希望大家多多指点~
以上所述就是本文的全部内容了,希望大家能够喜欢。