目标
- 使用反射模拟servlet执行
XML
- 在servlet中,为了灵活实现不同的路径执行不同的资源,我们需要使用xml进行配置
- 为了限定XML的内容,我们需要使用xml的约束(DTD约束或schema约束)
- 为了获得xml的内容,需要使用dom4j进行操作
XML简介
- xml:可扩展标记语言
- xml使用的是1.0版本,因为1.1发布了基本没人用(不向下兼容),所以一直都是用1.0版本
- xml一开始作数据传输,后来数据传输json应用更广泛,现在xml多用于配置文件
- xml区分大小写,需要有根元素
- xml属性值必须要有引号
XML语法
- 文档声明
- <?xml version="1.0" encoding="utf-8"?>
DTD约束
- Document Type Definition
- 开发中我们很少自己写DTD文档,都是根据(框架)给定的DTD文件,自己写配置文件(借助工具来实现)
- DTD文件:
<?xml version="1.0" encoding="UTF-8"?>
<!--
模拟servlet2.3规范,如果开发人员需要在xml使用当前DTD约束,必须包括DOCTYPE。
格式如下:
<!DOCTYPE web-app SYSTEM "web-app_2_3.dtd">
-->
<!ELEMENT web-app (servlet*,servlet-mapping* , welcome-file-list?) >
<!ELEMENT servlet (servlet-name,description?,(servlet-class|jsp-file))>
<!ELEMENT servlet-mapping (servlet-name,url-pattern+) >
<!ELEMENT servlet-name (#PCDATA)>
<!ELEMENT servlet-class (#PCDATA)>
<!ELEMENT url-pattern (#PCDATA)>
<!ELEMENT description (#PCDATA)>
<!ELEMENT jsp-file (#PCDATA)>
<!ELEMENT welcome-file-list (welcome-file+)>
<!ELEMENT welcome-file (#PCDATA)>
<!ATTLIST web-app version CDATA #IMPLIED>
- 通过工具生成的xml文档
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app SYSTEM "web-app_2_3.dtd">
<web-app version="1.0">
<servlet>
<servlet-name></servlet-name>
<servlet-class></servlet-class>
</servlet>
<servlet-mapping>
<servlet-name></servlet-name>
<url-pattern></url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file></welcome-file>
</welcome-file-list>
</web-app>
shema约束
scheam是新的xml文档约束
schema比DTD更加强大,是DTD的替代
schema本身也是xml文档,但scheam的扩展名为xsd
schema功能更加强大,数据类型更加完善
schema支持命名空间
schema文档
<?xml version="1.0" encoding="UTF-8"?>
<!--
模拟servlet2.5规范,如果开发人员需要在xml使用当前Schema约束,必须包括指定命名空间。
格式如下:
<web-app xmlns="http://www.example.org/web-app_2_5"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.example.org/web-app_2_5 web-app_2_5.xsd"
version="2.5">
-->
<xsd:schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.example.org/web-app_2_5"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://www.example.org/web-app_2_5"
elementFormDefault="qualified">
<xsd:element name="web-app">
<xsd:complexType>
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="servlet">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="servlet-name"></xsd:element>
<xsd:element name="servlet-class"></xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="servlet-mapping">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="servlet-name"></xsd:element>
<xsd:element name="url-pattern" maxOccurs="unbounded"></xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="welcome-file-list">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="welcome-file" maxOccurs="unbounded"></xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:choice>
<xsd:attribute name="version" type="double" use="optional"></xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:schema>
- 根据约束生成的xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://www.example.org/web-app_2_5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.example.org/web-app_2_5 web-app_2_5.xsd"
version="2.5">
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>xxxxxxxxxx</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
xml解析
- html解析的方式
- DOM:整个xml文档加载到内存,解析成一个document对象
- 优点:元素与元素之间保留结构关系,能够进行增删改查操作
- 缺点:xml文件过大会导致内存溢出
- SAX:逐行解析xml文件
- 优点:解析速度快,可以处理大文件
- 缺点:不能进行增删改查操作
- PULL:Android内置的解析方式,类似于SAX解析
- DOM:整个xml文档加载到内存,解析成一个document对象
- 使用dom4j解析xml文件
- 需要使用dom4j就需要导入相应的jar包
- dom4j的核心类是SaxReader,读取xml文件后获得Document对象,通过Document获取根元素后进行操作
- SaxReader:
- read(...):加载xml文档
- Document:
- getRootElement():获取根元素
- Element:
- elements(...):获取自定名称的所有元素
- element(...):获取指定名称的第一个元素
- getName():获取当前元素的元素名
- attribeValue():获取指定属性的属性值
- elementText(...):获取指定元素的问本值
- getText():获取当前元素的文本内容
public void testReadWebXML() {
try {
// 1.获取解析器
SAXReader saxReader = new SAXReader();
// 2.获得document文档对象
Document doc = saxReader.read("src/cn/itheima/xml/schema/web.xml");
// 3.获取根元素
Element rootElement = doc.getRootElement();
// System.out.println(rootElement.getName());//获取根元素的名称
// System.out.println(rootElement.attributeValue("version"));//获取根元素中的属性值
// 4.获取根元素下的子元素
List<Element> childElements = rootElement.elements();
// 5.遍历子元素
for (Element element : childElements) {
//6.判断元素名称为servlet的元素
if ("servlet".equals(element.getName())) {
//7.获取servlet-name元素
Element servletName = element.element("servlet-name");
//8.获取servlet-class元素
Element servletClass = element.element("servlet-class");
System.out.println(servletName.getText());
System.out.println(servletClass.getText());
}
}
} catch (DocumentException e) {
e.printStackTrace();
}
}
反射
- 通过接口来解耦
- 获得Class对象
- Class.forName("已知类的完整路径名");
- 已知类.class
- 已知对象.getClass()
- 使用默认的构造方法
- newInstance()
- Constructor对象
- Constructor对象是构造方法的描述对象
- 获取构造方法对象:
- Constructor<T> getConstructor(Class<T> ...parameterTypes),可变参数用于确定参数列表
- Constructor<T> getDeclaredConstructor(Class<T> ...parameterTypes)
- newInstance(Object...initargs):可变参数用于确定实际参数列表
- Method对象
- Method对象是普通方法的描述对象
- 获取方法:
- Method getMethod(String name,Class<T>...params)
- Method getDelcaredMethod(String name,Class<T>...params)
- Ojbect invoke(Object obj,Object...args)
- Filre对象
- 字段描述对象
- 获得方法:
- File getFile(String name)
- File getDeclaredFile(String name)
- 操作:
- Object get(Object obj)
- void set(Object obj,Object...value)
public class TestMyServlet2 {
//8.创建一个map集合
private HashMap<String, String> data = new HashMap<String,String>();
@Before
public void testReadWEBXml(){
try {
//1.创建解析器对象
SAXReader saxReader = new SAXReader();
//2.使用解析器加载web.xml文件得到document对象
Document document = saxReader.read("src/cn/itheima/web/servlet1/web.xml");
//3.获取根元素节点
Element rootElement = document.getRootElement();
//4.获取子节点(servlet和servlet-mapping)
List<Element> childElements = rootElement.elements();
//5.遍历
for (Element element : childElements) {
//6.判断元素的名称为servlet的元素节点
if("servlet".equals(element.getName())){
//7.分别获取servlet元素节点的servlet-name和servlet-class的值
String servletName = element.element("servlet-name").getText();
String servletClass = element.element("servlet-class").getText();
/*System.out.println(servletName);
System.out.println(servletClass);*/
data.put(servletName, servletClass);
}
//9.判断元素的名称为servlet-mapping的元素节点
if("servlet-mapping".equals(element.getName())){
//10.分别获取servlet元素节点的servlet-name和servlet-class的值
String servletName = element.element("servlet-name").getText();
String urlPattern = element.element("url-pattern").getText();
//11.将servletName作为key来获取servletClass的值
String servletClass = data.get(servletName);
//12.将url-pattern作为key,servletClass作为value存到map中去
data.put(urlPattern, servletClass);
//13.移除servletName
data.remove(servletName);
}
}
//System.out.println(data);
} catch (DocumentException e) {
e.printStackTrace();
}
}
@Test
public void testMyServlet(){
try {
//1.模拟在浏览器输入一个url
String url1 = "/myServlet2";
//2.将urlPattern作为key来获取servletClass
String className = data.get(url1);
//3.通过servletClass获取字节码文件
Class clazz = Class.forName(className);
//4.通过字节码文件创建实例对象
Object obj = clazz.newInstance();
//5.通过字节码文件获取方法(两个参数:第一个是方法名称;第二个参数是方法的参数)
Method method = clazz.getMethod("service", null);
//6.调用invoke方法执行实例对象里面的方法(前面写的方法init)【两个参数:第一个是调用方法的实例对象,第二个是方法的实参】
method.invoke(obj, null);
} catch (Exception e) {
e.printStackTrace();
}
}
}