我们经常上网,经常会进入一个登录注册的页面比如www.xxx.com/login,那么我们是怎么通过url,服务器怎么就知道给我们返回相应的页面呢?
网站通常都有web.xml配置文件,如下:
<?xml version="1.0" encoding="UTF-8"?>  
 <web-app>
 <servlet>
  <servlet-name>login</servlet-name>
  <servlet-class>com.sxt.server.basic.myServlet.LoginServlet</servlet-class>
 </servlet>
   <servlet>
  <servlet-name>reg</servlet-name>
  <servlet-class>com.sxt.server.basic.myServlet.RegisterServlet</servlet-class>
 </servlet>   
 <servlet-mapping>
  <servlet-name>login</servlet-name>
  <url-pattern>/login</url-pattern> 
  <url-pattern>/g</url-pattern> 
 </servlet-mapping>  
 <servlet-mapping>
  <servlet-name>reg</servlet-name>
  <url-pattern>/reg</url-pattern> 
 </servlet-mapping>
 </web-app>

这里的url-pattern就是我们访问的路径,我们通过输入这个路径,服务器能够找到为我们进行服务对应的类。比如说这里的/login,对应的servlet-name就是login,在 对应的class就是com.sxt.server.basic.myServlet.LoginServlet类。于是通过找到这个类的路径,通过反射创建一个实体类,就能够调用这个类的方法,就可以为我们服务。比如说返回给我们页面,把我们传输过去的数据存储在数据库。那么大概就是这个流程,接下来用代码可能更加直观。

首先,上面是我们的xml配置文件,既然是配置文件,那么肯定是有特定含义的。所以我们需要将他解析出来,方便我们使用。**这里使用的是SAX解析。**所以首先创建一个XmlTest.java类,在类中下如下代码:

public static void main(String args[]) throws Exception{
		// SAX解析
		// 1、获取解析工厂
		SAXParserFactory factory = SAXParserFactory.newInstance();
		// 2、从解析工厂获取解析器
		SAXParser parse = factory.newSAXParser();
}

获取到解析器以后,我们首先还要一个处理器,来处理我们特定的xml文件,但是既然是解析,我们肯定还要有对应的类来存储。所以根据根节点下面的和我们创建两个类Servlet和ServletMapping.代码如下:

package com.sxt.server.basic.myServlet;

public class Servlet {

	private String name;
	private String clz;
	
	public Servlet() {
		super();
	}

	public Servlet(String name, String clz) {
		super();
		this.name = name;
		this.clz = clz;
	}
	
	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 "Servlet [name=" + name + ", clz=" + clz + "]";
	}
		
}
package com.sxt.server.basic.myServlet;

import java.util.HashSet;
import java.util.Set;

public class ServletMapping {
			
	
	private String name;
	private Set<String> patterns;
	
	public ServletMapping() {
		super();
		patterns = new HashSet<String>();
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Set<String> getPatterns() {
		return patterns;
	}
	public void setPatterns(Set<String> patterns) {
		this.patterns = patterns;
	}
	
	public void addPattern(String pattern) {
		this.patterns.add(pattern);
	}
	@Override
	public String toString() {
		return "ServletMapping [name=" + name + ", patterns=" + patterns + "]";
	}
	
}

(这里补充一下,为什么ServletMapping里面要创建Set patterns;首先我们的url-pattern不是唯一的,可以看见web.xml文件就有的不只一个,因为我们可以通过不同的路径访问相同的网址啊,另外就是为什么是用Set而不是List,因为url不能重复,这一意思应该都能懂,如果你一个网址可以同时进几个页面,那么服务器怎么知道返回什么页面给你呢?)

我们创建好这两个类以后就可以开始写我们自己的处理器了WebHandler,首先这个类得要继承DefaultHandler,然后重写方法进行处理xml文件。我的代码(写在XmlTest类下面)如下:

class WebHandler extends DefaultHandler {
		
	private List<Servlet> servlets;
	private List<ServletMapping> servletMappings;
	private Servlet servlet;
	private ServletMapping servletMapping;
	private String tag;
	private boolean isMapping;
	
	
	@Override
	public void startDocument() throws SAXException {
		servlets = new ArrayList<Servlet>();
		servletMappings = new ArrayList<ServletMapping>();
		isMapping = false;
	}
	
	@Override
	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
		if(null!=qName) {
			tag = qName;
			if(tag.equals("servlet")) {
				servlet = new Servlet();
			}else if(tag.equals("servlet-mapping")) {
				servletMapping = new ServletMapping();
				isMapping = true;
			}
		}
	}
	
	@Override
	public void characters(char[] ch, int start, int length) throws SAXException {
		// TODO Auto-generated method stub
		String contents = new String(ch,start,length).trim();
		if(null!=tag) {
			if(isMapping) {
				if(null!=contents) {
					if(tag.equals("servlet-name")) {
						servletMapping.setName(contents);
					}else if(tag.equals("url-pattern")) {
						servletMapping.addPattern(contents);
					}
				}
			}else {
				if(null!=contents) {
					if(tag.equals("servlet-name")) {
						servlet.setName(contents);
					}else if(tag.equals("servlet-class")) {
						servlet.setClz(contents);
					}
				}
			}
		}
	}
	
	@Override
	public void endElement(String uri, String localName, String qName) throws SAXException {
		// TODO Auto-generated method stub
		if(null!=qName) {
			tag = qName;
			if(tag.equals("servlet")) {
				servlets.add(servlet);
			}else if(tag.equals("servlet-mapping")) {
				servletMappings.add(servletMapping);
			}
		}
		tag = null;
	}

	
	@Override
	public void endDocument() throws SAXException {
		// TODO Auto-generated method stub
		super.endDocument();
	}

