標籤:ram 解析 access 檔案中 調用 gen string obj div
java反射機制
java語言允許通過程式化的方式間接對Class的對象執行個體操作,Class檔案由類載入器裝載後,在JVM中將形成一份描述class結構的元資訊對象,通過該元資訊對象可以獲知class的結構資訊,通過該源資訊對象可以獲知class的結構資訊,如建構函式、屬性和方法等
舉一個反射的小例子:
car類主要包含三個屬性,和一個無參建構函式與一個帶參建構函式
public class Car { private String brand; private String color; private int maxspeed; public Car() { System.out.println("init car"); } public Car(String brand, String color, int maxspeed) { this.brand = brand; this.color = color; this.maxspeed = maxspeed; } public void introduce(){ System.out.println("品牌:"+brand+"顏色:"+color+"最大速度:"+maxspeed); } public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public int getMaxspeed() { return maxspeed; } public void setMaxspeed(int maxspeed) { this.maxspeed = maxspeed; }}
這裡利用反射擷取構造器執行個體化對象
public class ReflectTest { //通過預設構造方法初始化car對象 public static Car initDefaultConst() throws Throwable{ //1.通過類裝載器擷取Car類對象 ClassLoader loader = Thread.currentThread().getContextClassLoader(); Class clazz =loader.loadClass("com.xiongda.spring.reflect.Car"); //2.擷取類的預設構造器對象並執行個體化Car Constructor cons =clazz.getDeclaredConstructor(null); Car car =(Car) cons.newInstance(); //3.通過反射方法設定屬性 Method setBrand =clazz.getMethod("setBrand", String.class); setBrand.invoke(car, "平治"); Method setColor =clazz.getMethod("setColor", String.class); setColor.invoke(car, "黑色"); Method setMaxspeed =clazz.getMethod("setMaxspeed", int.class); setMaxspeed.invoke(car, 200); return car; } //通過帶參構造方法初始化car對象 public static Car initParamConst() throws Throwable{ //1.通過類裝載器擷取Car類對象 ClassLoader loader = Thread.currentThread().getContextClassLoader(); Class clazz =loader.loadClass("com.xiongda.spring.reflect.Car"); //2.擷取類的帶參構造器對象並執行個體化Car Constructor cons =clazz.getDeclaredConstructor(String.class,String.class,int.class); Car car =(Car) cons.newInstance("寶馬","紅色",180); return car; } public static void main(String[] args) throws Throwable { Car car1 =ReflectTest.initDefaultConst(); Car car2 =ReflectTest.initParamConst(); car1.introduce(); car2.introduce(); }}
在傳統方法中,我們一般是使用建構函式設定屬性或者set方法設定屬性
類裝載器ClassLoader
類裝載器就是尋找類的位元組碼檔案並構造出類在JVM內部表示的對象組件,主要工作由ClassLoader及其子類負責,ClassLoader是一個重要的java運行時系統組件,它負責在運行時尋找和裝入class位元組碼檔案
工作機制:
1.裝載:尋找和匯入class檔案
2.連結:執行校正,準備和解析步驟
3.初始化:對類的靜態變數、靜態代碼塊執行初始化工作
重要方法:
1.Class loadClass(String name)
2.Class defineClass(String name,byte[] b, int off, int len)
3.Class findSystemClass(String name)
4.Class findLoadedClass(String name)
5.ClassLoader getParent()
Class反射對象描述類語義結構,可以從Class對象中擷取建構函式,成員變數,方法等類元素的反射對象,並以編程的方式通過這些反射對象對目標對象進行操作。這些反射對象類在java.reflect包中定義
三個最主要的反射類:
Constructor
Method
Field
在Spring中,通過IOC可以將實作類別、參數資訊等配置在其對應的設定檔中,那麼當需要更改實作類別或參數資訊時,,只需要修改設定檔即可,我們還可以對其對象所需要的其它對象進行注入,這種注入都是在設定檔中做的
Spring的IOC的實現原理利用的就是java的反射機制,Spring的工廠類會幫我們完成設定檔的讀取、利用反射機制注入對象等工作,我們可以通過bean的名稱擷取對應的對象
基本的javabean
public class JavaBean { private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
讀取設定檔,然後通過Java反射機制執行個體化javabean
public class BeanFactory { private Map<String, Object> beanMap = new HashMap<>(); /** * bean工廠的初始化 * @param xml */ public void init(String sxml){ InputStream ins= null; Document doc = null; try { //1.建立讀取設定檔的reader對象 SAXReader reader =new SAXReader(); //2.擷取當前線程中的類裝載器對象 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); //3.從class目錄下擷取指定的xml檔案 ins = classLoader.getResourceAsStream(sxml); if(ins == null){ System.out.println("擷取流失敗"); } doc = reader.read(ins); Element root = doc.getRootElement(); Element foo; //4.遍曆xml檔案中的Bean執行個體 for(Iterator i =root.elementIterator("bean");i.hasNext();){ foo=(Element) i.next(); //5.針對每一個bean執行個體,擷取bean的屬性id和class Attribute id =foo.attribute("id"); Attribute cls=foo.attribute("class"); //6.利用java反射機制,通過class的名稱擷取class對象 Class bean = Class.forName(cls.getText()); //7.擷取對應class的資訊 java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(bean); //8.擷取其屬性描述 java.beans.PropertyDescriptor pd[] =info.getPropertyDescriptors(); //9.建立一個對象,並在接下來的代碼中為對象的屬性賦值 Object obj = bean.newInstance(); //10.遍曆該bean的property屬性 for(Iterator ite = foo.elementIterator("property");ite.hasNext();){ Element foo2 = (Element) ite.next(); //11.擷取該property的name屬性 Attribute name = foo2.attribute("name"); String value = null; //12.擷取該property的子項目value的值 for(Iterator ite1 =foo2.elementIterator("value");ite1.hasNext();){ Element node = (Element) ite1.next(); value = node.getText(); break; } //13.利用java的反射機制調用對象的某個set方法,並將值設定進去 for(int k=0;k<pd.length;k++){ if(pd[k].getName().equalsIgnoreCase(name.getText())){ Method mset = null; mset = pd[k].getWriteMethod(); mset.invoke(obj, value); } } } //14.將對象放入beanMap中,其中key為id值,value為對象 beanMap.put(id.getText(), obj); } } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | DocumentException | IntrospectionException e) { // TODO Auto-generated catch block if(doc ==null){ System.out.println("擷取失敗"); } e.printStackTrace(); } } public Object getBean(String beanName){ Object obj =beanMap.get(beanName); return obj; } public static void main(String[] args) throws Throwable { BeanFactory factory = new BeanFactory(); factory.init("config.xml"); JavaBean javabean =(JavaBean)factory.getBean("javaBean"); System.out.println("username "+javabean.getUsername()); System.out.println("password "+javabean.getPassword()); }}
xml檔案
<?xml version="1.0" encoding="UTF-8"?><beans> <bean id="javaBean" class="com.xiongda.spring.reflect.JavaBean"> <property name="username"> <value>極客學院</value> </property> <property name="password"> <value>1234567890</value> </property> </bean></beans>
注意:如果你是用eclipse的話,你這個xml檔案一定要放在bin目錄裡面,否則會報出一個null 指標異常。但是後來我發現,用IDEA的話就不存在這個問題。(解決這東西花了不少時間,至今還未明白為啥會存在這種區別)
(備忘:使用SAXReader需要導包dom4j)
java反射機制