手写WebServer(二)之SAX解析

SAX解析简介

SAX(Simple API for XML)是一种XML解析的替代方法。相比于DOM,SAX是一种速度更快,更有效的方法。它逐行扫描文档,一边扫描一边解析。而且相比于DOM,SAX可以在解析文档的任意时刻停止解析,但任何事物都有其相反的一面,对于SAX来说就是操作复杂,且无法修改文档内容。

SAX解析的使用

SAX解析以文件字符流的方式读取XML文档,并在读取到开始标签、内容标签和结束标签时触发一系列事件。以下面的xml为例,



    
        login
        com.miguel.server.server03.servlet.LoginServlet
    
    
        reg
        com.miguel.server.server03.servlet.RegisterServlet
    
    
        login
        /login
        /g
    
    
        reg
        /reg
    
    
        others
        com.miguel.server.server03.servlet.OthersServlet
    
    
        others
        /o
    

程序在读取以上内容时,会依次触发startDocument、startElement、characters、endElement、endDocument事件。

SAX中提供了四个处理器接口:ContentHandler,DTDHandler,EntityResolver,ErrorHandler,供我们实现,以处理不同的事件。但是,实现四个接口比较麻烦,所以SAX为我们提供了一个DefaultHandler,它实现了以上四个处理器接口。

虽然有了DefaultHandler,但是通过查看源码,可以发现几乎所有方法都是空实现。所以,我们在实际使用时,需要继承DefaultHandler,根据自身需求重写相应的方法,实现自己的处理器。可参考以下示例,

public class Entity {
    private String name;
    private String clz;

    public Entity() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getClz() {
        return clz;
    }

    public void setClz(String clz) {
        this.clz = clz;
    }

    @Override
    public String toString() {
        return "Entity{" +
                "name='" + name + '\'' +
                ", clz='" + clz + '\'' +
                '}';
    }
}
public class Mapping {

    private String name;
    private Set patterns = new HashSet();

    public Mapping() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Set getPatterns() {
        return patterns;
    }

    public void setPatterns(Set patterns) {
        this.patterns = patterns;
    }

    public void addPattern(String pattern) {
        patterns.add(pattern);
    }

    @Override
    public String toString() {
        return "Mapping{" +
                "name='" + name + '\'' +
                ", patterns=" + patterns +
                '}';
    }
}
public class WebContext {

    private List entities;
    private List mappings;
    private Map entityMap;
    private Map mappingMap;

    public WebContext(List entities, List mappings) {
        this.entities = entities;
        this.mappings = mappings;
        entityMap = new HashMap();
        mappingMap = new HashMap();

        for (Entity entity : entities) {
            entityMap.put(entity.getName(), entity.getClz());
        }
        for (Mapping mapping : mappings) {
            for (String pattern : mapping.getPatterns()) {
                mappingMap.put(pattern, mapping.getName());
            }
        }
    }

    public String getClz(String pattern) {
        String name = mappingMap.get(pattern);
        return entityMap.get(name);
    }
}
public class WebHandler extends DefaultHandler {
    private Entity entity;
    private Mapping mapping;
    private List entities = new ArrayList();
    private List mappings = new ArrayList();
    private boolean isMapping = false;
    private String tag;

    public WebHandler() {
    }

    public List getEntities() {
        return entities;
    }

    public List getMappings() {
        return mappings;
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        if (null != qName) {
            tag = qName;
        }
        if ("servlet".equals(tag)) {
            entity = new Entity();
            isMapping = false;
        } else if ("servlet-mapping".equals(tag)) {
            mapping = new Mapping();
            isMapping = true;
        }
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        if (length > 0) {
            String content = new String(ch, start, length);
            if (isMapping) {
                if ("servlet-name".equals(tag)) {
                    mapping.setName(content);
                }
                if ("url-pattern".equals(tag)) {
                    mapping.addPattern(content);
                }
            } else {
                if ("servlet-name".equals(tag)) {
                    entity.setName(content);
                }
                if ("servlet-class".equals(tag)) {
                    entity.setClz(content);
                }
            }
        }
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        if ("servlet".equals(qName)) {
            entities.add(entity);
        } else if ("servlet-mapping".equals(qName)) {
            mappings.add(mapping);
        }
        tag = null;
    }
}

在完成自己的处理器后,就可以开始使用SAX解析XML:

由于SAXParser只是一个接口,我们无法对其实例化。那么,我们可以获取它的实现类SAXParserImpl的实例吗?当然可以,但是SAXParserImpl的构造方法有两个,而且逐个构建需要传入的参数太麻烦,所以这里我们采用更简便的方法------获取工厂实例:

// 1.获取解析工厂
SAXParserFactory factory = SAXParserFactory.newInstance();
// 2.构建解析器
SAXParser parser = factory.newSAXParser();

这样我们就获取到了XML解析器SAXParser,接下来只要调用其parse方法,就可以完成XML解析。具体代码如下,

public class WebApp {

    private static WebContext context;

    static {
        // 1.获取解析工厂
        SAXParserFactory factory = SAXParserFactory.newInstance();
        // 2.构建解析器
        SAXParser parser = null;
        try {
            parser = factory.newSAXParser();
            // 3.构建处理器
            // 4.加载处理器
            WebHandler handler = new WebHandler();
            // 5.解析
            parser.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream("com/miguel/server/server03/web/web.xml"), handler);
            // 6.获取数据
            context = new WebContext(handler.getEntities(), handler.getMappings());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Servlet getServletByUrl(String url) {
        String className = context.getClz("/" + url);
        Class clz = null;
        try {
            clz = Class.forName(className);
            Servlet servlet = (Servlet) clz.getConstructor().newInstance();
            return servlet;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

 

你可能感兴趣的:(手写WebServer)