import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/**
 * servlet自封装
 * @author 
 * @date 20110323
 */
public class Action extends HttpServlet{ private static final long serialVersionUID = -6716601164831968512L;
//访问Action时可以省略的包路径
private String servletPackage;
//Action返回jsp时可以省略的路径
private String jspPath;
//发生错误时返回页
private String errorPage; protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
} protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//访问的方法名
String methodName = request.getParameter("method");
String uri = request.getRequestURI();
//访问的Action名
String className = uri.substring(1,uri.length()-3);
if(className.startsWith("jsp/") || className.startsWith("/")){
className = className.substring(className.lastIndexOf('/') + 1);
}
if(!className.startsWith(servletPackage)){
className = servletPackage + "." + className;
}
try {
Class clazz = Class.forName(className);
Method method = clazz.getDeclaredMethod(methodName,new Class[]{HttpServletRequest.class,HttpServletResponse.class});
Object forward = method.invoke(clazz.newInstance(), new Object[]{request,response});
if(forward == null){
return;
}else{
String forwardStr = forward.toString();
if(!forwardStr.startsWith("/")){
forwardStr = "/" + forwardStr;
}
if(!forwardStr.startsWith(jspPath)){
if(!forwardStr.startsWith("/jsp/face") && !forwardStr.startsWith("/jsp/login.jsp")){
forwardStr = jspPath + forwardStr;
}
}
String realPath = request.getSession().getServletContext().getRealPath(forwardStr);
boolean exists = new File(realPath).exists();
if(exists){
request.getRequestDispatcher(forwardStr).forward(request, response);
}else{
request.setAttribute("msg", "404,你懂的,未找到"+forwardStr);
request.getRequestDispatcher(errorPage).forward(request, response);
}
}
} catch (ClassNotFoundException e) {
request.setAttribute("msg", "404,你懂的,未找到"+className+"类");
request.setAttribute("stackTrace", e.getStackTrace());
request.getRequestDispatcher(errorPage).forward(request, response);
} catch (SecurityException e) {
request.setAttribute("msg", e.getMessage());
request.setAttribute("stackTrace", e.getStackTrace());
request.getRequestDispatcher(errorPage).forward(request, response);
} catch (NoSuchMethodException e) {
request.setAttribute("msg", "未在" + className + "中找到" + methodName + "方法,或者该方法的参数不是HttpServletRequest和HttpServletResponse");
request.setAttribute("stackTrace", e.getStackTrace());
request.getRequestDispatcher(errorPage).forward(request, response);
} catch (IllegalArgumentException e) {
request.setAttribute("msg", e.getMessage());
request.setAttribute("stackTrace", e.getStackTrace());
request.getRequestDispatcher(errorPage).forward(request, response);
} catch (IllegalAccessException e) {
request.setAttribute("msg", e.getMessage());
request.setAttribute("stackTrace", e.getStackTrace());
request.getRequestDispatcher(errorPage).forward(request, response);
} catch (InvocationTargetException e) {
//反射的Action抛出的异常
request.setAttribute("msg", e.getCause());
request.setAttribute("stackTrace", e.getCause().getStackTrace());
request.getRequestDispatcher(errorPage).forward(request, response);
} catch (InstantiationException e) {
request.setAttribute("msg", e.getMessage());
request.setAttribute("stackTrace", e.getStackTrace());
request.getRequestDispatcher(errorPage).forward(request, response);
}
} public void destroy() {
super.destroy();
} public void init() throws ServletException {
servletPackage = getServletConfig().getInitParameter("servletPackage");
jspPath = getServletConfig().getInitParameter("jspPath");
if(!jspPath.startsWith("/")){
jspPath = "/" + jspPath;
}
errorPage = getServletConfig().getInitParameter("errorPage");
super.init();
}

}

