【学习笔记】xml&反射
为了灵活实现不同路径执行不同的资源,我们需要使用xml进行配置;为了限定xml内容,需要使用xml约束(dtd或schema);为了获取xml内容,需要使用dom4j进行解析。
1.xml定义
xml全称是Extensible Markup Language,意思是可扩展的标记语言。xml语法和html相似,但html的元素是固定的,xml的标签可以自定义。
2.约束
2.1 dtd约束
DTD(Document Type Definition):文档类型定义,用于约束xml文档。规定xml文档中元素的名称,子元素的名称和顺序,元素属性等。
xml文档声明:
约束文件的元素声明:
约束文件的属性声明:
2.2 schema约束
schema定义:
引入方式:
命名空间:
图解:
3.xml解析概述
图解:
解析方式和解析器:
DOM解析原理和结构模型:
API:dom4j的使用:
如果要使用dom4j,必须导入jar包。dom4j必须使用核心类SaxReader加载xml文档获取Document,通过Document对象获得文档的根元素,然后就可以操作了。
示例代码:
package com.xing.xml; import java.util.List; //dom4j的使用 import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.junit.jupiter.api.Test; public class test { @Test public void test001() { try { //1.获取Document SAXReader saxReader=new SAXReader(); Document document; document = saxReader.read("src/com/xing/xml/web.xml"); //2.获得根元素 Element rootElement=document.getRootElement(); System.out.println("根元素:"+rootElement); System.out.println("根元素的version属性:"+rootElement.attributeValue("version"));//获取version属性 //3.获得根元素的子标签,Servlet,Servlet-Mapping List<Element> AllChildElements = rootElement.elements(); System.out.println("根元素的所有子标签:"+AllChildElements); //遍历所有子标签 for (Element childElement : AllChildElements) { //4.处理servlet,并获取子标签的内容如servlet-name if("servlet".equals(childElement.getName())) { //方式一:获得元素对象,然后获取文本 System.out.println("方式一获取标签servlet-name内容:"+childElement.element("servlet-name").getText()); //方式二:获取元素文本值 System.out.println("方式二获取标签servlet-class内容"+childElement.elementText("servlet-class")); } } } catch (DocumentException e) { e.printStackTrace(); } } }
4.反射
反射概述:
1、什么是反射技术?
动态获取指定类以及类中的内容(成员),并运行其内容。
应用程序已经运行,无法在其中进行new对象的建立,就无法使用对象。
这时可以根据配置文件的类全名去找对应的字节码文件,并加载进内存,并创建该类对象实例。这就需要使用反射技术完成
2、获取class对象的三种方式
获取Class对象的方式一:
通过对象具备的getClass方法(源于Object类的方法)。有点不方便,需要用到该类,并创建该类的对象,再调用getClass方法完成。
Person p = new Person();//创建Peron对象
Class clazz = p.getClass();//通过object继承来的方法(getClass)获取Person对应的字节码文件对象
获取Class对象的方式二:
每一个类型都具备一个class静态属性,通过该属性即可获取该类的字节码文件对象。比第一种简单了一些,仅用一个静态属性就搞定了。但是,还是有一点不方便,还必须要使用到该类。
Class clazz = Person.class;
获取Class对象方式三:
static Class forName(className)方法:
* 相对方便的多,不需要直接使用具体的类,只要知道该类的名字即可。
* 而名字完全可以作为参数进行传递 ,这样就可以提高扩展性。
* 为了动态获取一个类,第三种方式最为常用。
Class clazz = Class.forName("className");//必须类全名
创建Person对象的方式
以前:1,先加载cn.itcast.bean.Person类进内存。
2,将该类封装成Class对象。
3,根据Class对象,用new操作符创建cn.itcast.bean.Person对象。
4,调用构造函数对该对象进行初始化。
Person p = new Person();
通过方式三:(此外还可以使用构造,构造可以指定参数---如String.class)
String className = "Person";
//第一步,根据名称获取其对应的字节码文件对象
1,通过forName()根据指定的类名称去查找对应的字节码文件,并加载进内存。
2,并将该字节码文件封装成了Class对象。
3,直接通过newIntstance方法,完成该对象的创建。
4,newInstance方法调用就是该类中的空参数构造函数完成对象的初始化。
Class clazz = Class.forName(className);
//第二步,通过Class的方法完成该指定类的对象创建。
Object object = clazz.newInstance();//该方法用的是指定类中默认的空参数构造函数完成的初始化。
反射,代码示例:
package com.xing.testMap; public interface MyServlet { public void testMyServlet(); }
package com.xing.testMap; public class MyServletImpl implements MyServlet{ @Override public void testMyServlet() { System.out.println("hello,test"); } }
package com.xing.testMap; import org.junit.Test; /** * 如果直接使用new MyServletImpl(),这种编程方式称之为硬编码,即代码写死了 * 为了程序后期的可扩展性,使用反射较为合适 * @author Xing * */ public class test{ @Test public void test() throws Exception { //1.Class.forName()返回指定接口或类的对象。(实现类) //2.newInstance()通过class对象创建类的实例对象,相当于new xxx Class clazz=Class.forName("com.xing.testMap.MyServletImpl"); MyServlet myservlet=(MyServlet) clazz.newInstance(); //执行 myservlet.testMyServlet(); } }
以上程序使用反射可以创建对象的实例,但使用的是全限定类名,程序仍然是写死的,接下来我们将通过读取xml文件的类名(servlet-class)来优化
Class.forName("com.xing.testMap.MyServletImpl")
<?xml version="1.0" encoding="UTF-8"?> <!-- 传智播客DTD教学实例文档。 模拟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 version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app SYSTEM "web-app_2_3.dtd"> <web-app version="4.0"> <servlet> <servlet-name>helloMyservlet</servlet-name> <servlet-class>com.xing.testMapAndXml.MyServletImpl</servlet-class> </servlet> <servlet-mapping> <servlet-name>helloMyservlet</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> </web-app>
package com.xing.testMapAndXml; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.junit.Test; //MyServlet.java,MyServletImpl.java和上面的一样 public class test { @Test public void test() { try { //加载xml文件 SAXReader saxReader = new SAXReader(); Document document = saxReader.read("src/com/xing/testMapAndXml/web.xml"); //获取第一个子标签servlet Element element = document.getRootElement().element("servlet"); String servletElement = element.elementText("servlet-class");//读取xml文件里的类名 MyServlet myserlet=(MyServlet) Class.forName(servletElement).newInstance(); myserlet.testMyServlet(); } catch (Exception e) { e.printStackTrace(); } } }
模拟浏览器路径:上面我们已经解析了xml文件,不过获得的内容是固定的,我们希望如果用户访问的路径是/hello,将执行MyServlet程序,如果访问/hello2,将执行MyServlet2程序。
代码示例(接口和实现类省略了):
<?xml version="1.0" encoding="UTF-8"?> <!-- 传智播客Schema教学实例文档。 模拟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 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>helloMyservlet</servlet-name> <servlet-class>com.xing.testXmlAndBrowser.MyServletImpl</servlet-class> </servlet> <servlet-mapping> <servlet-name>helloMyservlet</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <servlet> <servlet-name>helloMyservlet2</servlet-name> <servlet-class>com.xing.testXmlAndBrowser.MyServletImpl2</servlet-class> </servlet> <servlet-mapping> <servlet-name>helloMyservlet2</servlet-name> <url-pattern>/hello2</url-pattern> </servlet-mapping> </web-app>
package com.xing.testXmlAndBrowser; import java.util.HashMap; import java.util.List; import java.util.Map; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.junit.Before; import org.junit.Test; public class test { //key:请求路径,value:实现类 private Map<String, String> data=new HashMap<String,String>(); @Before public void getMap() { try { SAXReader saxReader = new SAXReader(); Document document = saxReader.read("src/com/xing/testXmlAndBrowser/web.xml"); //获得所有子元素 List<Element> AllChildElements = document.getRootElement().elements(); //遍历所有子元素,1.解析到servlet,将其子标签servlet-name,servlet-class存放在Map中. //2.解析到servlet-mapping,获得子标签servlet-name,url-pattern,从Map中获得1的内容,组合成url=class键值对 for (Element childElement : AllChildElements){ if("servlet".equals(childElement.getName())) { String servletName = childElement.elementText("servlet-name"); String servletClass = childElement.elementText("servlet-class"); data.put(servletName,servletClass); } //如果是servlet-mapping,获得其内容,组成键值对存放于Map中 if("servlet-mapping".equals(childElement.getName())) { String servletName=childElement.elementText("servlet-name"); String urlPattern=childElement.elementText("url-pattern"); //获得servlet-name之前存放在Map中的servlet-class值.与urlPattern组成键值对 String servletClass = data.get(servletName); data.put(urlPattern,servletClass); //将之前存放的数据删除.Map(urlPattern,classImpl) data.remove(servletName); } } // System.out.println(data); } catch (Exception e) { e.printStackTrace(); } } @Test public void test() throws Exception { String key = "/hello"; String key2 = "/hello2"; MyServlet myServlet = (MyServlet) Class.forName(data.get(key)).newInstance(); MyServlet2 myServlet2 = (MyServlet2) Class.forName(data.get(key2)).newInstance(); myServlet.testMyServlet(); myServlet2.testMyServlet2(); } }