分享

代理模式

 印度阿三17 2021-02-08

文章目录

设计模式

一、使用代理的原因

在开发中,经常会遇到之前已经有很多代码,但是又有新的需求,如果对之前的代码进行直接修改,那无疑是很大的工作量,如果采用代理模式,可以通过不修改源代码对代码的执行过程及其结果进行修改

二、代理模式的介绍

代理模式分为两种:静态代理模式和动态代理模式。

静态代理模式实际上就是目标对象已确定,即目标对象已经写死。

动态代理模式运行时才确定目标对象。

这里着重于动态代理的实现

三、动态代理的实现:接口实现

3.1、具体使用

下面的部分操作先可当巫师的咒语。

JDkproxy.class

public class JDKProxy {

public JDKProxy() {
}

@SuppressWarnings("unchecked")
public static <T> T getProxy(Class<?> klass) {
try {
Object object = klass.getDeclaredConstructor().newInstance();
return (T) getProxy(object);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}

return null;
}

@SuppressWarnings("unchecked")
public static <T> T getProxy(Object object) {
Class<?> klass = object.getClass();
ClassLoader classLoader = klass.getClassLoader();
Class<?>[] interfaces = klass.getInterfaces();

return (T) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.println("前");
result = method.invoke(object, args);
System.out.println("后\n");
return result;
}
});
}
}

Interfaze.class

public interface Interfaze {
void doOneThing(String str);
String doOtherThing(int num, String str);
}

MyImplement.class Interfaze接口的实现类

public class MyImplement implements Interfaze{

public MyImplement() {
}

@Override
public void doOneThing(String str) {
System.out.println("doOneThing :"   str);
}

@Override
public String doOtherThing(int num, String str) {
System.out.println("doOtherThing :"   num   str);
return num   str;
}

}

Test.class

public class Text {
public static void main(String[] args) {
Interfaze oneproxy = JDKProxy.getProxy(new MyImplement());
oneproxy.doOneThing("abd");
String str = oneproxy.doOtherThing(34, "肥肠");
System.out.println(str);
}
}

输出结果

在这里插入图片描述

根据输出结果,可以看到Interfaze oneproxy可以去调用MyImplement()实现了它的方法。

不禁有人就开始发问了,为什么不用MyImplement()去他调用方法呢?非要绕这么大的弯!

你看输出结果,在执行方法的前后都执行了“前”和“后”的输出语句,这说明什么,我们在一定程度上修改了这个方法,而且没有破坏他。

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.println("前");
result = method.invoke(object, args);
System.out.println("后\n");
return result;
}
//在执行这个真正的方法前,我们有这个方法method,有他的参数args,那完全可以在执行调用的方法之前进行提前处理,在执行调用方法之后进行善后处理。这就是代理模式的真正精髓所在。

3.2、源码分析

  • 在经过上面的实例后,你是否会对以下问题好奇:

    1. 动态代理类 及其对象实例是如何生成的?

    2. 如何通过调用动态代理对象方法,从而调用目标对象方法?

此处有两个重要的源码分析点:

  • 关注1:创建动态代理类 & 对象:通过调用处理器类对象newProxyInstance()

解决的问题是:动态代理类 及其对象实例是如何生成的?

  • 关注2:通过调用动态代理对象方法从而调用目标对象方法

解决的问题是:如何通过调用动态代理对象方法,从而调用目标对象方法?

下面,我们将主要分析这两处源码。

3.2.1、(关注1)创建动态代理类&对象:通过调用处理器类对象newProxyInstance()

<-- 关注1:调用处理器 类的newProxyInstance() -->
@SuppressWarnings("unchecked")
public static <T> T getProxy(Object object) { //这里的object就是代理对象
Class<?> klass = object.getClass();
ClassLoader classLoader = klass.getClassLoader();
Class<?>[] interfaces = klass.getInterfaces();

        //Proxy.newProxyInstance()作用:根据指定的类装载器、一组接口 & 调用处理器 生成动态代理类实例,并最终返回
return (T) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.println("前");
result = method.invoke(object, args);
System.out.println("后\n");
return result;
}
});
}

//  复写InvocationHandler接口的invoke()
    //  动态代理对象调用目标对象的任何方法前,都会调用调用处理器类的invoke()
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            // 参数说明:
            // 参数1:动态代理对象(即哪个动态代理对象调用了method()
            // 参数2:目标对象被调用的方法
            // 参数3:指定被调用方法的参数
            throws Throwable {Object result = null;
System.out.println("前");
result = method.invoke(object, args);
System.out.println("后\n");
return result;
    }

<-- 关注2:newProxyInstance()源码解析-->
// 作用:根据指定的类装载器、一组接口 & 调用处理器 生成动态代理类及其对象实例,并最终返回
      
public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h) throws IllegalArgumentException { 
  // 参数说明:
        // 参数1:指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
        // 参数2:指定目标对象的实现接口
        // 即要给目标对象提供一组什么接口。若提供了一组接口给它,那么该代理对象就默认实现了该接口,这样就能调用这组接口中的方法
        // 参数3:指定InvocationHandler对象。即动态代理对象在调用方法时,会关联到哪个InvocationHandler对象

    ...  
    // 仅贴出核心代码

    // 1. 通过 为Proxy类指定类加载器对象 & 一组interface,从而创建动态代理类
    // >>关注3
    Class cl = getProxyClass(loader, interfaces); 

    // 2. 通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型
    Constructor cons = cl.getConstructor(constructorParams); 

    // 3. 通过动态代理类的构造函数 创建 代理类实例(传入调用处理器对象
    return (Object) cons.newInstance(new Object[] { h }); 

// 特别注意 
// 1. 动态代理类继承 Proxy 类 & 并实现了在Proxy.newProxyInstance()中提供的一系列接口(接口数组)
// 2. Proxy 类中有一个映射表
  // 映射关系为:(<ClassLoader>,(<Interfaces>,<ProxyClass>) )
  // 即:1级key = 类加载器,根据1级key 得到 2级key = 接口数组
  // 因此:1类加载器对象   1接口数组 = 确定了一个代理类实例
...

// 回到调用关注2的原处
}


