注册 登录  
 加关注
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

淡泊明智

 
 
 

日志

 
 

java动态代理详解  

2012-12-04 14:03:11|  分类: java基本概念详解 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问

?在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用

代理模式一般涉及到的角色有

     –抽象角色:声明真实对象和代理对象的共同接口

     –代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装

     –真实角色:代理角色所代表的真实对象,是我们最终要引用的对象

 

Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:

?(1)Interface InvocationHandler:该接口中仅定义了一个方法

–public object invoke(Object obj,Method method, Object[] args)

?在实际使用时,第一个参数obj一般是指代理类method是被代理的方法,如上例中的request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。

 

?(2)Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包含以下内容

?protected Proxy(InvocationHandler h):构造函数,用于给内部的h赋值。

?static Class getProxyClass (ClassLoader loader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。

?static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)

所谓Dynamic Proxy(动态代理)是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作

使用动态代理类时,我们必须实现InvocationHandler接口

通过代理的方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实现了非常灵活的动态代理关系

动态代理是指客户通过代理类来调用其它对象的方法

?动态代理使用场合:

   ?调试

   ?远程方法调用(RMI)

动态代理的步骤:

1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法

2.创建被代理的类以及接口

3.通过Proxy的静态方法

newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个代理

4.通过代理调用方法

------------------------------------

 动态代理作为代理模式的一种扩展形式,广泛应用于框架(尤其是基于AOP的框架)的设计与开发,本文将通过实例来讲解Java动态代理的实现过程。  

        通常情况下,代理模式中的每一个代理类在编译之后都会生成一个class文件,代理类所实现的接口和所代理的方法都被固定这种代理被称之为静态代理(Static Proxy)。那么有没有一种机制能够让系统在运行时动态创建代理类答案就是本文将要介绍的动态代理(Dynamic Proxy)。动态代理是一种较为高级的代理模式,它在事务管理、AOP(Aspect-OrientedProgramming,面向方面编程)等领域都发挥了重要的作用。

 

      传统的代理模式中,客户端通过Proxy类调用RealSubject类的request()方法,同时还可以在代理类中封装其他方法(如preRequest()和postRequest()等)。如果按照这种方法使用代理模式,那么代理类和真实主题类都应该是事先已经存在的,代理类的接口和所代理方法都已明确指定,如果需要为不同的真实主题类提供代理类或者代理一个真实主题类中的不同方法,都需要增加新的代理类,这将导致系统中的类个数急剧增加,因此需要想办法减少系统中类的个数。动态代理可以让系统能够根据实际需要来动态创建代理类让同一个代理类能够代理多个不同的真实主题类而且可以代理不同的方法

 

       从JDK 1.3开始,Java语言提供了对动态代理的支持,Java语言实现动态代理时需要用到位于java.lang.reflect包中的一些类,现简要说明如下:

 

      (1) Proxy类

      Proxy类提供了用于创建动态代理类和实例对象的方法,它是所创建的动态代理类的父类,它最常用的方法如下:


public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces):该方法用于返回一个Class类型的代理类,在参数中需要提供类加载器并需要指定代理的接口数组(与真实主题类的接口列表一致)。
public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces, InvocationHandler h):该方法用于返回一个动态创建的代理类的实例,方法中第一个参数loader
表示代理类的类加载器,第二个参数interfaces表示代理类所实现的接口列表(与真实主题类的接口列表一致),第三个参数h表示所指派的调用处理程序类。

      (2) InvocationHandler接口

      InvocationHandler接口是代理处理程序类的实现接口,该接口作为代理实例的调用处理者的公共父类,每一个代理类的实例都可以提供一个相关的具体调用处理者(InvocationHandler接口的子类)。在该接口中声明了如下方法:


public Object invoke(Objectproxy, Method method, Object[] args):该方法用于处理对代理类实例的方法调用并返回相应的结果,当一个代理实例中的业务方法被调用时将自动调用该方法。invoke()方法包含三个参数,其中第一个参数proxy表示代理类的实例,第二个参数method表示需要代理的方法,第三个参数args表示代理方法的参数数组。

        动态代理类需要在运行时指定所代理真实主题类的接口客户端在调用动态代理对象的方法时,调用请求会将请求自动转发给InvocationHandler对象的invoke()方法,由invoke()方法来实现对请求的统一处理

 

      下面通过一个简单实例来学习如何使用动态代理模式:

       Sunny软件公司欲为公司OA系统数据访问层DAO增加方法调用日志,记录每一个方法被调用的时间和调用结果,现使用动态代理进行设计和实现。

 

      本实例完整代码如下所示:

