最近在学习Java反射的一些知识,看到了一些有关代理的例子,好记性不如烂笔头,所以这里将它记录下来。接下来话不多说,直接进入主题。
代理:为其他对象提供一个代理以控制对某个对象的访问。
静态代理
- 接口
1 | public interface IDoSomething { |
- 被代理类的实现
1 | public class Sing implements IDoSomething { |
- 代理类的实现
1 | public class SingProxy implements IDoSomething{ |
- 测试类
1 | public class ProxyDemo { |
- 输出结果
1 | Befor singing |
以上就是简单的静态代理,不在过多的介绍,下面是动态代理。
动态代理
Java的动态代理可以动态的创建代理并动态的处理对所代理方法的调用。动态代理有两种实现方法,一种是使用JDK自带的,一种是使用Cglib实现。
实现JDK自带的动态代理
实现JDK自带的动态代理,关键是实现InvocationHandler,同时它要求被代理对象必须有接口。下面是实现的代码,我加上了必要的注释。
- 接口
1 | public interface IProxyClass { |
- 被代理类的实现
1 | public class ProxyClassImpl implements IProxyClass { |
实现InvocationHandler接口
这里我实现了InvocationHandler接口,并手动生成了代理类,保存到了电脑F盘上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
63
64
65
66public class DynamicProxyHandler implements InvocationHandler {
private Object proxied;
/**
* @param proxied 被代理对象
*/
public DynamicProxyHandler(Object proxied) {
this.proxied = proxied;
}
/**
* 返回代理对象
* @return
*/
public Object newProxyInstance() {
return Proxy.newProxyInstance(proxied.getClass().getClassLoader(), proxied.getClass().getInterfaces(), this);
}
/**
*
* @param proxy 代理对象
* @param method 代理方法
* @param args 方法参数
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//将代理对象生成字节码到F盘上,方便反编译出java文件查看,实际动态代理是不需要自己生成的
addClassToDisk(proxy.getClass().getName(), ProxyClassImpl.class,"F:/$Proxy0.class");
System.out.println("method:"+method.getName());
System.out.println("args:"+args[0].getClass().getName());
System.out.println("Before invoke method...");
Object object=method.invoke(proxied, args);
System.out.println("After invoke method...");
return object;
}
/**
* 用于生产代理对象的字节码,并将其保存到硬盘上
* @param className
* @param cl
* @param path
*/
private void addClassToDisk(String className, Class<?> cl, String path) {
//用于生产代理对象的字节码
byte[] classFile = ProxyGenerator.generateProxyClass(className, cl.getInterfaces());
FileOutputStream out = null;
try {
out = new FileOutputStream(path);
//将代理对象的class字节码写到硬盘上
out.write(classFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}测试类
1 | public class SimpleProxyDemo { |
- 输出结果
1 | com.sun.proxy.$Proxy0 |
从结果我们可以看到(IProxyClass)proxyHandler.newProxyInstance();实际返回的是com.sun.proxy.$Proxy0
,我们把生成的$Proxy0.class
文件,使用jad.exe进行反编译,使用命令(要求文件和jad.exe在同一个目录下,或者你可以吧jad加到环境变量中去):
jad -p java $Proxy0.class
得到的$Proxy0.java如下:
1 | public final class $Proxy0 extends Proxy |
可以看到实际调用的是25行的doSometing()方法。如果你想了解更加具体的JDK动态代理的实现原理可以访问Rejoy的博文JDK动态代理实现原理。
使用Cglib实现动态代理
Cglib不是jdk自带的jar包,需要下载并加入到项目中。个人觉得Cglib比使用jdk自带的实现动态代理更为先进,毕竟它不再需要接口,而且它还有其他强大的功能,大家可以自行研究。
- 实现MethodInterceptor接口
1 | public class CglibProxy implements MethodInterceptor{ |
- 被代理对象和测试类
1 | public class CglibDemo { |
- 输出结果
1 | before CGLIB$doSomething$0 |
本篇笔记参考于:
http://www.cnblogs.com/shijiaqi1066/p/3429691.html
http://rejoy.iteye.com/blog/1627405