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>