说到动态代理模式,就一定要提到代理模式。代理模式类似于实际生活中的中介机构,可以给用户提供更好的服务,而不是直接与服务提供方交互。这种模式主要的目的有两个:
1.保护目标对象
2.增强目标对象
静态代理模式:源代码中需要声明代理类,单一的一对一的代理,可扩展性比较低。当需要代理的目标对象数量多的时候,这种模式的成本较高。
动态代理模式:源代码中无需声明代理类,它可以适应复杂多变的业务,适应性强,可以为一类目标对象提供代理服务,通用性好。
创建源文件和返回代理对象类:MyProxy
在该类中有产生动态代理的实例对象方法,该实例对象是通过generateSourceFile(interfaces);
得到的源程序文件编译得到的。
具体实现如下:
// 产生动态代理实例对象
public static Object newProxyInstance(MyClassLoader classLoader,
Class<?> [] interfaces,
MyInvocationHandler handler) {
try {
// 动态产生源代码.java文件
String src = generateSourceFile(interfaces);
/* java文件输出到磁盘中*/
// MyProxy.class.getResource("");查找具有给定名称的资源。
// 用于搜索与给定类相关联的资源的规则由该类的定义class loader实现。
// 此方法委托给该对象的类加载器。 如果此对象由引导类加载器加载,
// 则该方法将委托给ClassLoader.getSystemResource(java.lang.String)
URL url = MyProxy.class.getResource("");
String path = url.getPath();//获取这个 URL的路径部分。
// 创建一个新的文件实例,将给定的字符串转为抽象地址路径
File file = new File(path + "$Proxy0.java");
FileWriter fw = new FileWriter(file);
fw.write(src);//将生成的代码写入到文件:$Proxy0.java中
fw.flush();
fw.close();
/* 将生成的Java文件编译成.class文件*/
// 获得programming language compiler编译器对象
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manager = javaCompiler
.getStandardFileManager(null,null,null);
Iterable iterable = manager.getJavaFileObjects(file);
// Creates a future for a compilation task with the given
// components and arguments.
JavaCompiler.CompilationTask task = javaCompiler
.getTask(null,manager,null,null,null,iterable);
task.call();//启动编译
manager.close();
/*将编译生成的.class文件加载到JVM中*/
// 在类路径中寻找到$Proxy0.class文件并返回对象
Class<?> proxyClass = classLoader.findClass("$Proxy0");
assert proxyClass != null;
Constructor<?> constructor = proxyClass.getConstructor(MyInvocationHandler.class);
file.delete();//删除.java文件
// 返回字节码重组后的新的代理对象
return constructor.newInstance(handler);//反射创建新的代理对象
} catch (IOException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException | ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
而对于源程序的生成则是需要手动的根据需求编写产生程序。
先看被代理对象的类图:
下面来看生成的.class文件:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package danran.proxy;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
public class $Proxy0 implements School {
MyInvocationHandler h;
public $Proxy0(MyInvocationHandler var1) {
this.h = var1;
}
public void Learn() {
try {
Method var1 = School.class.getMethod("Learn");
this.h.invoke(this, var1, new Object[0]);
} catch (Error var2) {
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
}
里面的关键代码:
Method var1 = School.class.getMethod("Learn");
this.h.invoke(this, var1, new Object[0]);
说明它是通过反射机制去获得被代理对象的"Learn()"方法,然后通过去调用成员变量MyInvocationHandler h;
的"Learn()"
方法,进而完成代理的功能。
而MyInvocationHandler
类:
public interface MyInvocationHandler {
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
它的实现类:MyTestProxy
public class MyTestProxy implements MyInvocationHandler {
private Object target;//被代理的对象的引用
// 返回代理对象实例
public Object getInstance(Object target) throws Exception {
this.target = target;
Class<?> clazz = target.getClass();
return MyProxy.newProxyInstance(new MyClassLoader(),clazz.getInterfaces(),this);
}
@Override
// 代理方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeAciton();
Object obj = method.invoke(this.target,args);
afterAction();
return obj;
}
private void afterAction() {
System.out.println("执行after....");
}
private void beforeAciton() {
System.out.println("执行before.....");
}
}
上面去调用成员变量MyInvocationHandler h;
的"Learn()"
方法时,会到这个实现类的invoke()
方法中,执行增强的afterAction()
和beforeAciton()
方法,以及反射调用实际的被代理的对象的"Learn()"
方法,至此动态模式完成。
测试代码:
/**
* @Classname MainTest
* @Description TODO
* @Date 2020/4/18 8:48
* @Created by Jason
*/
public class MainTest {
public static void main(String[] args) {
try {
School s = (School) new MyTestProxy().getInstance(new SchoolImpl());
s.Learn();
} catch (Exception e) {
e.printStackTrace();
}
}
}