MVC 框架最基础的功能就是跳转,struts2支持注解+xml配置跳转,在这里我设计成xml配置的跳转。
配置跳转需要用到的知识有:反射,xml读取。
反射是实现动态装配的基础,它使我们的程序更具动态性和可扩展性,几乎所有的流行框架都以它为基础实现。xml读取基本都会采用dom4j完成。
实现跳转的过程:xml配置命名空间,Action处理类,请求的action方法和跳转的页面,在form提交请求后,被中心Servlet处理,解析出请求的路径,根据xml配置的各种信息,反射调用目标Action类的处理方法,并且根据xml配置的目标跳转页面进行跳转。
因此提炼出的 核心配置有
1、namcespace:命名空间,不同模块有不同的namespace。
2、name:form请求的名字。
3、method:name对应的Action处理方法名,会被反射调用。
4、 class:Action处理类的全路径,用于在中心Servlet反射生成。
5、 result子标签:Action处理后的跳转页面,跳转方式为forward或redirect。
下面我们就开始实现这个简单的跳转功能:
1、首先建一个web工程(Eclipse),取名Controller。
2、接着建一个Filter,取名StrutsFilter,做中心处理器用。(也可以用Servlet,这里我用Filter)
3、根目录建control.xml当作跳转配置文件。
<?xml version="1.0" encoding="UTF-8"?> //标签名可自定义,这里高仿Struts看着习惯点。 <struts> <package name="user" namespace="/admin"> <action name="userAction" class="action.UserAction"> <result name="addUser" >/addUser.jsp</result> </action> </package> </struts>
4、配置web.xml,使其拦截所有action结尾的请求。
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <filter> <filter-name>struts3</filter-name> <filter-class>core.StrutsFilter</filter-class> </filter> <filter-mapping> <filter-name>struts3</filter-name> <url-pattern>*.action</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
5 、创建Entity( ActionEntity、PackageEntity、ResultEntity)
package core; import java.util.List; public class ActionEntity { private String name; private String classname; private List<ResultEntity> result; public List<ResultEntity> getResult() { return result; } public void setResult(List<ResultEntity> result) { this.result = result; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getClassname() { return classname; } public void setClassname(String classname) { this.classname = classname; } }
package core; import java.util.List; public class PackageEntity { private String name; private String namespace; private List<ActionEntity> action; public List<ActionEntity> getAction() { return action; } public void setAction(List<ActionEntity> action) { this.action = action; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getNamespace() { return namespace; } public void setNamespace(String namespace) { this.namespace = namespace; } }
package core; public class ResultEntity { private String name; private String page; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPage() { return page; } public void setPage(String page) { this.page = page; } }
6、创建解析xml的类(ConfigUtils),实现配置文件的解析功能。
package core; import java.util.ArrayList; import java.util.List; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; /** * 解析配置文件 * @author Candy * */ public class ConfigUtils { //创建封装对象,把从配置文件中读取到的值设置到对象里。(这里假设只有一个package,如果多个换成集合) public static PackageEntity packageEntity=new PackageEntity(); public static void config(){ //创建解析对象 SAXReader reader= new SAXReader(); try { //读取自定的配置文件 Document document= reader.read(Thread.currentThread().getContextClassLoader().getResourceAsStream("control.xml")); //得到根元素 Element root=document.getRootElement(); //得到根元素下面的package元素 Element packageElement=root.element("package"); packageEntity.setName(packageElement.attributeValue("name")); packageEntity.setNamespace(packageElement.attributeValue("namespace")); //得到package里面的所以action元素 List<Element> listActions=packageElement.elements("action"); //创建一个集合用来存得到的所有的action元素,后面要把这个集合设置到封装的package对象里面。 List<ActionEntity> action=new ArrayList<ActionEntity>(); //循环得到每一个action元素,并把得到的action的值设置到封装的action对象里。 for( Element actionElement :listActions){ ActionEntity actionEntity=new ActionEntity(); actionEntity.setName(actionElement.attributeValue("name")); actionEntity.setClassname(actionElement.attributeValue("class")); //得到action里面的所有result元素, List<Element> listResult=actionElement.elements("result"); //创建一个集合用来存储得到的所以的result元素,后面要把这个集合设置到封装的action对象里面 List<ResultEntity> result=new ArrayList<ResultEntity>(); //循环得到每一个result元素,并把得到的值设置到封装的result对象里。 for(Element resuleElement :listResult){ ResultEntity resultEntity=new ResultEntity(); resultEntity.setName(resuleElement.attributeValue("name")); resultEntity.setPage(resuleElement.getText()); //把创建的所有的并赋值后的result添加的上面定义的集合里面。 result.add(resultEntity); } //这里把上面有数据的result所有集合设置到所有的action封装对象里面。 actionEntity.setResult(result); //把包含所有result的所有action添加到上面定义的这个action的集合里 action.add(actionEntity); } //把含有所有数据的action这个集合设置到package这个封装的对象里。 packageEntity.setAction(action); } catch (DocumentException e) { e.printStackTrace(); } } }
7、实现StrutsFilter中跳转的核心功能。
package core; import java.io.IOException; import java.lang.reflect.Method; import java.util.List; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; /** * 实现基本跳转功能 * @author Candy * */ public class StrutsFilter implements Filter{ @Override public void destroy() { } @Override public void doFilter(ServletRequest request0, ServletResponse response0, FilterChain chain) throws IOException, ServletException { //得到封装的package对象。 PackageEntity pack=ConfigUtils.packageEntity; //得到用户访问的路径。 HttpServletRequest request=(HttpServletRequest)request0; String path=request.getServletPath(); //拆分得到的路径。 String [] pathArr=path.split("/"); //namespace的名字。 String nameSpace=pathArr[1]; //action的名字和方法的一个整体。 String action=pathArr[2]; //接着查拆分这个整体,得到action名 String actionName=action.split("!")[0]; //得到方法名 String methodName=action.split("!")[1].split("\\.")[0]; List<ActionEntity>listAction=pack.getAction(); ActionEntity doAction=null; for(ActionEntity ae:listAction){ if(ae.getName().equals(actionName)){ doAction=ae; break; } } try { //反射一个action的类 Class actionClass=Class.forName(doAction.getClassname()); //创建一个action的对象。 Object actionObject=actionClass.newInstance(); //再次得到类 Class cla=actionObject.getClass(); //调用用户访问路径里的方法 Method actionMethod= cla.getDeclaredMethod(methodName); //执行方法 String resultValue=(String)actionMethod.invoke(actionObject,null); //得到result的集合。然后查找对应的result返回。 List<ResultEntity> listResult=doAction.getResult(); ResultEntity result=null; for(ResultEntity re: listResult){ if(re.getName().equals(resultValue)){ result=re; break; } } //得到跳转的页面 String page=result.getPage(); request.getRequestDispatcher(page).forward(request, response0); } catch (Exception e) { e.printStackTrace(); } } @Override public void init(FilterConfig filterConfig) throws ServletException { ConfigUtils.config(); } }
8、到此为止一个简单的跳转功能就实现了。测试代码如下:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%String path = request.getContextPath();%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>首页</title> </head> <body> <a href="<%=path%>/admin/userAction!toAddUser.action">添加用户</a> </body> </html>
package action; public class UserAction { public String toAddUser(){ System.out.println("添加新用户"); return "addUser"; } }
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <body> 添加用户成功。 </body> </html>