JDK源码/轮子分析 :反射包 java.lang.reflect⑤ 之 Java 中 Proxy 动态代理类 探秘(一)

今天的主角是:java.lang.reflect.Proxy

什么是代理
当我们要去找一个目标A的时候,目标说你不能直接找我,先找B,要找我做什么事跟B说好,B会跟我来弹。
上面的情景中B就是代理。说白了就是个转话的,但又不仅仅是传说的。它还有讨价还价、“擦屁股”等的作用,反正目标只管他能做的专一的一件事,其他的前前后后的可以让代理做。听完这个大家是不是想到一个很熟悉的词,没错“拦截”,回到根源它们做的事他么的就是一样的,但是伟大的人类为了彰显语言的美妙,起欢起各种各样的名字。毕竟,五毛硬币和五毛纸币都可以叫五毛,这么区分下也是没错的。

上面是我的理解,要正式点的语言表达的话,我也在网上查了查,如下:
Proxy可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy这个词的原意是代理,用在这里表示由它来"代理"某些操作,可以译为"代理器"
什么是动态代理
首先要给大家再明确一下我这个系列是干什么的,就是给大家简单介绍反射包中的一些类及其常用用法的。从前面几个类Class 、Field 、Method 等我们可以得出一个规律:其实这些类都是类中某一部分信息的抽象,比如类、字段、方法、构造函数等。
那么今天这个 Proxy类是哪部分信息的抽象呢,它就是我们代理的抽象(说实在我不清楚这个代理算不算一个类中的信息,按理说每个类都可以有一个或无数个代理类,管他呢,反正代理模式是我们Java中很重要的一个模式就行了)。
所谓动态代理,就是在写代码的时候不确定是哪个代理,要在运行时才确定出来。这个Proxy就是动态代理类。就是在不确定是哪个代理类的时候统 一用它来表示,在代码中写它就没错了,它是所有代理类的抽象。


话不多说,我们先来看一下代理可以怎么用吧。
首先我们来一个不用代理的场景:我某个接口实现了十个类,每个类的执行方法前后都要加上日志打印出执行什么方法,开始和结束。
类(我只写了一个,请想像有类似的十个)

首先是接口
package com.hlmtest.java.reflect.proxy;
/**
 * 目标类的接口
 * @author mottohlm
 *
 */
public interface TargetInterface {
     
      /**
      * 做好事
      * @param good
      * @return
      */
      public String doSomethingGood(String good );
     
      /**
      * 做坏事
      * @param bad
      * @return
      */
      public String doSomethingBad(String bad );
}
 其中一个实现类
package com.hlmtest.java.reflect.proxy.impl;
import org.apache.log4j.Logger;
import com.hlmtest.java.reflect.proxy.TargetInterface;
/**
 * 目标类,实现了目标接口
 * @author mottohlm
 *
 */
public class Target implements TargetInterface {
      /**
      * 用于记录日志
      */
     Logger log = Logger. getLogger (Target. class );
     
      @Override
      public String doSomethingGood(String good ) {
            log .info( "日志开始:doSomethingGood方法开始执行啦!" );
           StringBuilder things = new StringBuilder ( "做好事第一步:出门多留心眼;" );
            things .append( "做好事第二步:发现好事--" + good + ";" );
            things .append( "做好事第三步:不留名,深藏功与名!" );
           alert( things .toString());
            log .info( "日志结束:doSomethingGood方法结束执行啦!" );
            return null ;
     }
      @Override
      public String doSomethingBad(String bad ) {
            log .info( "日志开始:doSomethingBad方法开始执行啦!" );
           StringBuilder things = new StringBuilder ( "无语;" );
            things .append( "做坏事第二步:发现坏事--" + bad + ";" );
            things .append( "做坏事第三步:无语" );
           alert( things .toString());
            log .info( "日志结束:doSomethingBad方法结束执行啦!" );
            return null ;
     }
      public void alert(String str ){
           System. out .println( str );
     }
}

调用类
package com.hlmtest.java.reflect.proxy;
import com.hlmtest.java.reflect.proxy.impl.Target;
public class TestTarger {
      public static void main(String[] args ) throws IllegalArgumentException, ClassNotFoundException {
            do1 ();
     }
     
      /**
      * 直接在方法中写死日志记录方法
      */   
      public static void do1 (){
           Target i = new Target();
            i .doSomethingGood( "扶老奶奶过马路" );        
           System. out .println( "<------------->" );          
            i .doSomethingBad( "作为老奶奶到街上碰瓷扶老奶奶过马路的人 " );
     }
}
执行结果

