代理类和AOP
客户端不用调用目标对象了,直接调用代理类。最终目标方法还是去实行了。
代理类的每个方法调用目标类的相同方法,并且在调用方法时候加上系统功能的代码
代理和目标实现了相同的接口,有相同的方法。通过接口进行引用
要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方法,非常麻烦。
JVM可以在运行期间动态生成类的字节码,这样动态生成的类往往被用作代理类,即动态代理类。
JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理
CGLIB库,可以动态生成一个类的子类(是一个个开源的库,不是sun公司的),一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库
代理类的各个方法通常除了要调用目标的响应方法和对外放回目标返回的结果外
通过Java 的API
getProxyClass 方法 返回字节码 需要告诉这个字节码实现了哪些接口 另外需要ClassLoader (每个Class都是由类加载器加载来的,就好像每个孩子都有妈妈一样) ,但是由内存出来的是没有经过类加载器哦!所以需要指定一个~!
找个接口测试一下:
public class ProxyTest { public static void main(String[] args) { Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); //找到Collection的接口(就是其字节码) 同时用这个接口相同的类加载器 String name = clazzProxy.getName();//既然是个字节码就可以igetName() System.out.println(name); } }
反正获取到了字节码,就玩字节码的那一套API就OK了,随便用
获取到构造方法:
public class ProxyTest { public static void main(String[] args) { Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); //找到Collection的接口(就是其字节码) 同时用这个接口相同的类加载器 String name = clazzProxy.getName();//既然是个字节码就可以igetName() System.out.println(name); System.out.println("开始打印构造函数相关"); Constructor[] constructors = clazzProxy.getConstructors(); for (Constructor c : constructors){ String constructorName = c.getName(); StringBuilder stringBuffer = new StringBuilder(name); //初始值 stringBuffer.append('('); Class[] parameterTypes = c.getParameterTypes(); for (Class clazzParam : parameterTypes){ stringBuffer.append(clazzParam.getName()).append(','); } if (parameterTypes != null && parameterTypes.length == 0){ stringBuffer.deleteCharAt(stringBuffer.length()-1); //删除掉最后那个字符 } stringBuffer.append(')'); System.out.println(stringBuffer); } } }
打印结果:
代理类:只有一构造方法 参数类型也一目了然,只有一个有参的构造方法。 参数是个新的类型哈,后面会说到他
代理类方法的获取:
public class ProxyTest { public static void main(String[] args) { Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); //找到Collection的接口(就是其字节码) 同时用这个接口相同的类加载器 System.out.println("打印所有方法:");; Method[] methods = clazzProxy.getMethods(); for (Method m : methods){ String methodName = m.getName(); System.out.println("方法名字"+methodName); Class<?>[] parameterTypes = m.getParameterTypes(); for (Class clazzParam : parameterTypes){ System.out.println("方法类型"+clazzParam.getName()); } } } }
截了一部分图:
下面我们创建动态代理类的实例对象,并且使用它的方法:
ublic interface InvocationHandler { /** * Processes a method invocation on a proxy instance and returns
是个接口,没法直接new对象,只能匿名对象。自己实现之
public class ProxyTest { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); //找到Collection的接口(就是其字节码) 同时用这个接口相同的类加载器 //clazzProxy.newInstance(); //搞到他的字节码了已经 但是没有不带参数的构造方法! 所以这么玩儿是不允许的! Constructor constructor = clazzProxy.getConstructor(InvocationHandler.class); //做好有参的构造函数 //内部类 class MyInvocationHandler implements InvocationHandler{ @Override //实现它的方法 里面只有一个方法哦 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; } } //实现的是Collection的接口哦 是Collection类的代理对象 Collection proxy = (Collection)constructor.newInstance(new MyInvocationHandler());//此时传入真实的值 此时我们要看下InvocationHandler 到底是咋回事 System.out.println(proxy); //proxy代理类是拥有Collection类中的所有方法哦 } }
创建了动态类,动态类去实例化对象,动态类的构造方法需要传入一个 InvocationHandler类的参数!
可以通过匿名内部类的方式:
public class ProxyTest { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); //找到Collection的接口(就是其字节码) 同时用这个接口相同的类加载器 //clazzProxy.newInstance(); //搞到他的字节码了已经 但是没有不带参数的构造方法! 所以这么玩儿是不允许的! Constructor constructor = clazzProxy.getConstructor(InvocationHandler.class); //做好有参的构造函数 //实现的是Collection的接口哦 是Collection类的代理对象 Collection proxy = (Collection)constructor.newInstance(new InvocationHandler(){ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; } });//此时传入真实的值 此时我们要看下InvocationHandler 到底是咋回事 System.out.println(proxy); //proxy代理类是拥有Collection类中的所有方法哦 } }
总结:
创建实现了Collection接口的动态类并查看其名称,分析Proxy.getProxyClass方法的各个参数
编写列出动态类汇总的构所有构造方法和参数签名
编写列出动态类中的所有方法和参数签名
创建动态类的实例对象
用反射获得构造方法
编写一个最简单的InvocationHandler类
调用构造方法创建动态类的实例对象,并将别写的InvocationHandler类的实例对象传进去
答应创建的对象和调用对象的没有返回值的方法和getClass方法,演示调用其他有返回值的方法报告了异常
将创建动态类的实例对象的代理改成匿名内部类的像是编写。
让jvm创建动态类及其实例对象,需要提供信息:
三个方面:
生成的类中有哪些方法,通过让其实现哪些接口的方式进行告知
产生的类字节码必须有一个关联的类加载器对象
生成的类中的方法的代码是怎样的,也得由我们提供。把我们的代码写在一个约定好了接口对象的方法中,把对象转给它,它调用我的方法。相当于插入了我的代码。提供执行代码的对象就是那个InvocationHandler对象,它是在创建动态类的实例对象的构造方法时候传递进去的。在上面的InvocationHandler对象的invke方法中加一点代码,又可以看到这些代码被调用执行了
可以把创建动态代理类和实例对象,一步到位: 用Proxy里面的一个方法就OK
用Proxy.newInstance 方法直接一步就创建出代理对象 传入的参数: 类加载器, 接口数组 , handler
public class ProxyTest { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); //找到Collection的接口(就是其字节码) 同时用这个接口相同的类加载器 //clazzProxy.newInstance(); //搞到他的字节码了已经 但是没有不带参数的构造方法! 所以这么玩儿是不允许的! Constructor constructor = clazzProxy.getConstructor(InvocationHandler.class); //做好有参的构造函数 //Collection接口的类加载器 Collection proxy = (Collection) Proxy.newProxyInstance( Collection.class.getClassLoader(), //这个接口的类加载器 new Class[]{Collection.class}, //数组类型的字节码 new InvocationHandler() { //InvocationHandler 匿名内部类 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { ArrayList target = new ArrayList(); System.out.println("返回之前加点代码"); Object returnValue = method.invoke(target, args); System.out.println("返回之后加点代码"); return returnValue; } } ); // 每次调用proxy 的方法, Handler的invoke方法就会被执行一次 都是处理全新的目标 target! proxy.add("toov5"); proxy.add("代理类的方法"); System.out.println(proxy.size()); } }
将target挪出来!~
public class ProxyTest { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); //找到Collection的接口(就是其字节码) 同时用这个接口相同的类加载器 //clazzProxy.newInstance(); //搞到他的字节码了已经 但是没有不带参数的构造方法! 所以这么玩儿是不允许的! Constructor constructor = clazzProxy.getConstructor(InvocationHandler.class); //做好有参的构造函数 //Collection接口的类加载器 Collection proxy = (Collection) Proxy.newProxyInstance( Collection.class.getClassLoader(), //这个接口的类加载器 new Class[]{Collection.class}, //数组类型的字节码 new InvocationHandler() { //InvocationHandler 匿名内部类 ArrayList target = new ArrayList(); @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("返回之前加点代码"); Object returnValue = method.invoke(target, args); System.out.println("返回之后加点代码"); return returnValue; } } ); // 每次调用proxy 的方法, Handler的invoke方法就会被执行一次 都是处理全新的目标 target! proxy.add("toov5"); proxy.add("代理类的方法"); System.out.println(proxy.size()); } }
分析:
InvocationHandler对象的运行原理:
动态申城的类实现了Collection接口(可以实现若干接口),生成的类有Collection接口汇总的所有方法和一个如下接受InvocationHandler参数的构造方法
构造方法接受一个Invocationhandler对象,接受对象了要干什么用呢? 该方法内部的代码会怎样呢?
实现Collection接口中的各个方法的代码又是怎样的呢? InvocationHandler接口中定义的invoke方法接接受的三个参数又是什么意思?
Client程序嗲用objProxy.add("toov5") 方法时,涉及到三个要素: objProxy对象,add方法, “toov5”参数
Class Proxy${
add(Object objcet){
return hanler.invoke(Object proxy, Method, Objcet[] args);
}
}
图示:
分析先打印动态代理的实例对象,结果为什么会是null呢? 调用有基本类型返回值的方法时为什么会空指针异常?
分析为什么动态代理的实例对象的getClass() 方法返回了正确的结果呢?
分析动态代理的工作原理:
怎样将目标类传进去?
直接在InvocationHandler实现类中创建目标类的实例对象。可以看运行效果和加入实质代码,但是没有实际意义
为InvocationHandler实现类注入目标类的实例对象,不能采用匿名内部类的形式了
让匿名的InvocationHandler实现类访外面方法中的目标实现类实例对象final类型的引用变量
功能接口:
public interface Advice { void beforeMethod(Method method); void afterMethod(Method method); }
功能接口实现类:
public class MyAdvice implements Advice{ long beginTime = 0; @Override public void beforeMethod(Method method) { long beginTime = System.currentTimeMillis(); System.out.println(method.getName()+"running start time"+beginTime); } @Override public void afterMethod(Method method) { long endTime = System.currentTimeMillis(); System.out.println(method.getName()+"running end time"+endTime); }
代理及其实现测试:
public class ProxyTest { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); //找到Collection的接口(就是其字节码) 同时用这个接口相同的类加载器 //clazzProxy.newInstance(); //搞到他的字节码了已经 但是没有不带参数的构造方法! 所以这么玩儿是不允许的! Constructor constructor = clazzProxy.getConstructor(InvocationHandler.class); //做好有参的构造函数 //Collection接口的类加载器 final ArrayList target = new ArrayList(); Collection proxy = (Collection)getProxy(target, new MyAdvice()); // 每次调用proxy 的方法, Handler的invoke方法就会被执行一次 都是处理全新的目标 target! proxy.add("toov5"); //把代理穿进去 方法传进去 参数传递进去 } private static Object getProxy( final Object target, final Advice advice){ //目标对象 功能对象 Object proxy = Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { //InvocationHandler 匿名内部类 @Override public Object invoke(Object proxy1, Method method, Object[] args) throws Throwable { long beginTime = System.currentTimeMillis(); advice.beforeMethod(method); //系统功能抽取成为了一个对象 通过接口形式 Object returnValue = method.invoke(target, args); advice.afterMethod(method); //系统功能抽取成为了一个对象 通过接口形式 return returnValue; //这个返回给代理 } } ); return proxy; } }
代理类和AOP的更多相关文章
- Spring AOP(基于代理类的AOP实现)
#基于代理类的AOP实现:step1: 1 package com.sjl.factorybean; /**切面类*/ import org.aopalliance.intercept.MethodI ...
- Spring框架学习07——基于传统代理类的AOP实现
在Spring中默认使用JDK动态代理实现AOP编程,使用org.springframework.aop.framework.ProxyFactoryBean创建代理是Spring AOP 实现的最基 ...
- java代理类及AOP
1.代理架构图 2.AOP 3.动态代理概念 4.动态代理工作原理图
- Spring框架学习08——自动代理方式实现AOP
在传统的基于代理类的AOP实现中,每个代理都是通过ProxyFactoryBean织入切面代理,在实际开发中,非常多的Bean每个都配置ProxyFactoryBean开发维护量巨大.解决方案:自动创 ...
- JAVA基础加强(张孝祥)_类加载器、分析代理类的作用与原理及AOP概念、分析JVM动态生成的类、实现类似Spring的可配置的AOP框架
1.类加载器 ·简要介绍什么是类加载器,和类加载器的作用 ·Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader ...
- Spring《八》AOP/代理类定义
Spring通知 Interception Around通知 MethodInterceptor类(方法执行前后通知) Before通知 MethodBeforeAdvice类(方法执行前通知) Af ...
- Spring AOP 代理类,BeanNameAutoProxyCreator cglib
BeanNameAutoProxyCreator支持拦截接口和类,但不支持已经被jdk代理过的类$Proxy8.使用cglib才能代理,如下 <!-- 通过bean的名字来匹配选择要代理的bea ...
- AOP 代理类的创建
AOP 代理类的创建 入口:AnnotationAwareAspectJAutoProxyCreator#postProcessAfterInitialization 和 AnnotationAwar ...
- Spring Aop中,获取被代理类的工具
在实际应用中,顺着过去就是一个类被代理.反过来,可能需要逆向进行,拿到被代理的类,实际工作中碰到了,就拿出来分享下. /** * 获取被代理类的Object * @author Monkey */ p ...
随机推荐
- 原生JavaScript和jQuery的较量
JavaScript和jQuery有很多相似知促,那么二者又是如何进行较量,我们先了解一下什么是JavaScript和jQuery,知其源头,才能知其所以然. 简介: [JavaScript] 一种直 ...
- (java)selenium webdriver学习--打开新窗口,并判断新窗口是否与目标窗口一致
描述:selenium webdriver学习--打开新窗口,并判断新窗口是否与目标窗口一致,若一致则切换到该窗口并获取标题 跳出if判断,获取父级标题,并关闭 HTML标签不太明显时,可以用路径表示 ...
- 置换及Pólya定理
听大佬们说了这么久Pólya定理,终于有时间把这个定理学习一下了. 置换(permutation)简单来说就是一个(全)排列,比如 \(1,2,3,4\) 的一个置换为 \(3,1,2,4\).一般地 ...
- go协程的特点
go奉行通过通信来共享内存,不像c和c++通过共享内存来通信 协程是轻量级的线程,编译器做优化** 有独立的栈空间 共享程序堆空间 调度由用户控制 协程是轻量级的线程 并行:多个cpu共同执行 并发 ...
- (转载) 从0开始搭建SQL Server AlwaysOn 第三篇(配置AlwaysOn)
这一篇是从0开始搭建SQL Server AlwaysOn 的第三篇,这一篇才真正开始搭建AlwaysOn,前两篇是为搭建AlwaysOn 做准备的 步骤 这一篇依然使用step by step的方式 ...
- 动手动脑-------找出指定文件夹下所有扩展名为.txt和.java的文件
思路:首先向获取文件,如果是文件的话,则判断它是否以".txt"或".java"结尾,如果是则输出它的路径.如果是文件夹的话,则需获取子文件,利用递归方法遍历子 ...
- 洛谷 P1456Monkey King
题目描述 要把打架的两堆猴子合并为一堆,查询的又是最大值,所以很容易想到可并堆. 题目要求打完架后战斗力最大的猴子的战斗力要减半,但不能直接在堆中进行这个操作,因为战斗力减半后这只猴子不一定是战斗力最 ...
- 各种trick和细节错误汇总
这篇博客主要是用来记自己写代码的时候犯的各种小技巧和低级失误,好提醒自己,从而尽量缩短debug时间. 点分治 1.求每一个子树到重心的距离的函数接口应该是dfs2(v, eg, e[i].w)而不是 ...
- Node.js 自学之旅(初稿篇)
学习基础,JQuery 原生JS有一定基础,有自己一定技术认知(ps:原型链依然迷糊中.闭包6不起来!哎!) 当然最好有语言基础,C#,java,PHP等等.. 最初学习这个东西的原因很简单,在园子里 ...
- Pytest权威教程-更改标准(Python)测试发现
目录 更改标准(Python)测试发现 在测试收集过程中忽略路径 测试期间收集的测试取消 保留从命令行指定的重复路径 更改目录递归 更改命名约定 将cmdline参数解释为Python包 找出收集的东 ...