木子的昼夜  

走过路过不要错过,上一篇讲了反射,可以看我上一篇文章或微信上搜索:木子的昼夜编程 。

这一篇准备写一下动态代理模式。

1. 理由

先给你一个理由,为什么学动态代理

第一他是一种设计模式,在你工作中如果可以结合它的使用,那代码绝对漂亮。

第二面试官们喜欢问的面试题就包括设计模式,如果掌握这种设计模式,那你下一份工作的薪水可能就会高一点点。

第三你可能看过很多源码包括但不限于Mybatis、Spring ,他们的作者在编写代码的时候就充分利用了动态代理的思想,你掌握了这个技术之后,就能很好的理解那些写框架的大佬们是怎么设计的框架,从而得到能力的提升。

2. 动起来

2.1 先说代理

代理就是一个类代表另一个类提供功能,你可以理解成你朋友圈的微商。

厂商生产鞋子->微商从厂商那里批发鞋子在朋友圈进行售卖->你购买鞋子

微商就是代理,代理的就是鞋子厂商,而你就是那个使用鞋子厂商生产的鞋子但是你需要与微商打交道。

网上经常会看到一个UML图,在下也献丑画一个。

file

2.2 代理的优缺点

优点:我的总结就是高扩展、智能化、职责清晰

缺点:成本加大(你购买的鞋子可能变贵,运输时间可能变长)

3. 先静再动

聊动态代理之前先写一个静态代理的例子。

package test;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 * 一个生活在互联网底层,做着增删改查的码农,不谙世事的造作
 * @create 2021-10-16 9:39
 *
 * 鞋厂规范
 */
public interface AShoeFactory {
    /**
     *
     * @param name 购鞋者名字
     * @param num 购买鞋子数量
     */
    void sellingShoes(String name, Integer num);
}

package test;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 * 一个生活在互联网底层,做着增删改查的码农,不谙世事的造作
 * @create 2021-10-16 9:44
 *
 * 具体的鞋厂:鸿星尔克
 */
public class ErkeFactory implements AShoeFactory  {

    @Override
    public void sellingShoes(String name, Integer num) {
        System.out.println(name+" 购买了 "+ num+" 双鞋,放烟花庆祝!!");
    }
}

package test;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 * 代理类:朋友圈某某某
 */
public class ProxyWechatMoments implements AShoeFactory{
    public ProxyWechatMoments(){}

    public ProxyWechatMoments(ErkeFactory erkeFactory){
        this.erkeFactory = erkeFactory;
    }
    ErkeFactory erkeFactory;
    @Override
    public void sellingShoes(String name, Integer num) {
        erkeFactory.sellingShoes(name, num);
    }
}

测试:

import test.ErkeFactory;
import test.ProxyWechatMoments;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 */
public class Test {
    public static void main(String[] args) {
        // 真实提供能力的类
        ErkeFactory erkeFactory = new ErkeFactory();
        // 代理类
        ProxyWechatMoments proxy =  new ProxyWechatMoments(erkeFactory);
        // 冲动消费
        proxy.sellingShoes("最最最最最小李子", 10);
    }
}

测试输出结果:

最最最最最小李子 购买了 10 双鞋,放烟花庆祝!!

4. 动起来

写了静态代理,静态代理实现比较简单。

缺点呢:扩展比较麻烦,如果上述例子中再加一个匹克鞋厂,我们就需要修改代码,会在代理类中用if else区分要购买哪个牌子的鞋、

4.1 动态代理之 JDK
package test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 * 动态代理类
 */
public class ProxyDynamicHandler implements InvocationHandler {
    private  AShoeFactory shoeFactory;
    public ProxyDynamicHandler(){}
    public ProxyDynamicHandler(AShoeFactory shoeFactory) {
        this.shoeFactory = shoeFactory;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 卖鞋前后做的事情 是代理模式中经常用到的功能,作为增强和过滤使用
        System.out.println("动态代理的好处,卖鞋前做一些事情");
        String name = (String)args[0];
        // 如果购买的人是 追忆流年时光 那我就不卖给他
        if ("追忆流年时光".equals(name)) {
            System.out.println("不卖, 谢谢 !");
        } else {
            // 这里用到了反射 没掌握的人可以看我上一篇文章 或关注公众号:木子的昼夜编程
            method.invoke(shoeFactory, args);
        }
        System.out.println("动态代理的好处,卖鞋后做一些事情");
        return null;
    }
}

import sun.applet.AppletClassLoader;
import test.AShoeFactory;
import test.ErkeFactory;
import test.ProxyDynamicHandler;

