动态代理模式,就是在内存中动态生成$Proxy0对象,该对象实现了要被代理对象的接口。
如下图,$Proxy0就是在内存中生成的。
1. 首先需要写一个class,实现InvocationHandler接口。重写invoke方法。
invoke方法有3个参数:
1.第一个参数是Proxy的一个动态实例。只有Proxy实例在InvocationHandler实现类里加载才可以产生第二个参数method,所以$Proxy实例需要把自己传给invoke方法。在InvocationHandler源码中对proxy的描述:proxy the proxy instance that the method was invoked on。
2.第二个参数method是真实对象要实现的业务方法,由$Proxy0实例的静态代码段得到。
3.第三参数args是method的参数。
主要起作用是method.invoke,将要被代理对象的接口传进去。
public class MyHandler implements InvocationHandler{
private People people;
public MyHandler(People people){
this.people=people;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
before();
//obj是要被代理的类
method.invoke(people, args);
after();
return null;
}
private void before(){
System.out.println("吃之前做一些准备工作");
}
private void after(){
System.out.println("吃之后做一些整理工作");
}
}
在使用的过程中,普通调用一般是
People people=new Damon();
动态代理则不同,
People proxyPeople=(People) Proxy.newProxyInstance(proxyTest.class.getClassLoader(),
new Class>[] {People.class},
new MyHandler(new Damon()));
proxyPeople.eat();
首先需要使用Proxy类的newProxyInstance方法。需要3个参数,其中前两个参数用来生成$Proxy的构造器,在以involcationHandler为参数生成$Proxy实例。
1.第一个参数是类加载器。不一定要指定具体class,用this.getClass().getClassLoader()也可以。
2. 第二个参数接口数组。需要实现的接口。这个会体现在$Proxy中。
3.第三个参数调用处理器,指派involcationHandler去实际执行增强业务。
当执行的时候,会自动在内存中生成$Proxy0对象。该对象实现了People接口。所以这是为什么JDK动态代理只能代理有接口的类。
其重写了接口的eat方法,调用了h.invoke();
查看$Proxy,可以使用ProxyGenerator生成byte数组,然后在用outputStream生成.class文件,进行反编译,查看java代码。
ProxyGenerator.generateProxyClass
自己重写Proxy类。主要实现其newProxyInstance静态方法。
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces, Handler myHandler) {
String fileName = "D:/Java_space_sum/0413/Proxy/src/invoke/myproxy/$Proxy0.java";
//创建一个$Proxy0的类结构,就是创建一个java文件, 字符串拼凑加上流String javaClassStr = getJavaStr(interfaces);//通过流的方式将javaStr输出到文件createJavaFile(javaClassStr,fileName);//编译生成文件compilerJava(fileName);//自定义一个类加载器,把对应的.class加载到内存中,并且生成代理实例Object h=LoadClass(myHandler);return h;}
1.指定java文件存放地址。
String fileName = "D:/Java_space_sum/0413/Proxy/src/invoke/myproxy/$Proxy0.java";
其实JDK支持读取当前文件所在路径,不需要hadcode.
InputStream resourceStream=MyProxy.class.getResourceAsStream("/");
2.创建一个类结构
private static String getJavaStr(Class>[] interfaces) {
Method[] methods = interfaces[0].getMethods();
String proxyClass = "package baoming" + rt + "import " + rt
+ "public class $Proxy0 implements" + interfaces[0].getName()
+ "{" + rt + "MyInvocaltionHandler h;" + rt + "public ";
return proxyClass;
}
3.生成java文件
private static void createJavaFile(String javaClassStr,String fileName) {
File f = new File(fileName);
FileWriter fw;
try {
fw = new FileWriter(f);
fw.write(javaClassStr);
fw.flush();
fw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
4.编译java文件,生成.class文件
private static void compilerJava(String fileName){
JavaCompiler systemJavaComplier=ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager standardJavaFileManager=systemJavaComplier.getStandardFileManager
(null, null, null);
Iterable extends JavaFileObject> javaFileObject=standardJavaFileManager.getJavaFileObjects(fileName);
systemJavaComplier.getTask(null, standardJavaFileManager,
null, null, null, javaFileObject);
}
5.自定义一个类加载器,把对应的.class文件加载到内存中。生成代理对象
private static Object LoadClass(Handler h){
MyClassLoader myClassLoader=new MyClassLoader("D:/Java_space_sum/0413/Spring0414/src/invoke/MyProxy");
Object newInstance = null;
try {
Class> findClass = myClassLoader.findClass("$Proxy0");
//内存里面的代理对象的反射对象
Constructor> constructor=findClass.getConstructor(Handler.class);
//拿到反射对象的构造器
newInstance=constructor.newInstance(h);
//这个newInstance就是我们需要的代理实例
return newInstance;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return newInstance;
}
cglib实现原理跟JDK动态代理不一样。cglib支持无接口实现的类。但是不支持使用final修饰的类。其底层使用到了ASM字节码包,所以又叫字节码代理。