`
nianien
  • 浏览: 16927 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

关于Java静态代理模式的实现

阅读更多

在java中有两类代理模式,动态代理模式和静态代理模式,关于动态代理的实现,涉及到字节码的重新编写,无法单纯的通过java代码实现,不过jdk内置实现了动态代理模式,基于接口的,其它的有cglib,aspectJ等,这里不献丑了.
之所以有动态代理的出现,主要是因为静态代理不够灵活,这里相对传统的静态代理模式进行一下改进,使我们能方便快捷的使用代理的设计思想,同时有不需要大量冗余的代码.
话不多说,先看一个传统的代理模式的实现:
//接口
public interface HelloService {
 
 void sayHello(String name);

}
//接口实现
public class HelloSerivceImpl  implements HelloService{

 @Override
 public void sayHello(String name) {
  Console.writeLine("Hi~,glad to meet U,"+name);
 
 }

}

//代理实现
public class HelloServiceProxy implements HelloService{

 private HelloService service;
 
 public HelloServiceProxy(HelloService service){
  this.service=service;
 }
 @Override
 public void sayHello(String name) {
  Method m=service.getClass().getDeclaredMethod("sayHello", String.class);
  this.before(service, m, new Object[]{name});
  service.sayHello(name);
  this.before(service, m, new Object[]{name});
 
 }

 public void after(Object target, Method method, Object[] params) {
  System.out.println(">>>>>>after the method of " +  method.getName());
 
 }

 public void before(Object target, Method method, Object[] params) {
  System.out.println(">>>>>>before the method of " + method.getName());

 
 }

}

//测试
static void test_static(){
 HelloService service=new HelloServiceProxy(new HelloSerivceImpl());
 service.sayHello("lining");
}

 

从上面我们可以看,代理的接口实现实际上是调用接口实例的对应方法,并且在其执行之前调用before方法,在执行后调用after方法,从而实现拦截器的效果.但是当接口的方法很多的时候,我们需要对每个方法重复上面的过程,这将充满重复的代码段,增加我们的工作量,为此我们又必须对其进行改造和优化.

 

从上面我们可以看出,代码重复的地方在于before和after方法,而且两个方法是分开的,能想到的一个简单的改进方法就是将方法名作为参数传递过去,进行调用,于是我们这里抽象出来一个统一执行的方法:

public class HelloServiceProxy implements HelloService {

 private HelloService service;

 public HelloServiceProxy(HelloService service) {
  this.service = service;
 }

 @Override
 public void sayHello(String name) {
  invoke("sayHello", name);
 }

 public void after(Object target, Method method, Object[] params) {
  System.out.println(">>>>>>after the method of " +  method.getName());
 }

 public void before(Object target, Method method, Object[] params) {
  System.out.println(">>>>>>before the method of " + method.getName());
 
 }

 private Object invoke(String name, Object... params) {
  Object result = null;
  try {
   Class<?>[] types = new Class[params.length];
   for (int i = 0; i < types.length; types[i] = params[i++].getClass())
    ;
   Method m = service.getClass().getDeclaredMethod(name, types);
   this.before(service,m, params);
   result = m.invoke(service, params);
   this.after(service,m, params);

  } catch (Exception e) {
   e.printStackTrace();
  }
  return result;
 }
}

 

从上面的代码我们可以看到,比最初的模式有了一定的改进,所有调用接口的方法统一起来的,所不同的仅仅是传递参数的不同,然而,能不能更进一步的,毕竟传递的方法名称是硬编码的,即使写错了,编译器也检测不出来.如果能够有一种方法可以自动获取方法名称,而我们仅需要提供方法参数岂不是更好?在这种想法的指导下,我们再进一步改进:

 

public class HelloServiceProxy implements HelloService {

 private HelloService service;

 public HelloServiceProxy(HelloService service) {
  this.service = service;
 }

 @Override
 public void sayHello(String name) {
  invoke(name);
 }

 public void after(Object target, Method method, Object[] params) {
  System.out.println(">>>>>>after the method of " +  method.getName());
 }

 public void before(Object target, Method method, Object[] params) {
  System.out.println(">>>>>>before the method of " + method.getName());
 
 }

 private Object invoke(Object... params) {
  Object result = null;
  String methodName = MethodTool.getCallerMethodName(1);
  try {
   Class<?>[] types = new Class[params.length];
   for (int i = 0; i < types.length; types[i] = params[i++].getClass())
    ;
   Method m = service.getClass().getDeclaredMethod(methodName, types);
   this.before(service,m, params);
   result = m.invoke(service, params);
   this.after(service,m, params);

  } catch (Exception e) {
   e.printStackTrace();
  }
  return result;
 }
}

 

到这里,我们基本上解决了有代码冗余的问题,这里有一个关键点,就是标红的代码段:

  String methodName = MethodTool.getCallerMethodName(1);
这段代码的意思获取调用当前方法的方法名称,是我封装的类库中的方法,不同人可以有自己不同的实现.

 

上面的代码适用于多数代理的情况,因此我们可以抽象出来一个基类,并将before和after方法抽象成拦截器接口,于是上面的代码可以改造成如下:

 

/**
 * 静态代理模式的抽象类<br>
 * 该类不能独立使用,而是应当继承某个接口以后作为接口类型使用<br>
 * 该类没有默认的构造方法,子类在实例化时需要显示调用该类的构造方法并提供构造参数<br>
 * 由于父类的实例化先于子类,父类初始化时子类的实例成员尚未初始化,因此子类使用其实例成员变量作为父类的构造参数<br>
 *
 * @author skyfalling
 *
 */
public abstract class AbstractProxy {

 /**
  * 被代理对象实例
  */
 private Object target;
 /**
  * 拦截器实例
  */
 private Interceptor interceptor;

 /**
  * 获取拦截器实例
  *
  * @return
  */
 protected Interceptor getInterceptor() {
  return interceptor;
 }

 /**
  * 设置拦截器实例<br>
  * 子类可以重置拦截器
  *
  * @param interceptor
  */
 protected void setInterceptor(Interceptor interceptor) {
  this.interceptor = interceptor;
 }

 /**
  * 构造方法,传递被代理实例,参数由子类在实例化时提供<br>
  * 这里需要注意的是,被代理的对象与该类的子类应该拥有相同的接口
  *
  * @param target
  *            被代理对象实例
  * @param interceptor
  *            拦截器实例
  */
 protected AbstractProxy(Object target) {
  this(target, null);
 }

 /**
  * 构造方法,传递被代理实例和拦截器,参数由子类在实例化时提供<br>
  * 这里需要注意的是,被代理的对象与该类的子类应该拥有相同的接口
  *
  * @param target
  *            被代理对象实例
  * @param interceptor
  *            拦截器实例
  */
 protected AbstractProxy(Object target, Interceptor interceptor) {
  this.target = target;
  this.interceptor = interceptor;
 }

 /**
  * 执行被代理对象中和调用者同名的方法<br>
  * 调用该方法时,将会触发被代理对象的同名方法,并且在被代理对象方法执行过程中使用拦截器进行拦截处理
  * 子类在接口实现中调用该方法,并传递参数,从而实现对代理对象的拦截处理<br>
  *
  * @param target
  * @param params
  * @return 返回代理结果
  */
 protected Object proxy(Object... args) {
  String methodName = MethodTool.getCallerMethodName(1);
  Object result = null;
  try {
   Class<?>[] types = new Class[args.length];
   for (int i = 0; i < types.length; types[i] = args[i++].getClass())
    ;
   // 被代理的方法
   Method method = this.target.getClass().getDeclaredMethod(
     methodName, types);

   // 代理前的处理
   if (this.interceptor != null) {
    this.interceptor.before(this.target, method, args);
   }
   try {
    // 执行被代理的方法
    result = method.invoke(this.target, args);
   } catch (Exception ex) {
    this.interceptor.doException(this.target, method, args, ex);
   }
   // 代理之后的处理
   if (this.interceptor != null) {
    this.interceptor.after(this.target, method, args);
   }

  } catch (Exception e) {
   ExceptionHandler.throwException(e);
  }
  return result;
 }

}

 

/**
 * 拦截器的接口声明
 *
 * @author skyfalling
 *            拦截对象的类型
 *
 */
public interface Interceptor {

 /**
  * 拦截前处理
  *
  * @param target
  *            被代理的实例
  * @param method
  *            被代理的方法
  * @param args
  *            被代理方法的参数
  */
 void before(Object target, Method method, Object[] args);

 /**
  * 拦截后的处理
  *
  * @param target
  *            被代理的实例
  * @param method
  *            被代理的方法
  * @param args
  *            被代理方法的参数
  */
 void after(Object target, Method method, Object[] args);

 /**
  * 被拦截方法出现异常时的处理
  *
  *
  * @param target
  *            被代理的实例
  * @param method
  *            被代理的方法
  * @param args
  *            被代理方法的参数
  * @param ex
  *            异常实例
  */
 void doException(Object target, Method method, Object[] args,Exception ex);
}


在进行了上述的抽象后,我们利用基类重新实现静态代理模式:

 

//静态代理类继承父类,实现接口方法

public class HelloServiceProxy extends AbstractProxy implements HelloService {


 @Override
 public void sayHello(String name) {
  this.proxy(name);
 }
 public HelloServiceProxy(HelloService service) {
  super(service,new ProxyInterceptor() {
   @Override
   public void after(Object target, Method method, Object[] params) {
    System.out.println(">>>>>>after the method of " +  method.getName());
   
   }

   @Override
   public void before(Object target, Method method, Object[] params) {
    System.out.println(">>>>>>before the method of " + method.getName());

   
   }
  });
 }

 

//测试方法

 static void test_static(){
  HelloService service=new HelloServiceProxy(new HelloSerivceImpl());
  service.sayHello("lining");
 }

 

好了,我的关于静态代理模式的实现就是这样,以后实现代理的时候,只需继承静态代理的基类和代理对象的接口,对于接口中的方法实现中统一调用proxy方法即可.暂时能够优化的程度就到这了,如果进一步优化,恐怕需要动态改写字节码了,研究过javaassit的,可以考虑用它实现.

第一次发帖,如果哪里有错,欢迎批评指正,莫要拍砖!

分享到:
评论

相关推荐

    java 设计模式之代理模式(Proxy Pattern)实现代码及设计详解:动态代理模式、静态代理模式

    java 代理模式实现代码及设计详解:动态代理模式、静态代理模式

    Java 代理 代理模式 静态代理与动态代理 常见的动态代理实现 .md

    - 代理模式 - 静态代理与动态代理 - 常见的动态代理实现 - JDK Proxy - CGLIB - JDK Proxy 和 CGLIB 的对比 - 动态代理的实际应用 - Spring AOP 说在前面:今天我们来聊一聊 Java 中的代理,先来聊聊故事...

    代理模式_静态代理.zip

    设计模式之代理模式_静态代理的一个java实现的demo。主类在Client中。仅代码实现,可供初学者参考使用

    java静态代理和动态代理详解

    在代理模式中,主要有两种类型:静态代理和动态代理。 1.静态代理: 静态代理是指在编译期间就已经确定代理类和被代理类的关系,需要手动编写代理类。代理类需要实现与被代理类相同的接口,并且在代理类中持有一个被...

    代理模式的各种实现 (动态代理、静态代理)

    Java各种代理模式以及AOP的实现代码 : 1.AspectJ - 静态代理 2. 动态代理 (1)JDK动态代理 (2)CGlib代理

    java设计模式【之】静态代理【源码】【场景:帮爸爸买菜】.rar

    java设计模式【之】静态代理【源码】【场景:帮爸爸买菜】.rar * 代理模式 * 在开发者的角度来看,创建一个代理对象,提供给用户使用,避免用户直接访问真正的对象 * 在用户角度来看,就是普通的类方法调用 * ...

    java静态代理与动态代理

     代理模式是常用的Java 设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个...

    Java的静态代理深入理解.md

    静态代理代理模式,顾名思义就是提供一个代理类,可以访问原对象并且替原对象进行一些操作。 优点:使用代理模式可以在保证不修改原有类的同时(即满足对扩展开放,对修改关闭的原则),对原有类增加一些功能实现。

    设计模式之代理模式Java实现和类设计图

    设计模式之代理模式Java实现和类设计图,包括静态代理和动态代理

    java三种方法实现代理模式(源码+jar包)

    三种方法实现代理模式:静态代理,动态代理,cglib代理

    Java中的静态代理最全讲义

    目录: 简介 1.1 代理模式概述 1.2 静态代理概述 静态代理的基本结构 ...静态代理的实现步骤 3.1 定义代理接口 3.2 实现真实主题类 3.3 实现代理类 3.4 使用代理类 静态代理的优缺点 最佳实践与注意事项

    Java设计模式:静态代理、装饰者、动态代理详解

    静态代理、装饰者、动态代理1. 静态代理设计模式1.1 ...代理模式是Java常见的设计模式之一。 代理模式是指不直接调用实际对象,而是通过调用代理,来间接的调用实际的对象。 为什么要采用这种间接的形式来调用对象呢?

    java动态代理实现与原理详细分析.docx

    关于Java中的动态代理,我们首先需要了解的是一种常用的设计模式--代理模式,而对于代理,根据创建代理类的时间点,又可以分为静态代理和动态代理。  代理模式是常用的java设计模式,他的特征是代理类与委托类有...

    Java 动态代理.md

    - 静态代理 - 动态代理 - JDK 动态代理 - CGLIB 动态代理 - Javassist 代理 - ASM 代理 这篇文章我们来聊一下 Java 中的动态代理。 动态代理在 Java 中有着广泛的应用,比如 AOP 的实现原理、RPC远程调用、...

    java设计模式【之】JDK动态代理【源码】【场景:帮爸爸买菜】.rar

    * 代理模式 * 在开发者的角度来看,创建一个代理对象,提供给用户使用,避免用户直接访问真正的对象 * 在用户角度来看,就是普通的类方法调用 * * 作用 * 1.保护被代理对象 * 2.增强被代理对象 * 3.完全...

    静态代理和动态代理Demo

    资源列举了设计模式中的静态代理和动态代理的简单java实现,jdk1.8版本经过测试验证,对于想学习设计模式的童靴应该有所帮助

    jdk动态代理模式的分析与底层实现

    基于java的jdk动态代理, 比较了静态代理与动态代理的区别,以及动态代理的底层实现,反编译class文件 jdk动态代理和cglib的区别

    aop思想的java实现

    aop思想的java代码实现。代码分三个部分,一个是不使用aop思想实现日志记录,一个是使用静态代理实现aop,最后是使用java 1.3之后的动态代理机制实现。

    aop思想的java代码实现

    这是一个aop思想的实现。配合本博客文章《aop》。其中有3个包,一个没有实现aop,一个用静态代理实现aop,一个使用java的动态代理机制实现aop。

    《剑指offer》Java代理.pdf

    Java 代理 代理模式 静态代理与动态代理 常见的动态代理实现 JDK Proxy CGLIB JDK Proxy 和 CGLIB 的对比 动态代理的实际应用 Spring AOP

Global site tag (gtag.js) - Google Analytics