解决方案 »

  1.   


    request.setAttribute("msg", "404,你懂的,未找到"+className+"类");
    万恶的C S D N
      

  2.   

    struts1 也是这么干的。楼主继续努力啊
      

  3.   

    很好,很强大啊,把类名方法直接暴露在 URL 中!
      

  4.   

    1。类名和方法名都暴露在url中。。
    2。有没有考虑是forward还是redirect?或者ajax??
    3。出错提示太露骨。。要是用户访问了你一个不存在的action 那你会怎么提示:404,你懂的,未找到"+className+"类"   而且,楼主,你这思想是从struts1里面学过来吧??  不过总的来说敢于自己写就是不错的。。敢于分享就是更好,只有分享,大家不断提建议,慢慢才会更好。
      

  5.   

    有些强大!看来你也WEB涉足很深嘛!不成精简直对不起党!给力!
      

  6.   

    好棒! 提一个小小的建议,能不能像SpringMVC一样支持restful风格的参数。
    把类名方法名参数直接暴露在URL中。
      

  7.   

    嗯,控制层封装的我只用过struts1,所以经常想它是怎么做的。后来觉得完全可以自己写个不用它的,于是就自己写了个,呵呵。
    我技术不是很好,所以也不知道forward还是redirect的区别,因为forward能在request里面放参数,所以一直用forward。
    关于ajax这个时能实现的,只要自定义的方法返回void就行。却是安全方面没考虑 谢谢指正!!
      

  8.   

    你这个应该是“控制器”跟贴近
    既然你知道reflect、IO,为什么不跑类名、包名这些东西配置到properties、xml文件中
    而且不必暴露在url上
      

  9.   

    精神很值得鼓励不过你貌似没能比Servlet更加的解除耦合!!!
      

  10.   

    以前做过类似的工作package cn.ialvin.web;import java.io.IOException;
    import java.util.regex.Pattern;import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;/**
     * 拦截所有 Action 的 Servlet。此 Servlet 接到 Action 请求时,将根椐 URI 路径来确定具体实例化哪个 Action 实例,并调用 Action 实例的 execute() 方法。
     * @author 林俊海(<a href='http://www.ialvin.cn' target="_blank"><b>ialvin.cn</b></a>) 广东 普宁 里湖
     */
    @SuppressWarnings("serial")
    public class Servlet extends HttpServlet {
    private static final Log log = LogFactory.getLog(Servlet.class);

    /**
     * 保存记录的 ServletContext 对象
     */
    private ServletContext application = null;
    /**
     * 所有 Action 类的基本包路径。
     * 这个路径可以在 Servlet 的配置中,由 "serversPackage" 参数所指定
     */
    private String servicesPackage = ""; /**
     * 本方法会从 Servlet 配置中,获取 Servlet 参数 "serverPackage"<br/>
     * 以方便用后在响应客户端请求时,"serverPackage" 参数指定的包中加载 Action 类以及实例。
     */
    public void init() throws ServletException {
    super.init();
    this.application = this.getServletContext();
    String serversPackage = this.getInitParamTrim("servicesPackage");
    if (!"".equals(serversPackage))
    this.servicesPackage = servicesPackage + ".";
    }

    /**
     * 根据 URI 中请求的 Action 路径,以及基本 Action 包路径,加载一个 Action 类<br/>
     * 如果不存在相应的 Action 类,将返回 null;
     * @param className
     * @return
     */
    @SuppressWarnings("unchecked")
    private Class<IAction> findClass(String className) {
    try {
    return (Class<IAction>)Class.forName(servicesPackage + className);
    } catch (ClassNotFoundException e) {
    return null;
    }
    }

    /** 
     * 本方法将根据客户端请求此 Servlet 时的 URI 来判定所请求的 Action<br/>
     * 方法中实现了对 Action 的加载和实例化,调用 Action 实例的特定方法。如:<br/>
     * init()、execute()、destory() 等。
     */
    public void service(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
    String actionPath = request.getServletPath();
    final Pattern transformRegExp = Pattern.compile("^/|\\.[^.]+$");
    actionPath = transformRegExp.matcher(actionPath).replaceAll("");
    actionPath = actionPath.replace('/', '.'); // IAction 是个接口, action 必须实现这个接口
    Class<IAction> cls = null;
    if (null == (cls=this.findClass(actionPath))) {
    response.sendError(404, "Action Not Found.");
    return;
    }

    if (IAction.class.isAssignableFrom(cls)) {
    response.sendError(404, "Action Not Found.");
    return;
    } IAction action = null;
    try {
    action = (IAction)cls.newInstance();
    action.init(this, application, request, response);
    action.beforeExecute();
    action.execute();
    } catch (RuntimeException e) {
    Servlet.log.error("Action Runtime Exception", e);
    response.sendError(500, "Server Error.");
    } catch (Exception e) {
    Servlet.log.error("Action Exception", e);
    response.sendError(500, "Server Error.");
    } finally {
    if (action != null) action.destroy();
    }
    } /**
     * 从 Servlet 配置中获取一个指定的参数
     * @param name 指定的参数的名称
     * @return String 内容的参数值,并且参数值已经去首尾空白字符。如果没有配置则返回空串""
     */
    private String getInitParamTrim(String name) {
    if (null == name) return "";
    String value = this.getInitParameter(name);
    if (null == value) return "";
    return value.trim();
    }
    }package cn.ialvin.web;import java.io.IOException;import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;/**
     * 定义 Action 类所必须实现的接口。
     * @author 林俊海(<a href='http://www.ialvin.cn' target="_blank"><b>ialvin.cn</b></a>) 广东 普宁 里湖
     */
    public interface IAction {

    /**
     * 用于 ActionServlet 在得到 Action 实例后,对该 Action 的初始化
     * @param svl  cn.ialvin.web.Servlet 的实例
     * @param app  当前应用上下文对象
     * @param request 客户端请求对象
     * @param response 服务端响应对象
     * @throws ServletException
     * @throws IOException
     */
    public void init(
    Servlet svl,
    ServletContext app,
    HttpServletRequest request,
    HttpServletResponse response
    ) throws ServletException, IOException; /**
     * 实例所实现的 beforeExecute() 方法将会在 execute() 方法执行之前被执行
     * @throws ServletException
     * @throws IOException
     */
    public void beforeExecute() throws ServletException, IOException;
    /**
     * 实例如对请求的具体响应方法。
     * @throws ServletException
     * @throws IOException
     */
    public void execute() throws ServletException, IOException;

    /**
     * destory() 将会在响应完毕被调用,一些需要做善后处理的事情,应该在此方法中实现
     * @throws ServletException
     * @throws IOException
     */
    public void destroy() throws ServletException, IOException;
    }
    package cn.ialvin.web;import java.io.IOException;
    import java.io.PrintWriter;import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;/**
     * 一个 Action 的基本模型,其中实现了部分 IAction 方法。<br>
     * 未实现 IAction 中的 execute() 方法,此方法将由子类各自实现。<br>
     * 类中并提供了多个常用方法,供子类使用。
     * @author 林俊海(<a href='http://www.ialvin.cn' target="_blank"><b>ialvin.cn</b></a>) 广东 普宁 里湖
     */
    public abstract class Action implements IAction {
    /**
     * cn.ialvin.web.Servlet 实例
     */
    protected Servlet servlet = null;
    /**
     * ServletContext 对象,通称 application
     */
    protected ServletContext application = null;
    protected HttpServletRequest request = null;
    protected HttpServletResponse response = null;
    protected PrintWriter out = null; /**
     * 此方法已被设置成 final 不可重写,主要用于 Action 实例在创建后<br>
     * 由 cn.ialvin.web.Servlet 调用,对实例进行初始化。
     */
    final public void init(Servlet servlet, ServletContext application,
    HttpServletRequest request,
    HttpServletResponse response) throws IOException {
    this.servlet = servlet;
    this.application = application;
    this.request = request;
    this.response = response;
    } /**
     * 方法中,对响应进行一些预处理<br>
     * 如:设置请求的字符集编码,响应的 ContentType 等.<br>
     * 子类可以对其进行重写
     */
    public void beforeExecute() throws ServletException, IOException {
    request.setCharacterEncoding("UTF-8");
    response.setCharacterEncoding("UTF-8");
    response.setHeader("ContentType", "text/html; charset=UTF-8");
    response.setContentType("text/html; charset=utf-8");
    this.out = this.getWriter();
    }
    /**
     * 方法中默认对响应流进行关闭<br>
     * 子类可以重写其实现内容
     */
    public void destroy() throws ServletException, IOException {
    if (null != out) {
    out.flush();
    out.close();
    }
    } /**
     * 对字符串进行 HTML 实体编码<br>
     * 如:encodeHTML("><&\"'") 得到 "&gt;&lt;&amp;&quot;'"
     * @param s 要编码的字符串
     * @see cn.ialvin.web.Encoder#encodeHTML(String)
     */
    protected String encodeHTML(String s) { return Encoder.encodeHTML(s); } /**
     * 对字符串进行 URI 编码<br>
     * 如:encodeURI("林俊海") 得到 "%E6%9E%97%E4%BF%8A%E6%B5%B7"
     * @param s 要编码的字符串
     * @see cn.ialvin.web.Encoder#encodeURIComponent(String)
     */
    protected String encodeURI(String s) { return Encoder.encodeURIComponent(s); } /**
     * 对字符串进行 escape 编码<br>
     * 如:escape("林俊海") 得到 "%u6797%u4FCA%u6D77"
     * @param s 要编码的字符串
     * @see cn.ialvin.web.Encoder#escape(String)
     */
    protected String escape(String s) { return Encoder.escape(s); } /**
     * 获取响应流
     * @return 响应流
     */
    protected PrintWriter getWriter() {
    if (null != out) return out;
    try {
    return this.response.getWriter();
    } catch (IOException e) {
    throw new RuntimeException(e);
    }
    }
    /**
     * 向客户端打印
     * @param x 要输出的内容
     */
    protected void w(Object x) {
    if (x == null) return;
    if (null == out) out = this.getWriter();
    out.print(x.toString());
    }
    /**
     * 向客户端打印一行
     * @param x 要输出的内容
     */
    protected void wln(Object x) { this.w(x); this.w("\r\n"); }
    /**
     * 向客户端输出脚本
     * @param js 要输出的 javascript 代码
     */
    protected void wScript(String js) {
    this.wln("<script type=\"text/JavaScript\">//<![CDATA[");
    this.wln(js);
    this.wln("//]]></script>");
    }
    /**
     * 向客户端输出 javascript 以让浏览器弹出消息框
     * @param msg 要在客户端弹出的消息内容
     */
    protected void alert(String msg) {
    this.wScript("alert(unescape(\"" + this.escape(msg) + "\"));");
    }
    /**
     * 向客户端输出 javascript 以让浏览器重定向并替换当前页面浏览历史记录
     * @param target 重定向的目标路径
     */
    protected void replace(String target) {
    this.wScript("window.location.replace(unescape(\"" + this.escape(target) + "\"));");
    }
    /**
     * 向客户端输出 javascript 以让浏览器执行后退操作
     */
    protected void back() {
    this.wScript("window.history.go(-1);");
    }
    /**
     * 获取一个请求参数
     * @param x 要获取的参数名称
     * @return 获取的参数值
     */
    protected String getParameter(String x) {
    if (request == null) return "";
    String v = request.getParameter(x);
    return ((null == v) ? "" : v);
    }
    /**
     * 获取一个请求参数,并将其首尾空白符号去掉
     * @param x 要获取的参数名称
     * @return 获取的参数值
     */
    protected String getParamTrim(String x) {
    return this.getParameter(x).trim(); //去字符串首尾空白字符
    }
    /**
     * 获取一个浮点型的请求参数
     * @param x 要获取的参数名称
     * @return 获得的浮点数值
     * @throws Exception 提交参数格式不合法时抛出
     */
    protected double getDoubleParameter(String x) throws Exception {
    double v = Double.parseDouble(this.getParamTrim(x));
    if (Double.isNaN(v)) throw new Exception();
    return v;
    }
    /**
     * 获取一个整型的请求参数
     * @param x 要获取的参数名称
     * @return 获得的整数值
     * @throws Exception 提交参数格式不合法时抛出
     */
    protected int getIntParam(String x) throws Exception {
    return Integer.parseInt(this.getParamTrim(x), 10);
    }
    /**
     * 进行请求转发<br>
     * 同:request.getRequestDispatcher(tar).forward(request, response);
     * @param tar 要转发的目标路径
     * @throws ServletException
     * @throws IOException
     */
    protected void forward(String tar) throws ServletException, IOException {
    this.request.getRequestDispatcher(tar).forward(request, response);
    }
    }自定义 Action 需要去实现 IAction ,或者继承 cn.ialvin.web.Action
      

  11.   


       如果是表单提交你提交完以后,如果用forward转发回页面你刷新试试... 看会出现什么问题???你会发现表单重复提交了。。 这个时候其实应该是用redirect的。
    如果是表