动态代理的动态编译实现
人说,善于总结才会成长。回顾这个帐号闲置了快两年了,还是一片空白。工作了也有那么一段时间了,期间很多人给予过我帮助,我也帮助过很多人。由于喜欢研究稀奇古怪的玩意,所以也做了一些稀奇古怪的成果,共享出来,他或许没有什么实用价值但他也许能给你带去灵感。
接触过动态代理的且不说接触过Spring也应该接触过Proxy,那么Proxy里面又是怎么一回事呢。我不太喜欢Proxy的使用方式,感觉不直观,于是我打算自己来弄了。
首先对一个已有类的方法进行代理,我又不想去改动这个类让它实现什么接口,让它继承什么父类。那么我们先来写一个类,他有一个需要被代理的方法,为了增加一点难度这个方法带返回值和参数。
package myproxy.realsub;public class RealSubject { public String greeting(String name){ System.out.println("How are u? "+name); return "Fine!"; }}package myproxy.proxy.ii;public interface Interceptor {public void before();public void after();}package myproxy.realsub;public class Interceptor implements myproxy.proxy.ii.Interceptor{long begin;long end;public void before() {System.out.println("On the street, a guy looks familiar.");begin=System.currentTimeMillis();}public void after() {end=System.currentTimeMillis();System.out.println("your boring conversation takes:"+(end-begin)+"ms");}}package myproxy.realsub;import myproxy.proxy.annotation.CutPoint;import myproxy.proxy.annotation.SetProxy;@SetProxy("myproxy.realsub.Interceptor")public class RealSubject {@CutPointpublic String greeting(String name){System.out.println("How are u? "+name);return "Fine!";}public void hello(){System.out.println("hello");}}package myproxy.proxy.annotation;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME) @Documentedpublic @interface CutPoint {}package myproxy.proxy.annotation;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME) @Documentedpublic @interface SetProxy {String value();}package myproxy.proxy.proxy;import java.io.PrintWriter;import java.io.StringWriter;import java.lang.reflect.Method;import java.net.URI;import java.net.URL;import java.net.URLClassLoader;import java.util.Arrays;import javax.tools.Diagnostic;import javax.tools.DiagnosticCollector;import javax.tools.JavaCompiler;import javax.tools.JavaFileObject;import javax.tools.SimpleJavaFileObject;import javax.tools.ToolProvider;import javax.tools.JavaCompiler.CompilationTask;import myproxy.proxy.annotation.CutPoint;import myproxy.proxy.annotation.SetProxy;public class ProxyFactory { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>(); public Object getProxyInstance(String className) {Object forReturn=null;try {Class<?> clazz = Class.forName(className);String interceptor=null;String interceptor_interface=null;if(clazz.isAnnotationPresent(SetProxy.class)){SetProxy sp=clazz.getAnnotation(SetProxy.class);interceptor=sp.value();interceptor_interface=Class.forName(interceptor).getInterfaces()[0].getName();}else throw new RuntimeException("No interceptor class annotation found!");Method[] methods = clazz.getDeclaredMethods();StringWriter writer = new StringWriter();PrintWriter out = new PrintWriter(writer);String src=null;src="public class $Proxy extends "+className+" {"+"\r\n "+className + " obj=new " + className + "();"+"\r\n " +interceptor_interface+" ict = new "+interceptor+"();"+"\r\n";for (Method m : methods) {src += " public " + m.getReturnType().getName() + " " + m.getName() + "(";int paraslength=m.getParameterTypes().length;for (int i = 0; i < paraslength; i++) {src += m.getParameterTypes()[i].getName() + " arg" + i + ",";}if(src.lastIndexOf(",")==src.length()-1)src = src.substring(0, src.length() - 1);src += "){"+"\r\n";if (m.isAnnotationPresent(CutPoint.class))src += " ict.before();"+"\r\n";if(!m.getReturnType().getName().equals("void"))src += " "+m.getReturnType().getName()+" re = obj."+m.getName()+"(";elsesrc +=" obj."+m.getName()+"(";for(int i=0;i<paraslength;i++){src+="arg"+i+",";}if(src.lastIndexOf(",")==src.length()-1)src=src.substring(0,src.length()-1);src+=");"+"\r\n";if (m.isAnnotationPresent(CutPoint.class))src+=" ict.after();"+"\r\n";if(!m.getReturnType().getName().equals("void"))src+=" return re;"+"\r\n";src+="\r\n"+" }"+"\r\n";}src+="}";out.println(src);out.close(); JavaFileObject file = new JavaSourceFromString("$Proxy", writer.toString()); Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(file); CompilationTask task = compiler.getTask(null, null, diagnostics, null, null, compilationUnits); boolean success = task.call(); for (Diagnostic<?> diagnostic : diagnostics.getDiagnostics()) { System.out.println(diagnostic.getCode()); System.out.println(diagnostic.getKind()); System.out.println(diagnostic.getPosition()); System.out.println(diagnostic.getStartPosition()); System.out.println(diagnostic.getEndPosition()); System.out.println(diagnostic.getSource()); System.out.println(diagnostic.getMessage(null)); } if (success) { try {URL[] urls=new URL[]{new URL("file:/"+System.getProperty("user.dir")+"/")};URLClassLoader ul=new URLClassLoader(urls);Class<?> cla=ul.loadClass("$Proxy");forReturn=cla.newInstance(); } catch ( Exception e) { e.printStackTrace(); } }} catch (Exception e) {e.printStackTrace();}return forReturn;}}class JavaSourceFromString extends SimpleJavaFileObject {final String code;JavaSourceFromString(String name, String code) {super(URI.create("string:///" + name.replace('.','/') + Kind.SOURCE.extension),Kind.SOURCE);this.code = code;}public CharSequence getCharContent(boolean ignoreEncodingErrors) {return code;}}package myproxy.test;import myproxy.proxy.proxy.ProxyFactory;import myproxy.realsub.RealSubject;public class Test {public static void main(String[] args) {//RealSubject rs = new RealSubject();RealSubject rsproxy = (RealSubject)new ProxyFactory().getProxyInstance("myproxy.realsub.RealSubject");//rs.greeting("billy");String res=rsproxy.greeting("Billy");System.out.println(res);//rsproxy.hello();}}