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

代理分静态代理和动态代理,为了能理解动态代理,首先我们来实现一套静态代理试试水深。

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

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

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

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

public String doSomething ( String a ) {
LOG . info ( " 在最终的方法执行之前 " );

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

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

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

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

我们来改动一些代码,让大家明白。
public class NomlProxyFactory {
public static ABigService getInstance ( int type ){
ABigService aIntance ;
if ( type == 1 ){
aIntance = new ABigServiceImpl ();
} else if ( type == 2 ){
return new ABigService () {
public String doSomething ( String a ) {
return " 第二个扩展类型 " ;
}
};
} else {
return new ABigService () {
public String doSomething ( String a ) {
return " 第三个扩展类型 " ;
}
};
}
return new NomlProxy ( aIntance );
}
}

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


现在静态代理已经建好了,我们来运行;
public static void main ( String [] args ){
final Logger 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 = new ABigServiceImpl ();

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

public class BigInvocationHandler implements InvocationHandler {

private Object aBigService ;
public BigInvocationHandler ( Object aBigService ){
this . aBigService = aBigService ;
}

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

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

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

ABigService a = new ABigServiceImpl ();
java.lang.reflect.InvocationHandler aBigHandler = new BigInvocationHandler ( 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 static Object newProxyInstance ( ClassLoader loader ,
Class <?>[] interfaces ,
InvocationHandler h )
throws IllegalArgumentException
{
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 static Class <?> getProxyClass0 ( ClassLoader loader ,
Class <?>... interfaces ) {
if ( interfaces . length > 65535 ) {
throw new IllegalArgumentException ( "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
return proxyClassCache . get ( loader , interfaces );
}

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

return var3;
}
}

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 = new BigInvocationHandler ( a );
2.通过class的创建方法创建出一个class文件,这个文件实现了代理目标的接口
Class <?> cl = getProxyClass0 ( loader , intfs );
3.通过反射,把这个class实例化,实例化出的类是一个实现了ABigService的Porxy对象所以你可以这样调用,
ABigService aBigService = ( ABigService ) Proxy . newProxyInstance ( aBigHandler . getClass (). getClassLoader (), a . getClass (). getInterfaces (), aBigHandler );
而在调用之前你必须要new 出一个真正的被代理对象,供代理类使用: ABigService aBigService = new ABigServiceImpl()而这一步就简单多了,如果你用的是ioc类型的框架,甚至可以省略这一步。
4.在调用proxyObj的doSomeThing时候实际上其中是调用了handler的invoke(Object,method,args)方法。

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










  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值