代理模式

代理模式概述

在我们的生活中,有很多普通而又可敬的建筑工人,他们自己有技术,却没有机会随便参与一个项目中去;这时候他们往往会去找也是搞建筑的包工头,包工头手上有很多活可以分派给工人去做。

这个过程中包工头就相当于是代理类,主要就是接活; 
建筑工人就相当于是被代理类,他们负责实际工作的完成。

被代理类和代理类都要实现一个同样的接口;就像上述例子中包工头和建筑工人都要是搞建筑这一行的,这样就能保证包工头接的活,下面的人都能完成;而下面的人接到的任务一定是建筑相关的,能够保证完成。

静态代理

静态代理代码结构比较简单,需要有三个要素:

  1. 共同的接口:定义方法不提供实现,供外部调用;
  2. 实现类:实现上述接口,提供上述方法的具体实现;
  3. 代理类:注入的是实现类,调用接口的方法实际是调用实现类的方法的实现。

下面看代码实现:

package com.wgs.静态代理;

/**
 * 静态代理
 * @author GenshenWang.nomico
 * @time 2017/05/28
 *
 */
public class StaticProxyDemo {

    //公共接口,没有任何实现,必须.
    static interface CommonService{
        public void sayHello();
    } 

    //公共接口的具体实现
    static class RealService implements CommonService{
        @Override
        public void sayHello() {
            System.out.println("hello,静态代理!");
        }

    }

    //产生一个代理类,此类必须实现公共接口,作用:具有sayHello功能的类
    static class StaticProxy implements CommonService{

        private CommonService realService;

        //通过构造器注入接口,实际注入接口的是RealService
        public StaticProxy(CommonService service){
            this.realService = service;
        }

        //此处模拟的即为AOP编程思想。sayHello()方法实现不变,只需在其上下加上一些诸如日志事务等代码,
        //无需在sayHello()中加上与业务代码无关的代码
        @Override
        public void sayHello() {
            System.out.println("模拟日志输出1.....");
            realService.sayHello();
            System.out.println("模拟日志输出2.....");
        }

    }

    //测试类
    public static void main(String[] args) {
        CommonService service = new RealService();
        CommonService proxy = new StaticProxy(service);
        proxy.sayHello();

    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

输出:

模拟日志输出1.....
hello,静态代理!
模拟日志输出2.....
  • 1
  • 2
  • 3

在上述例子中我们可以看到,在不修改sayHello()方法源码的情况下即可在其上下加上调试语句,这个过程是通过代理类实现的。

为什么叫静态代理呢? 
可以看到我们在程序中固定的为CommonServiceA接口和RealServiceA实现类造出了一个代理类StaticProxy。 
缺点就是我们如果再想为CommonServiceB类,CommonServiceC类…实现代理类,必须得另建对应代理类,就会显得很麻烦,这时候就需要动态代理去实现它了。


动态代理实现方式

一说到动态代理,我们第一个想到肯定是大名鼎鼎的Spring AOP了。在AOP的源码中用到了两种动态代理来实现拦截切入功能:jdk动态代理和cglib动态代理。两种方法同时存在,各有优劣。jdk动态代理是由java内部的反射机制来实现的,cglib动态代理是通过继承来实现的,底层则是借助asm(Java 字节码操控框架)来实现的(采用字节码的方式,给A类创建一个子类B,子类B使用方法拦截的技术拦截所以父类的方法调用)。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。。


  公用的接口和实现类

[java]  view plain  copy
  1. public interface UserService {  
  2.     public String getName(int id);  
  3.   
  4.     public Integer getAge(int id);  
  5. }  


[java]  view plain  copy
  1. public class UserServiceImpl implements UserService {  
  2.     @Override  
  3.     public String getName(int id) {  
  4.         System.out.println("------getName------");  
  5.         return "Tom";  
  6.     }  
  7.   
  8.     @Override  
  9.     public Integer getAge(int id) {  
  10.         System.out.println("------getAge------");  
  11.         return 10;  
  12.     }  
  13. }  


JDK的动态代理实现

  jdk的动态代理,依赖的是反射包下的invocationHandler接口,我们的代理类实现invocationHandler,重写invoke()方法,每当我们的代理类调用方法时,都会默认先经过invoke()方法。

[java]  view plain  copy
  1. public class UserInvocationHandler implements InvocationHandler {  
  2.     private Object target;  
  3.   
  4.     UserInvocationHandler() {  
  5.         super();  
  6.     }  
  7.   
  8.     UserInvocationHandler(Object target) {  
  9.         super();  
  10.         this.target = target;  
  11.     }  
  12.   
  13.     @Override  
  14.     public Object invoke(Object o, Method method, Object[] args) throws Throwable {  
  15.         if("getName".equals(method.getName())){  
  16.             System.out.println("++++++before " + method.getName() + "++++++");  
  17.             Object result = method.invoke(target, args);  
  18.             System.out.println("++++++after " + method.getName() + "++++++");  
  19.             return result;  
  20.         }else{  
  21.             Object result = method.invoke(target, args);  
  22.             return result;  
  23.         }  
  24.   
  25.     }  
  26. }  

测试类

[java]  view plain  copy
  1. public class M {  
  2.     public static void main(String[] args) {  
  3.         UserService userService = new UserServiceImpl();  
  4.         InvocationHandler invocationHandler = new UserInvocationHandler(userService);  
  5.           
  6.         UserService userServiceProxy = (UserService) Proxy.newProxyInstance(  
  7.                 userService.getClass().getClassLoader(),   
  8.                 userService.getClass().getInterfaces(),  
  9.                 invocationHandler);  
  10.           
  11.         System.out.println(userServiceProxy.getName(1));  
  12.         System.out.println(userServiceProxy.getAge(1));  
  13.     }  
  14. }  
测试效果


CGLIB的动态代理实现

  cglib依赖的是cglib包下的methodInterceptor接口,每调用代理类的方法,都会调用intercept方法
[java]  view plain  copy
  1. public class CglibMethodInterceptor implements MethodInterceptor {  
  2.     @Override  
  3.     public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {  
  4.         System.out.println("------before " + methodProxy.getSuperName() + "------");  
  5.         Object o1 = methodProxy.invokeSuper(o, args);  
  6.         System.out.println("------after " + methodProxy.getSuperName() + "------");  
  7.         return o1;  
  8.     }  
  9. }  


测试类

[java]  view plain  copy
  1. public class M {  
  2.     public static void main(String[] args) {  
  3.         CglibMethodInterceptor cglibProxy = new CglibMethodInterceptor();  
  4.   
  5.         Enhancer enhancer = new Enhancer();  
  6.         enhancer.setSuperclass(UserServiceImpl.class);  
  7.         enhancer.setCallback(cglibProxy);  
  8.   
  9.         UserService o = (UserService) enhancer.create();  
  10.         o.getName(1);  
  11.         o.getAge(1);  
  12.     }  
  13. }  
测试结果


ps:cglib的动态代理,需要cglib.jarasm.jar支持



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值