JAVA动态代理模拟

JAVA动态代理模拟

       所谓代理,就是有一个代理类和一个被代理类,代理类帮被代理类执行相关的动作,如房产中介帮房东代理出租房屋,当有客户租房子时,客户只要找房产中介就可以了,中介会帮房东去执行租出去这个动作。当然,中介可以在中间做一些事情,如带客户看房子,最主要的是万恶的中介会收中介费,扯远了。。。。。言归正传

      代理分类

       代理分为静态代理和动态代理两种,不管静态代理还是动态代理,代理类和被代理累一般都是实现同一个接口。

      静态代理

      所谓静态代理,就是如果被代理类的方法在执行之前或之后需要多种操作,那代理类就要写多种,如定义一个接口Flyable(飞行物),有一个fly()方法。有一个被代理类Bird实现了这个接口,现在要计算这只鸟飞行了多久,那我们就要在这只鸟飞行之前记录下时间,飞行之后记录下时间。代码如下:

package com.sf.simba.dynamicproxy.fly;
/**
 * 
 * @author -Simba-
 *
 */
public interface Flyable {
   void fly();
}


package com.sf.simba.dynamicproxy.fly;

import java.util.Random;
/**
 * 被代理类
 * @author -Simba-
 *
 */
public class Bird implements Flyable {

