2016年12月23日 星期五

內省

內省的包是 java.beans,就是要針對java bean使用的

※getPropertyDescriptors

利用setter/getter其中之一來達到抓取的目的,但有以下幾點要注意
就算沒有給屬性(所以屬性的修飾子是什麼無所謂),
.setter:public void set開頭就抓的到
.getter:public而且回傳值不是void,且沒有參數,就算回傳的是個null也可抓的到
set/get開頭後面一定要有,不能剛好是set/get


package inheritate;
    
public class Son extends Dad {
    private int son;
    private int son2;
    private String son3;
    
    // setter/getter...
}
------------------------------
package inheritate;
    
public class Dad extends Father {
    private String dad;
    
    // setter/getter...
}
------------------------------
package inheritate;
    
public abstract class Father implements GrandFather {
    private String father;
    
    // setter/getter...
}
------------------------------
package inheritate;
    
public interface GrandFather {
    String grandFather = "g";
    
    default String getGrandFather() {
        return grandFather;
    }
}

※雖然最後是介面,也有屬性,但內省是不會理他的,因為介面本來就沒有什麼setter/getter


※測試

Son son = new Son();
son.setSon(1);
son.setDad("d1");
son.setFather("d2");
    
/*
Son s;
try {
    Class<?> clazz = Class.forName("inheritate.Son");
    try {
        s = (Son) clazz.newInstance();
        s.setSon(2);
        s.setDad("s1");
        s.setFather("s2");
    } catch (InstantiationException | IllegalAccessException e) {
        e.printStackTrace();
    }
} catch (ClassNotFoundException e1) {
    e1.printStackTrace();
}
*/
    
BeanInfo bi = null;
try {
    bi = Introspector.getBeanInfo(son.getClass());
} catch (IntrospectionException e) {
    e.printStackTrace();
}
    
PropertyDescriptor[] pds = bi.getPropertyDescriptors();
System.out.println("pds長度=" + pds.length + System.getProperty("line.separator"));
    
for (PropertyDescriptor p : pds) {
    System.out.println("name=" + p.getName());
    System.out.println("propertyType=" + p.getPropertyType());
    Method rm = p.getReadMethod();
    try {
        System.out.println("invoke=" + rm.invoke(son));
    } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
        e.printStackTrace();
    }
    System.out.println();
}

※結果:
pds長度=6

name=class
propertyType=class java.lang.Class
invoke=class inheritate.Son

name=dad
propertyType=class java.lang.String
invoke=d1

name=father
propertyType=class java.lang.String
invoke=d2

name=son1
propertyType=int
invoke=1

name=son2
propertyType=int
invoke=0

name=son3
propertyType=class java.lang.String
invoke=null

※Son有三個屬性,而Dad、Father各有一個屬性,所以會有5個,但看結果會多一個class的屬性

※getBeanInfo有四個建構子,其中的兩個參數,有給class和int的
.class表示在哪個class就停止搜尋下去的意思,停止的class不會搜尋
所以以上面的結果,如果不想要Class,就可以在getBeanInfo增加第二個參數Object.class就不會出現class屬性了,又如果我給Father.class,那結果就是4個,因為Son繼承Dad,Father不會搜尋

.int有三種,都是static的
USE_ALL_BEANINFO:如果第二個參給這個值,它會連到三個參數的建構子,所以第二個肯定是null,這時等同一個參數的建構子
IGNORE_IMMEDIATE_BEANINFO:待補充
IGNORE_ALL_BEANINFO:待補充

※實體可不同,如下

BeanInfo bi = null;
try {
    bi = Introspector.getBeanInfo(Son.class, Dad.class);
} catch (IntrospectionException e) {
    e.printStackTrace();
}
    
PropertyDescriptor[] pds = bi.getPropertyDescriptors();
System.out.println("pds長度=" + pds.length + System.getProperty("line.separator"));
    
