言葉の前で言った:私は限られていますので、不適切な場合、私は基本的な実装を学ぶためのJDKダイナミックプロキシ方法の場合に学習JDKダイナミックプロキシで午前、、、個々の実装手順をまとめたもので、王は言いました。
私たちの手書きのHelloWorldプロセスに似たJDKの基礎となる基本的な動的プロキシが、JDKは、実行時にロードされたJavaファイルを生成し、コンパイルし
た.java動的に生成されたプロキシクラスを-の.classは、動的ファイルに>コンパイル- >の.classファイルがJVMにロードされます
- >オブジェクトを作成するには、このクラスを使用します
まず、ケースのニーズ
最初の飛行可能なインターフェースは、現在存在し、ケースの需要を説明し、抽象メソッドフライ(時間)メソッドは、鳥飛行可能なクラスは、インターフェイスを実装します。今、私たちは時に鳥の各フライトの記録日付プロキシオブジェクト、オブジェクトのメソッド飛行可能な鳥のインタフェースエージェント、分、秒を生成する必要があります。
第二に、仕事の準備
1.飛行可能なインタフェースを書きます
public interface Flyable {
void fly(long time);
}
复制代码
何も具体的なものは、メソッドが飛行するとき、長いパラメータを飛びます
2.鳥のインターフェイスを実装書きます
public class Bird implements Flyable {
@Override
public void fly(long time) {
try {
System.out.println("我是小鸟,我开始飞了");
Thread.sleep(time);
System.out.println("我是小鸟,我飞了" + time + "毫秒");
} catch (InterruptedException e) {
}
}
}
复制代码
鳥は、インターフェイスを実装飛びます
第三に、スタート実施機関
1.エグゼクティブ・インタフェースを書きます
//执行方法
public interface InvocationHandler {
//第一个参数,代理对象,第二个参数,所执行的方法反射对象,第三个参数及以后,传入的参数
Object invoke(Object proxy, Method method, Object... args);
}
复制代码
このインタフェースの役割:私たちはプロキシオブジェクトのメソッドの機能をカスタマイズすることができます
全ての方法は、実行の動的な薬剤は、このメソッド内で実行されるので、このインタフェースは特に重要であり、さ
2.プロキシオブジェクトのクラスを取得書きます
== ==ハイライト
プロキシオブジェクトのクラスを取得するために使用
public class MyProxy {
private static final String CLASS_BASE_PATH = "C:\\Users\\bai\\Desktop\\Java生成代码";
private static final String PACKAGE_NAME = "myproxy";
//获取代理对象
public static <T> T newProxyInstance(Class<T> clazz, InvocationHandler invocationHandler) {
String proxyClassName = clazz.getSimpleName() + "$MyProxy";
try {
//一、 生成java文件
generateProxyJavaFile(clazz, proxyClassName);
//二、 编译
compileJavaFile();
//三、 加载class文件到jvm中
ClassUtil.loadClass(new File(CLASS_BASE_PATH));
//四、 创建对象并返回
Class proxyClass = MyProxy.class.getClassLoader().loadClass(PACKAGE_NAME + "." + proxyClassName);
return (T) proxyClass.getConstructor(InvocationHandler.class).newInstance(invocationHandler);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
//生成代理类的java文件
private static void generateProxyJavaFile(Class clazz, String proxyClassName) throws IOException {
//...下面讲
}
//把java文件编译成.class文件
private static void compileJavaFile() throws FileNotFoundException{
//...下面讲
}
}
复制代码
4つのステップ、およびJavaを書くには、同様のhelloWord
1)Javaファイルを作成します。
ここでは、Javaコードは、Javaファイルを生成しているオープンソースのツールキット、Javapoet、そこにMavenのリポジトリに依存しなければなりません。
Javaファイルと手書きはファーストクラス、属性、コンストラクタ、メソッド、などと同様です。
Javapoetルールによって書かれ、難しいことではありません、それは通常、手書き、それを用いて達成することができることができます
いくつかの問題、javaファイルは、コードを見て参照して生成することができる工法
//生成代理类的java文件
private static void generateProxyJavaFile(Class clazz, String proxyClassName) throws IOException {
//构造一个类,实现传入接口,addSuperinterface功能是添加一个实现接口
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(proxyClassName).addSuperinterface(clazz);
//构建一个属性,用于保存执行对象
FieldSpec fieldSpec = FieldSpec.builder(InvocationHandler.class, "handler", Modifier.PRIVATE).build();
//添加到类中
classBuilder.addField(fieldSpec);
//构建一个构造器,初始化执行对象
MethodSpec constructor = MethodSpec.constructorBuilder()
//添加权限修饰符
.addModifiers(Modifier.PUBLIC)
//添加参数
.addParameter(InvocationHandler.class, "handler")
//方法体内容
.addStatement("this.handler = handler")
.build();
//把构造器添加到类中
classBuilder.addMethod(constructor);
//获取传入接口的所有公有方法(自己的,外部可以访问的方法,不包括继承的方法)
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
MethodSpec.Builder methodSpec = MethodSpec.methodBuilder(method.getName())
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.returns(method.getReturnType());
//生成handler.invoke()执行语句(实际执行方法),添加到方法体
StringBuilder invokeString = new StringBuilder("\tthis.handler.invoke(this, " + clazz.getName() + ".class.getMethod(\"" + method.getName() + "\",");
//存储执行方法参数列表
StringBuilder paramNames = new StringBuilder();
//这部分如果看不太懂,可以对照生成后的java文件
for (Parameter parameter : method.getParameters()) {
//添加外部方法参数列表
methodSpec.addParameter(parameter.getType(), parameter.getName());
//添加实际执行方法中
invokeString.append(parameter.getType() + ".class, ");
//存到执行方法参数列表中
paramNames.append(parameter.getName() + ",");
}
//把最后一个逗号替换为)
int lastCommaIndex = invokeString.lastIndexOf(",");
invokeString.replace(lastCommaIndex, invokeString.length(), "), ");
lastCommaIndex = paramNames.lastIndexOf(",");
paramNames.replace(lastCommaIndex, lastCommaIndex + 1, ")");
//把属性名追加到最后一个参数列表
invokeString.append(paramNames);
//添加方法体,执行InvocationHandler的invoke方法,并抓取异常
methodSpec.addCode("try{\n");
methodSpec.addStatement(invokeString.toString());
methodSpec.addCode("} catch (NoSuchMethodException e) {\n");
methodSpec.addCode("\te.printStackTrace();\n");
methodSpec.addCode("}\n");
//添加到类中
classBuilder.addMethod(methodSpec.build());
}
//生成java文件,第一个参数是包名
// String path = MyProxy.class.getResource("/").toString();
JavaFile javaFile = JavaFile.builder(PACKAGE_NAME, classBuilder.build()).build();
//把java文件写到执行路径下(默认会把包生成文件夹)
javaFile.writeTo(new File(CLASS_BASE_PATH));
}
复制代码
Javaファイルを生成した後
package myproxy;
import java.lang.Override;
class Flyable$MyProxy implements Flyable {
private InvocationHandler handler;
public Flyable$MyProxy(InvocationHandler handler) {
this.handler = handler;
}
@Override
public void fly(long arg0) {
try{
this.handler.invoke(this, myproxy.Flyable.class.getMethod("fly",long.class), arg0);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
复制代码
2).classファイルにコンパイル
Javaは現在の環境パラメータを使用してコンパイル、またはnull JVM、プロセスが複雑ではないが、同様のあるJavacコンパイルツールを提供してきた柔軟なパラメータの設定が可能
//把java文件编译成.class文件
private static void compileJavaFile() throws FileNotFoundException {
//1.获取javac编译器
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
//2.通过javac编译器获取一个编译java文件管理器
StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
//3.获取java文件对象
//-调用一个工具类,从指定路径下,递归获取所有指定后缀的文件
Set<File> javaFiles = FileUtil.getFilesForSuffix(new File(CLASS_BASE_PATH), ".java");
//-这里就一个java文件,就直接使用了
Iterator<File> iterator = javaFiles.iterator();
Iterable<? extends JavaFileObject> it = manager.getJavaFileObjects(iterator.next().getAbsoluteFile());
//4.编译
JavaCompiler.CompilationTask task = compiler.getTask(null, manager,
null, null, null, it);
task.call();
}
复制代码
3)負荷の.classファイル
JVMにロードしたクラスファイル
public class ClassUtil {
//加载class文件
public static <T> void loadClass(File classFolder) throws Exception {
//使用url类加载器,把文件夹路径添加进去,就可以直接加载文件夹下的所有class文件
//相当于把这个路径设置为源码路径之一
//获取URLClassLoader的addURL方法
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
boolean accessible = method.isAccessible();
try {
//如果方法没有权限访问,则设置可访问权限
if (accessible == false) {
method.setAccessible(true);
}
// 设置类加载器
URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
// 将当前类路径加入到类加载器中
method.invoke(classLoader, classFolder.toURI().toURL());
} finally {
//把方法的权限设置回去
method.setAccessible(accessible);
}
//获取文件夹中所有的.class文件
Set<File> files = FileUtil.getFilesForSuffix(classFolder, ".class");
for (File file : files) {
// 把文件名称转化为,和java.lang.String类似的全类名
String className = file.getAbsolutePath();
className = className.substring(classFolder.getAbsolutePath().length() + 1, className.length() - 6);
className = className.replace(File.separatorChar, '.');
// 加载Class类
Class.forName(className);
}
}
}
复制代码
III。テストのプロキシオブジェクト
努力の一連の後、最終的には、動的プロキシの準備を完了したテスト・フェーズに入ります
体を調製するための1 Invokeメソッド
public class MyInvocationHandler implements InvocationHandler {
private Bird bird;
public MyInvocationHandler(Bird bird) {
this.bird = bird;
}
@Override //第一个参数为代理对象,第二个参数为方法对象,后面的参数为方法参数
public Object invoke(Object proxy, Method method, Object... args) {
String dateString = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(System.currentTimeMillis());
System.out.println(dateString + "小鸟起飞");
try {
Object invoke = method.invoke(bird, args);
return invoke;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
复制代码
2.テストプロキシオブジェクト
public class MyProxyTest {
@Test
public void testProxy() {
Flyable flyable = MyProxy.newProxyInstance(Flyable.class, new MyInvocationHandler(new Bird()));
flyable.fly(1000);
}
}
复制代码
コンソールを印刷
2019-07-25 20:44:59
我是小鸟,我开始飞了
我是小鸟,我飞了1000毫秒
复制代码