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();//这个是请求的资源路径名
//判断请求的路径名是否action
if(servletPath.lastIndexOf(".action")==-1){
arg2.doFilter(request, response);
}else{
try {
//地址格式为:cccc/user.action /user.action
int lastSlashIndex = servletPath.lastIndexOf("/");
String requestActionName = servletPath
.substring(lastSlashIndex + 1);
String nameSpaceName = servletPath.substring(0,
lastSlashIndex);//这个是总项目后面那个文件夹地址
//2.到map中找到这个action
PackageWrapper 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返回的String
ActionWrapper 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 =》Map
realpath=config.getServletContext().getRealPath("/");
packagesMap=parseStrutsXml();
}
解析xml文件省略了,这是我自己实现的一个最简单的struts,当然强大的struts我只是模仿了皮毛中的皮毛,欢迎来讨论和相互学习。