Java 代理详解

 

Java 代理详解

       欢迎访问我的个人博客

       Java的世界,框架永远如夜空繁星Java的框架,多如牛毛Spring ,hibernate,ibatis/myBatis, struts1/2,  shiro, lucene等等,有时候会觉得这些框架都非常神奇,解决了很多开发的问题,让开发人员进行系统、服务开发时变得简单,框架内部变得神秘,其实无论什么框架,其实现无非是基于JDK来开发的,反射、代理、泛型、注解、以及各种设计模式是框架的灵魂。比如下图的代码

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
protected  Object createProxy(
         Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
 
     ProxyFactory proxyFactory =  new  ProxyFactory();
     // Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
     proxyFactory.copyFrom( this );
 
     if  (!shouldProxyTargetClass(beanClass, beanName)) {
         // Must allow for introductions; can't just set interfaces to
         // the target's interfaces only.
         Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass,  this .proxyClassLoader);
         for  (Class<?> targetInterface : targetInterfaces) {
             proxyFactory.addInterface(targetInterface);
         }
     }
 
     Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
     for  (Advisor advisor : advisors) {
         proxyFactory.addAdvisor(advisor);
     }
 
     proxyFactory.setTargetSource(targetSource);
     customizeProxyFactory(proxyFactory);
 
     proxyFactory.setFrozen( this .freezeProxy);
     if  (advisorsPreFiltered()) {
         proxyFactory.setPreFiltered( true );
     }
 
     return  proxyFactory.getProxy( this .proxyClassLoader);
}

 

用的正好是反射机制和代理模式,本文主要讲讲Java的代理模式的思想,以及他的分类和实现方式。

 

    1. 静态代理

      静态代理是代理最简单的一种方式,实现为,一个实现类,一个接口,一个代理类,一个测试类,即可完成静态代 理,举个“栗子”,我们去银行存钱我们没有存钱的能力,需要漂亮的营业员小姐(可能是娘炮的先生)帮我们把钱存到账户里面,我们实际是代理,真正的实现类是营业员小姐,我们在存钱时需要先给钱给营业员,而钱不会自己跑到营业员小姐那里,所以我们是代理类。代码如下

      银行员工接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
 
  */
package  cn.inspries.test.proxy;
/* 银行美女
  * ***********************************
  * @author sandy
  * @project cn.inspries.test.proxy
  * @create_date 2014-8-14 下午9:07:32
  * ***********************************
  */
public  class  BankBeauty  implements  MoneyManager{
@Override
public  void  putMoney( double  money) {
System.out.println( "您存了"  + money +  "元人民币,请对我的服务进行评价!" );
}
}

客户类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 
  */
package  cn.inspries.test.proxy;
/*
  * ***********************************
  * @author sandy
  * @project cn.inspries.test.proxy
  * @create_date 2014-8-14 下午9:06:50
  * ***********************************
  */
public  class  Custom  implements  MoneyManager{
     BankBeauty bankBeauty =  new  BankBeauty();
@Override
public  void  putMoney( double  money) {
System.out.println( "去到银行柜台!开始存钱" );
bankBeauty.putMoney(money);
System.out.println( "存好了" );
}
     
}

静态代理接口代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
 
  */
package  cn.inspries.test.proxy;
/*
  * ***********************************
  * @author sandy
  * @project cn.inspries.test.proxy
  * @create_date 2014-8-14 下午9:08:54
  * ***********************************
  */
public  interface  MoneyManager {
void  putMoney( double  money);
}

静态代理测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
 
  */
package  cn.inspries.test.proxy;
/*
  * ***********************************
  * @author sandy
  * @project cn.inspries.test.proxy
  * @create_date 2014-8-14 下午9:26:41
  * ***********************************
  */
public  class  Test {
public  static  void  main(String[] args) {
Custom custom =  new  Custom();
custom.putMoney( 200.0 );
}
}

输出结果:

    去到银行柜台!开始存钱

    您存了200.0元人民币,请对我的服务进行评价!

    存好了

上面的例子很简单这里就不再多做解释了

  1. 动态代理

    1. JDK动态代理主要使用java.lang.reflect包里面的一系列类去实现,通过实现InvocationHandler 的Object cn.inspries.test.proxy.dynic.DynicProxy.invoke(Object proxy, Methodmethod, Object[] args) throws Throwable 方法,通过这三个参数对对象来进行方法的调用,从而实现动态代理。

    2.  

     接口和实现类跟静态代理一样,代理类有所区别。代理类

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
/**
 
  */
package  cn.inspries.test.proxy.dynic;
 
import  java.lang.reflect.InvocationHandler;
import  java.lang.reflect.Method;
import  java.lang.reflect.Proxy;
 
