一、管理资源和配置文件的方法总结
首先考虑一下,既然类加载器可以找到我们的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);
}
}