import java.lang.reflect.Proxy;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 */
public class TestDynamic {
    public static void main(String[] args) {
        AShoeFactory factory = (AShoeFactory) Proxy.newProxyInstance(
                ErkeFactory.class.getClassLoader(),// 类加载器
                ErkeFactory.class.getInterfaces(),// 代理的类的所有接口 Jdk动态代理是基于接口的
                // 如果学过Spring的动态代理的话应该知道,默认如果你的Service有接口Spring会使用Jdk动态代理
                // 如果你的Service没有接口 Spring会使用CGLIB
                // 这个知识 是我记忆中的 对不对不负责 哈哈
                new ProxyDynamicHandler(new ErkeFactory()));
        factory.sellingShoes("正义的帅哥", 10);
        System.out.println("-------------------华丽丽的分割线------------------------");
        factory.sellingShoes("追忆流年时光", 10);
    }
}

输出:

动态代理的好处,卖鞋前做一些事情
正义的帅哥 购买了 10 双鞋,放烟花庆祝!!
动态代理的好处,卖鞋后做一些事情
-------------------华丽丽的分割线------------------------
动态代理的好处,卖鞋前做一些事情
不卖, 谢谢 !
动态代理的好处,卖鞋后做一些事情

4.2 CGLIB 动态代理

CGLIB 是一个很牛X的代码生成库,常被用在一些AOP框架里。

CGLIB底层使用了ASM来操作字节码

他是对字节码进行了操作,说实话不是很安全,但是用起来很爽。

就是没有约束的技术最疯狂。

CGLIB实现动态代理的方式就是认干爹,利用字节码修改功能创建一个被代理类(匹克鞋厂)的子类,提供功能的时候还是使用了被代理类的功能,就跟花钱找干爹一个道理

注: 这里的干爹是那种,70年代 80年代 很纯粹的那种两家关系好 就认干爹那种 不是现代意思。

pom.xml

<dependencies>
    <dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
    </dependency>
</dependencies>
package test;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 */
public class PeakFactory {
    public void sellingShoes(String name, Integer num) {
        System.out.println(name+" 购买了 "+ num+" 双匹克鞋,放烟花庆祝!!");
    }
}

自定义MethodHandler:

package test;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 */
public class ProxyDynamicByCglib implements MethodInterceptor {

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 卖鞋前后做的事情 是代理模式中经常用到的功能,作为增强和过滤使用
        System.out.println("动态代理的好处,卖鞋前做一些事情");
        String name = (String)args[0];
        // 如果购买的人是 追忆流年时光 那我就不卖给他
        if ("追忆流年时光".equals(name)) {
            System.out.println("不卖, 谢谢 !");
        } else {
            System.out.println("看一下代理对象的父类是个啥:"+obj.getClass().getSuperclass());
            // 调用业务类(父类)的方法
            proxy.invokeSuper(obj, args);

        }
        System.out.println("动态代理的好处,卖鞋后做一些事情");
        // 本次测试没有返回 无需返回
        return null;
    }
}


测试:

import net.sf.cglib.proxy.Enhancer;
import test.*;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 */
public class TestDynamicCGLIB {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        // 重点 设置实际提供方法的类为父类 Cglib使用自己的修改字节码功能 生成一个这个类的子类对象
        enhancer.setSuperclass(PeakFactory.class);
        enhancer.setCallback(new ProxyDynamicByCglib());// 自定义的Intrceptor
        PeakFactory factory = (PeakFactory) enhancer.create();

        factory.sellingShoes("正义的帅哥", 10);
        System.out.println("-------------------华丽丽的分割线------------------------");
        factory.sellingShoes("追忆流年时光", 10);
    }
}

输出:

动态代理的好处,卖鞋前做一些事情
看一下代理对象的父类是个啥:class test.PeakFactory
正义的帅哥 购买了 10 双匹克鞋,放烟花庆祝!!
动态代理的好处,卖鞋后做一些事情
-------------------华丽丽的分割线------------------------
动态代理的好处,卖鞋前做一些事情
不卖, 谢谢 !
动态代理的好处,卖鞋后做一些事情

5. 唠唠

代理的话我对JDK动态代理和CGLIB动态代理的底层了解不是很好,所以就只展开说了一下简单的应用。

其实大部分时候看架构源码,很多地方会用到代理模式,根据我的经验,会简单使用的就可以看懂架构中的代码流程,不用探索太深,当然不是不鼓励你去探索,知识说如果有时间的话再去探索,没时间就先学会怎么用。


欢迎关注公众号:木子的昼夜编程

posted on 2021-10-16 12:20  木子的昼夜  阅读(55)  评论(0编辑  收藏  举报