//https://www.zhihu.com/question/20794107 //https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513926&idx=1&sn=1c43c5557ba18fed34f3d68bfed6b8bd&chksm=80d67b85b7a1f2930ede2803d6b08925474090f4127eefbb267e647dff11793d380e09f222a8#rd //https://www.cnblogs.com/haodawang/p/5967185.html //https://blog.csdn.net/scplove/article/details/52451899
动态代理的注意点:
1,包:代理接口是public,则代理类被定义在顶层包(package为空),否则(default),代理类被定义在该接口所在包,
2,生成的代理类为public final,不能被继承,
3,类名:格式是“$ProxyN”,N是逐一递增的数字,代表Proxy被第N次动态生成的代理类,要注意,对于同一组接口(接口的排列顺序也相同),不会重复创建动态代理类,而是返回一个先前已经创建并缓存了的代理类对象。提高了效率。
4,类继承关系:
Proxy 类是它的父类,这个规则适用于所有由 Proxy 创建的动态代理类。(也算是java动态代理的一处缺陷,java不支持多继承,所以无法实现对class的动态代理,只能对于Interface的代理)而且该类还实现了其所代理的一组接口,这就是为什么它能够被安全地类型转换到其所代理的某接口的根本原因。
===================================================================
https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513926&idx=1&sn=1c43c5557ba18fed34f3d68bfed6b8bd&chksm=80d67b85b7a1f2930ede2803d6b08925474090f4127eefbb267e647dff11793d380e09f222a8#rd
IO大臣冷汗都出来了, 他睡意全无,赶紧召集家丁幕僚准备上山下乡、明察暗访,限他们两天把这个“以声明的方式编程”搞清楚。
两天内不断有快马回报,各种各样的信息如雪片般飞来。 IO大臣又花了一天时间整理,终于明白了这个“以声明的方式编程”。
原来这帮刁民犯懒,写完了代码以后有这样的需求:
在某些函数调用前后加上日志记录
给某些函数加上事务的支持
给某些函数加上权限控制
......
这些需求挺通用的,如果在每个函数中都实现一遍,那重复代码就太多了。 更要命的是有时候代码是别人写的,你只有class 文件,怎么修改? 怎么加上这些功能?
所以“刁民”们就想了一个损招,他们想在XML文件或者什么地方声明一下, 比如对于添加日志的需求吧, 声明的大意如下:
对于com.coderising这个package下所有以add开头的方法,在执行之前都要调用Logger.startLog()方法, 在执行之后都要调用Logger.endLog()方法。
对于增加事务支持的需求,声明的大意如下:
对于所有以DAO结尾的类,所有的方法执行之前都要调用TransactionManager.begin(),执行之后都要调用TransactionManager.commit(), 如果抛出异常的话调用TransactionManager.rollback()。
他们已经充分发挥了自己的那点儿小聪明,号称是开发了一个叫AOP的东西,能够读取这个XML中的声明, 并且能够找到那些需要插入日志的类和方法, 接下来就需要修改这些方法了。 但是Java帝国不允许修改一个已经被加载或者正在运行的类, 于是他们就不干了,就要抗议、就要游行,就要暴动, 真是可恶。
IO大臣决定向国王做一次汇报,看看国王的反应。
=========================================================================================================
现在他们的问题是要在sayHello()方法中调用Logger.startLog(), Logger.endLog()添加上日志, 但是这个sayHello()方法又不能修改了!”
“所以臣想了想, 可以动态地生成一个新类,让这个类作为HelloWorld的代理去做事情(加上日志功能), 陛下请看,这个HelloWorld代理也实现了IHelloWorld接口。 所以在调用方看来,都是IHelloWorld接口, 并不会意识到其实底层其实已经沧海沧田了。”
陛下天资聪慧,臣拜服,‘刁民’们需要写一个类来告诉我们具体把Logger的代码加到什么地方, 这个类必须实现帝国定义的InvocationHandler接口,该接口中有个叫做invoke的方法就是他们写扩展代码的地方。 比如这个LoggerHandler: ”
“ 看起来有些让朕不舒服,不过朕大概明白了, 无非就是在调用真正的方法之前先调用Logger.startLog(), 在调用之后在调用Logger.end(), 这就是对方法进行拦截了,对不对?”
“正是如此! 其实这个LoggerHandler 充当了一个中间层, 我们自动化生成的类$HelloWorld100会调用它,把sayHello这样的方法调用传递给他 (上图中的method变量),于是sayHello()方法就被添加上了Logger的startLog()和endLog()方法”
“此外,臣想提醒陛下的是,这个Handler不仅仅能作用于IHelloWorld 这个接口和 HelloWorld这个类,陛下请看,那个target 是个Object, 这就意味着任何类的实例都可以, 当然我们会要求这些类必须得实现接口。
======================================================================================================
臣民们使用LoggerHandler的时候是这样的:”
输出:
Start Logging
Hello World
End Logging
“如果想对另外一个接口ICalculator和类Calcualtor做代理, 也可以复用这个LoggerHandler的类:”
“折腾了变天,原来魔法是在Proxy.newProxyInstance(....) 这里,就是动态地生成了一个类嘛, 这个类对臣民们来说是动态生成的, 也是看不到源码的。”
“圣明无过陛下,我就是在运行时,在内存中生成了一个新的类,这个类在调用sayHello() 或者add()方法的时候, 其实调用的是LoggerHanlder的invoke 方法, 而那个invoke就会拦截真正的方法调用,添加日志功能了!