Java动态代理模式

即Proxy Pattern,23种java常用设计模式之一。代理模式的定义:对其他对象提供一种代理以控制对这个对象的访问。

简介


代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式的思想是为了提供额外的处理或者不同的操作而在实际对象与调用者之间插入一个代理对象。这些额外的操作通常需要与实际对象进行通信。

应用场景


例如:假设有一组对象都实现同一个接口,实现同样的方法,但这组对象中有一部分对象需要有单独的方法,传统的笨办法是在每一个应用端都加上这个单独的方法,但是代码重用性低, 耦合性高。
如果用代理的方法则很好的解决了这个问题。

代码示例


示例说明

假设有一个 Italk接口,有空的方法talk()(说话),所有的people对象都实现(implements)这个接口,实现talk()方法,前端有很多地方都将people实例化,执行talk方法,后来发现这些前端里有一些除了要说话以外还要唱歌(sing),那么我们既不能在Italk接口里增加sing()方法,又不能在每个前端都增加sing方法,我们只有增加一个代理类talkProxy,这个代理类里实现talk和sing方法,然后在需要sing方法的客户端调用代理类即可,代码如下:
//接口类Italk
public interface Italk {
public void talk(String msg);
}
//实现类people
public class People implements Italk {
public String username;
public String age;
public String getName() {
return username;
}
public void setName(String name) {
this.username= name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public People(String name1, String age1) {
this.username= name1;
this.age = age1;
}
public void talk(String msg) {
System.out.println(msg+"!你好,我是"+username+",我年龄是"+age);
}
}
//代理类talkProxy
public class TalkProxy implements Italk {
Italk talker;
public TalkProxy (Italk talker) {
//super();
this.talker=talker;
}
public void talk(String msg) {
talker.talk(msg);
}
public void talk(String msg,String singname) {
		talker.talk(msg);
		sing(singname);
}
private void sing(String singname){
System.out.println("唱歌:"+singname);
}
}
//应用端myProxyTest
public class MyProxyTest {
/**代理模式
* @param args
*/
public static void main(String[] args) {
//不需要执行额外方法的
Italk people1=new People("湖海散人","18");
people1.talk("No ProXY Test");
System.out.println("-----------------------------");
//需要执行额外方法的
TalkProxy talker=new TalkProxy(people1);
talker.talk("ProXY Test","七里香");
}
}

以上复制于百度百科。

以上是代理模式对现有类方法实现增强,JDK也还此类使用场景,例如创建single的线程池时候返回的就是代理对象,目的是屏蔽一些不必要的方法:
    static class DelegatedScheduledExecutorService
            extends DelegatedExecutorService
            implements ScheduledExecutorService {
        private final ScheduledExecutorService e;
        DelegatedScheduledExecutorService(ScheduledExecutorService executor) {
            super(executor);
            e = executor;
        }
        public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) {
            return e.schedule(command, delay, unit);
        }
        public  ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) {
            return e.schedule(callable, delay, unit);
        }
        public ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
            return e.scheduleAtFixedRate(command, initialDelay, period, unit);
        }
        public ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
            return e.scheduleWithFixedDelay(command, initialDelay, delay, unit);
        }
    }
上面代码主要屏蔽的是ScheduledExecutorService实现类的方法,因为我newSingleThreadScheduledExecutor的功能远远没有newScheduledThreadPool(5)功能多,但是他们都是ScheduledExecutorService的子类,所以只能屏蔽 newSingleThreadScheduledExecutor不需要的方法(上面主要是在API设计上考虑屏蔽的,并不是严格意义上的屏蔽,不让用户使用那种)。

下面主要讲解一下动态代理:

假如有这样一种场景,我们项目使用第三方类库,但是非开源的,我们无法修改源码(不考虑破解反编译之类的),只能在现有方法上做增强,但是如果使用代理模式为每一个类写一个代理类的话,当需要被代理的类特别多的时候是工作量是不无法忍受的。那么我们就可以使用动态代理实现,此处使用JDK动态代理:

非开源类库代码如下:

public interface Helloworld {
	
	
	public void  sayHello();
	
	public void  sayBye();

}


public class HelloworldImpl implements Helloworld{

