Servlet封装简单MVC框架实践小结          返回主页

对于简单的小项目,使用非框架的MVC方式开发是比较优先的灵活的选择,生产环境由于对合作开发、项目维护、升级有着较高的要求因此需要引入框架。在开发中,随着项目的增加,大量的Servlet引入导致项目变得难以维护,因此有了这篇文章,是我对Servlet的一个简单封装,目的是

减轻代码间的耦合
减少web.xml的配置
加深对MVC框架的理解

写到最后发现其实就是Struts1的原理。废话不多说,进入正文讲解。

实现思路

  1. 首先设置一个监听器 准备一个map 把所有的action的key,value放入到map中 把map放入application域中
  2. 定义一个过滤器 获取application域中的map 解析url 根据解析的url从map中把value提取出来 根据java的反射机制动态调用action 根据action返回的方法跳转到相应的页面
  3. 执行action的execute方法,该方法返回一个字符串。

代码说明

1. 定义监听器ServletListener实现ServletContextListener接口

    /**
     * tomcat销毁的时候执行
     */
    public void contextDestroyed(ServletContextEvent servletContextEvent)  {
        //回收所有映射
        servletContextEvent.getServletContext().setAttribute("mappings", null);
    }
    /**
     * tomcat启动时候执行
     */
    public void contextInitialized(ServletContextEvent servletContextEvent)  {
        Map<String, String> map = new HashMap<>();
        map.put("userAction", "com.snowalker.action.UserAction");
        //添加所有映射
        servletContextEvent.getServletContext().setAttribute("mappings", map);
    }

此处,在上下文初始化方法中map.put("userAction", "com.snowalker.action.UserAction");表示用户自定义的action类的注册,并放入Application域中。

2. 定义一个过滤器DispacheFilter实现Filter,在内部逻辑通过反射调用application中的userAction中的方法,实现页面的转发

        private ServletContext servletContext;

        public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain chain) throws IOException, ServletException {
            /**
             * 1、从 application域中获取map
             */
            HttpServletRequest request = (HttpServletRequest)arg0;
            HttpServletResponse response = (HttpServletResponse)arg1;
            Map<String, String> map = (HashMap<String, String>)this.servletContext.getAttribute("mappings");
            /**
             * 2、获取浏览器中的url,把url解析出来
             *     http://localhost:8080/itheima09_servlet_super/userAction.action
             *     ---->userAction
             */
            //mapping = userAction
            String mapping = ServletUtils.parse(request.getRequestURI());
            String value = map.get(mapping); //value就是action的类的全名
            try {
                Class class1 = Class.forName(value);    //获取到对应类的Class对象
                Method method = class1.getMethod("execute",     HttpServletRequest.class,HttpServletResponse.class);
                //调用了action中的方法
                String jspName = (String)method.invoke(class1.newInstance(), request,response);
                request.getRequestDispatcher(jspName).forward(request, response);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        public void init(FilterConfig fConfig) throws ServletException {
            //初始化时候加载servletContext
            servletContext = fConfig.getServletContext();
        }

注意:这里需要获取Application也就是servletContext对象,实验中通过HttpServletRequest获取不到,因此通过在Filter初始化时执行的生命周期方法init中的config中获取到应用上下文。

3. 工具类,ServletUtils对访问路径进行解析,获取到需要的参数

如:http://localhost:8080/ImitateStruts2_MoNiStruts2/userAction.action获取userAction

  public static String parse(String url) {
        String[] array = url.split("/");
        //截取通过/分隔的字符串数组的最后一个元素从0到.之前的部分子字符串
        String mapping = array[array.length -1].substring(0, array[array.length-1].indexOf(".")) ;
        return mapping;
    }
  1. 首先通过String的split方法通过“/”拆分字符串返回一个字符串数组
  2. 截取最后一个数组元素的下标从0到length-1的子元素,也就是我们需要的userAction
  3. 返回结果字符串到Filter中

4.Action定义,返回index.jsp,需要在项目根路径下放置index.jsp文件

public class UserAction {
    public String execute(HttpServletRequest request,HttpServletResponse response){
        return "index.jsp";
    }
}

web.xml配置,注册listener及filter,并映射filter路径为*.action

  <listener>
    <description>监听器</description>
    <listener-class>com.snowalker.servlet.listener.ServletListener</listener-class>
  </listener>
  <filter>
    <description>过滤器</description>
    <display-name>DispacheFilter</display-name>
    <filter-name>DispacheFilter</filter-name>
    <filter-class>com.snowalker.servlet.filter.DispacheFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>DispacheFilter</filter-name>
    <url-pattern>*.action</url-pattern>
  </filter-mapping>

测试

运行项目访问:

http://localhost:8080/itheima09_servlet_super/userAction.action

显示出index.jsp的内容

小结

到这里,我们完成了一个简单的Servlet的封装,较少了大量Servlet的定义,web.xml的体积大大减小。

项目中大量使用了Java反射技术,通过动态方法调用的方式进行业务逻辑的执行。

通过本项目模拟了一个简单MVC框架的原理,加深了对于复杂框架原理的理解。