Spring之二,基础深入,理解动态代理

来源:互联网 发布:自制相册软件下载 编辑:程序博客网 时间:2024/06/05 15:09
代理分静态代理和动态代理,为了能理解动态代理,首先我们来实现一套静态代理试试水深。

首先我们来简单聊聊代理,为什么要使用代理。当一个对象,在它被使用的地方,不方便获取一个目标对象或者在调用目标对象的时候,在之前或者之后希望可以执行一些代码,就要使用代理。

如何使用代理呢,代理又是如何在执行真正的方法之前执行一些业务逻辑的呢,我们先来实现一个静态代理。

首先定义一个接口
public interfaceABigService{
publicStringdoSomething(Stringa);
}
然后国际惯例将接口实现
public classABigServiceImplimplementsABigService {
publicStringdoSomething(Stringa){
return"被代理对象接收到了参数a,执行,并返回了结果:"+a;
}
}
这里写了一些奇怪的返回值,先不需要管,我们先来看如何实现一个代理。
在正常情况下我们调用这个服务时候应该是:
ABigService a=new ABigServiceImpl();

那么在使用代理时候呢?
首先我们要新建一个代理。顾名思义一个代理是代这个对象管理它。
public classNomlProxyimplements ABigService{
LoggerLOG=LoggerFactory.getLogger(NomlProxy.class);
privateABigServiceaBigService;
publicNomlProxy(ABigServiceaBigService){
this.aBigService=aBigService;
}

publicStringdoSomething(Stringa) {
LOG.info("在最终的方法执行之前");

LOG.info("接到了参数:"+a+"并且开始执行被代理对象的方法");
String result=aBigService.doSomething(a);

LOG.info("在最终的方法执行之后");
returnresult;
}
}

可以看到,代理对象中的一些元素,首先代理对象要包含一个目标对象的引用,他们是什么关系呢?
我们知道java对象之间的关系有两种,互补干涉的一种包含关系是聚合,依赖对方组成一个整体的关系叫做依赖。
很明显代理和其被代理对象在当前这种模式下是聚合关系,事实上使用代理类来直接new出目标对象也是可以的,但是使用这种方式有另一个好处,我们来看,现在我们要使用静态工厂模式,来创建一个代理工厂。
public classNomlProxyFactory{
public staticABigServicegetInstance(){
return newNomlProxy(newABigServiceImpl());
}
}

这样,我们创建了一个代理工厂,可以通过代理工厂来对目标对象进行初始化。那么这样有什么好处呢?

我们来改动一些代码,让大家明白。
public classNomlProxyFactory{
public staticABigServicegetInstance(inttype){
ABigServiceaIntance;
if(type==1){
aIntance=newABigServiceImpl();
}else if(type==2){
return newABigService() {
publicStringdoSomething(Stringa) {
return"第二个扩展类型";
}
};
}else{
return newABigService() {
publicStringdoSomething(Stringa) {
return"第三个扩展类型";
}
};
}
return newNomlProxy(aIntance);
}
}

当aBigService有多个扩展时候或者有多个实现的时候,我们就能够不改动代理类的情况下来直接使用静态工厂扩展功能了。


现在静态代理已经建好了,我们来运行;
public static voidmain(String[]args){
finalLogger LOG=LoggerFactory.getLogger(ServiceApp.class);
LOG.info("test start");
ABigService aBigServiceProxy=NomlProxyFactory.getInstance();
String result=aBigServiceProxy.doSomething("一个参数a");
LOG.info(result);

}
执行结果:
2017-06-08 21:10:12 [INFO ] testspr.proxy.NomlProxy.doSomething(NomlProxy.java:16) : 在最终的方法执行之前
2017-06-08 21:10:12 [INFO ] testspr.proxy.NomlProxy.doSomething(NomlProxy.java:18) : 接到了参数:一个参数a并且开始执行被代理对象的方法
2017-06-08 21:10:12 [INFO ] testspr.proxy.NomlProxy.doSomething(NomlProxy.java:21) : 在最终的方法执行之后
2017-06-08 21:10:12 [INFO ] testspr.ServiceApp.main(ServiceApp.java:23) : 被代理对象接收到了参数a,执行,并返回了结果:一个参数a

这就是代理类如何运行的,那么总结起来是有一些问题的
第一,每当我有一个新的接口需要代理时候,我将需要一个新的代理类,并且代理工厂也要同时做一些修正。
第二,每次修改会导致我另一个代理内的逻辑可能要进行重写才行。

这不优雅。

所以我们需要动态代理,废话。
ABigService a=newABigServiceImpl();

首先原有的对象是不能少的。代码不贴了,自己脑补下吧。
接下来重要的一步,我们要实现这样一套程序

public classBigInvocationHandlerimplementsInvocationHandler{

privateObjectaBigService;
publicBigInvocationHandler(ObjectaBigService){
this.aBigService=aBigService;
}

publicObjectinvoke(Objectobject,Methodmethod,Object[]args)throwsThrowable {
returnmethod.invoke(aBigService,args);
}
}
程序中第一部分是包含一个bigService的引用,在构造方法中实际是为其赋值。
在第三个方法中是实现了InvocationHandler的一个接口,这样看来这个接口就不得不提了。

