`
lvwenwen
  • 浏览: 959345 次
  • 性别: Icon_minigender_1
  • 来自: 魔都
社区版块
存档分类
最新评论

JAVA动态代理机制初探

阅读更多
功能代码的多余枝节
当我们书写执行一个功能的函数时,经常需要在其中写入与功能不是直接相关但很有必要的代码,如日志记录,信息发送,安全和事务支持等,以下代码是一个用户注册类的代码:

/**
* 用於用戶注冊的服務類
* @author: sitinspring(junglesong@gmail.com)
* @date: 2008-5-27-下午09:15:25
*/
public class RegisterService{
  /**
   * 注冊一個用戶
   * @param name 用戶名
   * @param pswd 用戶密碼
   * @param email 用戶郵件地址
   */
  public void register(String name,String pswd,String email){
    Logger.log("將注冊一個新用戶"+name);
   
    // 真正的,应该由本函數擔負的處理
    System.out.println("存儲用戶信息到數據庫");
   
    MailSender.send(email, "歡迎"+name+"注冊為本系統的用戶");
  }
}

Logger类代码

/**
* 模擬記錄器
* @author: sitinspring(junglesong@gmail.com)
* @date: 2008-5-27-下午09:17:56
*/
public class Logger{
  /**
   * 模擬記錄信息到文件中
   * @param str
   */
  public static void log(String str){
    System.out.println(getCurrTime()+"INFO:"+str);
  }
 
  /**
   * 取得當前時間
   * @return
   */
  private static String getCurrTime() {
    Date date = new Date();
    Format formatter = new SimpleDateFormat("HH时mm分ss秒");
    return formatter.format(date);
  }
}

MailSender类代码

/**
* 模擬郵件發送器
* @author: sitinspring(junglesong@gmail.com)
* @date: 2008-5-27-下午09:23:31
*/
public class MailSender{
  /**
   * 模擬發送郵件
   * @param title
   * @param msg
   */
  public static void send(String email,String concept){
    System.out.println("向"+email+"發送郵件 內容為:"+concept+"的郵件");
  }
}

枝节性代码给功能性代码带来的麻烦

诸如日志记录,信息发送,安全和事务支持等枝节代码虽然是必要的,但它会带来以下麻烦:
1.枝节性代码游离在功能性代码之外,它们不是函数的目的,这对OO是一种破坏。
2.枝节性代码会造成功能性代码对其它类的依赖,加深类之间的耦合度,而这是OO系统所竭力避免的。
3.枝节性代码带来的耦合度会造成功能性代码移植困难,可重用性降低。
4.从法理上说,枝节性代码应该“监视”着功能性代码,然后采取行动;而不是由功能性代码“通知”枝节性代码采取行动。这好比吟游诗人应该是主动记述骑士的功绩而不是骑士主动要求诗人记录自己的功绩的。

如何两种代码分离开来

毫无疑问,枝节性代码和功能性代码(主干性代码)需要分离开来才能降低耦合程度,符合现代OO系统的要求,而java提供的动态代理机制可以帮助我们实现这一点。
动态代理机制主要的类是java.lang.reflect.Proxy,它从一诞生就受到了重视,并在RMI,EJB和AOP中都得到广泛的应用,其重要程度唯有反射能与之相比。

Proxy代理模式

在讲述动态代理之前我们可以回顾一下代理模式,它的定义是这样的:代理可以提供对另一个对象的访问,同时隐藏实际对象的具体事实。代理一般会实现它所表示的实际对象的接口。代理可以访问实际对象,但是延迟实现实际对象的部分功能,实际对象实现系统的实际功能,代理对象对客户隐藏了实际对象。客户不知道它是与代理打交道还是与实际对象打交道。
如果我们使用代理模式,把枝节性代码放入代理类中,这样主干性代码保持在真实的类中,这样不就能有效降低耦合度吗?这种通过在耦合紧密的类之间引入一个中间类是降低类之间的耦合度的常见做法。
具体来说就是把枝节性代码放入代理类中,它们由代理类负责调用,而真实类只负责主干的核心业务,它也由代理类调用,它并不知道枝节性代码的存在和作用,因为这本不是它的任务。对外来说,代理类隐藏在接口之后,客户并不清楚也不需要清楚具体的调用过程。通过这样的处理,主干与枝节之间的交叉解开了,外界的调用也没有复杂化,这就有效降低系统各部分间的耦合度。
下面让我们先看看代码

消除了枝节代码的注册类

/**
* 用於用戶注冊的服務類
* @author: sitinspring(junglesong@gmail.com)
* @date: 2008-5-27-下午09:15:25
*/
public class RegisterService implements IService{
  /**
   * 注冊一個用戶
   * @param name 用戶名
   * @param pswd 用戶密碼
   * @param email 用戶郵件地址
   */
  public void register(String name,String pswd,String email){
    // 真正的,該由本函數擔負的處理
    System.out.println("存儲用戶信息到數據庫");
  }
}

注册类的代理类,枝节性代码都被转移到了这里

/**
* 注冊服務代理類
* @author: sitinspring(junglesong@gmail.com)
* @date: 2008-5-27-下午09:45:10
*/
public class RegisterServiceProxy implements InvocationHandler {
  // 代理對象
    Object obj;
   
    // 構造函數,傳入代理對象
    public RegisterServiceProxy(Object o) {
        obj = o;
    }

    /**
     * 调用被代理对象的将要被执行的方法,我们可以在调用之前進行日誌記錄,之后执行郵件發送
     */
    public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
        Object result = null;
        try {
          // 進行日誌記錄
            String name=(String)args[0];
          Logger.log("將注冊一個新用戶"+name);

          // 調用Object的方法
            result = m.invoke(obj, args);
           
            // 执行郵件發送
            String email=(String)args[2];
            MailSender.send(email, "歡迎"+name+"注冊為本系統的用戶");
        } catch (InvocationTargetException e) {
        } catch (Exception eBj) {
        } finally {
            // Do something after the method is called
        }
        return result;
    }
}

代理类RegisterServiceProxy的解释

该代理类的内部属性为Object类,实际使用时通过该类的构造函数RegisterServiceProxy(Object obj)对其赋值;此外,在该类还实现了invoke方法,该方法中的
method.invoke(obj,args);
其实就是调用被代理对象的将要被执行的方法,这是通过反射实现的,方法参数obj是实际的被代理对象,args为执行被代理对象相应操作所需的参数。通过动态代理类,我们可以在调用之前或者之后执行一些相关操作。

如何生成一个代理类的实例

代理类的实例需要特殊的方式生成,代码如下:

  public static IService genereteService(){
    return (IService)Proxy.newProxyInstance(
        IService.class.getClassLoader(),
            new Class[]{IService.class},
            new RegisterServiceProxy(new RegisterService()));
  }
Proxy即为java中的动态代理类,其方法Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,其中loader是类加载器,interfaces是被代理的真实类的接口,h是具体的代理类实例。

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

工厂方法的作用

对于代理类生成的细节,客户(需要使用RegisterService的程序员)是没有兴趣也没有必要知道的,我们可以让它隐藏在一个工厂方法中,对外返回一个接口,这样在调用时用户就不知道他是与代理打交道还是与实际对象打交道了。使用RegisterService类时示例代码如下:

IService service=RegisterServiceFactory.genereteService();   
service.register("sitinspring","123456","junglesong@gmail.com");
执行完的结果和前面的代码的是一样的。

动态代理在AOP中的应用

Spring的AOP支持可以被用于从系统核心逻辑中分离交叉业务(cross-business)如日志,事务管理和安全等,使用AOP,你可以用各种功能层来覆盖核心业务层,这些功能层可以灵活的应用到你的系统中,甚至核心业务层都不知道它们的存在,这是一个强大的概念。
AOP(aspect-oriented programming)的核心就是动态代理,掌握它对于理解AOP尤为重要,犹如反射对理解IoC一样。
分享到:
评论

相关推荐

    GWT通信机制初探

    标题 "GWT通信机制初探" 指的是探讨Google Web Toolkit (GWT) 的通信方式,这是一种用于构建高性能、跨浏览器的JavaScript应用程序的开发框架。GWT通过编译Java代码到JavaScript,允许开发者使用Java语言来编写前端...

    初探spring aop内部实现 java

    对于接口,Spring使用Java动态代理(JDK Proxy)创建代理对象;而对于类,Spring则使用CGLIB库生成子类代理。这两种方式都是在运行时生成代理对象,从而在方法调用前后插入切面逻辑。 接下来,我们转向DataSource的...

    Java ee 企业应用开发:第7章 在Spring中管理事务.ppt

    #### 7.1.1 从代理机制初探AOP 代理机制是AOP实现的基础。代理可以在目标对象前设置一个中介,处理额外的职责,例如日志记录。代理有两种主要形式: 1. **静态代理**:在编译时创建代理类,代理类和目标类都实现...

    Sharding生态圈初探.pptx

    《Sharding生态圈初探》 在当今的互联网领域,随着数据量的急剧增长,传统的RDBMS(关系型数据库管理系统)面临着高并发访问、高可用性以及存储稳定性等严峻挑战。RDBMS虽然具备强大的事务处理能力和数据一致性保证...

    spring 帮助文档

    Spring AOP实现了基于代理(proxy-based)的AOP机制,通过代理对象拦截目标对象的方法调用并执行额外的逻辑。 **3.1.2 Spring AOP基础概念** Spring AOP的核心概念包括切面(aspect)、连接点(join point)、通知...

    开源框架 Spring Gossip

    从代理机制初探 AOP 动态代理 <br>AOP 观念与术语 Spring AOP Advices Advices 包括了Aspect 的真正逻辑,由于缝合至Targets的时机不同,Spring 提供了几种不同的 Advices。 Before ...

    Tomcat源码研究.pdf

    - **动态部署**:Tomcat 6支持热部署,可以在运行时动态加载或卸载Web应用程序。这通过监听特定目录(通常是`webapps`目录)的变化来实现。 - **安全性**:通过配置文件和内置的安全管理器来控制访问权限。例如,...

    clouds.zip

    这包括如何配置Eureka进行服务注册与发现,使用Config实现配置管理,借助Hystrix实现熔断机制,利用Ribbon或Zuul实现客户端和服务端的负载均衡,以及如何通过Spring Cloud Bus实现系统的动态更新。 总的来说,...

    Spring开发指南

    Spring AOP部分会介绍AOP的基本概念、Spring中的AOP实现以及与动态代理(Dynamic Proxy)和CGLib等技术的结合使用。 DAO(Data Access Object)支持是Spring提供的数据访问抽象,它封装了数据访问的细节,使得...

    SpringBlade 开发手册.pdf

    Mapper、新增 API、修改 API、删除 API、查询 API、单条数据、多条数据、分页、微服务远程调用、声明式服务调用、Feign 熔断机制、Hystrix、生产部署、windows 部署、linux 部署、jar 部署、docker 部署、java 环境...

    Jetty权威指南.pdf

    Jetty还可以与其他Web服务器(如Apache)配合使用,通过mod_proxy或AJP代理协议将请求转发给Jetty处理。 #### 九、虚拟主机 **9.1 虚拟主机的配置方法** Jetty支持在同一台物理服务器上托管多个虚拟主机。通过...

    ICE中间件教程

    - **1.3.2 Slice映射机制**:定义了如何将Slice中的定义映射到具体的编程语言中,如C++或Java。 **1.4 Ice协议** - **1.4.1 Ice协议的组成**:包括消息格式、序列化规则等,确保客户端和服务器之间的通信能够高效...

    Nginx开发从入门到精通.pdf.docx

    - 随着互联网技术的发展,Nginx逐渐成为了处理动态内容、负载均衡、反向代理等任务的重要工具之一。 2. **使用简介**: - Nginx可以作为独立的Web服务器运行,也可以与后端的其他Web应用服务器(如Apache)配合...

    enterpriseSpider

    【企业级爬虫框架"enterpriseSpider"初探】 在当今数字化时代,数据是企业决策的重要依据,而网络爬虫作为一种高效的数据采集工具,被广泛应用于市场分析、竞争情报、用户行为研究等领域。"enterpriseSpider"便是一...

Global site tag (gtag.js) - Google Analytics