<-- 关注3:getProxyClass()源码分析 -->
// 作用:创建动态代理类

public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces)  
throws IllegalArgumentException { 
    
  ...
  // 仅贴出关键代码
  
<-- 1. 将目标类所实现的接口加载到内存中 -->
    // 遍历目标类所实现的接口  
    for (int i = 0; i < interfaces.length; i  ) {  
          
        // 获取目标类实现的接口名称 
        String interfaceName = interfaces[i].getName();  
        Class interfaceClass = null;  
        try {  
        // 加载目标类实现的接口到内存中  
        interfaceClass = Class.forName(interfaceName, false, loader);  
        } catch (ClassNotFoundException e) {  
        }  
        if (interfaceClass != interfaces[i]) {  
        throw new IllegalArgumentException(  
            interfaces[i]   " is not visible from class loader");  
        }  
    }  
       
<-- 2. 生成动态代理类 -->       
        // 根据传入的接口 & 代理对象 创建动态代理类的字节码
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(  
            proxyName, interfaces);  

            // 根据动态代理类的字节码 生成 动态代理类
            proxyClass = defineClass0(loader, proxyName,  
            proxyClassFile, 0, proxyClassFile.length);  
       
        }  
    // 最终返回动态代理类
    return proxyClass;  
    }  

// 回到调用关注3的原处

3.2.1总结

  • 通过调用处理器类对象的.newProxyInstance()创建动态代理类 及其实例对象(需传入目标类对象)

  • 具体过程如下:

    1. 通过 为Proy类指定类加载器对象 & 一组接口,从而创建动态代理类的字节码;再根据类字节码创建动态代理类

    2. 通过反射机制获取动态代理类的构造函数(参数类型 = 调用处理器接口类型

    3. 通过动态代理类的构造函数 创建 代理类实例(传入调用处理器对象

3.2.2、(关注2)通过调用动态代理对象方法从而调用目标对象方法

//使用代码
oneproxy.doOneThing("abd");


Interfaze oneproxy = JDKProxy.getProxy(new MyImplement());
System.out.println("oneProxy : "   oneproxy);
System.out.println(Proxy.class.isAssignableFrom(oneproxy.getClass()));
//输出结果:oneProxy : class com.sun.proxy.$Proxy0 true
// 这里关于isAssignableFrom()理解这个就可以 :父类.class.isAssignableFrom(子类.class)
//发现oneproxy其实是proxy的子类

oneproxy.doOneThing("abd");
// 该方法的逻辑实际上是调用了父类Proxy类的h参数的invoke()
            // h参数即在Proxy.newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h)传入的第3个参数InvocationHandler对象
            // 即调用了调用处理器的InvocationHandler.invoke()
            // 而复写的invoke()利用反射机制:Object result=method.invoke(proxied,args)
            // 从而调用目标对象的的方法

3.2.2总结

  • 动态代理类实现了与目标类一样的接口,并实现了需要目标类对象需要调用的方法

  • 该方法的实现逻辑 = 调用父类 Proxy类的 h.invoke()

其中h参数 = 在创建动态代理实例中newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h)传入的第3个参数InvocationHandler对象

  • InvocationHandler.invoke() 中通过反射机制,从而调用目标类对象的方法

四、动态代理的实现:继承实现

这里需要cglib- nodep -2.1.3.jar这个架包

4.1、具体使用

public class CGLIBproxy {

public CGLIBproxy() {
}

public static <T> T getProxy(Class<?> klass) {
try {
return getProxy(klass.newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}

return null;
}

@SuppressWarnings("unchecked")
public static <T> T getProxy(Object object) {
Class<?> klass = object.getClass();

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(klass);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object result = null;
System.out.println("前");
result = method.invoke(object, args);
System.out.println("后");
return result;
}
});

return (T) enhancer.create();
}
}

Enhancer是cglib中使用频率很高的一个类,它是一个字节码增强器,可以用来为无接口的类创建代理。它的功能与java自带的Proxy类挺相似的。它会根据某个给定的类创建子类,并且所有非final的方法都带有回调钩子。

4.2、方法介绍

Callback-MethodInterceptor

方法拦截器。这个东西和JDK自带的InvocationHandler很类似

Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable

这其中MethodProxy proxy参数一般是用来调用原来的对应方法的。比如可以proxy.invokeSuper(obj, args)。那么为什么不能像InvocationHandler那样用method来调用呢?因为如果用method调用会再次进入拦截器。为了避免这种情况,应该使用接口方法中第四个参数methodProxy调用invokeSuper方法。

测试类:

Complex twoProxy = CGLIBproxy.getProxy(Complex.class);
twoProxy.setReal(10);
twoProxy.setVir(-1.3);
System.out.println(twoProxy);

在这里插入图片描述

五、总结

接口实现:

img

继承实现:

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(klass);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object result = null;
System.out.println("前");
result = method.invoke(object, args);
System.out.println("后");
return result;
}
});

参考博文:设计模式:这是一份全面 & 清晰的动态代 理模式(Proxy Pattern)学习指南添加链接描述

来源:https://www./content-4-851001.html

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多