动态代理的实现原理
转载自:https://www.jianshu.com/p/4e14dd223897
java动态代理其实是一种设计模式,是Spring AOP 的核心技术。通俗的解释就是往没有耦合关系但却需要经过同样步骤处理的代码里加上这些“同样步骤”。这种动态代理其实是jdk的一个特性(jdk1.3以上支持)
在演化的过程中,代理是由 静态代理 演变 到现在的动态代理。从静态=》动态,讲的是代理类的代码由需要工程师手动编写到由jvm在运行时自动生成这个状态的变化。
所以要理解动态代理,就要先知道静态代理是什么,要知道如果没有jvm自动生成代理代码,那么代理代码是长什么样。
下面是一个完整的静态代理的代码,也就是全部由开发者敲入的代码:
这个例子来自:https://www.cnblogs.com/gonjan-blog/p/6685611.html,加上我的解读。
接口类
packageproxy.staticproxy;
publicinterface
Person
{
void
giveMoney();
}
一个实现了接口类的实体类:
packageproxy.staticproxy;
publicclass
Student
implements
Person
{
@Override
public
void
giveMoney()
{
System.
out
.println(name
+" 上交班费50元!");
}
private
String
name
;
public
Student(String
name
){
this.
name
=name
;
}
}
一个代理类,代理类实现了接口类,并且还“爱管闲事”,调用了上面实体类的方法。
packageproxy.staticproxy;
/**
* 如果使用动态代理这个类会被自动创建, 代理类的工作就是把实体类接过来,然后调用它的方法,也就是说本来实体类可以自己执行的方法现在由代理类来触发执行,这样做的好处是,在调用实体类方法的前后我们可以插入监控方法。比如这里只插入了一句话“这位同学最近学习有进步”
*/
publicclass
StudentProxy
implements
Person
{
@Override
public
void
giveMoney()
{
System.
out
.println("这位同学最近学习有进步!");
stu
.giveMoney();
}
Student
stu
;
public
StudentProxy
(Person
stu
){
if(
stu
.getClass()==
Student.class){
//person 可以是学生,老师等, 但是这里只为学生交钱
this.
stu
=(Student)
stu
;
}
}
}
最后有一个统管类, 生成一个实体类,并指挥代理类去调用实体类:
publicclass
StaticProxyTest
{
public
static
void
main(
String
[]args
){
Person zhangsan
=new
Student("张三");
Person monitor
=new
StudentProxy(
zhangsan
);// 为了帮张三交班费而生产一个班长角色
monitor
.giveMoney();
}
}
差不多看清楚代理的意思和作用(可以添加监控代码),接着看动态代理:
动态代理是jdk的技术,在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。
接着来理一理这两个东西Proxy 和InvocationHandler,他们是怎么配合的呢?
InvocationHandler 用于表示在执行某个方法之前,之后你想加入什么(监控)代码。这个是真正在干活的类。
Proxy 用于自动生成代理类,InvocationHandler 将会作为Proxy的一个参数来生成代理类。但这个生成的细节在这里不深究。
这两者是怎么挂上钩的呢?答案是通过Proxy.newInstance()方法,invocationHandler会作为参数传入Proxy, 由Proxy在编译的时候加工自动生成代理类,这个生成的代理类里就会有invocationHandler里指定的要执行的代码。 带着这个结论,去下面的代码中求证。
这里我先给出动态代理应用的方法, 这里采用倒序展示代码:
实现的主函数只有3行代码,对比下上面的staticProxyTest主方法,会发现非常相似,唯一的区别是第二句话由调用自己写的代理类变成调用Proxy自动生成代理类
publicclass
ProxyTest
{
public
static
void
main(
String
[]args
){
//创建一个实例对象,这个对象是被代理的对象
Person zhangsan
=new
Student("张三");
//创建一个与代理对象相关联的InvocationHandler
//创建一个代理对象stuProxy来代理zhangsan,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
Person stuProxy
=(
Person
)Proxy
.newProxyInstance(Person
.class.getClassLoader(),new
Class
<?>[]{Person
.class},new
StuInvocationHandler
<Person
>(zhangsan
));
//代理执行上交班费的方法
stuProxy
.giveMoney();
}
}
代码虽然简单,但是大家肯定很困惑Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, new StuInvocationHandler<Person>(zhangsan));到底做了什么事情?
回想下代理类的主要工作是什么? (这句话我上面说的)“代理类的工作就是把实体类接过来,然后调用它的方法,也就是说本来实体类可以自己执行的方法现在由代理类来触发执行,这样做的好处是,在调用实体类方法的前后我们可以插入监控方法。比如这里只插入了一句话“这位同学最近学习有进步”。
从静态代理studentProxy(stu)的实例化可以看到,代理类需要接收被代理的实例,但是看这句Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, new StuInvocationHandler<Person>(zhangsan));被代理的实例“zhangsan”并没有直接传给Proxy.newProxyInstance() , 而是传给了InvocationHandler的实现类StuInvocationHandler。接下来我们看看StuInvocationHandler到底做什么用?或者说实现了InvocationHandler的类到底在做什么?为什么要传入Proxy去生成代理类?
接着我贴出StuInvocationHandler的代码:
可以看出, StuInvocationHandler接收一个泛型类T , 实现了InvocationHandler的方法invoke(), invoke(proxy, method, args)里的method.invoke(target, args);就是方法真正执行的地方。当代码执行到stuProxy.giveMoney(); 下一步就到invoke()这里。这里就可以看到, 你可以在method.invoke()前后添加任何你想添加的代码用于追踪这个方法的一些信息,比如你可以统计这个方法的执行时间,可以记录log等
packageproxy.dynamicproxy;
importjava.lang.reflect.InvocationHandler;
importjava.lang.reflect.Method;
publicclass
StuInvocationHandler<T>
implements
InvocationHandler
{
T
target
;
public
StuInvocationHandler(T
target
){
this.
target
=target
;
}
@Override
public
Object
invoke(Object
proxy
,Method
method
,Object[]
args
)throws
Throwable
{
System.
out
.println("代理执行"+method
.getName()+"方法");
MonitorUtil.start();
//这是一个本地静态工具类,用于记下方法开始时间
Object
result
=method
.invoke(target
,args
);
MonitorUtil.finish(
method
.getName());//这是一个本地静态工具类,用于记下方法执行完成的时间
return
result
;
}
}
解释下当代码执行到stuProxy.giveMoney(); 下一步就到invoke()这里。 这个很容易验证,当你把断点设在stuProxy.giveMoney();按下一步,就会进入invoke方法里,那么这里为什么会这样的,在我们写的代码里看不到任何蛛丝马迹。别忘了,Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, new StuInvocationHandler<Person>(zhangsan)); 这里的stuProxy是JVM运行的时候生成的,所以要看它的代码才能知道解答上面的问题。先看看Proxy.newProxyInstance()的代码:
publicstatic
Object
newProxyInstance(ClassLoader
loader
,
Class<?>[]
interfaces
,
InvocationHandler
h
)
throws
IllegalArgumentException
{
Objects.requireNonNull(
h
);
final
Class<?>[]
intfs
=interfaces
.clone();
final
SecurityManager
sm
=System.getSecurityManager();
if
(
sm
!=null)
{
checkProxyAccess(Reflection.getCallerClass(),
loader
,intfs
);
}
/*
* Look up or generate the designated proxy class.
*/
Class<?>
cl
=getProxyClass0(
loader
,intfs
);
/*
* Invoke its constructor with the designated invocation handler.
*/
try
{
if
(
sm
!=null)
{
checkNewProxyPermission(Reflection.getCallerClass(),
cl
);
}
final
Constructor<?>
cons
=cl
.getConstructor(constructorParams
);
final
InvocationHandler
ih
=h
;
if
(!Modifier.isPublic(
cl
.getModifiers())){
AccessController.doPrivileged(new
PrivilegedAction<Void>()
{
public
Void
run()
{
cons
.setAccessible(true);
return
null;
}
});
}
return
cons
.newInstance(newObject[]{
h
});//不出意外,返回cons的实例, cons由cl来,cl由getProxyClass0(loader, intfs);来
}
catch
(IllegalAccessException|InstantiationException
e
){
throw
new
InternalError(
e
.toString(),e
);
}
catch
(InvocationTargetException
e
){
Throwable
t
=e
.getCause();
if
(
t
instanceofRuntimeException)
{
throw
(RuntimeException)
t
;
}
else
{
throw
new
InternalError(
t
.toString(),t
);
}
}
catch
(NoSuchMethodException
e
){
throw
new
InternalError(
e
.toString(),e
);
}
}
return cons.newInstance(new Object[]{h}); //不出意外,返回cons的实例, cons由cl来,cl由getProxyClass0(loader, intfs);来。而getProxyClass0(loader, intfs);就是生成代理的地方,所以,我们把这个方法产生的类输出来:
在主函数里加入下面代码以输出这个动态生成的代理类
publicclass
ProxyTest
{
public
static void
main(String
[]args
){
Person zhangsan
=new
Student("张三");
InvocationHandler stuHandler
=new StuInvocationHandler
<Person
>(zhangsan
);
//加入下面代码以输出类到文件
Person stuProxy
=(
Person
)Proxy
.newProxyInstance(Person
.class
.getClassLoader(),new Class
<?>[]{Person
.class
},stuHandler
);
stuProxy
.giveMoney();
byte
[]classFile
=ProxyGenerator
.generateProxyClass("$Proxy0",Student
.class
.getInterfaces());
String path
="C:\\xxx\\NEW-GIT-HUB\\goodgoodstudy\\javalearning\\src\\proxy\\dynamicproxy\\stuproxy.class";
try(
FileOutputStream fos
=new
FileOutputStream(path
)){
fos
.write(classFile
);
fos
.flush();
System
.out
.println("代理类class文件写入成功");
}
catch
(
Exception e
){
System
.out
.println("写文件错误");
}
}
}
输出后,可以下载JD-GUI(下载地址http://jd.benow.ca/)来反编译class文件。
这是我反编译的结果:
importjava.lang.reflect.InvocationHandler;
importjava.lang.reflect.Method;
importjava.lang.reflect.Proxy;
importjava.lang.reflect.UndeclaredThrowableException;
importproxy.dynamicproxy.Person;
publicfinal
class
$
Proxy0
extends
Proxy
implements
Person
{
private
static
Method
m1
;
private
static
Method
m2
;
private
static
Method
m3
;
private
static
Method
m0
;
public
$
Proxy0(InvocationHandlerparamInvocationHandler
)
throws
{
super(
paramInvocationHandler
);
}
public
final
boolean
equals(Object
paramObject
)
throws
{
try
{
return
((Boolean)this.
h
.invoke(this,m1
,new
Object[]
{
paramObject
})).booleanValue();
}
catch
(Error|RuntimeException
localError
)
{
throw
localError
;
}
catch
(Throwable
localThrowable
)
{
throw
new
UndeclaredThrowableException(
localThrowable
);
}
}
public
final
String
toString()
throws
{
try
{
return
(String)this.
h
.invoke(this,m2
,null);
}
catch
(Error|RuntimeException
localError
)
{
throw
localError
;
}
catch
(Throwable
localThrowable
)
{
throw
new
UndeclaredThrowableException(
localThrowable
);
}
}
public
final
void
giveMoney()
throws
{
try
{
this.
h
.invoke(this,m3
,null);
return;
}
catch
(Error|RuntimeException
localError
)
{
throw
localError
;
}
catch
(Throwable
localThrowable
)
{
throw
new
UndeclaredThrowableException(
localThrowable
);
}
}
public
final
int
hashCode()
throws
{
try
{
return
((Integer)this.
h
.invoke(this,m0
,null)).intValue();
}
catch
(Error|RuntimeException
localError
)
{
throw
localError
;
}
catch
(Throwable
localThrowable
)
{
throw
new
UndeclaredThrowableException(
localThrowable
);
}
}
static
{
try
{
m1
=Class.forName("java.lang.Object").getMethod("equals",
new
Class[]
{
Class.forName("java.lang.Object")
});
m2
=Class.forName("java.lang.Object").getMethod("toString",
new
Class[0]);
m3
=Class.forName("proxy.dynamicproxy.Person").getMethod("giveMoney",
new
Class[0]);
m0
=Class.forName("java.lang.Object").getMethod("hashCode",
new
Class[0]);
return;
}
catch
(NoSuchMethodException
localNoSuchMethodException
)
{
throw
new
NoSuchMethodError(
localNoSuchMethodException
.getMessage());
}
catch
(ClassNotFoundException
localClassNotFoundException
)
{
throw
new
NoClassDefFoundError(
localClassNotFoundException
.getMessage());
}
}
}
可以看到下面代码, 当动态代理类在调用giveMoney()的时候,走的就是下面的代码,这时候就可以很清晰的看到invoke的调用了。
publicfinal
void
giveMoney()
throws
{
try
{
this.
h
.invoke(this,m3
,null);
return;
}
。。。
现实项目中对动态代理的使用:
比如一个拦截操作db 的execute()方法,对于db update和delete操作做特殊处理,比如记录log,校验数据等:
publicObject
invoke(Object proxy
,Method method
,Object
[]args
)throws Throwable
{
if
(!
Objects
.equals(method
.getName(),"execute"))
{
return
method
.invoke(database
,args
);
}
SQLResult sqlResult
=parseSQL((
String
)args
[0],(
Object
[])args
[1]);
if
(
sqlResult
!=null)
{
if
(
sqlResult
.action
==UPDATE)
{
return
handleUpdateProcess(
method
,args
,sqlResult
);
}
else
if
(
sqlResult
.action
==DELETE)
{
return
handleDeleteProcess(
method
,args
,sqlResult
);
}
}
return
method
.invoke(database
,args
);
}
https://www.jianshu.com/p/4e14dd223897