Java代理模式及动态代理

1. 代理模式

    代理模式是较常见的模式之一,在许多框架中经常见到,比如Spring的面向切面的编程,MyBatis中缓存机制对PooledConnection的管理等。代理模式使得客户端在使用目标对象的时候间接通过代理类进行,代理类在操作目标对象的前后可以添加额外操作。


2. 代理模式UML

    代理模式的类图结构非常简单,如下所示


    Subject:抽象主题角色,一般为一个接口

    RealSubject:抽象主题角色的实现类,对其中的方法进行实现

    Proxy:代理类,其继承自抽象主题角色,实现其中的方法,并且持有实现类对象。


3. 举例

    说起来比较乱,举例如下:

    建立抽象主题类:

/**
 * @author hongyu.shao created on 15-1-26 下午6:16
 * @version 1.0.0
 */
public interface HelloWorldService {
    public void sayHello(String name);
}  


   建立实现类:

/**
 * @author hongyu.shao created on 15-1-26 下午6:17
 * @version 1.0.0
 */
public class HelloWorldServiceImpl implements HelloWorldService {

    @Override
    public void sayHello(String name) {
        System.out.println("hello,"+name);
    }
}


 建立代理类:

/**
 * @author hongyu.shao created on 15-1-26 下午8:23
 * @version 1.0.0
 */
public class HelloWorldServiceProxy implements HelloWorldService {
    private HelloWorldService helloWorldService;

    public HelloWorldServiceProxy(HelloWorldService helloWorldService){
        this.helloWorldService = helloWorldService;
    }

    @Override
    public void sayHello(String name) {
        System.out.println("shake hand!");
        helloWorldService.sayHello("hello,"+name);
        System.out.println("goodbye");
    }
}

main方法如下:

    public static void main(String[] args){
        HelloWorldService proxy = new HelloWorldServiceProxy(new HelloWorldServiceImpl());
        proxy.sayHello("shao");
    }

4. Java动态代理

通过上面的例子可以看出,针对每一个接口,都需要实现自己的代理类,这样会导致工程中的类数目爆炸增长。

在java中,提供了统一的代理接口InvocationHandler和代理类Proxy。其使用方法举例如下。

出了上面的HelloWorldService及其实现类HelloWorldServiceImpl,再建立一个Service如下:

Service接口:

/**
 * @author hongyu.shao created on 15-1-26 下午8:33
 * @version 1.0.0
 */
public interface BuyFoodService {

    public void buyFood();
} 


Service实现类:

/**
 * @author hongyu.shao created on 15-1-26 下午8:33
 * @version 1.0.0
 */
public class BuyFoodServiceImpl implements BuyFoodService{

    @Override
    public void buyFood() {
        System.out.println("chose food and pay money!");
    }
}

创建代理类:

/**
 * @author hongyu.shao created on 15-1-26 下午6:19
 * @version 1.0.0
 */
public class ServiceHandler implements InvocationHandler{

    private Object target;

    public Object bind(Object target){
        this.target=target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    @Override 
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        System.out.println("service initinal!");
        result = method.invoke(target,args);
        System.out.println("service destroy!");
        return result;
    }
}
该代理类实现了InvocationHandler接口,并且调用Proxy.newProxyInstance生成具体代理类对象。但是,新建的代理类对象ServiceHandler和具体的Service完全没有关系,是完全解耦的。


Main方法:

    public static void main(String[] args){
        ServiceHandler handler = new ServiceHandler();
        HelloWorldService helloWorldService = (HelloWorldService)handler.bind(new HelloWorldServiceImpl());
        helloWorldService.sayHello("shao");

        BuyFoodService buyFoodService = (BuyFoodService)handler.bind(new BuyFoodServiceImpl());
        buyFoodService.buyFood();
    }

可以看到,ServiceHandler既可以作为HelloWorldService的代理类,也可以作为BuyFoodService的代理类,具有很好的可扩展性。



5. MyBatis中对Java动态代理的简单使用

使用MyBatis连接池时,当关闭连接时,不是真正的关闭到数据库的连接,而是把连接归还到空闲连接池中。
MyBatis通过类PooledConnection对Connection进行二次封装,并实现InvocationHandler接口
class PooledConnection implements InvocationHandler {

  private static final String CLOSE = "close";
  private static final Class[] IFACES = new Class[]{Connection.class};

  private int hashCode = 0;
  private PooledDataSource dataSource;
  private Connection realConnection;
  private Connection proxyConnection;
  private long checkoutTimestamp;
  private long createdTimestamp;
  private long lastUsedTimestamp;
  private int connectionTypeCode;
  private boolean valid;

  /**
   * Constructor for SimplePooledConnection that uses the Connection and PooledDataSource passed in
   *
   * @param connection - the connection that is to be presented as a pooled connection
   * @param dataSource - the dataSource that the connection is from
   */
  public PooledConnection(Connection connection, PooledDataSource dataSource) {
    this.hashCode = connection.hashCode();
    this.realConnection = connection;
    this.dataSource = dataSource;
    this.createdTimestamp = System.currentTimeMillis();
    this.lastUsedTimestamp = System.currentTimeMillis();
    this.valid = true;

    proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
  }
  。。。。。
}

从构造函数中可以看出,类成员变量realConnection赋值为真正的数据库连接Connection对象,而对proxyConnection的赋值如下:
proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
这里,使用了Java的动态代理,采用ProxyConnection对象代理Connection