接下来我写一个代理类,用动态代理来代理所有实现 TargetInterface 接口的类
package com.hlmtest.java.reflect.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.apache.log4j.Logger;
public class TargetProxy {
     
      /**
      * 用于记录日志
      */
     Logger log = Logger. getLogger (TargetProxy. class );
      public TargetInterface getProxy(TargetInterface target ) throws IllegalArgumentException, ClassNotFoundException{
           
            //使用代码类 Proxy 创建className的对象,并且为其每个方法设置日志记录
            return (TargetInterface)Proxy. newProxyInstance (TargetProxy. class .getClassLoader(),
                      target .getClass().getInterfaces(), new InvocationHandler(){
                                 @Override
                                 public Object invoke(Object proxy ,
                                           Method method , Object[] args )
                                            throws Throwable {
                                      log .info( "日志开始:" + method .getName()+ "方法开始执行啦!" );
                                     Object obj = method .invoke( target , args );
                                      log .info( "日志结束:" + method .getName()+ "方法结束执行啦!" );
                                      return obj ;
                                }
                
           });
     }
}
再改一个实现类,将写在类方法中的日志记录注掉
package com.hlmtest.java.reflect.proxy.impl;
import org.apache.log4j.Logger ;
import com.hlmtest.java.reflect.proxy.TargetInterface;
/**
 * 目标类,实现了目标接口
 * @author mottohlm
 *
 */
public class Target implements TargetInterface {
      /**
      * 用于记录日志
      */
      //Logger log = Logger.getLogger(Target.class);
     
      @Override
      public String doSomethingGood(String good ) {
            //log.info("日志开始:doSomethingGood方法开始执行啦!");
           StringBuilder things = new StringBuilder ( "做好事第一步:出门多留心眼;" );
            things .append( "做好事第二步:发现好事--" + good + ";" );
            things .append( "做好事第三步:不留名,深藏功与名!" );
           alert( things .toString());
            //log.info("日志结束:doSomethingGood方法结束执行啦!");
            return null ;
     }
      @Override
      public String doSomethingBad(String bad ) {
            //log.info("日志开始:doSomethingBad方法开始执行啦!");
           StringBuilder things = new StringBuilder ( "无语;" );
            things .append( "做坏事第二步:发现坏事--" + bad + ";" );
            things .append( "做坏事第三步:无语" );
           alert( things .toString());
            //log.info("日志结束:doSomethingBad方法结束执行啦!");
            return null ;
     }
      public void alert(String str ){
           System. out .println( str );
     }
}

调用处新写方法
package com.hlmtest.java.reflect.proxy;
import com.hlmtest.java.reflect.proxy.impl.Target;
public class TestTarger {
      public static void main(String[] args ) throws IllegalArgumentException, ClassNotFoundException {
            do2 ();
     }
     
      /**
      * 直接在方法中写死日志记录方法
      */   
      public static void do1(){
           Target i = new Target();
            i .doSomethingGood( "扶老奶奶过马路" );        
           System. out .println( "<------------->" );          
            i .doSomethingBad( "作为老奶奶到街上碰瓷扶老奶奶过马路的人 " );
     }
      /**
      * 使用代理类来作日志记录方法
      * @throws ClassNotFoundException
      * @throws IllegalArgumentException
      */   
      public static void do2() throws IllegalArgumentException, ClassNotFoundException{
            //先得到代理对象
           TargetProxy proxy = new TargetProxy();
            //取得将要被代码的对象
           Target t = new Target();
            //取得代理
           TargetInterface obj = proxy .getProxy( t );
           
            obj .doSomethingGood( "扶老奶奶过马路" );
           System. out .println( "<------------->" );          
            obj .doSomethingBad( "作为老奶奶到街上碰瓷扶老奶奶过马路的人 " );
     }
}
执行结果

这样,我就不用在每个类中的方法处写日志记录啦。只要在这个接口的代理类中写一次就好了。这也就是动态代理的一个好处:解决多处重复逻辑代码。

在这里,大家应该也注意到一个问题,要想用动态代理,必需要实现一个接口,并且这个代理只能代理一个接口。这显然是不能满足要求的,请关注Java 中 Proxy 动态代理类 探秘(二),你将豁然开朗。

到此也就差不多了,大家想要了解更多的使用方法的话当然还是文档啦。

Field 的更多API可以查询:

其他

其他反射相关博客:



201800603

猜你喜欢

转载自blog.csdn.net/mottohlm/article/details/80556338
今日推荐