黑马程序员_java学习笔记代理类
张孝祥老师代理类讲解之总结
package com.qdzks; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collection; /* * java 张孝祥老师讲的代理类的总结 总结内容主要来自手抄的笔记 * 代理类的概念与作用 * 程序中的代理 * 要为自己存在的多个具有相同接口的目标类的各个方法增加一些系统功能:例如异常处理,日志,计算方法运行的时间, * 事务管理等,准备如何做呢? * 编写一个与目标类具有相同接口的代理类。代理类的每个方法调用目标类的相同方法,并在调用方法时加入系统功能的 * 代码。 * * AOP(Aspect Oriented Program)交叉业务是面向方面的编程 * * 动态处理技术 * .要为系统中的各个接口的类增加代理功能,那么需要太多的代理类,全部采用代理是件很麻烦的事 * .JVM可以在运行期间动态生成类的字节码,这种动态生成类往往被用作代理类,即动态代理类 * .JVM生成的代理类必须实现一个或多个接口,所以JVM生成的代理类只能用做具有相同接口的目标类的代理 * .CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以如果要为一个没有实现接口的类生成动态代理类 * 可以使用CGLIB库 * .代理类的各个方法中通常除了调用目标的相应方法和对外返回目标所返回的结果外,还可以在代理方法中在如下四个位置加上系统 * 功能和代码 * 1.在调用方法之前 * 2.在调用目标方法之后 * 3.在调用目标方法前后 * 4.在代理目标为方法异常的catch块中 * * 分析JVM动态生成的类 * .创建实现了Collection接口的动态类和查看其名称,分析Proxy.getProxyClass()方法的各个参数 * .编码列出动态类的所有构造方法和参数签名(格式:方法名(参数列表)) * .编码列出动态类中的所有方法和参数签名(格式:方法名(参数列表)) * */ public class ProxyReview { public static void main(String[] args)throws Exception{ //public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces) throws IllegalArgumentException Class clazzProxy1=Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);//注在这里接口.class 文件是可变参数的,不是一个集合 //获取动态类的.class文件的名字 System.out.println(clazzProxy1.getName()); System.out.println("----------begin constructor list--------------"); //利用反射获取clazzProxy1中的非私有构造方法的数组 Constructor[] constructors=clazzProxy1.getConstructors(); //对构造函数进行遍历 for(Constructor constructor : constructors){ //创建一个字符串缓冲区 StringBuilder sb=new StringBuilder(); String name=constructor.getName(); sb.append(name); sb.append("("); //对构造方法的参数列表进行遍历 Class[] clazzParams = constructor.getParameterTypes(); for(Class clazzParam : clazzParams ){ sb.append(clazzParam.getName()); } //因为添加到最后一个参数时也会有,所以在添加时需要删除最后一个, if(clazzParams!=null&&clazzParams.length!=0){ sb.deleteCharAt(clazzParams.length-1); } sb.append(")"); System.out.println(sb.toString()); } System.out.println("----------begin Method list--------------"); //利用反射获取clazzProxy1中的非私有方法的数组 Method[] methods=clazzProxy1.getMethods(); //对方法进行遍历 for(Method method : methods){ //创建一个字符串缓冲区 StringBuilder sb=new StringBuilder(); String name=method.getName(); sb.append(name); sb.append("("); //对非私有方法的参数列表进行遍历 //特别注意 在下面方法中 如果方法的没有参数则返回长度为0的数组 在遍历不会报空指针异常 /*小知识点剖析:在数组为零的时候,遍历时会跳过,不会出现空指针异常 * 就好比:for(int i=0;i<arr.length;i++){ System.out.println("dfadfadf"); } 不会被执行 * */ Class[] clazzParams = method.getParameterTypes(); for(Class clazzParam : clazzParams ){ sb.append(clazzParam.getName()); } //因为添加到最后一个参数时也会有,所以在添加时需要删除最后一个, if(clazzParams!=null&&clazzParams.length!=0){ sb.deleteCharAt(clazzParams.length-1); } sb.append(")"); System.out.println(sb.toString()); } //根据class文件创建一个动态类的对象 //-----------------方法一------------- //首先得到生成对象的构造函数,通过构造函数创建对象 class myInvocationHandler implements InvocationHandler{ //复写invoke方法 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub return null; } } Constructor constructor=clazzProxy1.getConstructor(InvocationHandler.class); Collection collection = (Collection)constructor.newInstance(new myInvocationHandler());//Proxy没有不带参数的构造方法所以给其穿个参数,参数类型为InvocationHandler是个借口,自己new一个myInvocationHandlerd对象 /*-----------------方法二------------- * 就是采用匿名内部类的方式 * */ Collection colletion2 = (Collection)constructor.newInstance( new InvocationHandler(){ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub return null; } } ); /* * 总结思考:让jvm创建动态类,需要给它提供哪些信息呢 * 三个方面: * 1.生成的类中有哪些方法通过让其实现哪些接口的方式进行告知 * 2.产生的类字节码文件必须有一个关联的类加载器对象 * 3.生成的类中的方法的代码是怎样的,也是由我们提供。把我们的代码卸载一个约定好了的接口对象的方法中,把对象给它,它调用 * 我的方法即相当于插入了我的代码,停供执行代码的对象就是那个InvocationHandler对象,它是在创建动态类的实例对象的构造方法 * 时传递进去的,在上面的InvocationHandler * */ Collection collection4 = (Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(),new Class[]{Collection.class}, new InvocationHandler(){ //定义一个目标类 ArrayList target=new ArrayList(); @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub Object retVal=method.invoke(target, args); return null; } } ); //调用动态类collection3的add方法 会报空指针异常,调用有返回值类型的方法都会报错 collection4.add("123"); System.out.println(collection4.size()); System.out.println(collection4); Collection collection3 = (Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(),new Class[]{Collection.class}, new InvocationHandler(){ //定义一个目标类 ArrayList target=new ArrayList(); @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub //调用目标类的方法,并传参 Object retVal=method.invoke(target, args); return null; } } ); //调用动态类collection3的add方法 会报空指针异常,调用有返回值类型的方法都会报错 collection4.add("123"); System.out.println(collection4.size()); System.out.println(collection4); /*总结: * 让Jvm创建动态类,需要给它提供哪些信息呢? * 三个方面: * 1.生成的类中有哪些方法,通过让其实现哪些接口的方式告知 * 2.产生的类字节码必须有一个关联的类加载器对象; * 3.生成的类中的方法的代码是怎样的,也得由我们提供,把我们的代码写在一个约定好了接口对象的方法中,把对象传递给它, * 它调 用我的方法,即相当于插入了我们的代码。提供执行代码的对象就是那个InvocationHandler对象的invoke方法中加一点代码, * 就可 以看到这些代码被调用运行了。 * 用Proxy.newInstance方法直接一步就创建出代理对象 * */ /*猜想分析动态生成类的内部代码 * .动态生成的类实现了Collection接口可以实现多个接口,生成的类有Collection接口中的所有方法和一个如下InvocationHanderder的构造方法 * .构造方法接受一个InvocationHandler对象,接受的对象是干什么呢,改方法内部代码是怎样的呢? * .实现InvocationHandler接口的动态类中各个方法的代码是怎样的呢?InvocationHandler接口中定义的三个参数时什么意思 * Client程序调用objProxy.add("abc")方法时,涉及三要素:objProxy对象、add方法、"abc"参数 Class Proxy${ add(Object object){ return handler.invoke(Object proxy,Method method,Object[] args); } } * * */ //下面是用myeclipse完整的一段代码其中有用到公告 final ArrayList target=new ArrayList(); Collection collection5 = getProxy(target,new MyAdvice()); collection5.add("abc"); collection5.add("bcd"); collection5.add("bxd"); System.out.println(collection5.size()); } //利用myeclipse将这两个对象抽成一个方法 private static Collection getProxy(final ArrayList target,final Advice advice) { Collection collection5 = (Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(),new Class[]{Collection.class}, new InvocationHandler(){ //定义一个目标类 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub //调用目标类的方法,并传参 advice.beforeMethod(method); Object retVal=method.invoke(target, args); advice.afterMethod(method); return null; } } ); return collection5; } } 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 is "+(startTime-endTime)); } } interface Advice { public abstract void beforeMethod(Method method); public abstract void afterMethod(Method method); }