下面看下 PooledConnection实现的invoke方法
  /**
   * Required for InvocationHandler implementation.
   *
   * @param proxy - not used
   * @param method - the method to be executed
   * @param args - the parameters to be passed to the method
   * @see java.lang.reflect.InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])
   */
  public Object invoke(Object proxy, Method method, Object[] args)
      throws Throwable {
    String methodName = method.getName();
    if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) {
      dataSource.pushConnection(this);
      return null;
    } else {
      try {
        return method.invoke(getValidConnection(), args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
  }

从上面可以看出,如果调用的方法是close方法,通过 dataSource.pushConnection(this);直接把连接放回连接池中,其他方法这直接调用Connection对象的方法。


6. Java动态代理源码分析

Java动态代理主要涉及到InvocationHandler接口和Proxy类。
一般实现InvocationHandler接口的invoke方法,在该方法中执行相应的逻辑。通过Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)产生动态代理对象。下面重点解析该方法:
    public static Object newProxyInstance(ClassLoader loader,
					  Class<?>[] interfaces,
					  InvocationHandler h)
	throws IllegalArgumentException
    {
	if (h == null) {
	    throw new NullPointerException();
	}

	/*
	 * Look up or generate the designated proxy class.
	 */
	Class cl = getProxyClass(loader, interfaces);

	/*
	 * Invoke its constructor with the designated invocation handler.
	 */
	try {
	    Constructor cons = cl.getConstructor(constructorParams);
	    return (Object) cons.newInstance(new Object[] { h });
	} catch (NoSuchMethodException e) {
	    throw new InternalError(e.toString());
	} catch (IllegalAccessException e) {
	    throw new InternalError(e.toString());
	} catch (InstantiationException e) {
	    throw new InternalError(e.toString());
	} catch (InvocationTargetException e) {
	    throw new InternalError(e.toString());
	}
    }

在该方法中,首先调用getProxyClass方法产生动态代理类的Class类,然后通过该Class类的构造方法进行实例化,其中构造函数的参数是实现了InvocationHandler接口的对象。进入getProxyClass方法中查看:

在该方法中,前半部分分别尝试从缓存中取Class对象,如果取不到,则根据传递的接口的访问权限确定动态生成的代理类的包路径,最后调用byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces); 生成动态代理类的二字码文件,为了了解动态代理类的生成过程,直接调用该方法,并对其生成的文件进行反编译。

其中测试main方法如下:
     public static void main(String[] args){
        byte[] classByte = ProxyGenerator.generateProxyClass("NewProxy",new Class[]{HelloWorldService.class});
        File file = new File("D:/classes/NewProxy.class");

         FileOutputStream stream = null;
         try {
             stream = new FileOutputStream(file);
             stream.write(classByte);
             stream.flush();
         } catch (FileNotFoundException e) {
             e.printStackTrace();
         } catch (IOException e) {
             e.printStackTrace();
         }finally {
             if(stream!=null){
                 try {
                     stream.close();
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
             }
         }
     }


我们重新使用了上文中创建的接口HelloWorldService,该main函数产生了针对于接口HelloWorldService的动态代理类二字码文件NewProxy.class,对其进行反编译得到如下代码。
import com.qunar.service.HelloWorldService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class NewProxy extends Proxy
  implements HelloWorldService
{
  private static Method m1;
  private static Method m3;
  private static Method m0;
  private static Method m2;

  public NewProxy(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 (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final void sayHello(String paramString)
    throws 
  {
    try
    {
      this.h.invoke(this, m3, new Object[] { paramString });
      return;
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final int hashCode()
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final String toString()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    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.qunar.service.HelloWorldService").getMethod("sayHello", new Class[] { Class.forName("java.lang.String") });
      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());
  }
}


该类实现了HelloWorldService接口,并实现了其sayHello的方法,在sayHello方法的内部则调用了实现了InvocationHandler接口的对象的invoke方法,这样在调用sayHello方法的时候就能透明的进行代理方法的调用了,是不是很奇妙。




  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
城市应急指挥系统是智慧城市建设的重要组成部分,旨在提高城市对突发事件的预防和处置能力。系统背景源于自然灾害和事故灾难频发,如汶川地震和日本大地震等,这些事件造成了巨大的人员伤亡和财产损失。随着城市化进程的加快,应急信息化建设面临信息资源分散、管理标准不统一等问题,需要通过统筹管理和技术创新来解决。 系统的设计思路是通过先进的技术手段,如物联网、射频识别、卫星定位等,构建一个具有强大信息感知和通信能力的网络和平台。这将促进不同部门和层次之间的信息共享、交流和整合,提高城市资源的利用效率,满足城市对各种信息的获取和使用需求。在“十二五”期间,应急信息化工作将依托这些技术,实现动态监控、风险管理、预警以及统一指挥调度。 应急指挥系统的建设目标是实现快速有效的应对各种突发事件,保障人民生命财产安全,减少社会危害和经济损失。系统将包括预测预警、模拟演练、辅助决策、态势分析等功能,以及应急值守、预案管理、GIS应用等基本应用。此外,还包括支撑平台的建设,如接警中心、视频会议、统一通信等基础设施。 系统的实施将涉及到应急网络建设、应急指挥、视频监控、卫星通信等多个方面。通过高度集成的系统,建立统一的信息接收和处理平台,实现多渠道接入和融合指挥调度。此外,还包括应急指挥中心基础平台建设、固定和移动应急指挥通信系统建设,以及应急队伍建设,确保能够迅速响应并有效处置各类突发事件。 项目的意义在于,它不仅是提升灾害监测预报水平和预警能力的重要科技支撑,也是实现预防和减轻重大灾害和事故损失的关键。通过实施城市应急指挥系统,可以加强社会管理和公共服务,构建和谐社会,为打造平安城市提供坚实的基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值