InvocationHandler是Java中定义的接口,他规范了Java的代理在调用的时候所进行的动作,其三个参数分别是当前的对象,当前调用的method,和当前这个method所需要的参数列表。

事实上这样我们所要写的代码已经全部完成了。
下面看如何调用。先不要问为什么!
public static voidmain(String[]args){
finalLogger LOG=LoggerFactory.getLogger(ServiceApp.class);
LOG.info("test start");
// ABigService aBigServiceProxy= NomlProxyFactory.getInstance();
// String result=aBigServiceProxy.doSomething("一个参数a");
// LOG.info(result);

ABigService a=newABigServiceImpl();
java.lang.reflect.InvocationHandler aBigHandler=newBigInvocationHandler(a);
ABigService aBigService= (ABigService)Proxy.newProxyInstance(aBigHandler.getClass().getClassLoader(),a.getClass().getInterfaces(),aBigHandler);
String result=aBigService.doSomething("aa");
LOG.info(result);

}
这样我们就可以调用刚才abigservice里的方法了,那么这几行代码里最关键的一处就一定是proxy.newProxyIntance了,它是如何创建一个AbigService对象的呢?
当我们调用newProxyIns这个方法的时候,

public staticObjectnewProxyInstance(ClassLoader loader,
Class<?>[]interfaces,
InvocationHandlerh)
throwsIllegalArgumentException
{
balabalabala......
//重点,这里创建了一个实现了ABigService的Proxy class
Class<?>cl=getProxyClass0(loader,intfs);
balabalabala......
//重点,之后的一部分创建出了这个class的实例
/*伪代码*/
newInstance();
}

Class<?>cl=getProxyClass0(loader,intfs);在调用这个方法的时候,会进行缓存,缓存代理过的proxy和class
/**
* Generate a proxy class. Must call the checkProxyAccess method
* to perform permission checks before calling this.
*/
private staticClass<?>getProxyClass0(ClassLoader loader,
Class<?>...interfaces) {
if(interfaces.length>65535) {
throw newIllegalArgumentException("interface limit exceeded");
}

// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
returnproxyClassCache.get(loader,interfaces);
}

在get方法中调用weakcache里的 补充器supplier,
然后再value=Objects.requireNonNull(valueFactory.apply(key,parameter));
在调用ProxyClassFactory,过程直接看堆栈,不细说。
最终重点来了:
动态代理的中级大BOSS:
public classProxyGenerator {
public static byte[] generateProxyClass(finalString var0, Class[] var1) {
ProxyGenerator var2 = newProxyGenerator(var0, var1);
final byte[] var3 = var2.generateClassFile();
if(saveGeneratedFiles) {
AccessController.doPrivileged(newPrivilegedAction() {
publicVoid run() {
try{
FileOutputStream var1 = newFileOutputStream(ProxyGenerator.dotToSlash(var0) +".class");
var1.write(var3);
var1.close();
return null;
} catch(IOException var2) {
throw newInternalError("I/O exception saving generated file: "+ var2);
}
}
});
}

returnvar3;
}
}

generateProxyClass的两个传入参数,proxy的对象和ABigService的interface

在这个方法中会产生一个二进制的class来直接用于之后生成代理对象,而这个代理对象,完成了实现ABigService接口。这样就完成了对代理。

如果我们打印出代理类的构造将会是这样的
aBigService实现的接口是
testspr.core.ABigService
aBigService的字段属性列表是
m1
m3
m0
m2
aBigService的构造参数是
com.sun.proxy.$Proxy0
aBigService的方法列表是
doSomething
hashCode
equals
toString
isProxyClass
getInvocationHandler
getProxyClass
newProxyInstance
getClass
notify
notifyAll
wait
wait
wait

可以看到实现的接口是testspr.core.ABigService
而method里实现了doSomething

总结一下动态代理步骤:
Proxy是所有动态代理出的代理对象的父类
1.通过实现InvocationHandler接口来创建自己的调用处理器
java.lang.reflect.InvocationHandler aBigHandler=newBigInvocationHandler(a);
2.通过class的创建方法创建出一个class文件,这个文件实现了代理目标的接口
Class<?>cl=getProxyClass0(loader,intfs);
3.通过反射,把这个class实例化,实例化出的类是一个实现了ABigService的Porxy对象所以你可以这样调用,
ABigServiceaBigService= (ABigService)Proxy.newProxyInstance(aBigHandler.getClass().getClassLoader(),a.getClass().getInterfaces(),aBigHandler);
而在调用之前你必须要new 出一个真正的被代理对象,供代理类使用:ABigServiceaBigService= new ABigServiceImpl()而这一步就简单多了,如果你用的是ioc类型的框架,甚至可以省略这一步。
4.在调用proxyObj的doSomeThing时候实际上其中是调用了handler的invoke(Object,method,args)方法。

总结,
第一使用动态代理的前提条件是要有一个目标对象,这是必须的。
第二我们调用的是动态代理生成的proxy对象,但是最终对调用目标对象。
第三handler其实是一个调用构造器,它主要完成调度功能。










原创粉丝点击