设计模式之动态代理 - 彻底搞懂JDK动态代理
2019-12-05 本文已影响0人
Liuzz25
目录
1.什么是JDK动态代理
2.简单案例
3.彻底搞懂JDK动态代理,自己动手实现JDK动态代理。
4.项目源码
1.什么是JDK动态代理
JDK动态代理是设计模式中代理模式的一种实现方式,在java的运行过程中,动态的生成代理类,其只能代理接口。
2.简单案例
本案例说明
(1)创建一个抽象类,Person接口,使其拥有一个没有返回值的doSomething方法。
/**
* 抽象类人
*/
public interface Person {
void doSomething();
}
(2)创建一个名为Bob的Person接口的实现类,使其实现doSomething方法。
/**
* 创建一个名为Bob的人的实现类
*/
public class Bob implements Person {
public void doSomething() {
System.out.println("Bob doing something!");
}
}
(3)创建JDK动态代理类,使其实现InvocationHandler接口。拥有一个名为target的变量,并创建getTarget获取代理对象方法。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK动态代理
* 需实现InvocationHandler接口
*/
public class JDKDynamicProxy implements InvocationHandler {
// 被代理的对象
Person target;
// JDKDynamicProxy构造函数
public JDKDynamicProxy(Person person) {
this.target = person;
}
// 获取代理对象
public Person getTarget() {
return (Person) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
// 动态代理invoke方法
public Person invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 被代理方法前执行
System.out.println("JDKDynamicProxy do something before!");
// 执行被代理的方法
Person result = (Person) method.invoke(target, args);
// 被代理方法后执行
System.out.println("JDKDynamicProxy do something after!");
return result;
}
}
(4)创建JDK动态代理测试类JDKDynamicTest。
/**
* JDK动态代理测试
*/
public class JDKDynamicTest {
public static void main(String[] args) {
System.out.println("不使用代理类,调用doSomething方法。");
// 不使用代理类
Person person = new Bob();
// 调用doSomething方法
person.doSomething();
System.out.println("-------------------------------------- 分割线 --------------------------------------");
System.out.println("使用代理类,调用doSomething方法。");
// 获取代理类
Person proxyPerson = new JDKDynamicProxy(new Bob()).getTarget();
// 调用doSomething方法
proxyPerson.doSomething();
}
}
运行结果如下图所示:
image.png
3.彻底搞懂JDK动态代理,自己动手实现JDK动态代理。
想要自己实现JDK动态代理,首先我们先梳理下原理:
1.拿到被代理对象的引用,然后获取其接口。
2.JDK代理重新生成一个类,同时实现我们给的代理所实现的接口。
3.同时拿到代理对象的引用。
4.动态生成class文件字节码。
5.编译源代码,并生成. class文件。
6.将class文件动态加载到JVM。
7.返回被代理后的代理对象。
好了原理梳理完了,下面就开始自己动手实现JDK动态代理吧。(ps:可能部分用语不专业,大家理解下工科妹子的表达能力)
第1步和第2步完全同上,这里就省略不写了啊。
(1)创建自定义InvocationHandler
/**
* 自定义InvocationHandler
*/
public interface LzzInvocationHandler {
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
(2)创建自定义一个ClassLoader
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
/**
* 自定义一个ClassLoader
*/
public class LzzClassLoader extends ClassLoader {
private File baseDir;
public LzzClassLoader() {
String basePath = LzzClassLoader.class.getResource("").getPath();
this.baseDir = new java.io.File(basePath);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String className = LzzClassLoader.class.getPackage().getName() + "." + name;
if (baseDir != null) {
File classFile = new File(baseDir, name.replaceAll("\\.", "/") + ".class");
if (classFile.exists()) {
FileInputStream in = null;
ByteArrayOutputStream out = null;
try {
in = new FileInputStream(classFile);
out = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int len;
while ((len = in.read(buff)) != -1) {
out.write(buff, 0, len);
}
return defineClass(className, out.toByteArray(), 0, out.size());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != in) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != out) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
classFile.delete();
}
}
}
return null;
}
}
(3)创建自定义Proxy类
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
* 自定义Proxy类
*/
public class LzzProxy {
// 换行符
private static String ln = "\r\n";
public static Object newProxyInstance(LzzClassLoader classLoader, Class<?>[] interfaces, LzzInvocationHandler h) {
try {
//1.生成源代码
String proxySrc = generateSrc(interfaces[0]);
//2.保存将生成出来的源代码
String filePath = LzzProxy.class.getResource("").getPath();
File f = new File(filePath + "$Proxy0.java");
FileWriter fw = new FileWriter(f);
fw.write(proxySrc);
fw.flush();
fw.close();
//3.编译源代码,生成.CLASS文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
Iterable iterable = manager.getJavaFileObjects(f);
JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable);
task.call();
manager.close();
//4.将class文件动态加载到JVM返回被代理后的对象
Class proxyClass = classLoader.findClass("$Proxy0");
Constructor c = proxyClass.getConstructor(LzzInvocationHandler.class);
f.delete();
return c.newInstance(h);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static String generateSrc(Class<?> interfaces) {
StringBuffer src = new StringBuffer();
src.append("package lzzly.custom;" + ln);
src.append("import java.lang.reflect.Method;" + ln);
src.append("public class $Proxy0 implements " + interfaces.getName() + "{" + ln);
src.append("LzzInvocationHandler h;" + ln);
src.append("public $Proxy0(LzzInvocationHandler h) {" + ln);
src.append("this.h = h;" + ln);
src.append("}" + ln);
for (Method m : interfaces.getMethods()) {
src.append("public " + m.getReturnType().getName() + " " + m.getName() + "(){" + ln);
src.append("try{" + ln);
src.append("Method m = " + interfaces.getName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{});" + ln);
src.append("this.h.invoke(this,m,null);" + ln);
src.append("}catch(Throwable e){e.printStackTrace();}" + ln);
src.append("}" + ln);
}
src.append("}");
return src.toString();
}
}
(4)创建CustomDynamicProxy自定义动态代理类
import java.lang.reflect.Method;
/**
* 自定义动态代理类
* 实现LzzInvocationHandler接口
*/
public class CustomDynamicProxy implements LzzInvocationHandler {
// 被代理的对象
Person target;
// JDKDynamicProxy构造函数
public CustomDynamicProxy(Person person) {
this.target = person;
}
// 获取代理对象
public Person getTarget() {
return (Person) LzzProxy.newProxyInstance(new LzzClassLoader(), target.getClass().getInterfaces(), this);
}
// 动态代理invoke方法
public Person invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 被代理方法前执行
System.out.println("LzzDynamicProxy do something before!");
// 执行被代理的方法
Person result = (Person) method.invoke(target, args);
// 被代理方法后执行
System.out.println("LzzDynamicProxy do something after!");
return result;
}
}
(5)创建自定义动态代理测试类
/**
* 自定义动态代理测试
*/
public class CustomDynamicTest {
public static void main(String[] args) {
System.out.println("不使用代理类,调用doSomething方法。");
// 不使用代理类
Person person = new Bob();
// 调用doSomething方法
person.doSomething();
System.out.println("-------------------------------------- 分割线 --------------------------------------");
System.out.println("使用代理类,调用doSomething方法。");
// 获取代理类
Person proxyPerson = new CustomDynamicProxy(new Bob()).getTarget();
// 调用doSomething方法
proxyPerson.doSomething();
}
}
运行结果如下图所示:
image.png
项目源码
码云-DynamicProxy:https://gitee.com/OrgXxxx/DynamicProxy