我们经常上网,经常会进入一个登录注册的页面比如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");
}
}
然后我们点击运行代码,得到
本文章是学习尚学堂教程以后自己理清的一些思路。