之前的文章讲了动态代理的实现原理,这里我们来手写一下jdk的动态代理(支持实现多接口和带参数的方法)。主要原理就是生成一个和实现目标对象相同接口的一个代理对象,我们调用方法的时候实际调用的是生成代理对象的方法。
import java.lang.reflect.Method;
public interface JtsInvocationHandler {
/**
*
* @param proxy 被代理对象
* @param method 调用的方法
* @param objects 方法的参数
* @return
* @throws Throwable
*/
Object invoke(Object proxy, Method method,Object... objects) throws Throwable;
}
public class JtsClassLoader extends ClassLoader {
private File classPathFile;
public JtsClassLoader() {
String classPath = this.getClass().getResource("").getPath();
this.classPathFile = new File(classPath);
}
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
String className = this.getClass().getPackage().getName() + "." + name;
if (classPathFile != null) {
//创建同名的class文件
File classFile = new File(classPathFile, name.replaceAll("\\.", "/") + ".class");
if (classFile.exists()) {//如果class文件已经存在
FileInputStream fis = null;
ByteArrayOutputStream out = null;
try {
fis = new FileInputStream(classFile);
out = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int len;
while ((len = fis.read(buff)) != -1) {
out.write(buff, 0, len);
}
return defineClass(className, out.toByteArray(), 0, out.size());//将字节转换为class
} catch (Exception e) {
e.printStackTrace();
} finally {//关闭流操作
try {
if (null != out) {
out.close();
}
if (null != fis) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
return super.findClass(name);
}
}
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;
import java.util.concurrent.atomic.AtomicLong;
public class JtsProxy {
public static final String ln = "\r\n";
public static Object getProxyInstance(JtsClassLoader classLoader, Class> interfaces[],
JtsInvocationHandler invocationHandler) throws Exception {
AtomicLong proxyNum = new AtomicLong(0);
String className = "$Proxy" + proxyNum.getAndIncrement();//存储生成的类名
//1.动态生成源代码
String src = generateCode(interfaces, className);
//2.Java文件输出到磁盘
String filePath = JtsProxy.class.getResource("").getPath();
//System.out.println(filePath); //查看Java文件
File f = new File(filePath + className + ".java");
FileWriter fw = new FileWriter(f);
fw.write(src);
fw.flush();
fw.close();
//3.将生成的Java文件编译成.class文件 JavaComiler since 1.6
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(className);
Constructor constructor = proxyClass.getConstructor(JtsInvocationHandler.class);
//删除文件
//f.delete(),这里为了方便看到Java代码就没有删除
//5.返回字节码重组后的代理对象
return constructor.newInstance(invocationHandler);
}
//生成代理类的源代码
private static String generateCode(Class> interfaces[], String className) {
StringBuffer sb = new StringBuffer();
sb.append(JtsProxy.class.getPackage() + ";" + ln)
.append("import java.lang.reflect.Method;" + ln)
.append("public class "+className+" implements ");
for (int i = 0; i < interfaces.length; i++) {//实现多个接口
Class> interf = interfaces[i];
if (i == 0) {
sb.append(interf.getName());
} else {
sb.append("," + interf.getName());
}
}
sb.append("{" + ln)
.append(" JtsInvocationHandler h;" + ln + ln)
.append(" public " + className + "(JtsInvocationHandler h) {" + ln)//构造方法
.append(" this.h = h;" + ln)
.append(" }" + ln + ln);
for (Class interf : interfaces) {
for (Method m : interf.getMethods()) {
//判断返回值是否为空
boolean isVoid = "void".equals(m.getReturnType().getName());
//方法的所有参数类型
Class>[] parameterTypes = m.getParameterTypes();
//方法的参数类型集合
StringBuffer paramTypeStr = new StringBuffer();
//方法的变量名集合
StringBuffer paramNameStr = new StringBuffer();
//存储参数类型+变量名
StringBuffer args = new StringBuffer();
for (int i = 0; i < parameterTypes.length; i++) {
String parameterName = parameterTypes[i].getName();
String arg = "arg" + i;
if (i == 0) {
paramTypeStr.append(parameterName + ".class");
paramNameStr.append(arg);
args.append(parameterName + " " + arg);
} else {
paramTypeStr.append("," + parameterName + ".class");
paramNameStr.append("," + arg);
args.append("," + parameterName + " " + arg);
}
}
sb.append(" @Override" + ln)
.append(" public " + m.getReturnType().getName() + " " + m.getName() + "(" + args + ") {" + ln)
.append(" try{" + ln)
.append(" Method m = " + interf.getName() + ".class.getMethod(\"" + m.getName() + "\"");
if (!"".equals(paramTypeStr.toString())) {
sb.append("," + paramTypeStr.toString());
}
sb.append(");" + ln + " ");
if (!isVoid) {
sb.append("return (" + m.getReturnType().getName() + ")");
}
//关键所在,就是这里调用了InvocationHandler的invoke方法。
sb.append("this.h.invoke(this,m");
if (!"".equals(paramNameStr.toString())) {
sb.append("," + paramNameStr);
}
sb.append(");" + ln)
.append(" }catch(Throwable e){" + ln)
.append(" e.printStackTrace();" + ln)
.append(" }" + ln);
if (!isVoid) {
sb.append(" return null;" + ln);
}
sb.append(" }" + ln + ln);
}
}
sb.append("}" + ln);
return sb.toString();
}
}
ps: 在实现方法的多个参数类型和参数这里,不知道各种大神有没有更好的写法,欢迎吐槽。这里暂时没做多个接口有相同方法的判断。
4.1测试接口1
public interface InterfaceOne {
void add(int i,int j);
}
4.2测试接口2
public interface InterfaceTwo {
String getStr(String str1, String str2);
void say();
}
4.3接口的实现
public class ServiceImpl implements InterfaceOne,InterfaceTwo {
@Override
public void add(int i,int j) {
System.out.println(i+j);
}
@Override
public String getStr(String str1,String str2) {
return str1+"," + str2;
}
@Override
public void say() {
System.out.println("I can say Hello world");
}
}
4.4 生成代理对象的工厂
import java.lang.reflect.Method;
public class ProxyFactory {
Object target;
public ProxyFactory(Object target){
this.target = target;
}
public Object getProxyInstance() throws Exception{//获取代理对象实例
return JtsProxy.getProxyInstance(
new JtsClassLoader(),
target.getClass().getInterfaces(),
new JtsInvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object... args) throws Throwable {
System.out.println("begin transaction");
Object returnValue = method.invoke(target, args);
System.out.println("commit transaction");
return returnValue;
}
}
);
}
}
4.5测试主程序
public class AppTest {
public static void main(String[] args) throws Exception{
InterfaceOne target = new ServiceImpl();
InterfaceOne proxy = (InterfaceOne)new ProxyFactory(target).getProxyInstance();
proxy.add(1,2);
InterfaceTwo proxy2 = (InterfaceTwo)new ProxyFactory(target).getProxyInstance();
System.out.println(proxy2.getStr("hello", "world"));
proxy2.say();
}
}
首先看下生成的代理类代理:
总结: 动态代理的做法是动态生成一个代理对象继承和被代理对象一样的接口,然后实现和被代理对象相同的方法,调用的时候是通过调用的InvocationHandler的invoke()方法的实现,在invoke方法里面就可以自定义我们想要做的东西了。 我写的生成代理对象只是生成了接口的方法,没有生成被代理对象原有的方法。jdk的动态代理也生成了被代理对象的原有的方法,包括equals()和hashcode()方法,要写的话写法和上面也是一样的。