`
nikoloss
  • 浏览: 33344 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

动态代理的动态编译实现

阅读更多
    人说,善于总结才会成长。回顾这个帐号闲置了快两年了,还是一片空白。工作了也有那么一段时间了,期间很多人给予过我帮助,我也帮助过很多人。由于喜欢研究稀奇古怪的玩意,所以也做了一些稀奇古怪的成果,共享出来,他或许没有什么实用价值但他也许能给你带去灵感。
    接触过动态代理的且不说接触过Spring也应该接触过Proxy,那么Proxy里面又是怎么一回事呢。我不太喜欢Proxy的使用方式,感觉不直观,于是我打算自己来弄了。
    首先对一个已有类的方法进行代理,我又不想去改动这个类让它实现什么接口,让它继承什么父类。那么我们先来写一个类,他有一个需要被代理的方法,为了增加一点难度这个方法带返回值和参数。
   
package myproxy.realsub;

public class RealSubject {
   public String greeting(String name){
      System.out.println("How are u? "+name);
      return "Fine!";
   }
}

现在我们要对这个方法进行代理为这个方法前后加上其他的业务逻辑。
一前一后,所以我们暂且定为before()和after()吧。于是制定了一个标准接口
package myproxy.proxy.ii;
public interface Interceptor {
	public void before();
	public void after();
}

完美了,接下来我们只需要做一个代理类它只需要实现这两个方法,这两个方法就是要在greeting()方法执行前后执行的。
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");
	}
}

好了,问题来了,或许我的RealSubject有很多方法,但我不希被全部代理,现在怎么办?这很有考虑的必要,实际当中我需要分别为不同的方法代理不同的实现,不是吗?OK,我想大家最容易想到的就是配置文件了,的确我就是被大量的配置文件整的头疼不已,难道我们没有更加直观的方法了吗?接触了一点spring觉得annotation就可以充当很不错的配置文件,于是我打算使用它来做配置。于是这么又有了思路。
package myproxy.realsub;

import myproxy.proxy.annotation.CutPoint;
import myproxy.proxy.annotation.SetProxy;

@SetProxy("myproxy.realsub.Interceptor")
public class RealSubject {
	@CutPoint
	public String greeting(String name){
		System.out.println("How are u? "+name);
		return "Fine!";
	}
	
	public void hello(){
		System.out.println("hello");
	}
}


SetProxy定义的就是我的代理实现类。CutPoint就是需要代理的标记。
这样就说明我的greeting方法需要被代理。
OK,我们在把这两个annotation的接口做出来。
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.METHOD)
@Retention(RetentionPolicy.RUNTIME) 
@Documented
public @interface CutPoint {

}

SetProxy如下
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) 
@Documented
public @interface SetProxy {
	String value();
}

(关于annotation的知识在此就不做描述了,大家有什么不明白的就百度一下吧!)
    OK,走到了这一步,我想大家累积的疑惑就越来越多了。
Interceptor的两个方法怎么注入到greeting前后呢?
下面我们就要实现这个重头戏功能了。
    大家不妨想象一下构思一下,我要怎么做才能在程序运行到greeting的时候停下来转而执行我Interceptor的方法。我的思路是有这么一个类class $Proxy,它里面有两个对象既有RealSubject rs=new RealSubject(),又有Interceptor ic=new Interceptor(),然后$Proxy执行它自己的greeting方法,它的greeting方法里面就是这么调用的
ic.before();
rs.greeting();
ic.after();
    OK,有了这个思路我们就得去实现它,这个$Proxy类不必真实存在,它完全是根据我们的需要才会出现的,所以我们得想法让它动态的诞生出来,这就意味着我们得动态创造一个类出来,天!你肯定会问,用程序写程序?Bingo!你回答对了,这就是我接下来要干的。JAVA promote API 里面提供了一个类JavaCompiler,它就是JAVA编译器,我们可以使用它去编译一段源码,那么现在我们的问题似乎就集中在怎么样去拼凑这段源码了,OK,有了这个思路我们就得去实现它。
一个类$Proxy,里面有两个对象,被代理对象和代理对象,一个和被代理方法同名同参数同返回值的方法(在此也就是greeting),还有什么问题?还有一个问题!就是这个类是动态拿到的假设我们通过一个工厂类成功的动态创造了这个$Proxy类,比如RealSubject rs=Factory.get("RealSubject");你能够去rs.greeting();这么调用吗?仔细想想,很显然不行,你怎么能把$Proxy类转化成RealSubject类呢?所以这个$Proxy类还需要继承RealSubject。这就是$Proxy的大致结构了,OK,有了这个思路我们就得去实现它!
这个ProxyFactory如下
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()+"(";
				else
					src +="      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;
	}
}

最主要的是那个src,也就是我们拼凑的$Proxy类的源码!好好去体会一下吧。关于JAVA编译器的内容在此不讲!
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();
	}
}

看看效果吧

On the street, a guy looks familiar.
How are u? Billy
your boring conversation takes:0ms
Fine!


总结:怎么样是不是比Proxy的使用更加直观,你不用去实现什么handler,你的本身的那个类也不用去实现接口,在使用过程中你只需要写一个代理类,before,after两个方法。通过annotation指定被代理方法。是不是非常直观,简便,初学者也可以去使用它。
分享到:
评论

相关推荐

    JAVA实现动态代理的简单流程

    动态代理,顾名思义,是在运行时动态创建代理对象的一种机制,它无需在编译期就确定代理类的具体实现,而是通过反射等技术在运行时自动生成代理类,从而达到灵活扩展原有接口功能的目的。这种机制广泛应用于AOP...

    AOP之JDK动态代理和CGLib动态代理

    代理对象在运行时动态生成,因此类图在编译期间并不知晓代理的存在。JDK动态代理的优点是它完全符合Java语言规范,不需要依赖额外的库,但缺点是只能对实现了接口的类进行代理,无法处理未实现接口的类。 **CGLib...

    一篇讲动态代理的好文 <动态代理的前世今生>

    - JDK提供了`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口来实现动态代理。通过这两个类/接口,可以在运行时动态地生成实现指定接口的代理类。 - 使用步骤: 1. 定义接口; 2. 实现`...

    静态代理和动态代理Demo

    在Java中,我们可以使用`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口来实现动态代理。`DynProxy.java`文件可能包含了动态代理的实现,它通常会在运行时根据接口生成代理对象,然后通过`...

    Android-一个android或java项目的编译时注解动态代理库

    Java中的`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口是实现动态代理的关键。在Android或Java项目中,动态代理常用于AOP(面向切面编程)。 本库“UniversalProxy”可能提供了一种统一...

    Java静态代理和动态代理

    Java提供了`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口来实现动态代理。动态代理类不需预先编写,而是由JVM在运行时根据接口动态生成。这样,我们可以通过接口而不是具体类来创建代理...

    Java 代理 代理模式 静态代理与动态代理 常见的动态代理实现 .md

    - **静态代理**:在程序编译时就已经确定代理类的具体实现方式。这意味着每次需要代理不同的操作时,都需要修改代理类的代码,这违反了软件工程中的开闭原则。 - **优点**:代码结构简单,易于实现。 - **缺点**:...

    Emit实现动态代理

    C#的Emit实现动态代理的过程大致如下: 1. **定义委托**:首先,我们需要定义一个委托类型,这个委托类型代表我们要代理的方法。例如,如果有一个名为`Execute`的方法,我们可以定义一个`Action`或者`Func`类型的...

    基于Java动态代理的AOP实现.zip

    本项目旨在通过Java的动态代理技术实现面向切面编程(AOP),涵盖了静态代理、JDK动态代理、CGLIB动态代理、AspectJ以及基于instrumentation的动态代理等多种实现方式。通过这些实现方式,可以在不修改原有代码的...

    Java动态代理详解; CGLib实现动态代理1

    Java 动态代理详解与 CGLib 实现 在 Java 中,代理模式是一种设计模式,它允许我们在不修改原有对象的基础上,为对象增加额外的功能。代理模式的核心在于代理类(Proxy)作为真实对象(RealSubject)的替代品,提供...

    c#动态读取代码并动态编译

    5. 测试:在单元测试中,动态编译可以用于生成模拟对象或代理类,以测试私有成员或模拟复杂的依赖关系。 总结来说,C#动态编译是一项强大而灵活的技术,它允许开发者在运行时生成、编译和执行代码,从而提高了开发...

    AOP技术及其在J2EE中动态代理实现

    接口型动态代理要求目标对象实现一个接口,然后创建一个代理类实现相同的接口。类型动态代理则允许代理目标类本身,它使用了字节码库(如CGLIB)在运行时生成目标类的子类。在AOP框架(例如Spring AOP)的支持下,...

    利用反射和动态代理机制实现自定义拦截器Interceptor

    利用反射和动态代理机制实现自定义拦截器Interceptor 在本文中,我们将探讨如何利用反射和动态代理机制来实现自定义拦截器Interceptor。拦截器Interceptor是一种常见的设计模式,用于在方法调用前后执行某些操作,...

    spring动态代理类的示范小版本,很简单的例子

    静态代理是在编译时就已经明确知道代理类和被代理类关系的方式,而Spring的动态代理则是在运行时生成代理类。在静态代理中,我们需要手动创建代理类并实现相同的方法,然后在代理类的方法中插入增强代码。相比于...

    包含静态代理和动态代理demo代码

    在这个“包含静态代理和动态代理demo代码”的压缩包中,我们可能会看到两种常见的Java代理实现方式的示例:静态代理和动态代理。 首先,我们来详细讲解静态代理。在静态代理中,代理类和真实类(目标类)都是在编译...

    静态代理、jdk动态代理、cglib动态代理

    Cglib 动态代理的实现方式是:我们首先需要定义一个类,然后使用 Cglib 库来生成该类的代理对象,该代理对象将拦截对被代理对象的所有方法调用,并控制对被代理对象的访问。 Cglib 动态代理的优点是:它的实现方式...

    动态代理的前世今生-绝对经典

    与静态代理(编译时已知)不同,动态代理在运行时可以生成代理类,使得我们可以在不修改原有代码的情况下扩展或修改其行为。这种灵活性使得动态代理成为实现诸如AOP(面向切面编程)、事件监听、事务管理等高级功能...

    静态代理和动态代理

    Java提供了`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口来实现动态代理。`Proxy`类用于创建代理对象,而`InvocationHandler`接口用于处理代理对象上的方法调用。 ```java // 创建...

Global site tag (gtag.js) - Google Analytics