Son son = new Son();
for (PropertyDescriptor p : pds) {
    Method wm = p.getWriteMethod();
    Method rm = p.getReadMethod();
    
    if (p.getName().equals("son3")) {
        try {
            wm.invoke(son, "xxx");
            System.out.println("invoke=" + rm.invoke(son));
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            e.printStackTrace();
        }
        System.out.println();
    } else if (p.getName().equals("son1")) {
        try {
            wm.invoke(son, 2);
            System.out.println("invoke=" + rm.invoke(son));
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

※Son的實體沒有放在getBeanInfo裡面,但還是OK的

※此例塞son3和son1屬性,其他就不塞,所以不塞的該是null就是null;該是0就是0

※getWriteMethod對應setter方法;getReadMethod對應getter方法,如果其中一個沒有,那對應的方法就會報錯

※由於屬性和setter/getter的名稱是可以不匹配的,所以java bean如下的設定也是可以的
package inheritate;
    
public class Son extends Dad {
    private int s;
    private int s2;
    private String s3;
    
    public int getSon() {
        return s;
    }
    
    public void setSon(int son1) {
        this.s = son1;
    }
    
    public int getSon2() {
        return s2;
    }
    
    public void setSon2(int son2) {
        this.s2 = son2;
    }
    
    public String getSon3() {
        return s3;
    }
    
    public void setSon3(String son3) {
        this.s3 = son3;
    }
}




※PropertyDescriptor

少了一個s,這時屬性修飾子依然無所謂,但setter/getter都一定要有

※測試

try {
    PropertyDescriptor pd = new PropertyDescriptor("son4", Son.class);
    Method m1 = pd.getWriteMethod();
    Method m2 = pd.getReadMethod();
    Son son = new Son();
    try {
        m1.invoke(son, "xxx"); // 呼叫setSon4
        System.out.println(m2.invoke(son)); // 呼叫getSon4
    } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
        e.printStackTrace();
    }
} catch (IntrospectionException e) {
    e.printStackTrace();
}

※直接利用PropertyDescriptor的建構子即可達到目的,第二個參數放Class,第一個參數放第二個參數裡的屬性名稱

※屬性和setter/getter的名稱是仍然是不匹配的,所以java bean如下的設定也是可以的
package inheritate;
    
public class Son extends Dad {
    private String son1;
    
    public String getSon4() {
        return "ooo";
    }
    
    public void setSon4(String son4) {
        this.son1 = son4;
    }
}





※getMethodDescriptors

BeanInfo bi = null;
try {
    bi = Introspector.getBeanInfo(Son.class, Dad.class);
} catch (IntrospectionException e) {
    e.printStackTrace();
}
MethodDescriptor[] mds = bi.getMethodDescriptors();
System.out.println("mds長度=" + mds.length + System.getProperty("line.separator"));

for (MethodDescriptor m : mds) {
    System.out.println("name=" + m.getName());
    System.out.println("value=" + m.getValue(m.getName()));
    if ("setSon3".equals(m.getName())) {
        m.setValue(m.getName(), "xxx");
        System.out.println("value=" + m.getValue(m.getName()));
    }
    
    Enumeration<String> attrs = m.attributeNames();
    while(attrs.hasMoreElements()){
        System.out.println(attrs.nextElement());
    }
    System.out.println();
}





※公用

apache提供了一個方便的工具,但要下載


<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.3</version>
</dependency>
    



※測試

public static void main(String[] args) throws Exception {
    Son son = new Son();
    BeanUtils.setProperty(son, "son", "1");
    BeanUtils.setProperty(son, "son2", "2");
    BeanUtils.setProperty(son, "son3", "xxx");
    
    System.out.println(BeanUtils.getProperty(son, "son"));
    System.out.println(BeanUtils.getProperty(son, "son2"));
    System.out.println(BeanUtils.getProperty(son, "son3"));
    
    Son s = new Son();
    PropertyUtils.setProperty(s, "son", 3);
    PropertyUtils.setProperty(s, "son2", 4);
    PropertyUtils.setProperty(s, "son3", "ooo");
    
    System.out.println(PropertyUtils.getProperty(s, "son"));
    System.out.println(PropertyUtils.getProperty(s, "son2"));
    System.out.println(PropertyUtils.getProperty(s, "son3"));
}

※BeanUtils在set時,可以全部都用字串塞值,但PropertyUtils一定要塞對的型態,否則會報錯

※還是要依照java bean才有辦法塞/取值

沒有留言:

張貼留言