抽象工厂,JDK 动态代理、适配器运用及感悟

这里写自定义目录标题

前言

在看小傅哥 的《重学java设计模式》的抽象工厂,之前理解的只是面试题要背的知识点,知道是通过反射调用方法的,spring中的AOP 和 事务也是用的动态代理。
在看 这篇文章又有了新的收获,怕忘了,就立即写出来。

问题场景

根据小傅哥的例子–改造升级redis 多集群配置,大致就是,随着系统升级,qps提高,之前单机版本的redis 无法满足需求,现在要升级至集群模式,且要平稳过渡,具体可以看上面链接。
为了说明更加方便,附上小傅哥例子中的目录结构:
itstack-demo-design-2-02
└── src
├── main
│ └── java
│ └── org.itstack.demo.design
│ ├── factory
│ │ ├── impl
│ │ │ ├── EGMCacheAdapter.java
│ │ │ └── IIRCacheAdapter.java
│ │ ├── ICacheAdapter.java
│ │ ├── JDKInvocationHandler.java
│ │ └── JDKProxy.java
│ ├── impl
│ │ └── CacheServiceImpl.java
│ └── CacheService.java
└── test
└── java
└── org.itstack.demo.design.test
└── ApiTest.java

关键代码

CacheService 缓存接口

public interface CacheService {

    /**
     * 获取数据
     * @param key
     * @return
     */
    String get(final String key);

    /**
     * 设置数据
     * @param key
     * @param value
     */
    void set(String key, String value);

    /**
     * 根据时间,设置数据
     * @param key
     * @param value
     * @param timeout
     * @param timeUnit
     */
    void set(String key, String value, long timeout, TimeUnit timeUnit);

    /**
     * 删除数据
     * @param key
     */
    void del(String key);

}

ICacheAdapter 适配器 接口

public interface ICacheAdapter {

    String get(String key);

    void set(String key, String value);

    void set(String key, String value, long timeout, TimeUnit timeUnit);

    void del(String key);

}

InvocationHandler类中方法

/**
 * 当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用
 */
public class JDKInvocationHandler implements InvocationHandler {

    private ICacheAdapter cacheAdapter;

    /**
     * 构造器目的:引入缓存适配器对象实例,
     * 在invoke方法中,可以使用反射机制,
     * 对某一个实例执行方法
     * @param cacheAdapter
     */
    public JDKInvocationHandler(ICacheAdapter cacheAdapter) {
        this.cacheAdapter = cacheAdapter;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//        return ICacheAdapter.class.getMethod(method.getName(), ClassLoaderUtils.getClazzByArgs(args)).invoke(cacheAdapter, args);
        // 获取ICacheAdapter 接口实例的方法对象,再使用invoke方法 类反射调用具体方法
        Object invoke = ICacheAdapter.class.getMethod(method.getName(), ClassLoaderUtils.getClazzByArgs(args)).invoke(cacheAdapter, args);
        return invoke;
    }

}

Proxy类中的方法

    /**
     * 得到一个动态的代理对象
     * 原材料:缓存适配器
     *
     * @param interfaceClass
     * @param cacheAdapter 作用:实际上真正的对象,通过动态代理生成出的对象,调用对象方法,实际调用的就是InvocationHandler对象的invoke方法
     * @return 生成一个动态代理对象
     * @param <T>
     * @throws Exception
     */
    public static <T> T getProxy(Class<T> interfaceClass, ICacheAdapter cacheAdapter) throws Exception {
        InvocationHandler handler = new JDKInvocationHandler(cacheAdapter);
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

        // [CacheService.class]
        // 为什么 jdk动态代理要传入实现类了,因为这个方法失效,更深层次影响是,Proxy.newProxyInstance会失败
        // 传入一个 接口有实现类,则
        Class<?>[] classes = interfaceClass.getInterfaces();
        /**用来动态创建一个代理对象的类
         * 1、loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
         * 2、interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
         * 3、handler:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上;在代理实例调用方法时,方法调用被编码分派到InvocationHandler的invoke方法。
         */
//        return (T) Proxy.newProxyInstance(classLoader, new Class[]{classes[0]}, handler);
        return (T) Proxy.newProxyInstance(classLoader, new Class[]{interfaceClass}, handler);
    }

疑惑

正常的动态代理是使用相同接口传入InvocationHandler和接口,但是这里小傅哥使用的分别是ICacheAdapter和CacheService这两个接口,上面代码中也可能看到,这两个接口完全没有任何继承关系,那么他们是如何关联上的呢?
在这里插入图片描述

解答

关键点是 相同方法名称
在看了这两个接口的方法名,发现这两个接口的方法名称是一样的,就想到了JDK动态代理对象调用方法时,是通过方法名称和参数进行调用的,也就是说,在调用代理对象方法时,会自动跳转到InvocationHandler的invoke方法中,然后通过查找传入InvocationHandler中的ICacheAdapter的相同方法名称并传入参数,就完成了。

总结

反过来也就理解了为什么这里要创建ICacheAdapter 适配器了,目的就是为了与CacheService 进行适配,用以动态代理,以前一直对适配器很模糊,现在经过这样发现,对适配器这个概念也就更具体了。
同时,动态代理配置适配器使用,也就更加灵活多变了,可能就本来就是动态代理的意义,只是我之前一直没有弄明白。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值