import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Calendar;
import java.util.GregorianCalendar;

//抽象UserDAO:抽象主题角色
interface AbstractUserDAO {
public Boolean findUserById(String userId);
}

//抽象DocumentDAO:抽象主题角色
interface AbstractDocumentDAO {
public Boolean deleteDocumentById(String documentId);
}

//具体UserDAO类:真实主题角色
class UserDAO implements AbstractUserDAO {
public Boolean findUserById(String userId) {
if (userId.equalsIgnoreCase("张无忌")) {
System.out.println("查询ID为" + userId + "的用户信息成功!");
return true;
}
else {
System.out.println("查询ID为" + userId + "的用户信息失败!");
return false;
}
}
}

//具体DocumentDAO类:真实主题角色
class DocumentDAO implements AbstractDocumentDAO {
public Boolean deleteDocumentById(String documentId) {
if (documentId.equalsIgnoreCase("D001")) {
System.out.println("删除ID为" + documentId + "的文档信息成功!");
return true;
}
else {
System.out.println("删除ID为" + documentId + "的文档信息失败!");
return false;
}
}
}

//自定义请求处理程序类
class DAOLogHandler implements InvocationHandler {
private Calendar calendar;
private Object object;

public DAOLogHandler() {
}

    //自定义有参构造函数,用于注入一个需要提供代理的真实主题对象
public DAOLogHandler(Object object) {
this.object = object;
}

//实现invoke()方法,调用在真实主题类中定义的方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeInvoke();
        Object result = method.invoke(object, args); //转发调用
        afterInvoke();
        return null;
    }

    //记录方法调用时间
    public void beforeInvoke(){
    calendar = new GregorianCalendar();
    int hour = calendar.get(Calendar.HOUR_OF_DAY);
    int minute = calendar.get(Calendar.MINUTE);
    int second = calendar.get(Calendar.SECOND);
    String time = hour + ":" + minute + ":" + second;
    System.out.println("调用时间:" + time);
    }

    public void afterInvoke(){
    System.out.println("方法调用结束!" );
    }
}

      编写如下客户端测试代码:

class Client {
public static void main(String args[]) {


 InvocationHandler handler = null;
AbstractUserDAO userDAO = new UserDAO(); //实际要调用的对象UserDAO,定义为接口对象
handler = new DAOLogHandler(userDAO);
AbstractUserDAO proxy = null;
        //动态创建代理对象,用于代理一个AbstractUserDAO类型的真实主题对象
proxy = (AbstractUserDAO)Proxy.newProxyInstance(AbstractUserDAO. class.getClassLoader(), new Class[]{AbstractUserDAO.class}, handler);
    proxy.findUserById("张无忌");
//调用代理对象的业务方法
   
    System.out.println("------------------------------");
  
  AbstractDocumentDAO docDAO = new DocumentDAO();
handler = new DAOLogHandler(docDAO);
AbstractDocumentDAO proxy_new = null;
//动态创建代理对象,用于代理一个AbstractDocumentDAO类型的真实主题对象
proxy_new = (AbstractDocumentDAO)Proxy.newProxyInstance(Abstract DocumentDAO.class.getClassLoader(), new Class[]{AbstractDocumentDAO.class}, handler);
    proxy_new.deleteDocumentById("D002"); //调用代理对象的业务方法
}
}

      编译并运行程序,输出结果如下:

调用时间:13:47:14

查询ID为张无忌的用户信息成功!

方法调用结束!

------------------------------

调用时间:13:47:14

删除ID为D002的文档信息失败!

方法调用结束!

       通过使用动态代理,我们可以实现对多个真实主题类的统一代理和集中控制。


       注:JDK中提供的动态代理只能代理一个或多个接口,如果需要动态代理具体类或抽象类,可以使用CGLib(Code Generation Library)等工具,CGLib是一个功能较为强大、性能和质量也较好的代码生成包,在许多AOP框架中都得以广泛应用,大家可以自行查阅相关资料来学习CGLib。

  评论这张
 
阅读(266)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018