开启生长之旅!这是我参与「日新计划 12 月更文挑战」的第1天

1.前语

咱们知道不论静态署理仍是动态署理,实际上都是关于源方针的一种操控,经过署理方针来直接拜访方针方针。可是静态署理有以下两个问题

静态署理存在的问题是:

1)一旦接口新增或许修正,那么署理方针和被署理方针就得去适配修正

2)静态署理是在代码编写时,去生成的,class文件必然会形成类爆破的风险

有没有计划处理以上的问题呢?显然是有的,那便是动态署理。动态署理,顾名思义,便是在运行时去动态生成署理,咱们下面分甭说两种完成办法,分别为JDK和Cglib。

2.两种完成计划

2.1 完成计划–JDK动态署理

如上面所说,一般被署理的方针会完成某个接口,咱们是否能够借用署理形式的这个特点做点文章呢?

JDK给咱们提供了一些API,能够完成动态署理,InvocationHandler和Proxy.newProxyInstance来完成。

动态署理类:

package com.itbird.design.proxy.demo.dynamic.v1;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
 * 署理方针
 * Created by itbird on 2022/7/4
 */
public class ProxyObject implements InvocationHandler {
    Object proxy;
    public ProxyObject(Object proxy) {
        this.proxy = proxy;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(proxy, args);
    }
}

代码很简单,便是完成InvocationHandler接口,重写invoke办法,这里有一个要害的构造函数,便是要入参源方针。所以咱们看一下调用。

package com.itbird.design.proxy.demo.dynamic.v1;
import java.lang.reflect.Proxy;
/**
 * Created by itbird on 2022/7/4
 */
public class Client {
    public void main() {
        SourceObject sourceObject = new SourceObject();
        // 回来的是 IBank 的一个实例方针,这个方针是由 Java 给咱们创立的 ,调用的是 jni,经过反射+classloader加载
        IObject proxy = (IObject) Proxy.newProxyInstance(IObject.class.getClassLoader(), // ClassLoader
                new Class[]{IObject.class},  //方针接口
                new ProxyObject(sourceObject)); //替换署理
        proxy.methodA();
    }
}

是否很简单,假如这时接口新增或许修正,我们发现没有,不再需要去修正署理类。因为其本质是,经过反射+classloader去完成的,所以不依赖于方针。

可是依然有一个问题,便是Proxy.newProxyInstance运用上有一个弊端,便是必须面向接口,假如源方针也便是被署理方针,假如没有完成某个接口,这时不就GG了。该怎么做呢?不要着急,Cglib能够帮咱们处理这个问题。

2.2 完成计划–Cglib

咱们知道一个方针在内存中存储,一般便是在栈或许堆上,那是否有一种办法或许组件,能够直接从内存copy这个方针,完成深克隆,也便是在内存中在copy一个出来。

Cglib署理形式的根本介绍

1) JDK动态署理形式要求方针方针是完成一个接口,可是有时分方针方针只是一个单独的方针,并没有完成任何的接口,这个时分可运用方针方针子类来完成署理-这便是Cglib署理

2)Cglib署理也叫作子类署理,它是在内存中构建一个子类方针从而完成对方针方针功能扩展 3)Cglib是一个强壮的高性能的代码生成包,它能够在运行期扩展java类与完成java接口.它广泛的被许多AOP的结构运用,例如Spring AOP,完成办法阻拦

4)Cglib包的底层是经过运用字节码处理结构ASM来转换字节码并生成新的类

5)Cglib由于是根据字节码的,显然这时android就不能运用了,因为android是dex文件,这怎么办?不要紧,有 dexmaker 和 cglib-for-android 库。

Cglib署理怎么完成呢?

第一步:引进cglib的android jar文件

动态代理实现的两种方式

界说源方针,能够看到此时的被署理方针,并未完成任何接口

package com.itbird.design.proxy.demo.dynamic.v2;
/**
 * 原始方针,预对其拜访,加以操控的方针,没有完成接口
 * Created by itbird on 2022/7/4
 */
public class SourceObject  {
    public void methodA() {
        //TODO 详细的完成
    }
    public void methodB() {
        //TODO 详细的完成
    }
    public void methodC() {
        //TODO 详细的完成
    }
    public void methodD() {
        //TODO 详细的完成
    }
    public void methodE() {
        //TODO 详细的完成
    }
}

第二步:代码完成,首先运用Cglib完成办法阻拦

package com.itbird.design.proxy.demo.dynamic.v2;
import leo.android.cglib.proxy.MethodInterceptor;
import leo.android.cglib.proxy.MethodProxy;
/**
 * 署理方针
 * Created by itbird on 2022/7/4
 */
public class ProxyMethodInterceptor implements MethodInterceptor {
    /**
     * 重写办法阻拦在办法前和办法后参加事务
     * Object obj为方针方针
     * Method method为方针办法
     * Object[] params 为参数,
     * MethodProxy proxy CGlib办法署理方针
     */
    @Override
    public Object intercept(Object o, Object[] objects, MethodProxy methodProxy) throws Exception {
        //参数:Object为由CGLib动态生成的署理类实例,Method为上文中实体类所调用的被署理的办法引证,Object[]为参数值列表,MethodProxy为生成的署理类对办法的署理引证。
        //回来:从署理实例的办法调用回来的值。
        //其间,proxy.invokeSuper(obj,arg) 调用署理类实例上的proxy办法的父类办法(即实体类TargetObject中对应的办法)
        System.out.println("调用前");
        Object result = methodProxy.invokeSuper(o,objects);
        System.out.println(" 调用后"+result);
        return result;
    }
}

第三步,调用

package com.itbird.design.proxy.demo.dynamic.v2;
import android.content.Context;
import leo.android.cglib.proxy.Enhancer;
/**
 * Created by itbird on 2022/7/4
 */
public class Client {
    public void main(Context context) {
        //Enhancer类是CGLib中的一个字节码增强器
        Enhancer enhancer = new Enhancer(context);
        //将被署理类TargetObject设置成父类,然后设置阻拦器TargetInterceptor
        enhancer.setSuperclass(SourceObject.class);
        enhancer.setInterceptor(new ProxyMethodInterceptor());
        //履行enhancer.create()动态生成一个署理类,并从Object强制转型成父类型TargetObject
        SourceObject object = (SourceObject) enhancer.create();
        //在署理类上调用办法
        object.methodA();
    }
}

我们发现,此时完成了源方针的办法调用。