代理模式
使用代理模式必须要让代理类和目标类实现相同的接口,客户端通过代理类来调用目标方法,代理类会将所有的方法调用分派到目标对象上反射执行,还可以在分派过程中添加"前置通知"和后置处理(如在调用目标方法前校验权限,在调用完目标方法后打印日志等)等功能。
![](https://i-blog.csdnimg.cn/blog_migrate/620ec86053dc262e08617427ba27798c.png)
使用动态代理的五大步骤
1.通过实现InvocationHandler接口来自定义自己的InvocationHandler;
2.通过Proxy.getProxyClass获得动态代理类
3.通过反射机制获得代理类的构造方法,方法签名为getConstructor(InvocationHandler.class)
4.通过构造函数获得代理对象并将自定义的InvocationHandler实例对象传为参数传入
5.通过代理对象调用目标方法
动态代理的使用
例1(方式一)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
public
class
MyProxy {
public
interface
IHello{
void
sayHello();
}
static
class
Hello
implements
IHello{
public
void
sayHello() {
System.out.println(
"Hello world!!"
);
}
}
//自定义InvocationHandler
static
class
HWInvocationHandler
implements
InvocationHandler{
//目标对象
private
Object target;
public
HWInvocationHandler(Object target){
this
.target = target;
}
public
Object invoke(Object proxy, Method method, Object[] args)
throws
Throwable {
System.out.println(
"------插入前置通知代码-------------"
);
//执行相应的目标方法
Object rs = method.invoke(target,args);
System.out.println(
"------插入后置处理代码-------------"
);
return
rs;
}
}
public
static
void
main(String[] args)
throws
NoSuchMethodException, IllegalAccessException, InvocationTargetExc eption, InstantiationException {
//生成$Proxy0的class文件
System.getProperties().put(
"sun.misc.ProxyGenerator.saveGeneratedFiles"
,
"true"
);
//获取动态代理类
Class proxyClazz = Proxy.getProxyClass(IHello.
class
.getClassLoader(),IHello.
class
);
//获得代理类的构造函数,并传入参数类型InvocationHandler.class
Constructor constructor = proxyClazz.getConstructor(InvocationHandler.
class
);
//通过构造函数来创建动态代理对象,将自定义的InvocationHandler实例传入
IHello iHello = (IHello) constructor.newInstance(
new
HWInvocationHandler(
new
Hello()));
//通过代理对象调用目标方法
iHello.sayHello();
}
}
|
代理类Class源码样例
$Proxy0.class
来看看例1(MyProxy)的代理类是怎样的?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
public
final
class
$Proxy0
extends
Proxy
implements
IHello {
//继承了Proxy类和实现IHello接口
//变量,都是private static Method XXX
private
static
Method m3;
private
static
Method m1;
private
static
Method m0;
private
static
Method m2;
//代理类的构造函数,其参数正是是InvocationHandler实例,Proxy.newInstance方法就是通过通过这个构造函数来创建代理实例的
public
$Proxy0(InvocationHandler var1)
throws
{
super
(var1);
}
//接口代理方法
public
final
void
sayHello()
throws
{
try
{
super
.h.invoke(
this
, m3, (Object[])
null
);
}
catch
(RuntimeException | Error var2) {
throw
var2;
}
catch
(Throwable var3) {
throw
new
UndeclaredThrowableException(var3);
}
}
//以下Object中的三个方法
public
final
boolean
equals(Object var1)
throws
{
try
{
return
((Boolean)
super
.h.invoke(
this
, m1,
new
Object[]{var1})).booleanValue();
}
catch
(RuntimeException | Error var3) {
throw
var3;
}
catch
(Throwable var4) {
throw
new
UndeclaredThrowableException(var4);
}
}
public
final
int
hashCode()
throws
{
try
{
return
((Integer)
super
.h.invoke(
this
, m0, (Object[])
null
)).intValue();
}
catch
(RuntimeException | Error var2) {
throw
var2;
}
catch
(Throwable var3) {
throw
new
UndeclaredThrowableException(var3);
}
}
public
final
String toString()
throws
{
try
{
return
(String)
super
.h.invoke(
this
, m2, (Object[])
null
);
}
catch
(RuntimeException | Error var2) {
throw
var2;
}
catch
(Throwable var3) {
throw
new
UndeclaredThrowableException(var3);
}
}
//对变量进行一些初始化工作
static
{
try
{
m3 = Class.forName(
"com.mobin.proxy.IHello"
).getMethod(
"sayHello"
,
new
Class[
0
]);
m1 = Class.forName(
"java.lang.Object"
).getMethod(
"equals"
,
new
Class[]{Class.forName(
"java.lang.Object"
)});
m0 = Class.forName(
"java.lang.Object"
).getMethod(
"hashCode"
,
new
Class[
0
]);
m2 = Class.forName(
"java.lang.Object"
).getMethod(
"toString"
,
new
Class[
0
]);
}
catch
(NoSuchMethodException var2) {
throw
new
NoSuchMethodError(var2.getMessage());
}
catch
(ClassNotFoundException var3) {
throw
new
NoClassDefFoundError(var3.getMessage());
}
}
}
|
由于JDK生成的代理类已经继承了Proxy(Java只支持单一继承),因此目标类必须是接口。代理类也实现了目标接口,因此在
代理类中,实现了目标接口中定义的方法。调用代理类中的代理方法时,都会通过
super
.h.invoke(
this
, m3, (Object[])
null
);
调用InvocationHandler的invoke()方法,传入代理对象this,Method方法和方法参数,由于在handler中持有了目标类的实例,因此在InvocationHandler 的
invoke()方法中回掉目标对象的方法,并且在目标方法调用前,后,返回后,异常情况下进行处理(如:日志、安全、缓存等操作)
public
Object invoke(Object proxy, Method method, Object[] args)
throws
Throwable {
System.out.println(
"------插入前置通知代码-------------"
);
//执行相应的目标方法
Object rs = method.invoke(target,args);
System.out.println(
"------插入后置处理代码-------------"
);
return
rs;
}