一、管理資源和配置文件的方法總結
首先考慮一下,既然類加載器可以找到我們的class文件並加載,那麼它能不能找到我們項目中使用的其他資源呢?Class代表字節碼文件,它對自己的路徑有沒記錄呢?
首先說一下上面的目錄結構:
prosql.properties在工程目錄下
sql.properties在源目錄下
packagesql.properties在zhenkai.lianxi的包目錄下
subpackagesql.properties在zhenkai.lianxi包的子包nuli的目錄下
下面我結合上面的工程目錄和一個小程序對Class與ClassLoader類裏有一些與獲取資源路徑和資源流的方法進行簡單的梳理:
public class LoaderSourcePath {
public static void main(String[] args){
readPro();
}
public static void readPro(){
//通過類加載器訪問資源,類加載器的默認路徑就是classpath指定的當前路徑,即eclipse下的bin目錄
//所以可以直接訪問到sql.properties(eclipse會將src目錄下及包內的非java文件原樣搬運到bin的對應位置)
System.out.println(LoaderFile.class.getClassLoader().getResource("sql.properties"));
//因爲類加載器的默認路徑就是classpath的根路徑,所以要訪問包內的文件需要指定相對於bin目錄的路徑
System.out.println(LoaderFile.class.getClassLoader().getResource("zhenkai/lianxi/packagesql.properties"));
//通過Class實例訪問資源,因爲Class實例記錄的就是class字節碼文件所在的路徑,所以可以直接訪問包內的資源
System.out.println(LoaderFile.class.getResource("packagesql.properties"));
//要訪問classpath路徑下*(即bin目錄)的資源問件,需要通過"../"返回上一級直到返回至bin目錄下
//但是類加載器不可以通過這種方法返回上一級去訪問工程目錄下的資源文件
System.out.println(LoaderFile.class.getResource("../../sql.properties"));
//通過class訪問子包中的資源,需要子包的相對路徑
System.out.println(LoaderFile.class.getResource("nuli/subpackagesql.properties"));
//Flie的默認路徑是工程路徑,所以可以直接訪問prosql.properties
File f1 = new File("prosql.properties");
//Flie的默認路徑是工程路徑,所以要通過它訪問源目錄下或者包內資源,需要用基於工程的相對路徑表示
File f2 = new File("bin/zhenkai/lianxi/packagesql.properties");
System.out.println(f1.exists());
System.out.println(f2.exists());
//通過當前線程上下文類加載器訪問資源同類加載器
System.out.println(Thread.currentThread().getContextClassLoader().getResource("sql.properties"));
}
}
這裏要注意一點:在使用相對路徑訪問資源是,路徑不可以以“/”開頭,一旦以“/”開頭就變成絕對路徑了。
使用Class和ClassLoader中的getResourceStream(String name)方法獲取資源輸入流時,同樣遵循上面的規則。
二、JavaBean與內省
1、JavaBean是一種特殊的Java類,主要用於傳遞數據信息,這種java類中的方法主要用於訪問私有的字段,且方法名符合某種命名規則。如果要在兩個模塊之間傳遞多個信息,可以將這些信息封裝到一個JavaBean中,這種JavaBean的實例對象通常稱之爲值對象(Value Object,簡稱VO)。這些信息在類中用私有字段來存儲,如果讀取或設置這些字段的值,則需要通過一些相應的方法來訪問。JavaBean的屬性是根據其中的setter和getter方法來確定的,而不是根據其中的成員變量。
如果方法名爲setId,中文意思即爲設置id,至於你把它存到哪個變量上,不用管。
如果方法名爲getId,中文意思即爲獲取id,至於你從哪個變量上取,也不用管。
去掉set前綴,剩餘部分就是屬性名,如果剩餘部分的第二個字母是小寫的,則把剩餘部分的首字母改成小的,如下:
setId()的屬性名---id
isLast()的屬性名---last
setCPU的屬性名---CPU
getUPS的屬性名---UPS
總之,一個類被當作javaBean使用時,JavaBean的屬性是根據方法名推斷出來的,它根本看不到java類內部的成員變量。
一個符合JavaBean特點的類可以當作普通類一樣進行使用,但把它當JavaBean用肯定是有原因的:
首先在JavaEE開發中,經常要使用到JavaBean進行數據傳遞,其次很多環境也要求按JavaBean方式進行操作。
JDK中提供了對JavaBean進行操作的一些API,這套API就稱爲內省,用內省這套api操作JavaBean比用普通類的方式更方便。其次還有一些第三方的工具包,會比使用JDK中的API更加方便。
下面的Person類即是一個javabean:
public class Person{
private String name;
private int age;
private Date birthday;
public Person() {}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name, int age,Date birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String toString() {
return "Person [name=" + name + ", age=" + age + ", birthday="
+ birthday + "]";
}
}
1、使用內省操作JavaBean
public class JavaBeanClass {
public static void main(String[] args){
}
//使用PropertyDescriptor來設置屬性值
public static void setProperty(Person p, String name, Object value)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
//創建PropertyDescriptor實例時需要傳入屬性名和javabean對應的class實例對象
PropertyDescriptor pd = new PropertyDescriptor(name,p.getClass());
//getWriteMethod()方法得到的就是javabean中的setter方法所對應的反射類型Method實例
Method setName = pd.getWriteMethod();
setName.invoke(p,value);
}
//使用PropertyDescriptor來獲取屬性值
public static Object getProperty1(Person p, String name)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
PropertyDescriptor pd = new PropertyDescriptor(name,p.getClass());
//getReadMethod()方法得到的就是javabean中的getter方法所對應的反射類型Method實例
Method getName = pd.getReadMethod();
return getName.invoke(p);
}
public static Object getProperty2(Person p, String name)
throws IntrospectionException, IllegalArgumentException,
IllegalAccessException, InvocationTargetException{
//在程序中把一個類當作JavaBean來看,就是調用IntroSpector.getBeanInfo方法,
//得到的BeanInfo對象封裝了把這個類當作JavaBean看的結果信息。
BeanInfo in = Introspector.getBeanInfo(p.getClass());
PropertyDescriptor[] pds = in.getPropertyDescriptors();
//採用遍歷BeanInfo的所有屬性的方式來查找和設置某個屬性。
for(int i=0;i<pds.length;i++){
if(pds[i].getName().equals(name)){
Method methodGet = pds[i].getReadMethod();
return methodGet.invoke(p, (Object[])null);
}
}
return null;
}
}
2、使用Beanutils工具包
我們使用第三方工具包時爲了保證當用戶拿到程序後依然可以使用這些工具包,就需要把工具包導入工程中
(1)、首先要在工程下建一個lib文件用來存放我們要使用到的jar包
(2)、將lib下的jar包通過BuildPath將jar包增加到BuildPath中去
具體如下所示目錄:
在這個工具包裏有一些比較使用的方法,通過下面的程序,簡單的列舉一下:
public class BeanUtilsDemo {
public static void main(String[] args) throws Exception {
Person p = new Person("jzk",24);
//BeanUtils中此處age屬性可以使用字符串格式,PropertyUtils不可以
BeanUtils.setProperty(p, "age","25");
PropertyUtils.setProperty(p, "age",26);
//BeanUtils中此返回的age類型爲字符串,PropertyUtils中此返回的age類型爲Integer
System.out.println(BeanUtils.getProperty(p, "age").getClass().getName());
System.out.println(PropertyUtils.getProperty(p, "age").getClass().getName());
//describe()方法可以將JavaBean中屬性和屬性值以map形式返回
System.out.println(BeanUtils.describe(p));
System.out.println(PropertyUtils.describe(p));
Map map = new HashMap();
map.put("name", "jkk");//map中鍵值不必與javabean中屬性值相同
map.put("age", "10");//此處即可以爲字符串類型也可以爲int型
BeanUtils.populate(p, map);//PropertyUtils不具有此方法
System.out.println(p);
//可以將一個javabean的屬性拷貝到另一個javabean上
Person pp = new Person("jjk",30,new Date());
BeanUtils.copyProperties(p, pp);
System.out.println(p);
//可以對javabean屬性進行級聯設置
BeanUtils.setProperty(p, "birthday.time", "111");//int String 皆可
System.out.println(BeanUtils.getProperty(pp, "birthday.time"));
//setProperty()方法可以對map進行設置
Map mapBean = new HashMap();
mapBean.put("first", "one");
BeanUtils.setProperty(mapBean, "first", "firstOne");
System.out.println(mapBean);
}
}