概述
-------android培训、java培训、期待与您交流! ----------
内容源自 张孝祥老师的Java高新技术.
以下是我看视频的笔记:
---------------------------------------------------------------------------
这些笔记对应张孝祥老师的Java高新技术的如下视频:
50.创建动态类及查看其方法列表信息
51.创建动态类的实例对象及调用其方法
52.完成InvocationHandler对象的内部功能
53.分析InvocationHandler对象的运行原理
54.总结分析动态代理类的设计原理与结构
55.编写可生成代理和插入通告的通用方法
56.实现类似spring的可配置的AOP框架
-------------------------------------------------------
49.分析代理类的作用与原理及AOP概念
比如:
要为已存在的多个具有相同接口的目标类的各个方法
增加一些系统功能:
如: 异常处理, 日志, 计算方法的运行时间, 事务管理, 等等.
如何处理这种要求呢?
使用代理机制来处理这种问题.
如下
class X{
void sayHello(){syso:helloworld;}
}
代理类:
class XProxy{
void sayHello(){
starttime
X.sayHello() //这里调用X的方法
endtime
}
}
即,
做一个X的代理XProxy.
即, 其中的方法和X一样.
在X代理XProxy的方法里面, 调用X的方法.
即, 在调用原来的方法的前后可以加上一些功能.
然后, 客户端, 调用的是代理.
代理和目标类 有着相同的 接口.
即, 我们会把是先把目标类中的要用上代理的方法做成一个接口.
然后, 代理只需要去实现这个接口就可以了.
然后, 在调用时, 客户端实际上是就是调用代理的这个接口的方法.
我们采用工厂模式 和 配置文件 的方式进行管理,
则不需要修改客户端程序.
在配置文件中配置 是使用普通类, 还是使用代理类.
这样以后很容易切换.
如:
想要日志功能时, 就配置代理类,
否则, 就配置为目标类.
这样, 增加系统功能很容易.
在运行一段时间后, 想去掉系统功能也很容易.
AOP(aspect oriented program), 面向方面的编程.
系统中存在交叉业务
如多个类, 各处理各的事,
但不管处理什么事, 都要有 安全, 事务, 日志 等功能.
即, 安全, 事务, 日志 等功能, 要贯穿在好多模块里面.
即, 安全, 事务, 日志就是这些类的就交叉业务.
安全 事务 日志
StudentService -----|------|------|-------
CourseService -----|------|------|-------
MiscService -----|------|------|-------
用具体的程序代码描述交叉业务:
method1 method2 method3
{ { {
------------------------------------切面
... ... ...
------------------------------------切面
} } }
AOP的目标就是要使交叉业务模块化,
可以采用将切面代码移动到原始方法的周围,
这与直接在方法中编写切面代码的运行效果是一样的.
如下所示:
------------------------------------切面
func1 func2 func3
{ { {
... ... ...
} } }
------------------------------------切面
使用代理技术正好可以解决这种问题,
代理是实现AOP功能的核心和关键技术.
只要涉及AOP, 就会涉及到代理技术.
要为系统中的各种接口的类, 增加代理功能,
那将需要太多的代理类,
全部采用静态代理方式, 将是一件非常麻烦的事情.
JVM可以在运行期动态生成出类的字节码,
这种动态生成的类 往往被用作代理类, 即动态代理类.
注意:
动态生成的类 不一定是代理类.
只是多用来生成代理类而已.
JVM生成的动态类, 必须实现 一个/多个接口.
所以, JVM生成的动态类
只能用作 具有相同接口 的目标类 的代理.
如果目标类没有接口的话, 怎么办?
有一个第三方的开源类库:CGLIB
其可以动态生成一个类的子类,
一个类的子类也可以用作该类的代理
----这个就是项妙的地方了.
---- 在子类中重写父类的方法,
并在重写的方法中调用父类的方法.
代理类中的方法, 所做的功能可以添加的位置有:
1 在调用目标方法 之前
2 在调用目标方法 之后
3 在调用目标方法 之前 和 之后 都加
4 在调用目标方法 异常的catch块中
-------------------------------------------------------
50.创建动态类及查看其方法列表信息
我们现在, 用JVM的接口来生成动态代理类
----要求目标类要用接口.
Proxy
全称: java.lang.reflect.Proxy
----即, 其用的是反射的机制.
里面全是 static方法
----即, 这是一个工具类.
其有一个方法:
Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
即, 动态得到一个代理类.
ClassLoader loader: 指定类加载器来加载这个所生成的代理类.
Class<?>... interfaces: 指定这个代理类所实现的接口的Class对象.
如:
我们要生成一个动态代理类来实现Collection接口,
其所用的类加载器, 通常是Collection类的加载器.
-----即, 我们一般是这样做的.
应用:
public static void main(String[] args) {
// 生成实现了Collection接口的动态代理类
// 并打印其构造方法
// 打印其所有方法
//1 生成一个动态代理类
Class<?> clazzProxy =
Proxy.getProxyClass(Collection.class.getClassLoader(),
Collection.class);
//2 获取这个动态代理类的名称: com.sun.proxy.$Proxy0
System.out.println(clazzProxy.getName());
//3 获取这个动态代理类的构造方法, 并打印出来
//com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)
Constructor[] constructors = clazzProxy.getConstructors();
System.out.println("---------------构造方法-------------");
for (Constructor constructor : constructors){
StringBuilder str = new StringBuilder();
str.append(constructor.getName());
str.append("(");
Class[] clazzParas = constructor.getParameterTypes();
for (Class clazzPara : clazzParas){
str.append(clazzPara.getName() + ",");
}
//注意, 这一步是必须的, 因为我们不能保证构造方法有参数
//注意, 下面这两个条件, 我只是为了保险起见才这么做的.
if (clazzParas!=null && clazzParas.length!=0){
str.deleteCharAt(str.length()-1);
}
str.append(")");
System.out.println(str);
}
//4 获取这个动态代理类的方法, 并打印出来
Method[] methods = clazzProxy.getMethods();
System.out.println("---------------一般方法-------------");
for (Method method : methods){
StringBuilder str = new StringBuilder();
str.append(method.getName());
str.append("(");
Class[] clazzParas = method.getParameterTypes();
for (Class clazzPara : clazzParas){
str.append(clazzPara.getName() + ",");
}
//注意, 这一步是必须的, 因为我们不能保证构造方法有参数
//注意, 下面这两个条件, 我只是为了保险起见才这么做的.
if (clazzParas!=null && clazzParas.length!=0){
str.deleteCharAt(str.length()-1);
}
str.append(")");
str.append(":" + method.getReturnType().getName());
System.out.println(str);
}
}
得到结果:
com.sun.proxy.$Proxy0 -----动态代理类放在com.sun.proxy包中---名称带$表示复合类, 0表示序号
---------------构造方法-------------
com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)
---------------一般方法-------------
add(java.lang.Object):boolean
remove(java.lang.Object):boolean
equals(java.lang.Object):boolean ------- Collection里已有
toString():java.lang.String
hashCode():int ------- Collection里已有
clear():void
contains(java.lang.Object):boolean
isEmpty():boolean
iterator():java.util.Iterator
size():int
toArray([Ljava.lang.Object;):[Ljava.lang.Object;
toArray():[Ljava.lang.Object;
spliterator():java.util.Spliterator
addAll(java.util.Collection):boolean
stream():java.util.stream.Stream
forEach(java.util.function.Consumer):void
containsAll(java.util.Collection):boolean
removeAll(java.util.Collection):boolean
removeIf(java.util.function.Predicate):boolean
retainAll(java.util.Collection):boolean
parallelStream():java.util.stream.Stream ------- 代理类自身的
isProxyClass(java.lang.Class):boolean ------- 代理类自身的
getInvocationHandler(java.lang.Object):java.lang.reflect.InvocationHandler ------- 代理类自身的
newProxyInstance(java.lang.ClassLoader,Ljava.lang.Class;,java.lang.reflect.InvocationHandler):java.lang.Object ------- 代理类自身的
getProxyClass(java.lang.ClassLoader,[Ljava.lang.Class;):java.lang.Class ------- 代理类自身的
wait():void
wait(long,int):void
wait(long):void
getClass():java.lang.Class
notify():void
notifyAll():void
一个是:
我们看到生成的动态代理类的名称为:
com.sun.proxy.$Proxy0 ---而Proxy是放在java.lang.reflect包中的.
-----动态代理类放在com.sun.proxy包中
-----名称带$表示复合类,
-----Proxy 表示这是一个代理类
-----0表示序号
一个是:
我们在命名一个字节码变量时, 常常用 clazz来命名.
这是一个习惯.
一个是:
其构造方法:
com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)
只有一个.
参数为: InvocationHandler类型.
后面会说到.
一个是:
这个类中的方法, 不仅仅有Collection的方法,
还有一些代理类自己的方法:
parallelStream():java.util.stream.Stream
isProxyClass(Class):boolean
getInvocationHandler(Object):InvocationHandler
newProxyInstance(ClassLoader,[Ljava.lang.Class;,InvocationHandler):Object
getProxyClass(ClassLoader,[Ljava.lang.Class;):Class
其中 [Ljava.lang.Class 相当于 Class[].
一个是:
InvocationHandler 是一个接口, 其只有一个方法:
public Object invoke(Object proxy, //动态代理类对象
//invoke内部可能会用到动态代理类对象
Method method, //所调用的方法的Method对象
Object[] args) //方法的参数
-----在这个方法里, 我们可 实现这些系统的功能.
具体的内容下面就要讲到了.
-------------------------------------------------------
51.创建动态类的实例对象及调用其方法
通过上面, 我们仅仅得到了动态代理类的Class对象.
现在,
我们利用 动态代理类 来创建其实例对象.
如何来创建 动态代理类的对象呢?
当然 只有两个方法:
一种: Class::newInstance()
---用无参构造方法
---但我们知道, 动态代理类没有无参构造方法.
---所以不能采用这种方法.
一种: $Proxy0 的构造方法.
然后调用它来创建对象.
---下面我们说的就是这一种.
后面还会介绍一种:
Proxy.newProxyInstance(ClassLoader, Class<?>[], InvocationHandler):Object ---后面基本只用这种
这是一个 static 方法.
此方法是综合以下两个步骤:
Proxy.getProxyClass(ClassLoader, Class<?>... interfaces):Class<?>
和
$Proxy0(InvocationHandler): Object
动态代理类的构造方法只有一种:
$Proxy0(InvocationHandler)
即, 要传向一个 InvocationHandler 对象.
但这个是一个 接口, 不能 new InvocationHandler()!
所以, 我们常常是用匿名类的方法来实现:
Collection<?> col = (Collection<?>) constructor.newInstance(new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
}
});
完整代码如下:
//创建 动态代理类的 实例对象
try {
//1 获得构造方法
// 这个clazzProxy就是刚才我们上面:
// 用Proxy.getProxyClass(_,_)得到的动态代理类Class对象
Constructor constructor = clazzProxy.getConstructor(InvocationHandler.class);
//2 调用这个构造方法
// 一个是: 获得的对象类型, 我们要用那个接口Collection来接收.
// 一个是: 这里有类型强制转换
// 一个是: 现在暂时将invoke方法返回null, 即没有实现其中功能.
Collection<?> col = (Collection<?>) constructor.newInstance(new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
}
});
//3 使用这个 动态代理类对象col
System.out.println(col); //null
System.out.println(col.toString()); //null
System.out.println(col.size()); //异常 java.lang.NullPointerException
} catch (Exception e){
e.printStackTrace();
}
注意:
System.out.println(col); //null
得到null, 并不代表着col就为null, 有可能其toString为null.
System.out.println(col.toString()); //null
我们看到, 确实是toString为"null".
如果是 col为null, 其会指空指针异常
System.out.println(col.size()); //异常 java.lang.NullPointerException
这是因为, 这是一个接口啊, 这个Collection没有指向任何实际集合啊.
所以, 不能得到集合的大小, 所以报异常.
-------------------------------------------------------
52.完成InvocationHandler对象的内部功能
总结下:
我们上面要创建一个动态代理类, 需要三个信息:
一个是: 该代理类的加载器 ----我们要指定
一个是: 该代理类实现的接口 ----我们要指定, 如Collection接口
一个是: 用该代理类创建实例对象---我们要指定InvocationHandler对象
---在这里, 我们可以做一些系统的功能.
上面, 将这三个信息, 分成几个部分来完成.
而实际上,
Proxy中有一个static方法newProxyInstance可以一步到位:
Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
其参数就是我们上面所说的那三个:
ClassLoader loader: 该代理类的加载器 ----我们要指定
Class<?>[] interfaces: 该代理类实现的接口 ----我们要指定, 如Collection接口
InvocationHandler h: 用该代理类创建实例对象---我们要指定InvocationHandler对象
---在这里, 我们可以做一些系统的功能.
因为实现的接口可能不止一个, 所以是一个数组.
因为最后要还传入一个InvocationHandler对象,
所以, 那么得用数组, 而不能用可变参数.
现在, 我们用这个方式来写一个:
public static void main(String[] args) {
//注意, 这里将类型改为final
final ArrayList<String> list = new ArrayList<String>();
//创建 动态代理类的实例----其已经和这个list对象挂钩了
Collection<String> proxy =
(Collection<String>) Proxy.newProxyInstance(Collection.class.getClassLoader(),
new Class[]{Collection.class},
new InvocationHandler(){
//目录标对象记录到这里来
private Object target = list; //要求这个list是final的.
//这个功能, 是用于计算目标类方法的运行时间
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()+"运行时间为:"+(endTime-startTime));
return retVal;
}
}
);
//调用动态代理类对象方法 来 调用list的方法
proxy.add("hello");
proxy.add("world");
proxy.add("nihao");
System.out.println(proxy.size());;
}
这里几点注意处:
一个是:
InvocationHandler对象, 同样也是用匿名内部类实现的, 如下:
new InvocationHandler(){
//目录标对象记录到这里来
private Object target = list; //要求这个list是final的.
//这个功能, 是用于计算目标类方法的运行时间
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()+"运行时间为:"+(endTime-startTime));
return retVal;
}
}
其中, 我们得设一个域: private Object target = list; //要求这个list是final的.
用于记录 这个动态代理类对象 所要 代理的 那个目标对象 是哪一个.
因为, 我们设立 代理的目标就是, 对目标类进行AOP编程的.
代理类对象, 要和 目标类对象 一一对应.
注意, 这里, 我们是直接将list传给这个target数据域的.
因为匿名内部类不能有构造方法.
而且, 刚好在匿名内部类内部是可以直接访问其外的方法的局部变量的.
所以就这么做了.
一个是:
见下面讲解 动态代理类 的原理部分吧.
-------------------------------------------------------
53.分析InvocationHandler对象的运行原理
JVM所自动生成的动态代理类的内部实质是:
class $Proxy0{
//一个数据域
private InvocationHandler handler;
//构造方法
public $Proxy0(InvocationHandler handler)
{
this.handler = handler;
}
//一般方法:
int size(){
return handler.invoke(this, //动态代理类对象
this.getClass().getMethods("size"), //size这个Method对象
null); //参数
}
...
}
注意:
一个是:
内部一个数据域:
private InvocationHandler handler;
用于记录, 这个代理类对象对应的 InvocationHandler 是哪一个.
这个 数据域 , 我们得在构造方法中对它进行赋值.
一个是:
我们调用:
proxy.add("haha");
转为三个要素:
一个是 col---动态代理类的对象
一个是 add ---所调用的方法
一个是 "haha" ---参数.
所以,
InvocationHandler的invoke方法的参数, 也对应这三个.
handler.invoke(proxy, //动态代理类对象
this.getClass().getMethods("add"), //size这个Method对象
"haha"); //参数
还把这个结果返回.
proxy.size()的机制也一样.
一个是:
如果 我们在invoke内部这么调用:
return method.invoke(proxy, args);
结果呢, 会是一个死循环.
要注意:
动态代理类 和 目标类, 都实现了 共同的接口.
所以, 如果 method.invoke(proxy, args)
那么, 就是代理类对象不断调用自身了, 所以会死循环.
一个是:
在invoke方法中,
我们可以加之前, 之后, 前后, 异常处理,
我们也可以对这个参数args进行修改.
我们 在invoke中, 可以用到代理类对象,
为什么 要传入这个代理类对象呢?
因为 我们在invoke中, 会进行一些操作,
可能会用到动态代理类对象
所以, 我们invoke的参数中, 保留了Object proxy, 以备用.
而, 其内部, 必然会 用到 目标类对象.
而目标类对象, 我们通常是设一个数据域来处理.
一个是:
在我们最上面的例子中:
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
}
invoke直接返回null.
也正是这个原因, 导致当时调用 col.size()会抛出异常.
因为 size应返回int型的,
而它, 却返回了null了.
所以 抛异常.
如果col有指向具体的集合的话, size会返回0,1,等对应的包装器对象,
所以不会返回null的, 所以不会异常的的.
一个是:
我们调用 动态代理类对象的 proxy.getClass(),
我们会返回$Proxy0啊,
而不是 目标的 Class对象: java.util.Arraylist.
为什么呢?
因为 动态代理类 只对 其所实现的接口的那些方法去调用invoke方法
而对于Object的几个方法,
只有 hashCode, equals, toString才派发给handler去invoke目标类方法去处理,
其它的 方法, 如getClass, wait之类, 保留动态代理类自身的内容的.
即, 不会去调用invoke实用目标的方法的.
比如, 上面我们调用col.toString, 打印的null ,
即, 对toString, 其会去目标那里去实现.
-----注意一点: Object 不是 接口.
那为什么会对 toString去调用invoke呢?----我已查证, Collection中没有toString.
总结下, 动态代理类的机制:
像GUI中的注册监听器一样,
当外界有 事件发生时, 两件事:
控制内部会得到一个事件对象,
触发注册到控制内部的事件监听器.
然后, 事件监听器调用自己内部的方法来完成对事件的影应.
----不过, 这个说法勉强.
因为GUI的事件监听器, 其中不需要我们直接传一个target进去的.
而是, 而是通过它的响应方法的参数来传进去的.
----而这个代理类的 InvoketionHandler的target对象, 需要我们手动传进去. 麻烦.
动态代理类是这样的:
proxy调用接口方法时,
实际上是 注册到自身的handler上的invoke来响应.
在handler中, 其包含的内容有:
一个是: 目标对象要用---否则怎么调得动方法呢
一个是: invoke方法要有.
总之:
最终是要调用 InvocationHandler对象的invoke方法的.
所以 invoke要知道几个要素:
一个是 target, 即, 要知道其调用的是哪一个目标类
一个是 proxy, 即, 要知道其代理类对象是哪一个.
invoke内部可能会用到动态代理类对象--如取一些信息之类
一个是 method, 得知道自己要调接口的哪一个方法
一个是 agrs, 得知道要往这个方法传哪些参数.
下面三个元素, 以invoke的参数形式传进去,
而第一个元素, 通常是以 InvocationHandler的数据成员形式存在.
-------------------------------------------------------
54.总结分析动态代理类的设计原理与结构
55.编写可生成代理和插入通告的通用方法
其工作原理图为:
中间那个圈 要注意.
我们上面是将这个圈的代码直接写死了.---这叫硬编码.
这样的话, 这个代理类只能处理这种 代码.
不灵活.
所以, 我们可以把这个圈里的东西封装成一个方法.
----提高了代码的复用性.
即, 下面, 我们就要 让这种系统功能的代码 以参数的形式传给它.
通常, 我们是传一个对象给 InvocationHandler中的一个数据域.
然后 invoke方法中, 去调用 这个数据域的方法就可以了.
通常, 我们会把这个东西设为一个接口/抽象类.
名称称为Advice
在这个接口中, 设几个方法:
前面执行的操作,
后面执行的操作,
异常处理中执行的操作.
其实有多个方法的话, 那么改为抽象类更好一些.
如下:
public abstract class Advice { //视频是将其做interface处理.
//这个会不会有影响? ---至少, 在我这里, 代码是可以运行的.
//这个是不是抽象类没关系, 这只是AOP所执行的方法而已.
//而代理类那些则必须是接口!
public void front(Object proxy, Method method, Object[] args){}
public void behind(Object proxy, Method method, Object[] args){}
public void exceptionProcess(Object proxy, Method method, Object[] args, Throwable exception){}
}
为什么这里的方法参数要这些?
因为, 在我们要添加的系统功能的这些方法, 可能会用到这些信息.---当然, 可以不用, 需要时再写.
因为, 我们这里要做一个框架, 即要有广泛的适应性, 所以建议把invoke方法的参数都写上去.
即, 要在 InvocationHandler中设置两个数据域.
一个是 Object target; 域, 以放目标对象.
一个是 Advice advice; 域, 以放一些处理的方法.
而invoke应当写成这样:
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object retValue = null;
try{
advice.front(proxy, method, args);
retValue = method.invoke(target, args);
advice.behind(proxy, method, args);
} catch (Exception e){
advice.exceptionProcess(proxy, method, args, e);
}
return retValue;
}
即, 也要用一些异常处理的操作.---这个纯粹是我写的.
好了, 看下我的代码:
先写一个MyAdvice:
----即, 我们使用代理的最关键部分---AOP的核心.
public class MyAdvice extends Advice{
private long startTime;
private long endTime;
@Override
public void front(Object proxy, Method method, Object[] args) {
startTime = System.currentTimeMillis();
System.out.println("开始执行"+method.getName());
}
@Override
public void behind(Object proxy, Method method, Object[] args) {
endTime = System.currentTimeMillis();
System.out.println("执行完成"+method.getName()+", 执行时间:"+(endTime-startTime)+"毫秒.");
}
@Override
public void exceptionProcess(Object proxy, Method method, Object[] args, Throwable exception) {
//还没有想好要怎么处理它
}
}
--------当然, 我们也可以用 匿名内部类的形式来写这一个类.
呵呵.
然后是写一个 获得动态代理对象的方法:
------这是我们这一单元的核心部分了.
------这个代码是可以重用的.
------我把很对类型都改为Object类型了.---这样, 可以目标对象的类型可以随意.
我们可以看到:
参数两个:
一个是 目标;----要在谁的方法这前做
一个是 建议;----即要做什么事
public static Object getProxy(final Object obj, final Advice adv) {
return Proxy.newProxyInstance(
obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(), //获取其所有接口.
new InvocationHandler(){
//目录标对象记录到这里来
private Object target = obj;
//记录这个Advice对象
private Advice advice = adv;
//实现invoke方法
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object retVal = null;
try{
advice.beforeMethod(method);
retVal = method.invoke(target, args);
advice.afterMethod(method);
} catch (Throwable t){
advice.exceptionMethod(method, t);
}
return retVal;
}
}
);
}
然后是, 进行一个调用:
public static void main(String[] args) {
//注意, 这里将类型改为final
final ArrayList<String> list = new ArrayList<String>();
//创建 动态代理类的实例----其已经和这个list对象挂钩了
Collection<String> proxy =
(Collection<String>) getProxy(list, new MyAdvice());
//调用动态代理类对象方法 来 调用list的方法
proxy.add("hello");
proxy.add("world");
proxy.add("nihao");
System.out.println(proxy.size());;
}
以后写Spring, 其实就是写MyAdvice.
即, 我们传入目标, 还有这个Advice对象 就行了.
这就是一个小小的框架.
即, 给你目标, 给你系统功能, 你得给我一个代理.
-------------------------------------------------------
56.实现类似spring的可配置的AOP框架
如果, 我们要做一个类似于Spring框的架的AOP,
其大概的实现思路是怎么样的?
Object obj = BeanFacktory.getBean("XXX");
"XXX"---是指Bean的名字, 如java.util.Array
这个"XXX"是由一个配置文件来提供的.
这个BeanFacktory.getBean(__)内部怎么写?
两种可能:
一种是 如果是 cn.itcast.ProxyFactoryBean这个特殊的类的话,
那么 调用其方法来创建代理.
一种是: 不是这种特殊的类.
则用 BeanFacktory.getBean(__)来创建Bean对象.
比如配置文件如下:
#xxx=java.util.ArrayList
xxx=cn.itcast.ProxyFactoryBean
xxx.target=java.util.ArrayList
xxx.advic=cn.itcast.MyAdvice
如果是 ArrayList的话,
BeanFacktory.getBean("xxx")返回ArrayList的javabean对象
如果是 ProxyFactoryBean的话,
那么做调用 ProxyFactoryBean
则 还会取下其target和advice是谁,
然后生成 一个其对应的动态代理类对象 返回.
那么:
obj就是创建出的javaBean对象.
或者是 一个代理.
BeanFactory构造方法 接受一个配置文件.
即有一个数据域: Properties型的数据域.
对javabean而言, 一个重要的特性, 其必须有一个不带参数的构造方法.
public class BeanFactory {
//因为BeanFactory是根据配置文件来产生javabean的,
//所以要有一个域来保留这个配置文件的信息
private Properties properties;
//构造方法
public BeanFactory(InputStream inputStream){
properties = new Properties();
try {
properties.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//非静态方法---因为每一个配置文件对应一个对象
public Object getBean(){
String clazzName = properties.getProperty("className");
Class<?> clazz = null;
Object obj = null;
try {
clazz = Class.forName(clazzName);
obj = clazz.newInstance();
if (obj instanceof ProxyBeanFactory){
Advice advice = (Advice)(Class.forName(properties.getProperty("advice")).newInstance());
Object target = Class.forName(properties.getProperty("target")).newInstance();
ProxyBeanFactory proxyFactory = (ProxyBeanFactory)obj;
proxyFactory.setAdvice(advice);
proxyFactory.setTarget(target);
obj = proxyFactory.getProxy();
return obj;
}
return obj;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
public static void main(String[] args) throws Exception{
InputStream config =
BeanFactory.class.getResourceAsStream("config.properties");
BeanFactory beanFactory = new BeanFactory(config);
config.close(); //用完, 就要关掉.
Object obj = beanFactory.getBean();
System.out.println(obj.getClass());
System.out.println(obj);
}
}
public class ProxyBeanFactory {
private Advice advice;
private Object target;
public Advice getAdvice() {
return advice;
}
public void setAdvice(Advice advice) {
this.advice = advice;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
//与之前写的那个getProxy不一样了
//这里没有参数, 而是把advice和target做为数据成员处理.
public Object getProxy() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler(){
//目录标对象记录到这里来
private Object target = ProxyBeanFactory.this.target;
//记录这个Advice对象
private Advice advice = ProxyBeanFactory.this.advice;
//实现invoke方法
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object retVal = null;
try{
advice.beforeMethod(method);
retVal = method.invoke(target, args);
advice.afterMethod(method);
} catch (Throwable t){
advice.exceptionMethod(method, t);
}
return retVal;
}
}
);
}
}
这个就是 Spring的BeanFactory的原理了.
这上面讲了 Spring 的 两个框架:
一个是 getProxy 方法
一个是 BeanFactory和ProxyBeanFactory两个框架了.
道理其实就是这么简单.
最后
以上就是认真硬币为你收集整理的黑马程序员-----Java代理机制的学习笔记的全部内容,希望文章能够帮你解决黑马程序员-----Java代理机制的学习笔记所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复