Tomcat简单实现--5.3 配置Servlet

配置型Servlet

Context类中的改动

  • 我们前面已经使用了Servlet的简单测试,但是Tomcat中的Servlet是通过配置文件解析进行配置的。设计的逻辑是这样的:

    • 在Conf文件夹下使用一个context.xml文件来来保存servlet的配置文件的位置。
    <?xml version='1.0' encoding='utf-8'?>
    <Context>
        <WatchedResource>WEB-INF/web.xml</WatchedResource>
    </Context>
    

    这个文件指定了context文件下的servlet配置所在的路径,都是在应用目录/WEB-INF/web.xml文件中。

    • 在Constant类中设置一个变量用于保存这个文件对象。

      public final static File contextFile = new File(confFolder, "context.xml");
      
    • 建立一个工具类用于解析出context.xml文件中watchedResource标签指定的文件

      package jerrymice.util;
      
      import cn.hutool.core.io.FileUtil;
      import cn.hutool.log.LogFactory;
      import org.jsoup.Jsoup;
      import org.jsoup.nodes.Document;
      import org.jsoup.nodes.Element;
      
      public class ContextXmlUtil {
          public static String getWatchedResource(){
              try{
                  String xml = FileUtil.readUtf8String(Constant.contextFile);
                  Document d = Jsoup.parse(xml);
                  Element element = d.selectFirst("WatchedResource");
                  return element.text();
              }catch(Exception e){
                  LogFactory.get().info(e.toString());
                  return "WEB-INF/web.xml";
              }
          }
      }
      

      这个类只有一个工具方法就是用于解析context.xml文件指定的位置文件的位置。

    • 在Context类中添加一个属性用于标记这个文件。建立四个哈希表用于储存这个文件中列出的映射信息。

      private File contextWebXmlFile;
      this.url_servletName = new HashMap<>();
      this.url_servletClassName = new HashMap<>();
      this.servletName_className = new HashMap<>();
      this.className_servletName = new HashMap<>();
      

      获取这个文件,判断是否存在,如果存在的话,先判断其中是否存在这重复的映射,如果存在的话抛出异常。

      WEB-INF\web.xml文件的大概样子是下面这种形式的:

      <?xml version="1.0" encoding="UTF-8"?>
      <web-app>
          <servlet>
              <servlet-name>HelloServlet</servlet-name>
              <servlet-class>jerrymice.webappservlet.HelloServlet</servlet-class>
          </servlet>
      
          <servlet-mapping>
              <servlet-name>HelloServlet</servlet-name>
              <url-pattern>/hello</url-pattern>
          </servlet-mapping>
      </web-app>
      
    • 检查文件中是否存在重复映射信息的代码如下:

      /**
           * 通过检查xml文件中的映射是否有重复
           * @param document: 从web.xml文件中解析获得的doc对象
           * @param mapping: 想要检查的元素映射,例如 servlet servlet-name
           * @param msg: 反馈的重复信息提示
           * @throws WebConfigDuplicatedException
           */
          private void checkDuplicated(Document document, String mapping, String msg) throws WebConfigDuplicatedException {
              Elements elements = document.select(mapping);
              // 判断的逻辑是先将元素提取出来,放到Set中,从而判断是否重复
              Set<String> elementText = new HashSet<>();
              for(Element element: elements){
                  String text = element.text();
                  boolean b = elementText.add(text);
                  if (!b){
                      // 说明其中已经有这个元素了,即重复,抛出异常
                      throw new WebConfigDuplicatedException(StrUtil.format(msg, text));
                  }
              }
          }
      
          /**
           * 检查uri, servletName和className是否重复
           * @throws WebConfigDuplicatedException
           */
          private void checkDuplicated(Document document) throws WebConfigDuplicatedException{
              checkDuplicated(document, "servlet-mapping url-pattern","servlet url重复,请保持其唯一性:{}");
              checkDuplicated(document, "servlet servlet-name","servlet名称重复,请保持其唯一性:{}");
              checkDuplicated(document, "servlet servlet-class", "servlet类名重复,请保持其唯一性:{}");
          }
      
    • 上面的映射对应了servlet的几个属性值,一个是它的名字,另外一个是它对应的Java类的名称,还有一个是访问这个servlet时使用的uri路径,使用四个哈希表来保存信息之间的映射关系,二这些映射关系都是在上面的文件中定义的。

          /**
           * 从Document对象中解析出映射信息
           * @param document: config文件中context.xml中指定的watchedSourceFile
           */
          private void parseServletMapping(Document document){
      
              // 获取url和ServletName之间的映射
              Elements mappingElements = document.select("servlet-mapping url-pattern");
      
              for (Element element: mappingElements){
                  // 获取这个Servlet对应的url
                  String urlPattern = element.text();
                  // 获取这个Servlet对应的name
                  String servletName = element.parent().select("servlet-name").first().text();
                  url_servletName.put(urlPattern, servletName);
              }
      
              // 获取servletName和className之间的映射
              Elements servletNameElements = document.select("servlet servlet-name");
      
              for(Element element : servletNameElements){
                  String servletName = element.text();
                  String className = element.parent().select("servlet-class").first().text();
                  className_servletName.put(className, servletName);
                  servletName_className.put(servletName, className);
              }
      
              // 获取url和className之间的映射信息
              // 先将所有的url拿出来
              Set<String> urls = url_servletName.keySet();
              for (String url: urls){
                  // 获取url对应的servletName
                  String servletName = url_servletName.get(url);
                  // 根据servletName获取className
                  String className = servletName_className.get(servletName);
                  url_servletClassName.put(url, className);
              }
          }
      

HttpServlet中的改动

  • 现在当一个请求传入时,首先获取它的uri,然后获取context,在根据contexturi来获取这个servlet对应的类,根据反射来创建这个类的对象然后调用这个对象的doGet()方法。

  • String uri = request.getUri();
    System.out.println(uri);
    if (uri == null) {
          // 说明此时没有请求过来
          return;
    }
    // 获取request的context,context的path是访问的文件夹路径,docBase是实际在系统中的绝对路径
    Context context = request.getContext();
    // 根据context和uri来获取className
    String servletClassName = context.getServletClassName(uri);
    if (null != servletClassName) {
       	// 如果请求的类存在,根据反射获取这个类的一个实例
        Object servletObj = ReflectUtil.newInstance(servletClassName);
        // 调用这个对象的方法
        ReflectUtil.invoke(servletObj, "doGet", request, response);
    }
    

测试

  • 启动服务器,然后访问指定的uri来访问指定的servlet
  • Tomcat简单实现--5.3 配置Servlet
上一篇:CSS Modules 配置,及 Antd 组件样式重写


下一篇:phpstorm查看类的继承关系