精华内容
下载资源
问答
  • AOP动态代理实现方式

    2011-06-25 13:17:57
    AOP动态代理描述了Spring AOP中使用Java高级技术功能实现动态代理,为Spring IOC等高级开发实现了逻辑功能;
  • 动态代理实现方法以及对象HooK

    千次阅读 2017-07-13 18:19:21
    上一篇文章里面已经把动态代理的作用以及实现方法分析了一下,很明显我们可以用HooK做很多事情,比如例子里面的代理做了拿了回扣和偷换行货这种肮脏龌龊的事情。在真正应用的时候我们可以做更多的事情,比如用户登录...

    上一篇文章里面已经把动态代理的作用以及实现方法分析了一下,很明显我们可以用HooK做很多事情,比如例子里面的代理做了拿了回扣和偷换行货这种肮脏龌龊的事情。

    在真正应用的时候我们可以做更多的事情,比如用户登录的时候动态代理他的验证方法,是不是就可以获取用户的账号密码呢?还有比如Activity的启动,我们使用动态代理的手段将contextImp对象进行动态代理,对startActivity()函数进行修改,比如修改Intent的flag或者Intent内部数据等等,导致跳转的页面发生变化,或者改变传递的信息等等。

    而实现HOOK的步骤一般分为三步:
    1. 寻找Hook点,原则是静态变量或者单例对象,尽量Hook pulic的对象和方法,非public不保证每个版本都一样,需要适配。
    2. 选择合适的代理方式,如果是接口可以用动态代理;如果是类可以手动写代理也可以使用cglib。
    3. 偷梁换柱——用代理对象替换原始对象

    下面以改变startActivity() 逻辑为例来展示HOOK的威力。

    首先我们得找到被Hook的对象,我称之为Hook点;什么样的对象比较好Hook呢?自然是容易找到的对象。什么样的对象容易找到?静态变量和单例;在一个进程之内,静态变量和单例变量是相对不容易发生变化的,因此非常容易定位,而普通的对象则要么无法标志,要么容易改变。我们根据这个原则找到所谓的Hook点。

    然后我们分析一下startActivity的调用链,找出合适的Hook点。
    我们知道对于Context.startActivityActivity.startActivity的调用链与之不同,由于Context的实现实际上是ContextImpl;我们看ConetxtImpl类的startActivity方法:

    @Override
    public void startActivity(Intent intent, Bundle options) {
        warnIfCallingFromSystemProcess();
        if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
            throw new AndroidRuntimeException(
                    "Calling startActivity() from outside of an Activity "
                    + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                    + " Is this really what you want?");
        }
        mMainThread.getInstrumentation().execStartActivity(
            getOuterContext(), mMainThread.getApplicationThread(), null,
            (Activity)null, intent, -1, options);
    }

    可以看到contextImp的startActivity的逻辑是:

    (1) 先判断intent的flag是不是FLAG_ACTIVITY_NEW_TASK类型的,如果是走步骤2;如果不是,说明startActivity 不是在activity中调用的,在activity中调用的话,走的流程如下:

     Step 1. Activity.startActivity通过指定名称“activity.subactivity”来告诉应用程序框架层,它要隐式地启动SubActivity。所不同的是传入的参数intent没有Intent.FLAG_ACTIVITY_NEW_TASK标志,表示这个SubActivity和启动它的MainActivity运行在同一个Task中。
    
     Step 2. Activity.startActivityForResult
    
     Step 3. Instrumentation.execStartActivity
    
     Step 4. ActivityManagerProxy.startActivity
    
     详见罗老师源码分析:[Android应用程序内部启动Activity过程(startActivity)的源代码分析](http://blog.csdn.net/Luoshengyang/article/details/6703247)
    

    (2) 调用了ActivityThread类的mInstrumentation成员的execStartActivity方法。

    注意到,ActivityThread 实际上是主线程,而主线程一个进程只有一个mInstrumentation,只在应用刚刚打开第一个activity的时候创建(单例模式),之后不会发生变化,而且看到不管是Activity中startActivity还是在其他地方,调用的都是mInstrumentation.execStartActivity(),因此mInstrumentation对象是一个良好的Hook点。
    分析完我们的HOOK的点后,接下来就要替换了我们的mInstrumentation对象了,代码如下:

    第一步首先通过反射把当前进程的ActivityThread对象拿到手:

    // 先获取到当前的ActivityThread对象
    Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
    Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
    currentActivityThreadMethod.setAccessible(true);
    Object currentActivityThread = currentActivityThreadMethod.invoke(null);

    第二步,虽然动态代理可以非常方便的进行代理对象,但是我们的mInstrumentation对象不是接口,因此没有办法采用动态代理方式创建代理类,那就没办法只能通过继承来静态代理我们的mInstrumentation,然后覆写我们的mInstrumentation的execStartActivity方法,代码如下:

    public class EvilInstrumentation extends Instrumentation {
    
        private static final String TAG = "EvilInstrumentation";
    
        // ActivityThread中原始的对象, 保存起来
        Instrumentation mBase;
    
        public EvilInstrumentation(Instrumentation base) {
            mBase = base;
        }
    
        public ActivityResult execStartActivity(
                Context who, IBinder contextThread, IBinder token, Activity target,
                Intent intent, int requestCode, Bundle options) {
    
            // Hook之前, XXX到此一游!
            Log.d(TAG, "\n执行了startActivity, 参数如下: \n" + "who = [" + who + "], " +
                    "\ncontextThread = [" + contextThread + "], \ntoken = [" + token + "], " +
                    "\ntarget = [" + target + "], \nintent = [" + intent +
                    "], \nrequestCode = [" + requestCode + "], \noptions = [" + options + "]");
    
            // 开始调用原始的方法, 调不调用随你,但是不调用的话, 所有的startActivity都失效了.
            // 由于这个方法是隐藏的,因此需要使用反射调用;首先找到这个方法
            try {
                Method execStartActivity = Instrumentation.class.getDeclaredMethod(
                        "execStartActivity",
                        Context.class, IBinder.class, IBinder.class, Activity.class, 
                        Intent.class, int.class, Bundle.class);
                execStartActivity.setAccessible(true);
                return (ActivityResult) execStartActivity.invoke(mBase, who, 
                        contextThread, token, target, intent, requestCode, options);
            } catch (Exception e) {
                // 某该死的rom修改了  需要手动适配
                throw new RuntimeException("do not support!!! pls adapt it");
            }
        }
    }

    上面代码就是静态代理的代码,execStartActivity中先是打印一些信息,然后通过反射拿到Instrumentation的execStartActivity方法,进行调用,之所以要反射是因为这个方法不可见,必须要反射才能调用,这是静态代理所不能实现的功能。
    创建了Instrumentation的代理对象,又找到了HOOK点,最后就只需要把需要替换的对象换掉就可以了。

    第三步,使用反射进行Instrumentation对象的替换:代码如下:

    public static void attachContext() throws Exception{
        // 1先获取到当前的ActivityThread对象
        Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
        Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
        currentActivityThreadMethod.setAccessible(true);
        Object currentActivityThread = currentActivityThreadMethod.invoke(null);
    
        // 2拿到原始的 mInstrumentation字段
        Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation");
        mInstrumentationField.setAccessible(true);
        Instrumentation mInstrumentation = (Instrumentation) mInstrumentationField.get(currentActivityThread);
    
        //3 创建代理对象,使用反射偷梁换柱
        Instrumentation evilInstrumentation = new EvilInstrumentation(mInstrumentation);
        mInstrumentationField.set(currentActivityThread, evilInstrumentation);
    }

    最后都完成了,那么就要测试一下了,看看能不能打印出我们代理函数里面的数据了,打印结果如下:

    07-11 22:19:20 9207-9207/com.dynamic_proxy_hook.app D/EvilInstrumentation:执行了startActivity,参数如下:
    who = [android.app.Application@76726c01],
    contextThread = [android.app.ActivityThread$ApplicationThread@4353489dd1],
    token = [null],
    target = [null],
    intent = [Intent { act=android.intent.action.test dat=sadjksadk flg-0x10000000}],
    requestCode = [-1],
    options = [null]

    可以看到打印出来了,结果就是HOOK成功了。

    展开全文
  • JAVA动态代理实现方法

    2009-07-22 14:31:00
    在目前的Java开发包中包含了对动态代理的支持,但是其实现只支持对接口的的实现。 其实现主要通过是java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。 Examda提示:Proxy类主要用来获取动态...

     在目前的Java开发包中包含了对动态代理的支持,但是其实现只支持对接口的的实现。
      其实现主要通过是java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。
        Examda提示: Proxy类主要用来获取动态代理对象,InvocationHandler接口用来约束调用者实现,如下,HelloWorld接口定义的业务方 法,HelloWorldImpl是HelloWorld接口的实现,HelloWorldHandler是 InvocationHandler接口实现。代码如下:
      业务接口:
      public interface HelloWorld {
      void sayHelloWorld() ;
      }
      业务接口实现:
      public class HelloWorldImpl implements HelloWorld {
      public void sayHelloWorld() {
      System.out.println("Hello World!");
      }
      }
      InvocationHandler实现,需要在接口方法调用前后加入一部份处理工作,这里仅仅在方法调用前后向后台输出两句字符串,其代码如下:
       import java.lang.reflect.InvocationHandler;
      import java.lang.reflect.Method;
      public class HelloWorldHandler implements InvocationHandler {
      //要代理的原始对象
      private Object objOriginal;
      /**
      * 构造函数。
      * @param obj Examda提示: 要代理的原始对象。
      */
      public HelloWorldHandler(Object obj) {
      this.objOriginal = obj ;
      }
      public Object invoke(Object proxy, Method method, Object[] args)
      throws Throwable {
      Object result ;
      //方法调用之前
      doBefore();
      //调用原始对象的方法
      result = method.invoke(this.objOriginal ,args);
      //方法调用之后
      doAfter();
      return result ;
      }
      private void doBefore() {
      System.out.println("before method invoke!");
      }
      private void doAfter() {
      System.out.println("after method invoke!");
      }
      }
      测试代码:
      import java.lang.reflect.InvocationHandler;
      import java.lang.reflect.Proxy;
      public class Test {
      public static void main(String[] args) {
      HelloWorld hw = new HelloWorldImpl();
      InvocationHandler handler = new HelloWorldHandler(hw);
      HelloWorld proxy = (HelloWorld) Proxy.newProxyInstance(
      hw.getClass().getClassLoader(),
      hw.getClass().getInterfaces(),
      handler);
      proxy.sayHelloWorld();
      }
      }
      Ø 首先获取一个业务接口的实现对象;
      Ø 获取一个InvocationHandler实现,此处是HelloWorldHandler对象;
      Ø 创建动态代理对象;
      Ø 通过动态代理对象调用sayHelloWorld()方法,此时会在原始对象HelloWorldImpl. sayHelloWorld()方法前后输出两句字符串。
      运行测试类输出如下:
      before method invoke!
      Hello World!
      after method invoke!
      此处Test类中的方法调用代码比较多,在我们的实际应用中可以通过配置文件来来简化客户端的调用实现。另外也可以通过动态代理来实现简单的AOP。

    展开全文
  • Java两种动态代理JDK动态代理和CGLIB动态代理

    万次阅读 多人点赞 2018-08-07 15:33:35
    JDK动态代理 cglib动态代理 测试 代理模式 代理模式是23种设计模式的一种,他是指一个对象A通过持有另一个对象B,可以具有B同样的行为的模式。为了对外开放协议,B往往实现了一个接口,A也会去实现接口。但是B是...

    目录

    代理模式

    JDK动态代理

    cglib动态代理

    测试


    代理模式

    代理模式是23种设计模式的一种,他是指一个对象A通过持有另一个对象B,可以具有B同样的行为的模式。为了对外开放协议,B往往实现了一个接口,A也会去实现接口。但是B是“真正”实现类,A则比较“虚”,他借用了B的方法去实现接口的方法。A虽然是“伪军”,但它可以增强B,在调用B的方法前后都做些其他的事情。Spring AOP就是使用了动态代理完成了代码的动态“织入”。

    使用代理好处还不止这些,一个工程如果依赖另一个工程给的接口,但是另一个工程的接口不稳定,经常变更协议,就可以使用一个代理,接口变更时,只需要修改代理,不需要一一修改业务代码。从这个意义上说,所有调外界的接口,我们都可以这么做,不让外界的代码对我们的代码有侵入,这叫防御式编程。代理其他的应用可能还有很多。

    上述例子中,类A写死持有B,就是B的静态代理。如果A代理的对象是不确定的,就是动态代理。动态代理目前有两种常见的实现,jdk动态代理和cglib动态代理。

    JDK动态代理

    jdk动态代理是jre提供给我们的类库,可以直接使用,不依赖第三方。先看下jdk动态代理的使用代码,再理解原理。

    首先有个“明星”接口类,有唱、跳两个功能:

    package proxy;
    
    public interface Star
    {
        String sing(String name);
        
        String dance(String name);
    }
    

    再有个明星实现类“刘德华”:

    package proxy;
    
    public class LiuDeHua implements Star
    {   
        @Override
        public String sing(String name)
        {
             System.out.println("给我一杯忘情水");
    
            return "唱完" ;
        }
        
        @Override
        public String dance(String name)
        {
            System.out.println("开心的马骝");
    
            return "跳完" ;
        }
    }
    

    明星演出前需要有人收钱,由于要准备演出,自己不做这个工作,一般交给一个经纪人。便于理解,它的名字以Proxy结尾,但他不是代理类,原因是它没有实现我们的明星接口,无法对外服务,它仅仅是一个wrapper。

    package proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class StarProxy implements InvocationHandler
    {
        // 目标类,也就是被代理对象
        private Object target;
        
        public void setTarget(Object target)
        {
            this.target = target;
        }
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
        {
            // 这里可以做增强
            System.out.println("收钱");
            
            Object result = method.invoke(target, args);
            
            return result;
        }
        
        // 生成代理类
        public Object CreatProxyedObj()
        {
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
        }  
       
    }
    

    上述例子中,方法CreatProxyedObj返回的对象才是我们的代理类,它需要三个参数,前两个参数的意思是在同一个classloader下通过接口创建出一个对象,该对象需要一个属性,也就是第三个参数,它是一个InvocationHandler。需要注意的是这个CreatProxyedObj方法不一定非得在我们的StarProxy类中,往往放在一个工厂类中。上述代理的代码使用过程一般如下:

    1、new一个目标对象

    2、new一个InvocationHandler,将目标对象set进去

    3、通过CreatProxyedObj创建代理对象,强转为目标对象的接口类型即可使用,实际上生成的代理对象实现了目标接口。

            Star ldh = new LiuDeHua();
    
            StarProxy proxy = new StarProxy();
    
            proxy.setTarget(ldh); 
      
            Object obj = proxy.CreatProxyedObj();
            
            Star star = (Star)obj;

    Proxy(jdk类库提供)根据B的接口生成一个实现类,我们成为C,它就是动态代理类(该类型是 $Proxy+数字 的“新的类型”)。生成过程是:由于拿到了接口,便可以获知接口的所有信息(主要是方法的定义),也就能声明一个新的类型去实现该接口的所有方法,这些方法显然都是“虚”的,它调用另一个对象的方法。当然这个被调用的对象不能是对象B,如果是对象B,我们就没法增强了,等于饶了一圈又回来了。

    所以它调用的是B的包装类,这个包装类需要我们来实现,但是jdk给出了约束,它必须实现InvocationHandler,上述例子中就是StarProxy, 这个接口里面有个方法,它是所有Target的所有方法的调用入口(invoke),调用之前我们可以加自己的代码增强。

    看下我们的实现,我们在InvocationHandler里调用了对象B(target)的方法,调用之前增强了B的方法。

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
        {
            // 这里增强
            System.out.println("收钱");
            
            Object result = method.invoke(target, args);
            
            return result;
        }

    所以可以这么认为C代理了InvocationHandler,InvocationHandler代理了我们的类B,两级代理。

    整个JDK动态代理的秘密也就这些,简单一句话,动态代理就是要生成一个包装类对象,由于代理的对象是动态的,所以叫动态代理。由于我们需要增强,这个增强是需要留给开发人员开发代码的,因此代理类不能直接包含被代理对象,而是一个InvocationHandler,该InvocationHandler包含被代理对象,并负责分发请求给被代理对象,分发前后均可以做增强。从原理可以看出,JDK动态代理是“对象”的代理。

    下面看下动态代理类到底如何调用的InvocationHandler的,为什么InvocationHandler的一个invoke方法能为分发target的所有方法。C中的部分代码示例如下,通过反编译生成后的代码查看,摘自链接地址。Proxy创造的C是自己(Proxy)的子类,且实现了B的接口,一般都是这么修饰的:

    public final class XXX extends Proxy implements XXX
    

    一个方法代码如下:

    
      public final String SayHello(String paramString)
      {
        try
        {
          return (String)this.h.invoke(this, m4, new Object[] { paramString });
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
    

    可以看到,C中的方法全部通过调用h实现,其中h就是InvocationHandler,是我们在生成C时传递的第三个参数。这里还有个关键就是SayHello方法(业务方法)跟调用invoke方法时传递的参数m4一定要是一一对应的,但是这些对我们来说都是透明的,由Proxy在newProxyInstance时保证的。留心看到C在invoke时把自己this传递了过去,InvocationHandler的invoke的第一个方法也就是我们的动态代理实例类,业务上有需要就可以使用它。(所以千万不要在invoke方法里把请求分发给第一个参数,否则很明显就死循环了)

    C类中有B中所有方法的成员变量

      private static Method m1;
      private static Method m3;
      private static Method m4;
      private static Method m2;
      private static Method m0;
    

    这些变量在static静态代码块初始化,这些变量是在调用invocationhander时必要的入参,也让我们依稀看到Proxy在生成C时留下的痕迹。

    static
      {
        try
        {
          m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
          m3 = Class.forName("jiankunking.Subject").getMethod("SayGoodBye", new Class[0]);
          m4 = Class.forName("jiankunking.Subject").getMethod("SayHello", new Class[] { Class.forName("java.lang.String") });
          m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
          m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
          return;
        }
        catch (NoSuchMethodException localNoSuchMethodException)
        {
          throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        }
        catch (ClassNotFoundException localClassNotFoundException)
        {
          throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
        }
      }
    

    从以上分析来看,要想彻底理解一个东西,再多的理论不如看源码,底层的原理非常重要。

    jdk动态代理类图如下

    cglib动态代理

    我们了解到,“代理”的目的是构造一个和被代理的对象有同样行为的对象,一个对象的行为是在类中定义的,对象只是类的实例。所以构造代理,不一定非得通过持有、包装对象这一种方式。

    通过“继承”可以继承父类所有的公开方法,然后可以重写这些方法,在重写时对这些方法增强,这就是cglib的思想。根据里氏代换原则(LSP),父类需要出现的地方,子类可以出现,所以cglib实现的代理也是可以被正常使用的。

    先看下代码

    package proxy;
    
    import java.lang.reflect.Method;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    public class CglibProxy implements MethodInterceptor
    {
        // 根据一个类型产生代理类,此方法不要求一定放在MethodInterceptor中
        public Object CreatProxyedObj(Class<?> clazz)
        {
            Enhancer enhancer = new Enhancer();
            
            enhancer.setSuperclass(clazz);
            
            enhancer.setCallback(this);
            
            return enhancer.create();
        }
        
        @Override
        public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable
        {
            // 这里增强
            System.out.println("收钱");
            
            return arg3.invokeSuper(arg0, arg2);
        } 
    }
    

    从代码可以看出,它和jdk动态代理有所不同,对外表现上看CreatProxyedObj,它只需要一个类型clazz就可以产生一个代理对象, 所以说是“类的代理”,且创造的对象通过打印类型发现也是一个新的类型。不同于jdk动态代理,jdk动态代理要求对象必须实现接口(三个参数的第二个参数),cglib对此没有要求。

    cglib的原理是这样,它生成一个继承B的类型C(代理类),这个代理类持有一个MethodInterceptor,我们setCallback时传入的。 C重写所有B中的方法(方法名一致),然后在C中,构建名叫“CGLIB”+“$父类方法名$”的方法(下面叫cglib方法,所有非private的方法都会被构建),方法体里只有一句话super.方法名(),可以简单的认为保持了对父类方法的一个引用,方便调用。

    这样的话,C中就有了重写方法、cglib方法、父类方法(不可见),还有一个统一的拦截方法(增强方法intercept)。其中重写方法和cglib方法肯定是有映射关系的。

    C的重写方法是外界调用的入口(LSP原则),它调用MethodInterceptor的intercept方法,调用时会传递四个参数,第一个参数传递的是this,代表代理类本身,第二个参数标示拦截的方法,第三个参数是入参,第四个参数是cglib方法,intercept方法完成增强后,我们调用cglib方法间接调用父类方法完成整个方法链的调用。

    这里有个疑问就是intercept的四个参数,为什么我们使用的是arg3而不是arg1?

        @Override
        public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable
        {
            System.out.println("收钱");
            
            return arg3.invokeSuper(arg0, arg2);
        }

     因为如果我们通过反射 arg1.invoke(arg0, ...)这种方式是无法调用到父类的方法的,子类有方法重写,隐藏了父类的方法,父类的方法已经不可见,如果硬调arg1.invoke(arg0, ...)很明显会死循环。

    所以调用的是cglib开头的方法,但是,我们使用arg3也不是简单的invoke,而是用的invokeSuper方法,这是因为cglib采用了fastclass机制,不仅巧妙的避开了调不到父类方法的问题,还加速了方法的调用。

    fastclass基本原理是,给每个方法编号,通过编号找到方法执行避免了通过反射调用。

    对比JDK动态代理,cglib依然需要一个第三者分发请求,只不过jdk动态代理分发给了目标对象,cglib最终分发给了自己,通过给method编号完成调用。cglib是继承的极致发挥,本身还是很简单的,只是fastclass需要另行理解。

    参考

    https://blog.csdn.net/jiankunking/article/details/52143504

    http://www.php.cn/java-article-407212.html

    https://www.cnblogs.com/chinajava/p/5880887.html

    https://rejoy.iteye.com/blog/1627405

    测试

       public static void main(String[] args)
        {
            int times = 1000000;
            
            Star ldh = new LiuDeHua();
            StarProxy proxy = new StarProxy();
            proxy.setTarget(ldh);
            
            long time1 = System.currentTimeMillis();
            Star star = (Star)proxy.CreatProxyedObj();
            long time2 = System.currentTimeMillis();
            System.out.println("jdk创建时间:" + (time2 - time1));
            
            CglibProxy proxy2 = new CglibProxy();
            long time5 = System.currentTimeMillis();
            Star star2 = (Star)proxy2.CreatProxyedObj(LiuDeHua.class);
            long time6 = System.currentTimeMillis();
            System.out.println("cglib创建时间:" + (time6 - time5));
            
            long time3 = System.currentTimeMillis();
            for (int i = 1; i <= times; i++)
            {
                star.sing("ss");
                
                star.dance("ss");
            }
            long time4 = System.currentTimeMillis();
            System.out.println("jdk执行时间" + (time4 - time3));
            
            long time7 = System.currentTimeMillis();
            for (int i = 1; i <= times; i++)
            {
                star2.sing("ss");
                
                star2.dance("ss");
            }
            
            long time8 = System.currentTimeMillis();
            
            System.out.println("cglib执行时间" + (time8 - time7));   
        }

    经测试,jdk创建对象的速度远大于cglib,这是由于cglib创建对象时需要操作字节码。cglib执行速度略大于jdk,所以比较适合单例模式。另外由于CGLIB的大部分类是直接对Java字节码进行操作,这样生成的类会在Java的永久堆中。如果动态代理操作过多,容易造成永久堆满,触发OutOfMemory异常。spring默认使用jdk动态代理,如果类没有接口,则使用cglib。

    展开全文
  • 实现动态代理的两种方式: 第一种是JDK提供的基于接口的动态代理,要求被代理的类必须至少实现一个接口。 2. 第二种是第三方cglib提供的基于子类的动态代理,。至少要继承一个类。 我们这里 来讲解,动态代理,以及...
  • 动态代理是 AOP(Aspect Orient Programming)编程思想,理解...CGLIB通过继承的方式进行代理,无论目标对象有没有实现接口都可以代理,但是无法代理final对象与final方法。(final类型不能有子类,final方法不能被重载)

    什么是代理模式

    举个栗子,比如兰蔻想找安妮海瑟薇代言香水广告,假设兰蔻方代表为Jack,那么是不是Jack直接闯到Anne豪宅扯一嗓子“Anne 妹纸,我这有个私活你接不接”;实际情况肯定不是这样,且不说这个操作太鲁莽(搞不好还会被一枪崩掉),合作本身涉及的事项就很多,比如有木有档期,出场费怎么定价,要买哪些保险,税费怎么处理,违约责任等等,显然这里面财务、保险、法律相关的问题Anne一个人肯定搞不定,需要专业人士处理;而这所有的一切都可以交给Anne的经纪人(或者团队)Team来协办,作为一个艺人Anne只管拍广告就可以了。

    在这个场景中,Jack是客户(代表兰蔻集团),Anne是艺人提供代言服务直接参与广告拍摄,Team是代理(帮Anne处理商务事宜),所有合作细节都是Jack找Team商谈完成,找Team谈合作一样能达成目标(拍代言广告),而且比直接找Anne本人效率更高,更稳妥。

    静态代理

    若代理类在程序运行前就已经存在,这种方式称为静态代理 ,这种情况下的代理类通常都是我们在Java代码中定义的(hard coding)。 通常情况下, 静态代理中的代理类和目标类(委托l类)会实现同一接口或是派生自相同的父类。
    在这里插入图片描述
    此例中,Atm是被代理的目标对象(委托对象),Crs持有Atm的引用,是代理对象,调用Crs的withdraw方法实际被转到Atm的withdraw

    public interface Account {/*目标类与代理类都实现该接口*/
    	String withdraw(double amount);
    }
    
    public class Atm implements Account{/*自动取款机,目标类(被代理)*/
    	@Override
    	public String withdraw(double amount) {
    		return "取出"+ amount +"元";
    	}
    }
    
    public class Crs implements Account {/*自动存取款机,此处为Atm的代理类*/
    
    	private Account account = new Atm();/*持有被代理对象的引用,静态代理*/
    	@Override
    	public String withdraw(double amount) {
    		return account.withdraw(amount);/*取款调用的是目标对象的方法*/
    	}
    	
    	public String deposit() {/*代理类可以有自己的业务操作*/
    		return "存款120元";
    	}
    }
    
    public class Test {
    	public static void main(String[] args) {
    		Crs crs = new Crs();/*创建代理对象*/
    		System.out.println(crs.withdraw(100));
    	}
    }
    

    静态代理的不足

    静态代理实现简单容易理解,但是静态代理不能使一个代理类反复作用于多个不同的目标对象,代理对象直接持有目标对象的引用,导致代理对象和目标对象的耦合。如果Account接口还有另一个实现类也需要进行事务控制,那么就要定义一个新的代理类,这样就会产生许多重复的模版代码,代码复用率不高。而动态代理就可以很好的解决这类问题。

    JDK动态代理

    静态代理的代理关系在编译时确定,而动态代理的代理关系在运行时确定,动态代理更灵活。

    JDK动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的

    目标与代理的共同接口

    public interface Account {/*目标类与代理类都实现该接口,接口定义业务操作*/
    	String withdraw(double amount);
    }
    

    目标对象

    public class Atm implements Account{/*自动取款机,目标类*/
    	@Override
    	public String withdraw(double amount) {
    		return "取出"+ amount +"元";
    	}
    }
    

    handler

    //InvocationHandler,对代理类方法的调用会被转到该类的invoke()方法。
    public class AtmInvocationHandler implements InvocationHandler {
    	private Object target;
    	
    	public AtmInvocationHandler(Object target) {/*绑定对象*/
    		this.target = target;
    	}
    
    	public Object getProxy() {
    		/* 获取代理对象
    		
    		参数说明:
    		loader,指定代理对象的类加载器;
    		interfaces,代理对象需要实现的接口数组;
    		handler,方法调用的实际处理者
    
    		newProxyInstance()会返回一个实现了指定接口的代理对象,对该对象的所有方法调用都会转发给InvocationHandler.invoke()方法。
    		*/
    		return Proxy.newProxyInstance(target.getClass().getClassLoader(),
    				target.getClass().getInterfaces(), this); // 需要绑定接口
    	}
    
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		Object result = null;
    		System.out.println("取款操作");
    		result = method.invoke(target, args);
    		System.out.println(result);
    		System.out.println("取款完成");
    		return result;
    	}
    
    }
    
    public class Test {
    	public static void main(String[] args){
    		AtmInvocationHandler handler = new AtmInvocationHandler(new Atm());
    		Object atmProxy = handler.getProxy();
    		Account accout = (Account)atmProxy;
    		Object result = accout.withdraw(100.0);
    	}
    }
    
    取款操作
    取出100.0元
    取款完成
    

    JDK动态代理小结

    代理类AtmInvocationHandler实现了InvocationHandler接口,与静态代理不同它持有的目标对象类型是Object,因此代理类AtmInvocationHandler能够代理任意的目标对象,给目标对象添加事务控制的逻辑。因此动态代理真正实现了将代码中横向切面逻辑的剥离,实现了代码复用。

    JDK动态代理的缺点:
    (一)通过反射类Proxy和InvocationHandler回调接口实现JDK动态代理,要求目标类必须实现一个接口,对于没有实现接口的类,无法通过这种方式实现动态代理。
    (二)动态代理会为接口中的声明的所有方法添加上相同的代理逻辑,不够灵活

    CGLIB动态代理

    CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB支持接口、继承方式实现代理。

    原 理 \color{blue}{原理} :动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势将横切逻辑织入(weave)目标对象

    CGLIB代理实现步骤:

    1、定义目标对象

    public class Atm{/*自动取款机,目标类,不需要实现指定接口*/
    	public String withdraw(double amount) {
    		return "取出"+ amount +"元";
    	}
    	
    	public String checkBalance() {
    		return "当前余额:" + 1200 + "元";
    	}
    }
    

    2、定义拦截器。在调用目标对象的方法时,CGLib会回调MethodInterceptor接口的intercept方法实施拦截,来切入代理逻辑,类似于JDK中的InvocationHandler接口

    public class AtmInterceptor implements MethodInterceptor {
    	/**
         * obj:cglib生成的代理对象
         * method:被代理对象方法
         * args:方法入参
         * methodProxy: 代理方法
        */	
    	@Override
    	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
    		System.out.println("事务开始");
    		Object result = proxy.invokeSuper(obj, args);
    		System.out.println(result);
    		System.out.println("事务结束");
    		return result;
    	}
    }
    

    3、在需要使用目标对象的时候,通过CGLIB动态代理获取代理对象。

    public class Test {
    	public static void main(String[] args) {
    		//class 文件缓存目录,如果不研究动态类的源码可以不设置
    		System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\cglib_classes");	
    		//用于创建代理对象的增强器,可以对目标对象进行扩展
    		Enhancer enhancer = new Enhancer();
    		//将目标对象设置为父类
    		enhancer.setSuperclass(Atm.class);
    		//设置目标拦截器
    		enhancer.setCallback(new AtmInterceptor());
    		// 创建代理对象
    		Atm atm = (Atm)enhancer.create();
    		// 通过代理对象调用目标方法
    		Object result = atm.withdraw(100);
    		atm.checkBalance();
    	}
    }
    

    目标对象的每个方法调用都会被拦截

    CGLIB debugging enabled, writing to 'D:\cglib_classes'
    事务开始
    取出100.0元
    事务结束
    事务开始
    当前余额:1200元
    事务结束
    

    以上代码通过CGLIB的Enhancer指定要代理的目标对象(即包含实际业务逻辑的对象),再通过调用create()方法得到代理对象,所有对代理对象的非final方法的调用都会指派给AtmInterceptor.intercept()方法,在intercept()方法中可以加入目标对象之外的业务逻辑,比如参数校验、日志审计、安全检查等功能;通过调用MethodProxy.invokeSuper()方法,将调用转发给原始对象,也就是本例的Atm 。CGLIG中MethodInterceptor的作用与JDK代理中的InvocationHandler类似,都是方法调用的中转派发。

    两种动态代理方式的比较

    JDK动态代理不需要任何外部依赖,但是只能基于接口进行代理;CGLIB通过继承的方式进行代理,无论目标对象有没有实现接口都可以代理,但是无法代理final对象与final方法。(final类型不能有子类,final方法不能被重载)

    动态代理是 AOP(Aspect Orient Programming)编程思想,理解动态代理原理,对学习AOP框架至关重要。

    展开全文
  • Java动态代理实现接口方法

    千次阅读 2015-03-30 11:50:53
    最近正在看Mybatis源码,重点研究了自定义Mapper接口里方法实现如何与xml配置文件进行绑定,最后了解到是通过java动态代理实现了接口方法。 主要使用了Proxy.newProxyInstance(loader, interfaces, ...
  • SpringJDK动态代理实现

    千次阅读 2021-01-17 22:18:01
    目录JDK动态代理代码实现 JDK动态代理 JDK动态代理使用Proxy类里面的newProxyInstance方法创建代理对象。 Modifier and Type Method and Description static Object newProxyInstance(ClassLoader loader, ...
  • 【Spring基础】CGLIB动态代理实现原理

    万次阅读 多人点赞 2018-06-09 18:11:19
    前言 ... 一 CGLIB介绍 CGLIB(Code Generation Library)是一个开源项目!是一个强大的,高性能,高质量的Code生成类库, 它可以在运行期扩展Java类与实现...Hibernate用它来实现PO(Persistent Object 持久化对象)...
  • Mybatis使用JDK动态代理实现Mapper接口,事先保存好Mapper接口,和接口声明的方法,返回值,参数类型,然后代理类的方法调用的时候使用MapperMethod这个事先放入方法缓存里的对象来真实调用功能。 笔者极度简化了...
  • Java动态代理实现

    千次阅读 2018-12-05 16:42:07
    在java的动态代理机制中,有两个重要的类和接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。 下面先贴上代码: 首先需要一个接口: /* *...
  • 动态代理实现的三种方式

    千次阅读 2017-06-22 13:18:05
    动态代理实现有三种方式,jdk动态代理(基于接口),cglib动态代理(基于继承),javassist(hibernate中使用这种方式)实现动态代理一 jdk实现动态代理package com.lzzl.jdkproxy;public interface Pet { public ...
  • 动态代理目前实现方式有两种:JDK动态代理、CGLib动态代理 首先来说一下第一种:JDK动态代理 JDK 1.3之后,Java提供了动态代理技术,允许开发者在运行期间创建接口的代理实例。在Sun刚推出动态代理时,还很难...
  • 模拟JDK动态代理实现

    千次阅读 2016-06-01 19:46:37
    在Sun刚推出动态代理时,还很难想象它有多大的实际用途,现在动态代理实现AOP的绝好底层技术。 JDK的动态代理主要涉及java.lang.reflect包中的两个类:Proxy和InvocationHandler。其中InvocationHandler是一个...
  • 使用过spring aop的人应该都知道,spring是通过动态代理实现的。而动态代理听过的有jdk的动态代理以及cglib的动态代理。究竟这两种代理方式有什么区别,好奇研究了下。 jdk动态代理示例 这里举个简单的例子,普通...
  • Java实现动态代理的两种方式。 相对来说cglib更加方便。可以实现实现接口的类(非final类)
  • 在Spring框架中,aop是基于代理模式才能实现的功能,Spring给我们提供了两种代理模式:jdk动态代理和cglib动态代理。他们各有优缺点。 jdk动态代理 jdk动态代理是jdk自带的,所以使用它不需要额外导入jar包,它是...
  • Cglib动态代理实现解析

    千次阅读 2018-04-11 23:05:14
    在 JDK 动态代理源码解读 已经知道了JDK 动态代理实现逻辑,这里我们来学习一下Cglib 的实现逻辑。以方便对动态代理有一个全面的认识。 首先,我们来看一下生成代理类的时序图,对比起JDK的实现,它复杂了很多。 ...
  • java 动态代理实现原理

    千次阅读 2015-07-10 21:08:26
    上篇讲了:java动态代理浅析 这篇讲讲其内部实现原理。 1、相关的类和接口 1.1 java.lang.reflect.Proxy 这是 Java 动态代理机制的主类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。 Proxy 的...
  • Java JDK 动态代理实现和代码分析

    千次阅读 2021-06-02 20:51:44
    JDK 动态代理实现步骤5. JDK 动态代理 API5.1 java.lang.reflect.Proxy5.1 java.lang.reflect.InvocationHandler二、JDK 动态代理的实现(代码)1. 项目结构图2. IRentService 接口3. LandlordServiceImpl 真实类4....
  • Android 动态代理以及利用动态代理实现 ServiceHook

    万次阅读 多人点赞 2017-02-25 20:44:15
    Android 利用 ServiceHook 实现特殊功能
  • spring动态代理实现方式

    千次阅读 2019-08-15 18:31:25
    java动态代理: 利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。 cglib动态代理: 利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。 JDK...
  • Java JDK 动态代理(AOP)使用及实现原理分析

    万次阅读 多人点赞 2019-05-08 21:28:06
    四、动态代理怎么实现的? 五、结论 一、什么是代理? 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息...
  • 动态代理实现

    千次阅读 2018-03-10 23:26:01
    静态代理 之前通过1+N的问题实践了下静态...我们可以通过java内建的动态代理功能或者通过cglib(code generation library)来实现。需要注意的是,java的动态代理只能对接口进行代理,需要对类进行代理只能使用cgli...
  • java动态代理实现

    千次阅读 2019-01-15 14:42:46
    关于Java中的动态代理,我们首先需要了解的是一种常用的设计模式--代理模式,而对于代理,根据创建代理类的时间点,又可以分为静态代理和动态代理。  一、代理模式  代理模式是常用的java设计模式,他的特征是...
  • Spring AOP动态代理实现方式

    千次阅读 2019-06-06 23:20:57
    "JDK"动态代理和"CGLIB"动态代理 ...CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那...
  • cglib动态代理实现 什么是代理     代理分为静态代理和动态代理,在未产生动态代理之前,代理只是为了给某一个类创建一个代理类来为这个类的对象动态添加一些职责和功能。而动态代理产生后,将...
  • 动态代理实现AOP

    千次阅读 热门讨论 2017-10-14 21:32:29
    今天说和小张哥一起讨论AOP,正好看到了相关的视频,今天就总结一下AOP是如何使用动态代理实现的。AOP对JAVA程序员来说并不陌生,他是spring的一个核心内容——面向切面编程,先把概念放在这里,因为这一篇博客...
  • Spring动态代理实现

    千次阅读 2013-07-31 20:36:17
    第一次写自己的技术博客,希望这点东西能够帮助到一些人,同时有什么不对的地方请大家多多指教。...JDK的动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。其中InvocationHandl
  • 动态代理的两种实现方式

    千次阅读 多人点赞 2019-10-15 18:41:48
    1.动态代理概述 代理模式:在不修改目标对象的情况下,对目标对象进行功能增强 动态代理的代理类在物理上是不存在的,代理类是在程序运行的时候产生的. 目标对象:被增强功能的对象 代理对象:用来对目标对象进行...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 343,826
精华内容 137,530
关键字:

动态代理实现方式