• springmvc 动态代理 JDK实现与模拟JDK纯手写实现。


    首先明白 动态代理和静态代理的区别;

         静态代理:①持有被代理类的引用  ② 代理类一开始就被加载到内存中了(非常重要)

                动态代理:JDK中的动态代理中的代理类是动态生成的。并且生成的动态代理类为$Proxy0

     静态代理实例1、创建一个接口:

    package proxy;
    
    public interface People {
    	public void zhaoduixiang()throws Throwable;
    	
    }
    

     2、创建一个实现类,张三,张三能够吃饭,张三可以找对象

    package proxy;
    
    public class ZhangSan implements People {
    
    	public void zhaoduixiang() throws Throwable{
    		System.out.println(" 我只要漂亮的");
    		
    	}
    	
    }
    

     3、创建一个实现父类,父类持有张三的引用

    package proxy;
    
    public class HisDady implements People {
            ZhangSan zs;
            public HisDady(ZhangSan zs){
                this.zs=zs
            }
    	public void zhaoduixiang() throws Throwable{
                    before()
    		zs.zhaoduixiang();
    		afger() 
    	}
              
    	public void afger() throws Throwable{
    		System.out.println(" 家境要好");
    		
    	}
              
    	public void before() throws Throwable{
    		System.out.println(" 要有学历");
    		
    	}
              
    	
    }                        
    

     动态代理:

    1、创建一个申明类ProxyHandler

    package proxy;
    
    import java.awt.event.InvocationEvent;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    
    public class ProxyHandler  implements InvocationHandler{
    	People peaple=null;
    	public ProxyHandler(People people){
    		this.peaple=people;
    	}
    
    	public void before()throws Throwable{
    		System.out.println("吃饭之前要洗手");
    	}
    	public void after()throws Throwable{
    		System.out.println("吃饭以后要洗碗");
    	}
    
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args)
    			throws Throwable {
    		before();
    		method.invoke(peaple,null);
    		after();
    		return null;
    	}
    
    }
    

    2、创建一个test类 。

    package proxy;
    
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.lang.reflect.Proxy;
    
    import sun.misc.ProxyGenerator;
    
    public class Test {
    	public static void main(String[] args) throws Throwable{
    		System.out.println("JDK写的动态代理");
    		
    		People people=(People) Proxy.newProxyInstance(People.class.getClassLoader(),
    				new Class[]{People.class}, new ProxyHandler(new ZhangSan()));
    		people.eat();
    	}
    	public void sleep() throws Throwable{
    		System.out.println("睡觉");
    	}
    	public void sport()throws Throwable{
    		System.out.println("睡觉");
    	}
    }
    

     执行结果:

    打印如图所示,那么People一定执行了ProxyHandler 中的invoke方法,并且首先执行before()  再执行method.invoke(peaple,null);最后执行 after();

    那么问题来了,为什么会这么执行了?我们明明调用的People.eat()方法啊。

    解析步骤:我们打印此时的People对象的名字,看看接口对应中到底是个什么东西:

            在test中的      people.eat();后面加入:
            
            System.out.println(people.getClass().getName());

    打印结果为:Proxy.$Proxy0 ,这是个什么鬼东西,原来,people对象中保存的是一个类名为 $Proxy0的对象,我们调用的是$Proxy0的eat()方法,

    所以我们最好看看$Proxy0的源码:

       如何读取$Proxy0的代码(由于$Proxy0JDK会自动删除,所以我们要把它写入到一个对应的文件中 ,代码如下):

    package proxy;
    
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.lang.reflect.Proxy;
    
    import sun.misc.ProxyGenerator;
    
    public class Test {
    	public static void main(String[] args) throws Throwable{
    		System.out.println("JDK写的动态代理");
    		
    		People people=(People) Proxy.newProxyInstance(People.class.getClassLoader(),
    				new Class[]{People.class}, new ProxyHandler(new ZhangSan()));
    		people.eat();
    		
    		System.out.println(people.getClass().getName());
    		creatProxyClassFile();
    	}
    	public static void creatProxyClassFile(){
    		byte[] data=ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{People.class});
    		try {
    			FileOutputStream out= new FileOutputStream("$Proxy0.class");
    			out.write(data);
    			out.close();
    		} catch (FileNotFoundException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    		
    	}
    	public void sleep() throws Throwable{
    		System.out.println("睡觉");
    	}
    	public void sport()throws Throwable{
    		System.out.println("睡觉");
    	}
    }
    

     打开对应的项目工作空间,你会发现一个对应$Proxy0文件;

    那么其中有什么玄妙,导致我们调用$Proxy0.eat()方法的时候,会调用invoke()方法了

    我们看看Proxy0类的源码,请用反编译软件打开Proxy0.class:

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    import proxy.People;
    
    public final class $Proxy0 extends Proxy
      implements People
    {
      private static Method m1;
      private static Method m3;
      private static Method m0;
      private static Method m2;
    
      public $Proxy0(InvocationHandler paramInvocationHandler)
        throws 
      {
        super(paramInvocationHandler);
      }
    
      public final boolean equals(Object paramObject)
        throws 
      {
        try
        {
          return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
    
      public final void eat()
        throws Throwable
      {
        this.h.invoke(this, m3, null);
      }
    
      public final int hashCode()
        throws 
      {
        try
        {
          return ((Integer)this.h.invoke(this, m0, null)).intValue();
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
    
      public final String toString()
        throws 
      {
        try
        {
          return (String)this.h.invoke(this, m2, null);
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
    
      static
      {
        try
        {
          m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
          m3 = Class.forName("proxy.People").getMethod("eat", new Class[0]);
          m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
          m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
          return;
        }
        catch (NoSuchMethodException localNoSuchMethodException)
        {
          throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        }
        catch (ClassNotFoundException localClassNotFoundException)
        {
          throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
        }
      }
    }
    

     请注意其中的一些方法和属性,比如public final void eat() throws Throwable { this.h.invoke(this, m3, null); }   ,其中 该对象的eat()方法是调用的invoke 方法,那h是什么意思, 

    因为Proxy0 :public final class $Proxy0 extends Proxy,在Proxy中查看  发现:   protected InvocationHandler h;,所以h就是一个我们传入的InvocationHandler 对象,也就是调用的是InvocationHandler 的invoke方法。  这样我们就明白了为什么会出现console中打印的结果了吧。

    纯手写动态代理,不使用任何JDK:

    1、 创建一个MyInvocationHandler 接口,模拟InvocationHandler

    package proxy;
    
    import java.lang.reflect.Method;
    
    public interface MyInvocationHandler {
    	public Object invoke(Object proxy,Method method,Object args)
    		throws Throwable;
    }
    

     2、实现该接口MyProxyHandler,模拟ProxyHandler,和原来动态代理完全一个样,只是改了下代码形式

    package proxy;
    
    import java.lang.reflect.Method;
    
    public class MyProxyHandler implements MyInvocationHandler {
    	People peaple=null;
    	public MyProxyHandler(People people){
    		this.peaple=people;
    	}
    	public Object invoke(Object proxy, Method method, Object args)
    			throws Throwable {		
    		before();
    		method.invoke(peaple,null);
    		after();
    		return null;
    	}
    	public void before(){
    		System.out.println("吃饭之前要洗手");
    	}
    	public void after(){
    		System.out.println("吃饭以后要洗碗");
    	}
    
    }
    

     3、新建一个Proxy类,MyProxy,用来模拟Proxy

    package proxy;
    
    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 javax.tools.JavaCompiler;
    import javax.tools.JavaCompiler.CompilationTask;
    import javax.tools.StandardJavaFileManager;
    import javax.tools.ToolProvider;
    
    import org.omg.CORBA.INTF_REPOS;
    
    public class MyProxy {
    	static String rt="
    	";
    	public static Object createProxyInstance(ClassLoader loader,Class intf,MyInvocationHandler handler){
    		try {
    			Method[] methods=intf.getMethods();
    			//1用流的方式创建一个Java文件,
    				String proxyClass="package proxy;"+rt
    						+"import java.lang.reflect.Method;"+rt
    						+"public class $Proxy0 implements "+intf.getName()+"{"+rt
    						+"MyInvocationHandler h;"+rt
    						+"public $Proxy0(MyInvocationHandler h)"+"{"+rt
    						+"this.h= h;"+rt+"}"
    						+getMethodString(methods,intf)+rt+"}";
    			//2 把类生成文件,
    			String filename="D:/WorkSpace/proxy/src/proxy/$Proxy0.java";
    			File f=new File(filename);
    	
    			FileWriter fw=new FileWriter(f);
    			fw.write(proxyClass);
    			fw.flush();
    			fw.close();
    			
    			//3编译Java文件
    			JavaCompiler compiler=ToolProvider.getSystemJavaCompiler();
    			StandardJavaFileManager fileMgr	=compiler.getStandardFileManager(null, null, null);
    			Iterable units=fileMgr.getJavaFileObjects(filename);
    			CompilationTask t=compiler.getTask(null, fileMgr, null, null, null, units);
    			t.call();
    			fileMgr.close();
    			
    			//把class加载到内存中去
    			MyClassLoader loader1=new MyClassLoader("D:/WorkSpace/proxy/src/proxy/");
    			Class proxy0Class=loader1.findClass("$Proxy0");
    			Constructor m=proxy0Class.getConstructor(MyInvocationHandler.class);
    			Object o=m.newInstance(handler);
    			return o;
    		} catch (IOException e) {
    			e.printStackTrace();
    		} catch (ClassNotFoundException e) {
    			e.printStackTrace();
    		} catch (NoSuchMethodException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (SecurityException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (InstantiationException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} 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();
    		}
    		return null;
    		
    	}	
    	public static String getMethodString(Method[] methods,Class intf){
    		String proxyMe="";
    		for(Method method: methods){
    			proxyMe+="public void "+method.getName()+"() throws Throwable {"+rt
    					+"Method md= "+intf.getName()+".class.getMethod(""+method.getName()
    					+"",new Class[]{});"+rt
    					+"this.h.invoke(this,md,null);"+rt+"}"+rt;
    					
    		}
    		return proxyMe;
    	}
    }
    

     4、创建一个测试:

    package proxy;
    
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.lang.reflect.Proxy;
    
    import sun.misc.ProxyGenerator;
    
    public class Test {
    	public static void main(String[] args) throws Throwable{
    		System.out.println("JDK写的动态代理");
    		
    		People people=(People) Proxy.newProxyInstance(People.class.getClassLoader(),
    				new Class[]{People.class}, new ProxyHandler(new ZhangSan()));
    		people.eat();
    		
    		
    /*		People people1=(People) MyProxy.createProxyInstance(People.class
    				.getClassLoader(),People.class, new MyProxyHandler(
    						new ZhangSan()));
    		System.out.println(people1.getClass().getName());
    		System.out.println("自己写的动态代理");
    		people1.eat();	*/	
    		
    		
    		System.out.println(people.getClass().getName());
    		creatProxyClassFile();
    	}
    	public static void creatProxyClassFile(){
    		byte[] data=ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{People.class});
    		try {
    			FileOutputStream out= new FileOutputStream("$Proxy0.class");
    			out.write(data);
    			out.close();
    		} catch (FileNotFoundException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    		
    	}
    }
    

     结果:

    此次是我观看动脑学院免费教学课后的练习,非常感谢Jack老师,老师讲的很好,我学了很多。

  • 相关阅读:
    在ubuntu8.04上用evolution蒙受163邮件
    软件史上最宏大的法度员之Linux之父
    试用 Deluge 的 Web 界面
    在Ubuntu 8.04里把Firefox 3Beta5晋级到RC1
    Linux 对象箱 — 文件治理器 [17 款]
    现在的Linux不该该是小朋友的全国
    奉行LINUX的方案
    运用 Kompare 比力文件
    最好不要装64位的Linux
    Ubuntu安放telnet进程
  • 原文地址:https://www.cnblogs.com/softwarewebdesign/p/5926687.html
Copyright © 2020-2023  润新知