	@Override
	public void fly() {
		System.out.println("The bird is flying.......");
		try {
			Thread.sleep(new Random().nextInt(2000));
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}


package com.sf.simba.dynamicproxy.fly;
/**
 *  时间代理类
 *  代理类和被代理类实现同一个接口
 * @author -Simba-
 *
 */
public class TimeBirdProxy implements Flyable {
	private Flyable flyable;
	
	public TimeBirdProxy(Flyable flyable){
		this.flyable = flyable;
	}
	@Override
	public void fly() {
		long start = System.currentTimeMillis();
		flyable.fly();
		long end = System.currentTimeMillis();
		System.out.println("The Flyable has flown "+(end-start)+" millisecond");


	}


}



package com.sf.simba.dynamicproxy.fly;
/**
 * 测试类
 * @author -Simba-
 *
 */
public class ProxyMain {

	public static void main(String[] args) {
		Flyable bird = new Bird();
		Flyable timeproxy = new TimeBirdProxy(bird);
		timeproxy.fly();

	}
}

测试结果如下:

The bird is flying.......
The Flyable has flown 1774 millisecond

结果测试可以记录时间了。


    如果现在我要在被代理类Bird调用fly()方法后要记录下日志,那是不是要在写一个记录日志的代理类,那如果我要在调用之前做一下权限验证呢?那我是不是要在写一个权限验证的代理类,如果我还有更多的需求呢,那我这个代理类是不是要一直写下去?在比如,我现在要代理一个不是Flyable接口的接口但也是记录时间、记录日志、权限校验。。。。。。。。那这样就会导致类爆炸,对以后的维护带来非常的不便,那我们有没有办法让这个代理类动态生成呢?就是这个代理类不管代理什么我们都可以生成,那这样就好维护多了。

     动态代理

          从上面的例子中我们可以发现,静态代理会导致类爆炸,难以维护,那么如果这个代理类如果能够动态生成就好了,那么怎么样才能让我们这个代理类动态生成呢,那就是让这个代理类变成一个字符串,动态的改变这个字符串,然后动态编译成字节码,通过ClassLoad装载字节码生成二进制文件到内存,然后生成实例对象(代理对象),然后执行这个代理对象。代码如下

         

package com.sf.simba.dynamicproxy.fly;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;

import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

/**
 * 动态代理生成类
 * @author -Simba-
 *
 */
public class Proxy {	
	public static Object newProxyInstance() throws Exception{
		 String rn = "\r\n";
		   String classStr = "package com.sf.simba.dynamicproxy.fly; " +rn+
		            "import com.sf.simba.dynamicproxy.fly.Flyable;" +rn+
				   " public class TimeBirdProxy implements Flyable { " + rn+
				   "    private Flyable flyable; " + rn+
				   "    public TimeBirdProxy(Flyable flyable){ " + rn+
				   "      this.flyable = flyable; " + rn+
				   "    } " + rn+
				   "  @Override " + rn+
				   "  public void fly() { " + rn+
				   "    long startTime = System.currentTimeMillis(); " + rn+
				   "    flyable.fly(); " + rn+
				   "        long endTime = System.currentTimeMillis(); " + rn+
				   "        System.out.println(\" TimeBirdProxy: The Flyable has flown \" + (endTime - startTime)+\" millisecond\"); " + rn+
				   "  } " + rn+
				   "\n" + rn+
				   "}";
		   
		   //定义动态文件生成到哪里叫什么名字
		   String fileName = System.getProperty("user.dir")+"/src/com/sf/simba/dynamicproxy/fly/TimeBirdProxy.java";
		   File file = new File(fileName);
		   FileWriter fw = new FileWriter(file);
		   //把字符串写入文件
		   fw.write(classStr);
		   fw.flush();
		   fw.close();
		   
		   //获得编译器
		   JavaCompiler complier = ToolProvider.getSystemJavaCompiler();
		   StandardJavaFileManager fileManager = complier.getStandardFileManager(null, null, null);
		   Iterable<? extends JavaFileObject> units = fileManager.getJavaFileObjects(fileName);
		   CompilationTask task = complier.getTask(null, fileManager, null, null, null, units);
		   //编译,生成字节码
		   task.call();
		   fileManager.close();
		   
		   URL[] url = new URL[]{new URL("file:/"+System.getProperty("user.dir")+"/src/")};
		   //加载字节码
		   URLClassLoader classloder = new URLClassLoader(url);
		   Class<?> loadClass = classloder.loadClass("com.sf.simba.dynamicproxy.fly.TimeBirdProxy");
		   //获得代理对象构造方法
		   Constructor<?> constructor = loadClass.getConstructor(Flyable.class);
		   //生成代理对象实例
		   
		   Object newInstance = constructor.newInstance(new Bird());//new Bird()为被代理对象
		   System.out.println(newInstance);
		   return newInstance;
		   
		   
	}

}
测试类如下:

package com.sf.simba.dynamicproxy.fly;
/**
 * 测试类
 * @author -Simba-
 *
 */
public class ProxyMain {

	public static void main(String[] args) throws Exception {
		//Flyable bird = new Bird();
		Flyable timeproxy = (Flyable)Proxy.newProxyInstance();
		timeproxy.fly();

	}
}

测试结果:

com.sf.simba.dynamicproxy.fly.TimeBirdProxy@100a15d
The bird is flying.......
 TimeBirdProxy: The Flyable has flown 767 millisecond

从上面这个例子可以看出,我不需要这个TimeBirdProxy类了,这个类被Proxy类自动生成了,但这样只能生成时间的代理类,如果要生成日志的代理类我们还是要改字符串,这明显不是我们想要的,所以我们要动态生成fly这个方法,然后在执行。我们需要这样一个接口,接口里的方法能够动态执行被代理类的方法,而代理类持有这个接口的引用,在调用fly()这个方法时调用这个接口里的方法,我们定义这个接口为InvocationHandler,方法为Invoke,这里有点绕,容易晕,先上一个类图。





代码如下:

 package com.sf.simba.dynamicproxy.fly;


import java.lang.reflect.Method;
/**
 * 动态代理类持有此接口的引用
 * @author -Simba-
 *
 */
public interface InvocationHandler {
	 public void invoke(Object o,Method m);
}

时间处理类如下:

package com.sf.simba.dynamicproxy.fly;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
 * 时间记录处理类
 * @author -Simba-
 *
 */
public class TimeHandler implements InvocationHandler {
    /**
     * 这个target为被代理类
     */
	private Object target;
	
	private long starttime;
	private long endtime;
	
	
	public TimeHandler(Object target){
		this.target = target;
	}
	
	public void pre(){
		starttime = System.currentTimeMillis();
	}
	/**
	 * 为简单起见,假设被代理对象的方法没有参数,在JDK中的动态代理中是有参数的
	 */
	@Override
	public void invoke(Object o, Method m) {
		
         pre();//方法调用前的操作
         try {
			m.invoke(target, null);
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
         end();//方法调用后的操作
         System.out.println(" TimeBirdProxy: The Flyable has flown " + (endtime - starttime)+" millisecond");
	}
	
	public void end(){
		endtime = System.currentTimeMillis();
	}

}


Proxy类变为(注意持有的是InvocationHandler的引用了,而不是Flyable的引用,在生成构造方法的时候传入的是InvocationHandler类):

package com.sf.simba.dynamicproxy.fly;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;

import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

/**
 * 动态代理生成类
 * @author -Simba-
 *
 */
public class Proxy {

	
	public static Object newProxyInstance(InvocationHandler handler) throws Exception{
		 String rn = "\r\n";
		   String classStr = "package com.sf.simba.dynamicproxy.fly; " +rn+
		            "import com.sf.simba.dynamicproxy.fly.Flyable;" +rn+
		            "import com.sf.simba.dynamicproxy.fly.InvocationHandler;" +rn+
				   " public class TimeBirdProxy implements Flyable { " + rn+
				   "    private InvocationHandler handler; " + rn+
				   "    public TimeBirdProxy(InvocationHandler handler){ " + rn+
				    "    super(); " + rn+
				   "      this.handler = handler; " + rn+
				   "    } " + rn+
				   "  @Override " + rn+
				   "  public void fly() { " + rn+
				   "try { "+rn+
				  " java.lang.reflect.Method md = Flyable.class.getMethod(\"fly\");"+rn+
				   " handler.invoke(this,md);"+rn+
				  "}catch(Exception e){" +rn+
				  " e.printStackTrace();" +rn+
				  "}"+rn+
				   "  } " + rn+
				   "\n" + rn+
				   "}";
		   System.out.println("动态生成的代理类为:\n"+classStr);
		   //定义动态文件生成到哪里叫什么名字
		   String fileName = System.getProperty("user.dir")+"/src/com/sf/simba/dynamicproxy/fly/TimeBirdProxy.java";
		   File file = new File(fileName);
		   FileWriter fw = new FileWriter(file);
		   //把字符串写入文件
		   fw.write(classStr);
		   fw.flush();
		   fw.close();
		   
		   //获得编译器
		   JavaCompiler complier = ToolProvider.getSystemJavaCompiler();
		   StandardJavaFileManager fileManager = complier.getStandardFileManager(null, null, null);
		   Iterable<? extends JavaFileObject> units = fileManager.getJavaFileObjects(fileName);
		   CompilationTask task = complier.getTask(null, fileManager, null, null, null, units);
		   //编译,生成字节码
		   task.call();
		   fileManager.close();
		   
		   URL[] url = new URL[]{new URL("file:/"+System.getProperty("user.dir")+"/src/")};
		   //加载字节码
		   URLClassLoader classloder = new URLClassLoader(url);
		   Class<?> loadClass = classloder.loadClass("com.sf.simba.dynamicproxy.fly.TimeBirdProxy");
		   //获得代理对象构造方法
		   Constructor<?> constructor = loadClass.getConstructor(InvocationHandler.class);
		   //生成代理对象实例
		   
		   Object newInstance = constructor.newInstance(handler);//new Bird()为被代理对象
		  System.out.println("-------------------生成的对象类为:"+newInstance.getClass().getName());
		   return newInstance;
		   
		   
	}

}

测试类如下:

package com.sf.simba.dynamicproxy.fly;
/**
 * 测试类
 * @author -Simba-
 *
 */
public class ProxyMain {


	public static void main(String[] args) throws Exception {
		Flyable bird = new Bird();
		InvocationHandler handler = new TimeHandler(bird);
		Flyable timeproxy = (Flyable)Proxy.newProxyInstance(handler);
		timeproxy.fly();


	}
}
生成的结果如下:

动态生成的代理类为:
package com.sf.simba.dynamicproxy.fly; 
import com.sf.simba.dynamicproxy.fly.Flyable;
import com.sf.simba.dynamicproxy.fly.InvocationHandler;
 public class TimeBirdProxy implements Flyable { 
    private InvocationHandler handler; 
    public TimeBirdProxy(InvocationHandler handler){ 
    super(); 
      this.handler = handler; 
    } 
  @Override 
  public void fly() { 
try { 
 java.lang.reflect.Method md = Flyable.class.getMethod("fly");
 handler.invoke(this,md);
}catch(Exception e){
 e.printStackTrace();
}
  } 


}
-------------------生成的对象类为:com.sf.simba.dynamicproxy.fly.TimeBirdProxy
The bird is flying.......
 TimeBirdProxy: The Flyable has flown 1824 millisecond


             通过动态生成的代理类我们可以看出,在代理类的fly()方法中,并没有去执行Bird的fly()方法(持有的引用有Flyable改为了InvocationHandler),而是通过反射机制获取Flyable的fly对应的Method,把Method作为参数传给InvocationHandler的invoke方法,然后执行InvocationHandler的invoke方法,而在这个invoke方法又去掉Flyable的fly对应的反射的invoke方法,在调用这个invoke方法前已经先把Bird这个对象传入,并在调用之前或之后加入时间的计算。如果要生成日志代理对象,只有实现InvocationHandler的invoke方法,并在方法前或者后加上需要的操作。

            但是这个还是不够完美,还是没有达到我们可以代理任何接口的动态代理,如果我们要实现代理任何接口的代理,那我们就要把Flyable的接口换成动态的就好。代码如下:

package com.sf.simba.dynamicproxy.fly;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

/**
 * 动态代理生成类
 * @author -Simba-
 *
 */
public class Proxy {

	
	public static Object newProxyInstance(Class<?> infc,InvocationHandler handler) throws Exception{
		 String rn = "\r\n";
		 String methodStr = "";
		 //获得此接口的所有方法,即所有方法都要加代理
		 Method[] methods = infc.getMethods();
			 for(Method m : methods){
				 methodStr +="@Override"+rn+ 
						 "public  "+m.getReturnType().getName()+"  "+m.getName()+"(){"+rn+
						  "try { "+rn+
						  " java.lang.reflect.Method md = "+infc.getName()+".class.getMethod(\""+m.getName()+"\");"+rn+
						  " handler.invoke(this,md);"+rn+
						  "}catch(Exception e){" +rn+
						  " e.printStackTrace();" +rn+
						  "}"+rn+
						  "}"+rn;
			 }
			
			 String classStr = "package com.sf.simba.dynamicproxy.fly; " +rn+
			            "import com.sf.simba.dynamicproxy.fly.Flyable;" +rn+
			            "import com.sf.simba.dynamicproxy.fly.InvocationHandler;" +rn+
			        " public class TimeBirdProxy implements  "+ infc.getName()+" { "+ rn +
					
					" private InvocationHandler handler;"+rn+
					
					" public TimeBirdProxy(InvocationHandler handler){"+rn +
					"	super();" +rn +
					" this.handler = handler;"+rn +
					" }"+rn +
					
					methodStr
					+ rn +
				"}";
		   System.out.println("动态生成的代理类为:\n"+classStr);
		   //定义动态文件生成到哪里叫什么名字
		   String fileName = System.getProperty("user.dir")+"/src/com/sf/simba/dynamicproxy/fly/TimeBirdProxy.java";
		   File file = new File(fileName);
		   FileWriter fw = new FileWriter(file);
		   //把字符串写入文件
		   fw.write(classStr);
		   fw.flush();
		   fw.close();
		   
		   //获得编译器
		   JavaCompiler complier = ToolProvider.getSystemJavaCompiler();
		   StandardJavaFileManager fileManager = complier.getStandardFileManager(null, null, null);
		   Iterable<? extends JavaFileObject> units = fileManager.getJavaFileObjects(fileName);
		   CompilationTask task = complier.getTask(null, fileManager, null, null, null, units);
		   //编译,生成字节码
		   task.call();
		   fileManager.close();
		   
		   URL[] url = new URL[]{new URL("file:/"+System.getProperty("user.dir")+"/src/")};
		   //加载字节码
		   URLClassLoader classloder = new URLClassLoader(url);
		   Class<?> loadClass = classloder.loadClass("com.sf.simba.dynamicproxy.fly.TimeBirdProxy");
		   //获得代理对象构造方法
		   Constructor<?> constructor = loadClass.getConstructor(InvocationHandler.class);
		   //生成代理对象实例
		   
		   Object newInstance = constructor.newInstance(handler);//new Bird()为被代理对象
		   System.out.println("-------------------生成的对象类为:"+newInstance.getClass().getName());
		   return newInstance;
		   
		   
	}

}

测试类:

package com.sf.simba.dynamicproxy.fly;
/**
 * 测试类
 * @author -Simba-
 *
 */
public class ProxyMain {

	public static void main(String[] args) throws Exception {
		Flyable bird = new Bird();
		InvocationHandler handler = new TimeHandler(bird);
		Flyable timeproxy = (Flyable)Proxy.newProxyInstance(Flyable.class,handler);
		timeproxy.fly();

	}
}

生成结果如下:


动态生成的代理类为:
package com.sf.simba.dynamicproxy.fly; 
import com.sf.simba.dynamicproxy.fly.Flyable;
import com.sf.simba.dynamicproxy.fly.InvocationHandler;
 public class TimeBirdProxy implements  com.sf.simba.dynamicproxy.fly.Flyable { 
 private InvocationHandler handler;
 public TimeBirdProxy(InvocationHandler handler){
super();
 this.handler = handler;
 }
@Override
public  void  fly(){
try { 
 java.lang.reflect.Method md = com.sf.simba.dynamicproxy.fly.Flyable.class.getMethod("fly");
 handler.invoke(this,md);
}catch(Exception e){
 e.printStackTrace();
}
}


}
-------------------生成的对象类为:com.sf.simba.dynamicproxy.fly.TimeBirdProxy
The bird is flying.......
 TimeBirdProxy: The Flyable has flown 1960 millisecond


         至此,动态代理的模拟已经完成,而JDK下的动态代理比这个更加复杂,加上了参数和一个类实现多个接口的情况,并且JDK动态代理中是直接把java类生成二进制文件,而不是像本文中通过JavaComplier生成字节码然后在生成二进制加入到内存。









         


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值