------- android培训、java培训、期待与您交流! ----------
关键字:JavaBean 代理类 AOP
——————————————————————————————————————————————
五、JavaBean 内省与beanutils工具包
内省(IntroSpector)主要用于对JavaBean进行操作。
1.JavaBean是一种特殊的Java类,主要用于传递数据信息,这种java类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。
用 途:如果要在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称之为值对象(Value Object,简称VO)。这些信息在类中用私有字段来存储,如果读取或设置这些字段的值,则需要通过一些相应的方法来访问。
特 点:JavaBean的属性是根据其中的setter和getter方法来确定的,而不是根据其中的成员变量。去掉set和get前缀,剩余部分就是属性名。如果剩余部分的第二个字母是小写的,则把剩余部分的首字母改成小的。JavaBean必须有一个不带参数的构造方法。
好 处:一个符合JavaBean特点的类可以当作普通类一样进行使用,但如果把它当做JavaBean,那么就可以调用JDK提供的对专门对JavaBean进行操作的API,以实现对一些对普通类来说比较复杂的功能。
2.内省:JDK提供的对JavaBean进行操作的API,就被称为内省。(参看avaAPI文档中的java.beans包和java.beans.beancontext)
3.对JavaBean的简单内省操作
public static void setProperty(Object pt1, String propertyName, Object value) throws IntrospectionException, IllegalAccessException, InvocationTargetException { PropertyDescriptor dp1 = new PropertyDescriptor(propertyName,pt1.getClass()); Method methodSetX = dp1.getWriteMethod(); methodSetX.invoke(pt1,value); }
public static Object getProperty(Object pt1, String propertyName) throws IntrospectionException, IllegalAccessException, InvocationTargetException { PropertyDescriptor dp = new PropertyDescriptor(propertyName,pt1.getClass()); Method methodGetX = dp.getReadMethod(); Object retVal = methodGetX.invoke(pt1); return retVal; }
ReflectPoint pt1 = new ReflectPoint(1,1); String propertyName = "x"; Object retVal = getProperty(pt1, propertyName); System.out.println("getX:"+retVal); Object value = 7; setProperty(pt1, propertyName, value); System.out.println("setX:"+pt1.getX());
上边即是用java.beans包中的PropertyDescriptor类把ReflectPoint当做JavaBean进行操作。
4.使用BeanInfo类和IntroSpector类查看把ReflectPoint当做一个JavaBean看时,显示的信息,并获取"x"属性的值(即用另一种方式,实现3中getProperty方法)
public static Object getProperty(Object pt1, String propertyName) throws IntrospectionException, IllegalAccessException, InvocationTargetException { BeanInfo beanInfo = Introspector.getBeanInfo(pt1.getClass()); PropertyDescriptor[] dps = beanInfo.getPropertyDescriptors(); Object retVal = null; for(PropertyDescriptor dp:dps) { if(dp.getName().equals(propertyName)){ retVal = dp.getReadMethod().invoke(pt1); break; } } return retVal;
5.beanutils工具包
Apache提供的开源的操作JavaBean的工具包,它要和Apache的logging工具包导入到Project中,然后Build Path才可以使用。
里边的BeanUtil和PropertyUtils类使用示例如下:
ReflectPoint pt1 = new ReflectPoint(1,1);
BeanUtils.setProperty(pt1, "x", 25);
System.out.println(BeanUtils.getProperty(pt1, "x").getClass().getName());
//BeanUtils可以实现属性链操作
BeanUtils.setProperty(pt1, "date.time", "111");//time不一定在类中存在,只是说明date有getTime和setTime方法。
System.out.println(BeanUtils.getProperty(pt1, "date.time"));
PropertyUtils.setProperty(pt1, "x", 160);
System.out.println(PropertyUtils.getProperty(pt1, "x").getClass().getName());
BeanUtils:以字符串(String)的形式对JavaBean 的属性进行操作,getProperty方法得到的属性值是以字符串形式返回的(网络开发时,获取的用户数据类型都是String)。
PropertyUtils:以属性本身的类型最JavaBean的属性进行操作,getProperty()方法得到的属性值是以属性本来的类型返回的。
[ 知识积累]
JDK1.7的新特性之一:Map map = {name:"zhangssan",age:18};
BeanUtils也可以对Map集合进行操作:BeanUtils.setProperty(map, name, "lisi");
六、.代理类与AOP
(一)、代理的概念与作用
1.<代理模式>简单示例
目标类: class X { void sayHello() { System.out.println("hello java"); } } 代理类: XProxy { void sayHello() { starttime; X.sayHello(); endTime; } }
2.程序中的代理
(1)要为已存在的多个具有相同接口的目标类的各个方法增加一系列的系统功能,例如,异常处理,日志、计算方法等的运行时间、事务管理等等,你如何做?
(2)编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。
(3)如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类还是代理类,这样以后很容易切换。譬如,想要日志功能时就配置代理类,否则配置目标类,这样增加系统功能很容易。以后运行一段时间后,又想去掉系统功能也很容易。
3.代理分为两种:静态代理和动态代理。
<AOP>
1.系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方法。
2.交叉业务的房产问题即为面向方面的编程(AOP,Aspect oriented program),AOP的目标就是要
使交叉业务模块化,可以采用将切面代码移动到原始方法的周围,这与直接在方法中编程切面代码的运行效果是一样的。
3.使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。
( 二)、动态代理技术(JVM动态代理和CGLIB库动态代理)
1.要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情。(引出动态代理)
2.JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作 代理类 ,即动态代理类。
3.JVM生成的动态类必须具有一个或多个接口,所以,JVM生成的动态类(Proxy)只能用作具有相同接口的目标类的代理。
4.CGLIB库(一个开源库)可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的 类生成动态代理类,可以使用CGLIB库。
5.代理类的各个方法中通除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下 四个位置加上系统功能代码:
(1).在调用目标方法之前
(2).在调用目标方法之后
(3)在调用目标方法之前或之后
(4)在处理目标方法异常的catch块中。
6.创建JVM动态代理方法:
方法一:获取动态类字节码(Class)--->用字节码获取Constructor---->用Constructor创建动态类实例
a.使用Proxy获取动态类字节码(以Collection为例)
Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); System.out.println(clazzProxy1.getName());
打印clazzProxy1的所有构造方法和成员方法。
System.out.println("----begin constructors list-------");
/*
* $Proxy0
* $Proxy0(InvocationHandler ,int)
*/
printConstructors(clazzProxy1);
System.out.println("----begin methods list-------");
printMethods(clazzProxy1);
public static void printMethods(Class clazzProxy1) { Method[] methods = clazzProxy1.getMethods(); for(Method method:methods){ String name = method.getName(); StringBuilder sBuilder = new StringBuilder(name); sBuilder.append("("); Class[] clazzParams = method.getParameterTypes(); for(Class clazzParam:clazzParams){ sBuilder.append(clazzParam.getName()).append(","); } if(clazzParams!=null &&clazzParams.length!=0) sBuilder.deleteCharAt(sBuilder.length()-1); sBuilder.append(")"); System.out.println(sBuilder); } } public static void printConstructors(Class clazzProxy1) { Constructor[] constructors=clazzProxy1.getConstructors(); for(Constructor constructor:constructors){ String name = constructor.getName(); StringBuilder sBuilder = new StringBuilder(name); sBuilder.append("("); Class[] clazzParams = constructor.getParameterTypes(); for(Class clazzParam:clazzParams){ sBuilder.append(clazzParam.getName()).append(","); } if(clazzParams!=null &&clazzParams.length!=0) sBuilder.deleteCharAt(sBuilder.length()-1); sBuilder.append(")"); System.out.println(sBuilder); }
b.获取Constructor
Constructor constructor =clazzProxy1.getConstructor(InvocationHandler.class);
C.用Constructor创建代理类实例:因为需要用到InvocationHandler接口类型的参数,可以先定义一个内部实现类,也可以直接用匿名内部类在参数列表上实现接口。
class MyInvocationHandler1 implements InvocationHandler{ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; } } Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHandler1()); System.out.println(proxy1.toString());
Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler(){ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; } });
此时,proxy1的打印值为null,调用其无返回值的方法没有报错,调用其有返回值的方法则报空指针异常,因为它总是调用InvocationHandler的invoke方法,而这个方法此时总是返回null。
方法二:直接用Proxy的静态方法newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
Collection proxy3=(Collection)Proxy.newProxyInstance( Collection.class.getClassLoader(), new Class[]{Collection.class}, new InvocationHandler(){ ArrayList target= new ArrayList(); public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long startTime = System.currentTimeMillis(); Object retVal = method.invoke(target, args); long endTime = System.currentTimeMillis(); System.out.println(method.getName()+" running time:"+(endTime - startTime)); return retVal; } }); proxy3.add("123"); proxy3.add("453"); proxy3.add("789"); System.out.println(proxy3.size());
注意:如果InvocationHandler的invoke方法只是Eclipse自动生成的代码的话,那现在得到的代理类实例,也只是一个骨架,没有代理的目标,就像一个代理某个产品的专卖店,只是装饰好了门面,没有摆放产品。
提示:在invoke前和后的代码,因为跟其目标没有多大关系,可专门封装在一个类中;另外,在invoke中也可以对args参数进行过滤(比如如果是args传入的是字符串,可以实现敏感信息过滤)。
7. 猜想分析动态生成的类的内部代码
class Proxy${ add(Object obj){ return handler.invoke(Object proxy, Method method, Object[] args); } } int size() { return handler.invoke(this,this.getClass().getMethod("size"),null); }
注意:Proxy动态代理类在调用方法时,从Ojbect继承的方法 的 hashCode、equals 和 toString三个方法会委托给InvocationHandler,其他从Object 继承的方法都不委托。这既是为什么proxy1.getClass().getName()返回的是 Proxy$ 而不是ArrayList的原因。
(三)将动态生成的类成为目标的代理
动态代理类的骨架用Proxy.newProxyInstance()方法就能生成,执行要再传入两个参数:要代理的目标target和增加的系统功能的封装对象(advice),就能让动态生成的类成为目标的代理啦。
1.建立增加的系统功能封装类
import java.lang.reflect.Method; public interface Advice { void beforeMethod(Method method); void afterMethod(Method method); }
import java.lang.reflect.Method; public class MyAdvice implements Advice { long startTime=0; @Override public void beforeMethod(Method method) { // TODO Auto-generated method stub System.out.println("方法开始啦"); startTime = System.currentTimeMillis(); } @Override public void afterMethod(Method method) { // TODO Auto-generated method stub System.out.println("方法结束啦"); long endTime = System.currentTimeMillis(); System.out.println(method.getName()+" running time:"+(endTime - startTime)); } }
2.建立目标:(以Collection为例)
final ArrayList target = new ArrayList();
3.成为目标的代理
public static Object getProxy(final Object target,final Advice advice) { Object proxy3 =Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(),//注意,这里参数是Class数组 new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { advice.beforeMethod(method); Object retVal = method.invoke(target, args); advice.afterMethod(method); // return method.invoke(proxy, args);//这就变成了死循环。 return retVal; } }); return proxy3; }
注意:为了能让InvocationHandler匿名内部类访问到targe和Advice,需要将其设为final。你不仅可以用Collection指向这个代理,所有它实现的接口引用都可以指向它~。它的父类是java.lang.reflect.Proxy。
(四)实现AOP功能的封装和配置
功能描述:建立一个BeanFactory,可以根据配置文件中的bean名称到底一个JavaBean,如果bean的名字的是“ProxyFactoryBean”则返回ProxyFactoryBean根据配置文件中的".advice"和“.target”提供的目标和Advice返回的代理类。
1.ProxyFactoryBean类代码
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import cn.itcast.day3.Advice; public class ProxyFactoryBean { private Object target; private Advice advice; public Object getTarget() { return target; } public void setTarget(Object target) { this.target = target; } public Advice getAdvice() { return advice; } public void setAdvice(Advice advice) { this.advice = advice; } public Object getProxy() { Object proxy =Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { advice.beforeMethod(method); Object retVal = method.invoke(target, args); advice.afterMethod(method); return retVal; } }); return proxy; } }
2.BeanFacotry代码:
import java.io.IOException; import java.io.InputStream; import java.util.Properties; import cn.itcast.day3.Advice; public class BeanFactory { Properties prop =new Properties(); public BeanFactory(InputStream inStream){ try { prop.load(inStream); } catch (IOException e) { e.printStackTrace(); } } public Object getBean(String name){ String className = prop.getProperty(name); Class clazz=null; Object bean=null; try { clazz = Class.forName(className); bean = clazz.newInstance(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } if(bean instanceof ProxyFactoryBean){ Object proxy = null; try { ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean)bean; Advice advice = (Advice) Class.forName(prop.getProperty(className+".advice")).newInstance(); Object target =Class.forName(prop.getProperty(className+".target")).newInstance(); proxyFactoryBean.setAdvice(advice); proxyFactoryBean.setTarget(target); proxy = proxyFactoryBean.getProxy(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return proxy; }else{ return bean; } } }
3.配置文件内容:
xxx=java.util.ArrayList #xxx=cn.itcast.day3.aopframework.ProxyFactoryBean xxx.advice=cn.itcast.day3.MyAdvice xxx.target=java.util.ArrayList
4.框架测试类:
import java.io.InputStream; public class AopFrameworkTest { public static void main(String[] args) { InputStream ips =AopFrameworkTest.class.getResourceAsStream("config.properties"); BeanFactory beanFactory = new BeanFactory(ips); Object bean =beanFactory.getBean("xxx"); System.out.println(bean.getClass().getName()); } }
注意: Spring的两大核心技术----AOP和代理,上边的代码即实现了一个简单的AOP框架。
------- android培训、java培训、期待与您交流! ----------