模拟JDK动态代理-独立逻辑代码
实现动态代理可以简单的分为四个步骤,我们只需要分析清楚逻辑代码流程,代码什么的都不是事,下面给大家简单介绍说下我的实现步骤。
1. 分析业务代码,明白需要横切的逻辑代码,通过File创建代理类的源码;
2. 使用JavaCompiler将代理类的源码进行编译成字节码文件;
3. 利用反射原理将字节码加载到JVM内存中,并实例化代理对象;
4. 返回代理对象以供调用;
话不多说,直接上代码吧!demo中使用的是项目jar包,文末附上源码
DEMO–查询方法添加一个记录查询时间的功能
步骤1
–项目结构
–为query方法添加功能
编写Proxy代理类
package com.coffice.proxy;
/**
* Created by coffice on 2017-12-14.
*/
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import com.coffice.logic.ILogicExecute;
public class Proxy {
//代理类java文件
static File file;
//代理类java文件的内容
static String clazzContent="";
//需要添加逻辑处理java类的接口类
static Class invokeInterface;
//获取当前操作系统临时目录
static String rootDirectory=System.getProperty("java.io.tmpdir");
//公开此方法用来获取代理对像
public static Object getInstance(Class clazz,ILogicExecute preExcute,String ProxyFilDirectory) throws IOException, InstantiationException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException{
if(ProxyFilDirectory != null)Proxy.rootDirectory = ProxyFilDirectory;
File tempDirectory=new File(rootDirectory+"com\\proxy");
createJavaFileFromContext(tempDirectory, createClassContext(clazz, preExcute));
return compilerJavaFile(preExcute);
}
//公开此方法用来获取代理对像(重载)
/**
*
* @param clazz 需要代理的类
* @param preExcute 独立逻辑代码处理类的对象
* @return
*/
public static Object getInstance(Class clazz,ILogicExecute preExcute) throws IOException, InstantiationException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException{
File tempDirectory=new File(rootDirectory+"com\\proxy");
createJavaFileFromContext(tempDirectory, createClassContext(clazz, preExcute));
return compilerJavaFile(preExcute);
}
/**
* 使用JavaCompiler编译代理类
*
* @param preExcute 独立逻辑代码处理类的对象
* @return 返回代理对象
*/
static Object compilerJavaFile(ILogicExecute preExcute) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException{
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMange = compiler.getStandardFileManager(null, null, null);
Iterable it=fileMange.getJavaFileObjects(file.getPath());
compiler.getTask(null, fileMange, null, null, null, it).call();
URL url = new URL("file:/"+rootDirectory);
URL[] urls = new URL[]{url};
URLClassLoader urlClassLoader = new URLClassLoader(urls);
Class clazzProxy =urlClassLoader.loadClass("com.proxy.Proxy$");
Constructor constructor= clazzProxy.getConstructor(invokeInterface);
Object object =constructor.newInstance(preExcute);
return object;
}
/**
* 创建代理类的java文件
*
* @param directory 代理类存放的文件目录
* @param Context 创建的代理类的类容
* @throws IOException
*/
static void createJavaFileFromContext(File directory,String Context) throws IOException{
if(!directory.exists() && !directory.isDirectory()){
directory.mkdirs();
}
file=new File(directory+"\\Proxy$.java");
if(!file.exists()){
file.createNewFile();
}
FileWriter fw = new FileWriter(file);
fw.write(clazzContent);
fw.flush();
fw.close();
}
/**
* 生成代理类的内容
*
* @param clazz 需要代理的类
* @param preExcute 独立逻辑代码处理类的对象
* @return 返回代理类的内容
*/
static String createClassContext(Class clazz,ILogicExecute preExcute){
String line="\n";
String tab="\t";
String methodContent="";
//获取独立逻辑代码处理类所实现的接口
invokeInterface=preExcute.getClass().getInterfaces()[0];
//拼接代理类的头部以及构造函数
clazzContent+="package com.proxy;"+line
+"import java.lang.reflect.Method;"+line
+"import "+clazz.getName()+";"+line
+"import "+invokeInterface.getName()+";"+line
+"public class Proxy$ implements "+clazz.getSimpleName()+"{"+line
+tab+"private "+invokeInterface.getSimpleName()+" pre;"+line
+tab+"public Proxy$ ("+invokeInterface.getSimpleName()+" pre) {"+line
+tab+tab+" this.pre = pre;"+line
+tab+"}";
Method[] methods = clazz.getMethods();
//拼接代理类的方法(反射执行独立逻辑代码处理类中的方法,动态调用)
for(Method m : methods){
methodContent +=line+tab+"public void " + m.getName() +"(){"+line
+tab+"try {"+line
+tab+tab+"Method md = "+clazz.getSimpleName()+".class.getMethod(\""+m.getName()+"\");"+line
+tab+tab+"pre.invoke(md);"+line
+tab+"}catch(Exception e) {"+line
+tab+tab+"e.printStackTrace();"+line+tab+"}"+line
+tab+tab+"}";
}
clazzContent+=methodContent+line+line+tab+"}";
return clazzContent;
}
}
以上为Proxy.java核心代码,然后我们再看看逻辑处理java类的接口吧!
package com.coffice.logic;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public interface ILogicExecute {
void invoke(Method method) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
}
那么问题来了,为什么我需要提供这个接口呢?这个时候反射的魅力就出来了,大家看图!
XXX.invoke();
想必这个方法大家是很熟悉的了,我们可以需要执行的dao层的方法作为参数传递给我们编写的实现了ILogicExecute接口的类,至于为什么有这个接口呢,是为了把我们说需要独立的逻辑代码封装在我们的实现类中执行,这样才做到逻辑代码的动态分离,我们的实现类中需要提供一个构造函数,来传递我们的dao层处理逻辑代码的目标对象,最后目标对象和方法在我们的ILogicExecute的实现类中都拿到了,通过invoke()方法就能执行目标对象的主逻辑代码了,我们也可以在这之间加入我们附加的代码。