内省 IntroSpector
主要是对JavaBean进行操作。什么是JavaBeanJavaBean。是一种特殊的java类,主要是用于传递数据信息,这种java类中的方法主要用于访问私有字段,且方法名符合某种规则在多个模块中传递信息的时候,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称之为值对象(Value Object ,VO)JavaBean类的书写规则:
1.JavaBean类必须是具体的和公共的,不能是抽象的
2.类中必须有一个无参的构造方法,为了实现序列化Serializable
3.类中的属性应该声明为私有权限,定义getter和setter方法对属性进行操作
package cn.itcast.day1;
public class PersonBean {
private String name;
private int age;
public PersonBean(){}
public PersonBean(String name, int age) {
super();
this.name = name;
this.age = age;
}
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;
}
}
下面用PropertyDescriptor来获取和设置PersonBean中的name值
package cn.itcast.day1;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class IntroSepctorTest {
/**
* @param args
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
PersonBean personBean = new PersonBean("lisi", 18);
String propertyName = "name";
PropertyDescriptor pd = new PropertyDescriptor(propertyName,
personBean.getClass());
Object retVal = getProperty(personBean, pd);
System.out.println(retVal);
Object value = "zxx";
setProperty(personBean, pd, value);
System.out.println(getProperty(personBean, pd));
}
private static void setProperty(Object bean, PropertyDescriptor pd,
Object value) throws IllegalAccessException,
InvocationTargetException {
Method methodSetName = pd.getWriteMethod();
methodSetName.invoke(bean, value);
}
private static Object getProperty(Object bean, PropertyDescriptor pd)
throws IllegalAccessException, InvocationTargetException {
Method methodGetName = pd.getReadMethod();
Object retVal = methodGetName.invoke(bean);
return retVal;
}
}
这是内省的一种简单的应用方式, 还有一种复杂的应用方式:
package cn.itcast.day1;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class IntroSepctorTest {
/**
* @param args
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
PersonBean personBean = new PersonBean("lisi", 18);
String propertyName = "name";
Object retVal = getProperty(personBean, propertyName);
System.out.println(retVal);
Object value = "zxx";
setProperty(personBean, propertyName, value);
System.out.println(getProperty(personBean, propertyName));
}
private static void setProperty(Object bean, String propertyName,
Object value) throws IllegalAccessException,
InvocationTargetException, IntrospectionException {
/*
* PropertyDescriptor pd = new PropertyDescriptor(propertyName,
* bean.getClass()); Method methodSetName = pd.getWriteMethod();
* methodSetName.invoke(bean, value);
*/
BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
if (pd.getName().equals(propertyName)) {
Method methodSetName = pd.getWriteMethod();
methodSetName.invoke(bean, value);
break;
}
}
}
private static Object getProperty(Object bean, String propertyName)
throws IllegalAccessException, InvocationTargetException,
IntrospectionException {
/*
* PropertyDescriptor pd = new PropertyDescriptor(propertyName,
* bean.getClass()); Method methodGetName = pd.getReadMethod(); Object
* retVal = methodGetName.invoke(bean);
*/
BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());// BeanInfo是把一个java类当作javabean看的结果
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
Object retVal = null;
for (PropertyDescriptor pd : pds) {
if (pd.getName().equals(propertyName)) {//当找到这个属性就对它进行操作
Method methodGetName = pd.getReadMethod();
retVal = methodGetName.invoke(bean);
break;//操作完成直接跳出循环
}
}
return retVal;
}
}
在开发中最常用的方式是用apache开发的开源的BeanUtils工具包
System.out.println(BeanUtils.getProperty(personBean, "name"));
BeanUtils.setProperty(personBean, "age", "38");// 参数类型为object
BeanUtils.setProperty(personBean, "name", "lhm");
System.out.println(BeanUtils.getProperty(personBean, "name").getClass()
.getName());
System.out.println(BeanUtils.getProperty(personBean, "age").getClass()
.getName());// 返回值类型为String
BeanUtils.setProperty(personBean, "birthday.time",
System.currentTimeMillis());
System.out.println(BeanUtils.getProperty(personBean, "birthday"));
/*java7的新特性
Map map={name:"zxx",age:18};
BeanUtils.setProperty(map, "name", "lhm");
BeanUtils.getProperty(map, "name");*/
PropertyUtils.setProperty(personBean, "age", 38);// 参数类型是javabean类中属性的类型
System.out.println(PropertyUtils.getProperty(personBean, "age")
.getClass().getName());// 返回值类型为javabean类中属性的类型
类加载器
什么是类加载器?就是加载类的工具。类加载器的作用:在源程序中用到的类,首先JVM要在硬盘上找到这个类的class文件,加载到内存中再进行一些处理java虚拟机中有多个类加载器,系统默认提供了三个主要的类加载器每个类加载器负责加载特定位置的类BootStrap,ExtClassLoader,AppClassLoader
类加载器本身也是java类,因为其他java类的类加载器也要被类加载器加载,所以要有第一个不是java类的类加载器,来加载这些类加载器,就是BootStrap,BootStrap不是java类,它不需要被类加载器加载,是嵌套在java虚拟机内核里面的,是用C++写的。java虚拟机中所有类加载器采用具有父子关系的树形结构进行组织,在实例化每一个类加载器对象时,需要为其指定一个父级类加载器对象或者默认系统类加载器为其父级类加载器。
类加载器的委托机制
当java虚拟机需要加载一个类时,到底要用哪个类加载器去加载呢?首先用当前线程的类加载器去加载线程中的第一个类,如果类A中引用了类B,java虚拟机将用加载类A的类加载器去加载类B,还可以用ClassLoader.loadClass()方法去指定某个类加载器去加载某个类。第个类加载器加载时,都先委托给其上级类加载器。当所有上级类加载器没有加载到类,回到发起者类加载器,如果还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的下级,因为没有getChild方法,即使有,那有多个下级,找哪一个呢?
有一道面试题,能不能自己写一个类叫java.lang.System
能写,但写出之后,java虚拟机永远不会调用这个System类。因为当我用到System类时,java虚拟机会AppClassLoader ,而这时就会委托给它的上级ExtClassLoader,又委托给BootStrap类加载器去加载java类库中的System,这样就不会用到自己写的System类。如果自己写一个类加载器,而不指定它的上级类加载器,就可以用到了。
编写自己的类加载器
package cn.itcast.day2;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class MyClassLoader extends ClassLoader {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
String srcPath=args[0];
String destDir=args[1];
String destFileName=srcPath.substring(srcPath.lastIndexOf("\\")+1);
String destPath=destDir+"\\"+destFileName;
InputStream in=null;
try {
in=new FileInputStream(srcPath);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
OutputStream out=null;
try {
out=new FileOutputStream(destPath);
cipher(in,out);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally{
if(in!=null)
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(out!=null)
try {
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private static void cipher(InputStream ips,OutputStream ops){
int b=-1;
try {
while((b=ips.read())!=-1){
ops.write(b ^ 0xff);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private String classDir;
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// TODO Auto-generated method stub
String classFileName=classDir+"\\"+name+".class";
try {
FileInputStream fis = new FileInputStream(classFileName);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
cipher(fis, bos);
fis.close();
byte[] buf = bos.toByteArray();
return defineClass(buf, 0, buf.length);
} catch (Exception e) {
// TODO: handle exception
}
return super.findClass(name);
}
public MyClassLoader(){}
public MyClassLoader(String classDir){
this.classDir=classDir;
}
}
代理
当我们想在原有的类上增加一些功能,比如测试程序运行时间,我们不用修改原有的代码。而是创建一个代理类,在这个类中定义一个相同的方法,在方法中调用目标类的方法。这样不但保持了代码的封装性,也控制了对目标类的对象的访问。
这个被代理的类一般称为目标类或委托类。一般情况下,代理类和目标类有同样的接口。
代理类为目标类提供了额外的处理和操作:日志记录,性能统计,安全控制,事务处理,异常处理等等。代理类的对象并不实现真正的服务,而是通过调用目标类的对象的相关方法提供服务。
采用代理是为了通过不修改源代码的情况下给程序动态统一添加功能,利用代理技术可以将业务逻辑中一些非业务逻辑的代码分离出来,把他们独立到业务逻辑类外,比如日志记录,性能统计,安全控制,事务处理,异常处理等。这样做,不仅降低了业务逻辑和非业务逻辑的耦合性,提高程序的可重用性,同时提高了开发的效率。
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,只要在配置文件中明确要用目标类还是代理类。这样以后很容易切换,如果想要日志功能时,就配置代理类,否则配置目标类。这样,增加系统功能很容易,以后运行一段时间后,又想换掉系统功能也很容易。