1. 程式人生 > >XML&反射

XML&反射

.get declare 數列 編碼 容易 pattern ace 創建對象 getname

一、學習目標 使用反射模擬servlet執行。 可以編寫xml存放任意內容 通過DTD約束編寫指定格式的XML 通過Schema約束編寫指定格式的XML 可以使用D0M4解析XML 會使用反射對類、方法、構造進行相應操作 二、相關技術 為了靈活實現不同路徑(/hello)執行不同的資源(HelloMyServlet)我們需要使用XML進行配置;為了限定XML內容,我們需要使用XML約束(DTD或schema);為了獲得xml的內容,我們需要使用dom4j 進行解析。 什麽是XML? XML全稱是Extensible Markup Language,意思是可以擴展的標記語言。XML標簽是可以由用戶自定義的。(用於配置文件) XML的作用? 1、配置文件(主要
) 2、存放數據(傳輸數據) XML語法? 1、XML文檔聲明 <?xml version="1.0" encoding="UTF-8"> a、文檔聲明必須為<?xml 開頭,以?>結束 b、文檔聲明必須從文檔的0行0列位置開始 c、文檔只有三個屬性 version encoding 2、元素element 3、屬性 4、註釋 5、轉義字符 6、CDATA區 DTD約束? DTD(Document Type Definition),文檔類型定義,用來約束XML文檔。 DTD重點要求:很少自己編寫DTD約束文檔,都是通過框架提供的DTD約束文檔編寫對象的XML文檔。常見框架使用DTD約束有:struts2、hibernate等。 DTD文檔聲明 1、內部DTD,在CML文檔內部嵌入DTD,只對當前XML有限 2、外部DTD-本地DTD,DTD文檔在本地系統上,公司內部自動項目使用 關鍵字:SYSTEM
3、外部DTD-公共DTD,DTD文檔在網絡上,一般都由框架提供 關鍵字:PUBLIC 元素聲明? 定義元素語法:<!ELEMENT 元素名 元素描述> 元素名:自定義 元素描述:符號和數據類型 常見符號: ? :該對象可以出現,但只能出現一次 *:任意次或零次 + :最少出現一次 () :給元素分組 | :選擇一個 ,:順序出現 常見類型: #PCDATA表示內容是文本,不能是子標簽 約束文檔: <?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> 註:通過框架提供的DTD文檔,自己能夠寫出xml文件。
Schema約束 ? Schema約束比DTD強大很多,是DTD的替代者。主要用於spring 重點要求: 通過schema約束文檔編寫xml文件。 <?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> dom4j 解析? 當數據存儲在XML中,我們就希望通過程序獲得XML的內容。人們為不同問題提供不同的解析方式,並提交對應的解析器,方便開發人員操作XML。 開發中比較常見的解析方式有三種: 1、DOM:java主要用的。 DOM將文檔一次性加載到內存形成樹形結構,進行解析。 優點:方便對數形結構進行操作,可以進行增、刪、改的操作。 缺點:如果文檔特別大,加載到內存中,容易導致內存溢出。 2、SAX:事件驅動方式,邊讀邊解析。 優點:不會導致內存溢出。 缺點:不能增刪改操作。 3、PULL 常見的解析開發包: JAXP:sun公司提供支持DOM和SAX開發包 JDom:dom4j兄弟 jsoup:一種處理HTML特定解析開發包 dom4j:比較常用的解析開發包,hibernate底層采用 API使用: 如果需要使用dom4j,必須導入jar包。 dom4j必須使用核心類SaxReader加載xml文檔獲得Document,通過Document對象獲得文檔的根元素,然後就可以操作了。 常見API如下: 1.SaxReader對象 read(...):加載執行xml文檔 2.Document對象 getRootElement():獲得根元素 3.Element對象 a、elements() 獲得所有子元素 b、element()獲得指定名稱第一個子元素 c、getName()獲得當前元素的元素名 d、attributeValue()獲得指定屬性名的屬性值 e、elementText()獲得指定名稱子元素的文本值 f、getText()獲得當前元素的文本內容 實現步驟(掌握): 1、導入jar包 :dom4j-1.6.1.jar 2、編寫web.xml 3、解析web.xml web.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>ct.FirstServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>helloServlet</servlet-name> <url-pattern>/test</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> </web-app> package com.scalpel.xml.dom4j; import java.util.List; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.junit.Test; public class TestDom4j { @Test public void testReadWebXML() { try { //1.獲得解析器 SAXReader saxReader = new SAXReader(); //2.獲得document文檔對象 Document doc = saxReader.read("src/com/scalpel/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) { if( "servlet".equals(element.getName())) { Element elementName = element.element("servlet-name"); Element elementClass = element.element("servlet-class"); System.out.println(elementName.getText()); System.out.println(elementClass.getText()); } } } catch (DocumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } 三、反射 為了模擬服務器端程序,且可以同時存在多個類似程序。故提供接口,接口中有3個方法,我們人為約定三個方法的調用順序: public interface MyServlet { public void init(); public void service(); public void destory(); } 實現接口方法的類,然後Test類調用。 @Test public void testMyServlet() { MyServlet my = new MyServlet(); my.init(); my.service(); my.destory(); } 測試程序我們可以直接new MyServlet();這種編碼方式我們成為硬編碼,即代碼寫死了。為了後期程序的可擴展,開發中通常使用實現類的全限定類名。通過反射加載字符串指定的類,並通過反射創建實例。 什麽是反射? JACA反射機制是在運行狀態中,對應任意一個類,都能夠知道這個類的所有屬性和方法,對應任意一個對象,都能夠調用它的任意一個方法和屬性。 反射 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對象方式三: * 去找找Class類中是否有提供獲取的方法呢? * 找到了,static Class forName(className); * 相對方便的多,不需要直接使用具體的類,只要知道該類的名字即可。 * 而名字完成可以作為參數進行傳遞 ,這樣就可以提高擴展性。 * 所以為了動態獲取一個類,第三種方式最為常用。 Class clazz = Class.forName("cn.itcast.bean.Person");//必須類全名 創建Person對象的方式 以前:1,先加載cn.itcast.bean.Person類進內存。 2,將該類封裝成Class對象。 3,根據Class對象,用new操作符創建cn.itcast.bean.Person對象。 4,調用構造函數對該對象進行初始化。 cn.itcast.bean.Person p = new cn.itcast.bean.Person(); 通過方式三:(此外還可以使用構造,構造可以指定參數---如String.class) String className = "cn.itcast.bean.Person"; //1,根據名稱獲取其對應的字節碼文件對象 1,通過forName()根據指定的類名稱去查找對應的字節碼文件,並加載進內存。 2,並將該字節碼文件封裝成了Class對象。 3,直接通過newIntstance方法,完成該對象的創建。 4,newInstance方法調用就是該類中的空參數構造函數完成對象的初始化。 Class clazz = Class.forName(className); //2,通過Class的方法完成該指定類的對象創建。 Object object = clazz.newInstance();//該方法用的是指定類中默認的空參數構造函數完成的初始化。 清單1,獲取字節碼文件中的字段。 Class clazz = Class.forName("cn.itcast.bean.Person"); //獲取該類中的指定字段。比如age Field field = clazz.getDeclaredField("age");//clazz.getField("age"); //為了對該字段進行操作,必須要先有指定類的對象。 Object obj = clazz.newInstance(); //對私有訪問,必須取消對其的訪問控制檢查,使用AccessibleObject父類中的setAccessible的方法 field.setAccessible(true);//暴力訪問。建議大家盡量不要訪問私有 field.set(obj, 789); //獲取該字段的值。 Object o = field.get(obj); System.out.println(o); 備註:getDeclaredField:獲取所有屬性,包括私有。 getField:獲取公開屬性,包括從父類繼承過來的,不包括非公開方法。 清單2,獲取字節碼文件中的方法。 //根據名稱獲取其對應的字節碼文件對象 Class clazz = Class.forName("cn.itcast.bean.Person"); //調用字節碼文件對象的方法getMethod獲取class對象所表示的類的公共成員方法(指定方法),參數為方法名和當前方法的參數,無需創建對象,它是靜態方法 Method method = clazz.getMethod("staticShow", null); //調用class對象所表示的類的公共成員方法,需要指定對象和方法中的參數列表 method.invoke(null, null); ……………………………………………………………………………………………………… Class clazz = Class.forName("cn.itcast.bean.Person"); //獲取指定方法。 Method method = clazz.getMethod("publicShow", null); //獲取指定的類對象。 Object obj = clazz.newInstance(); method.invoke(obj, null);//對哪個對象調用方法,是參數組 好處:大大的提高了程序的擴展性。 @Test public void testMyServletClass() { try { String className = "com.scalpel.web.servlet.MyServlet"; Class clazz = Class.forName(className); MyServlet my = (MyServlet) clazz.newInstance(); my.init(); my.service(); my.destory(); } catch (Exception e) { e.printStackTrace(); } } 四、讀取配置文件,根據配置文件調用類的方法實現 1、添加dom4j-1.6.1.jar包 (使用dom4j解析配置文件) 2、添加xsd文檔 (web-app_2_5.xsd,改文檔為Schema約束文檔) 3、編寫IMyServlet.java接口類 4、實現接口類MyServlet.java 5、編寫web.xml文件 6、編寫測試類 TestMyServletAll.java (根據解析web.xml文件來調用MyServlet類裏面的方法) 代碼: IMyServlet.java package com.scalpel.web.servletAll; public interface IMyServlet { public void init(); public void service(); public void destory(); } MyServlet.java package com.scalpel.web.servletAll; public class MyServlet implements IMyServlet { @Override public void init() { System.out.println("MyServlet star"); } @Override public void service() { System.out.println("MyServlet service"); } @Override public void destory() { System.out.println("MyServlet end"); } } web.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>MyServlet</servlet-name> <servlet-class>com.scalpel.web.servletAll.MyServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>MyServlet</servlet-name> <url-pattern>/myServlet</url-pattern> </servlet-mapping> <servlet> <servlet-name>MyServletTwo</servlet-name> <servlet-class>com.scalpel.web.servletAll.MyServletTwo</servlet-class> </servlet> <servlet-mapping> <servlet-name>MyServletTwo</servlet-name> <url-pattern>/myServletTwo</url-pattern> </servlet-mapping> </web-app> TestMyServletAll.java package com.scalpel.web.servletAll; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.junit.Test; public class TestMyServletAll { @Test public void testMyServlet() { try { //1.創建解析器對象 SAXReader saxReader = new SAXReader(); //2.使用解析器加載web.xml文檔得到document對象 Document document = saxReader.read("src/com/scalpel/web/servletAll/web.xml"); //3.獲取根元素節點 Element rootElement = document.getRootElement(); //4.根據元素名稱獲取子元素節點(獲取第一個servlet) Element servletElement = rootElement.element("servlet"); //5.根據元素名稱獲取servlet-class的文本節點 String servletClass = servletElement.element("servlet-class").getText(); //System.out.println(servletClass); //6.通過類全名獲取字節碼文件 Class clazz = Class.forName(servletClass); //7.創建實例對象 MyServlet my = (MyServlet) clazz.newInstance(); //8.調用實例對象裏面的方法 my.init(); my.service(); my.destory(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } 備註:很簡單的代碼。但是思想很重要,在公司中,每個人負責的模塊不一樣,不一定能夠看到別人負責的源代碼,當你需要調用其他同事寫的接口的時候,就需要查看web.xml(配置文件)文檔,然後解析,調用其他同事的接口。最後完成自己負責模塊的功能。

XML&反射