代理分静态代理和动态代理,为了能理解动态代理,首先我们来实现一套静态代理试试水深。
首先我们来简单聊聊代理,为什么要使用代理。当一个对象,在它被使用的地方,不方便获取一个目标对象或者在调用目标对象的时候,在之前或者之后希望可以执行一些代码,就要使用代理。
如何使用代理呢,代理又是如何在执行真正的方法之前执行一些业务逻辑的呢,我们先来实现一个静态代理。
首先定义一个接口
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其实是一个调用构造器,它主要完成调度功能。