	public void sayHello() {

		System.out.println("HelloworldImpl sayHello ...");
	}

	public void sayBye() {
		System.out.println("HelloworldImpl sayBye ...");		
	}

}


以上为非开源代码。

接下来我们需要序sayHello和sayBye前后打印日志操作的话,我们只能使用JDK的动态代理模式实现,代理类需要实现java.lang.reflect.InvocationHandler接口:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class HelloWorldInvocationHandler implements InvocationHandler {

	private Helloworld agent;

	public HelloWorldInvocationHandler  (Helloworld agent) {
		super();
		this.agent = agent;
	}

	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("前面日志记录完毕");
		Object result = method.invoke(agent, args);
		System.out.println("后面日志记录完毕");

		return result;
	}

	public void sayLove() {
		System.out.println("I love you !");
	}

}

测试调用:

public class MainProxy {

	public static void main(String[] args) throws Exception {
		//测试代码还有另一种写法
		// 生成HelloWorld的代理类:class com.sun.proxy.$Proxy0
		Class proxyClass = Proxy.getProxyClass(MainProxy.class.getClassLoader(), Helloworld.class);

		
		final Constructor cons = proxyClass.getConstructor(InvocationHandler.class);

		// com.daxin.proxy.jdk.MyInvocationHandler
		final InvocationHandler ih = new HelloWorldInvocationHandler(new HelloworldImpl());

		// System.out.println(ih);

		// HelloworldImpl
		Helloworld helloWorld = (Helloworld) cons.newInstance(ih);

		helloWorld.sayHello();
		helloWorld.sayBye();

	}

}

输出:

 前面日志记录完毕

 HelloworldImpl sayHello ...
后面日志记录完毕
前面日志记录完毕
HelloworldImpl sayBye ...
后面日志记录完毕


进行代码解释:

Class proxyClass = Proxy.getProxyClass(MainProxy.class.getClassLoader(), Helloworld.class);

这一行完成代理类字节码的生成,可以传递VM参数 -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true生成代理类字节码,生成的字节码在项目的com/sun/proxy目录下。

字节码如下:


package com.sun.proxy;

import com.daxin.proxy.jdk.Helloworld;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
//注意:this.h成员其实就是Proxy类中的成员,类型为:InvocationHandler,也就是我们自己定义InvocationHandler
public final class $Proxy0 extends Proxy
  implements Helloworld
{
  private static Method m1;
  private static Method m3;
  private static Method m2;
  private static Method m0;

  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 sayHello()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    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);
    }
  }

  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);
    }
  }

  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("com.daxin.proxy.jdk.Helloworld").getMethod("sayHello", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}




final Constructor cons = proxyClass.getConstructor(InvocationHandler.class);
作用是为了获取proxyClass的构造参数为InvocationHandler类型的构造器。


	final InvocationHandler ih = new HelloWorldInvocationHandler(new HelloworldImpl());

创建一个HelloWorldInvocationHandler实例。


Helloworld helloWorld = (Helloworld) cons.newInstance(ih);
真正产生代理对象的核心代码。

注意:当你对代理对象调用System.out.println(helloWorld);时候,你会发现输出的是:com.daxin.proxy.jdk.HelloworldImpl@7d4991ad ,此时是不是很好奇,为什么不是Proxy代理对象呢 ? 现在你可以回头看一下生成的字节码的toString了。


  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);
    }
  }
注意:这里面的this.h,h是继承至Proxy类的成,就是我们自己定义的InvocationHandler。然后调用m2(m2就是toString方法)。此时 InvocationHandler里面的invoke代码就是如下了:

	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		
		System.out.println("前面日志记录完毕");
		//method为toString ,所以是在对agent调用toString方法,而agent方法是HelloworldImpl
		Object result = method.invoke(agent, args);
		System.out.println("后面日志记录完毕");

		return result;
	}

所以导致

		Helloworld helloWorld = (Helloworld) cons.newInstance(ih);
		    System.out.println(helloWorld);
输出为com.daxin.proxy.jdk.HelloworldImpl@7d4991ad



上面的实现是基于JDK实现的动态代理,还有cglib的实现,以后进行补充











你可能感兴趣的:(Java)