------- android培训、java培训、期待与您交流! ----------
内省(IntroSpector)
JavaBean
总之,一个类被当作javaBean使用时,JavaBean的属性是根据方法名推断出来的,它根本看不到java类内部的成员变量。
JDK中提供了对JavaBean进行操作的一些API,这套API就称为内省。如果要你自己去通过getX方法来访问私有的x,怎么做,有一定难度吧?用内省这套api操作JavaBean比用普通类的方式更方便
采用遍历BeanInfo的所有属性方式来查找和设置某个RefectPoint对象的x属性。在程序中把一个类当作JavaBean来看,就是调用IntroSpector.getBeanInfo方法,得到的BeanInfo对象封装了把这个类当作JavaBean看的结果信息,得到BeanInfo最好采用“obj.getClass()”方式,而不要采用“类名.class”方式,这样程序更通用
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
ReflectPoint pt1 = new ReflectPoint(3,5);
Object retVal = getProperty(pt1);
System.out.println(retVal);
PropertyDescriptor pd2 = null;
String propertyName = "y";
Object value = 7;
setProperty(pt1, propertyName, value);
//先通过调用普通java类的方法的方式获得结果,然后在这之前插入BeanUtil的get和set操作,见下面的代码
//System.out.println(pt1.getY());
System.out.println(BeanUtils.getProperty(pt1, "y"));
BeanUtils.setProperty(pt1, "y", "99");
System.out.println(pt1.getY());
PropertyUtils.setProperty(pt1, "y", 999);
System.out.println(PropertyUtils.getProperty(pt1, "y").getClass().getName());
}
private static Object getProperty(ReflectPoint pt1) {
Object retVal = null;
PropertyDescriptor pd = null;
try {
pd = new PropertyDescriptor("y",pt1.getClass());
retVal = pd.getReadMethod().invoke(pt1);
} catch (Exception e) {
e.printStackTrace();
}
return retVal;
}
private static void setProperty(Object pt1, String propertyName,
Object value) {
/*PropertyDescriptor pd2;
try {
pd2 = new PropertyDescriptor(propertyName,pt1.getClass());
pd2.getWriteMethod().invoke(pt1,value);
} catch (Exception e) {
e.printStackTrace();
}*/
try {
BeanInfo beanInfo = Introspector.getBeanInfo(pt1.getClass());
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
for(PropertyDescriptor pd :pds){
if(pd.getName().equals(propertyName)){
pd.getWriteMethod().invoke(pt1,value);
break;
}
}
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IntrospectionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
代理
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一 段时间后,又想去掉系统功能也很容易
AOP(Aspectoriented program ,简称AOP)
面向方面编程。系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,即为AOP。
AOP的目标就是要使交叉业务模块化,可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果相同。
代理技术可以很好的解决这种问题,是实现AOP功能的核心和关键技术。
动态代理技术:
JVM可以在运行期动态生成出类的字节码,这种动态生成的类被用作代理类,即动态代理类。
JVM生成的动态类必须实现一个或多个接口,所以JVM生成的动态类只能用作具有相同接口的目标类的代理。
CGLIB库(目前开源,也许将来会加到jdk中)可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码。
a.在调用目标方法之前
b.在调用目标方法之前
c.在调用目标方法前后
d.在处理目标方法异常的catch块中
创建动态类的实例对象:
首先用反射获得构造方法。
编写一个最简单的invocationhandler类
调用构造方法创建动态类的实例对象,并将编写的invocationhandler类的实例对象传进去。
JVM创建动态类所需信息:
生成的类中有哪些方法,通过让其实现哪些接口的方式进行告知。
产生的类的字节码必须有一个关联的类加载器对象。
生成的类中的方法的代码,我们把代码写在一个约定好的接口对象的方法中,把对象传给他,它调用我们的方法,即插入了我们的代码。
提供执行代码的对象就是那个invocationhandler对象,是在创建动态类的实例对象的构造方法时传进去的。
也可以用Proxy.newinstance方法直接一步就创建出代理对象
动态代理工作原理图
用于为某个对象生成和返回其代理对象,源对象必须实现接口,生成的代理对象会实现与源对象相同的接口
注意:
源对象的类必须自己定义时就实现接口,从该类的祖辈类上继承的接口是无效的
该方法接口两个参数:一个是目标对象,另一个是封装了用户系统功能代码的Advice对象,该对象必须实现Advice接口
getProxy
由此联想到spring的一个问题:spring无法将通知应用到目标类的父类的方法上,例如,我们写的Action继承了DispatachAction,那么spring无法将advice应用到execute方法上。