	public List<Servlet> getServlets() {
		return servlets;
	}

	public List<ServletMapping> getServletMappings() {
		return servletMappings;
	}

}

稍微解释一下,这里的代码:

private List<Servlet> servlets;
	private List<ServletMapping> servletMappings;
	private Servlet servlet;
	private ServletMapping servletMapping;
	private String tag;
	private boolean isMapping;

这里的 Servlet servlet; ServletMapping servletMapping;是保存单个标签里面的内容的,List servlets;List servletMappings;就不用多解释是因为这些标签都不止一个。tag的话是元素解析开始的时候,用来记做标签的名字,方便后面标签关闭的时候能够结束对这个标签里面内容的解析。因为我们不只一类标签,所以加了一个判断,好在接下来的内容解析的时候不会混乱。

public void startDocument()	//文档解析开始
public void startElement(String uri, String localName, String qName, Attributes attributes)	//元素解析开始
public void characters(char[] ch, int start, int length)	//内容解析开始
public void endElement	//元素解析结束
public void endDocument()		//文档解析结束

整个文档解析开始是在元素解析之前,那么照我们这个例子来说,文档解析在 标签解析之前,执行一次,所以我用来让定义的变量实例化。
下来就是重复的解析元素了,所以第一个解析的是 ,但是我们没有相应的代码处理它,因为没什么用。元素解析开始时,我们对qName也就是标签作判断,看是哪个标签。判断标签后new出对应的对象进行存储数据。如这时候判断是 标签,这时tag=servlet.那么就创建对象 servlet = new Servlet();,并且此时isMapping还是false。
接下来就是内容解析, 由于内容为空,所以不进行任何操作。
然后又是元素解析,这时tag=servlet-name,在元素解析时候不进行操作。
然后进入内容解析,由于tag=servlet-name,所以进入对应的if条件利用对象的方法将contents进行存储。
接下来又是元素解析,但是此时它内容是为空的,好在我们在添加内容时做了内容不能为空的判断,不然就被覆盖了。
然后就又是重复步骤。
在元素解析结束的时候,判断是不是对应的,然后将对象添加到队列中。

public List<Servlet> getServlets() {
		return servlets;
	}

	public List<ServletMapping> getServletMappings() {
		return servletMappings;
	}

这两个方法方便我们将解析出来的数据解析出来。

这个时候我们已经将xml配置文件的数据取出来了,那么我们要怎么才能通过url找到对应的类呢?显然,这是两个不同的List,所以我们还需要将这两个List进行一定的处理,能够让我们输入url-pattern里面的内容,就帮我们找到对应的类,应运而生我们创建WebContext.java,代码如下:

package com.sxt.server.basic.myServlet;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class WebContext {

	private List<Servlet> servlets = null;
	private List<ServletMapping> servletMappings = null;
	
	//key --> servlet-name value --> servlet-class
	private Map<String,String> servletMap = new HashMap<String,String>();
	//key --> url-pattern value --> servlet-name
	private Map<String,String> servletMappingMap = new HashMap<String,String>();

	public WebContext(List<Servlet> servlets, List<ServletMapping> servletMappings) {
		super();
		this.servlets = servlets;
		this.servletMappings = servletMappings;
		
		//将servlet对应的list转换为map
		for(Servlet servlet:servlets) {
			servletMap.put(servlet.getName(),servlet.getClz());
		}
		
		//将servlet-mapping对应的list转换为map
		for(ServletMapping servletMapping:servletMappings) {
			for(String pattern:servletMapping.getPatterns()) {
				servletMappingMap.put(pattern,servletMapping.getName());
			}
		}
	}
	
	/**
	 * 通过url路径找到对应的类
	 */
	public String getClz(String pattern) {
		
		String name = servletMappingMap.get(pattern);
		return this.servletMap.get(name);
	}
}

这里我们将List传递进来,将对应的List转换成Map,通过这样一对一的方式我们才能够方便的查找。这里我们需要注意的是,一个url-pattern有多个,显然不能作为Map的value,但是我们可将其设置为key,这样一个url对应一个name,再通过name寻找对应的class。所以提供一个对外的方法getClz().
到这里我们就能够进行对XmlTest.java的补充了:

// 3、编写处理器
		// 4、加载文档 Document 注册处理器
		WebHandler handler = new WebHandler();
		// 5、解析
		parse.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream("com/sxt/server/basic/myServlet/web.xml"),
				handler);
		
		//获取数据
		WebContext context = new WebContext(handler.getServlets(),handler.getServletMappings());
		String className = context.getClz("/login");
		System.out.println(className);
		Class clz = Class.forName(className);
		IServlet servlet = (IServlet)clz.getConstructor().newInstance();
		servlet.service();

我们解析出来一个,通过handler得到相应的List,创建context对象,然后传入url通过对应的方法取得className,通过className创建对象,然后就是调用对应的方法。这里忘了创建对应的类了,补充一下。

package com.sxt.server.basic.myServlet;

public interface IServlet {

	void service();
}

创建接口,以供其他实现类方便使用。

package com.sxt.server.basic.myServlet;

public class LoginServlet implements IServlet{

	public void service() {
		// TODO Auto-generated method stub
		System.out.println("LoginServlet");
	}
}
package com.sxt.server.basic.myServlet;

public class RegisterServlet implements IServlet{
	
	public void service() {
		// TODO Auto-generated method stub
		System.out.println("RegisterServlet");
	}
}

然后我们点击运行代码,得到

xml解析url_java


本文章是学习尚学堂教程以后自己理清的一些思路。