/*
  * ***********************************
  * @author sandy
  * @project cn.inspries.test.proxy
  * @create_date 2014-8-14 下午9:06:50
  * ***********************************
  */
public  class  DynicProxy  implements  InvocationHandler {
     Object targetObject;
 
     public  Object newProxyInstance(Object targetObject) {
         this .targetObject = targetObject;
         return  Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
                 targetObject.getClass().getInterfaces(),  this );
     }
 
     @Override
     public  Object invoke(Object proxy, Method method, Object[] args)
             throws  Throwable {
         Object returnObject =  null ;
         try  {
             System.out.println( "调用动态代理方法了!" );
             returnObject = method.invoke(targetObject, args);
         catch  (Exception e) {
             e.printStackTrace();
             throw  e;
         }
         return  returnObject;
     }
 
}

   JDK动态代理测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    /**
 
  */
package  cn.inspries.test.proxy.dynic;
import  cn.inspries.test.proxy.BankBeauty;
import  cn.inspries.test.proxy.MoneyManager;
/*
  * ***********************************
  * @author sandy
  * @project cn.inspries.test.proxy
  * @create_date 2014-8-14 下午9:26:41
  * ***********************************
  */
public  class  DynicTest {
public  static  void  main(String[] args) {
DynicProxy dynicProxy =  new  DynicProxy();
MoneyManager moneyManager =  (MoneyManager) dynicProxy.newProxyInstance( new  BankBeauty());
moneyManager.putMoney( 2000 );
}
}

输出

调用代理方法了!

您存了2000.0元人民币,请对我的服务进行评价!

      1. Cglib动态代理,JDK动态代理的局限是,被代理的服务类必须实现接口,否则无法使用代理,将会报错,而Spring里面很多地方用的是类,他是通过Cglib来完成无接口的动态代理的。代码也比较简洁如下。

         

Cglib动态代理类

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
/**
 
  */
package  cn.inspries.test.proxy.dynic_cglib;
import  java.lang.reflect.Method;
import  org.springframework.cglib.proxy.Enhancer;
import  org.springframework.cglib.proxy.MethodInterceptor;
import  org.springframework.cglib.proxy.MethodProxy;
/*
  * ***********************************
  * @author sandy
  * @project cn.inspries.test.proxy
  * @create_date 2014-8-14 下午9:06:50
  * ***********************************
  */
public  class  CglibDynicProxy  implements  MethodInterceptor {
Object targetObject;
public  Object newProxyInstance(Object targetObject) {
this .targetObject = targetObject;
Enhancer enhancer =  new  Enhancer();
enhancer.setSuperclass( this .targetObject.getClass());
enhancer.setCallback( this );  // call back method
return  enhancer.create();  // create proxy instance
}
@Override
public  Object intercept(Object target, Method method, Object[] args,
MethodProxy proxy)  throws  Throwable {
System.out.println( "使用cglib的MethodInterceptor.intercept方法调用代理了!" );
Object result = proxy.invokeSuper(target, args);
System.out.println( "方法走完了,哈哈" );
return  result;
}
}

Cglib测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
 
  */
package  cn.inspries.test.proxy.dynic_cglib;
import  cn.inspries.test.proxy.BankBeauty;
import  cn.inspries.test.proxy.MoneyManager;
/*
  * ***********************************
  * @author sandy
  * @project cn.inspries.test.proxy
  * @create_date 2014-8-14 下午9:26:41
  * ***********************************
  */
public  class  DynicTest {
public  static  void  main(String[] args) {
  CglibDynicProxy cglibDynicProxy =  new  CglibDynicProxy();
  MoneyManager manager = (MoneyManager) cglibDynicProxy.newProxyInstance( new  BankBeauty());
  manager.putMoney( 1688 );
}
}

 

程序输出:

使用cglib的MethodInterceptor.intercept方法调用代理了!

您存了1688.0元人民币,请对我的服务进行评价!

方法走完了,哈哈

总结:动态代理和静态代理的区别

1、静态代理由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的class文件,代理类和委托类的关系在运行前就确定了。

2、静态代理只服务于一个接口。而动态代理可以服务于多个接口。

3、静态代理中如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

4、动态代理类的代码是在程序运行期间由JVM根据反射等机制动态的生成,不存在代理类的字节码文件。代理类和委托类的关系在程序运行时候确定。

5、动态代理与普通的代理相比较,最大的好处是接口中声明的所有方法都被转移到一个集中的方法中处理(invoke),这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。

JDK动态代理方式和CGlib区别是,使用jdk代理是,代理类必须实现接口,而Cglib不论有没有接口都可以实现,所以cglib局限较小。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值