懒人必备!Java通过动态代理,将操作动态委托给其他实现类(装饰器模式),大幅提高开发效率

先看一段让(懒)人崩溃的代码

    public byte readByte() {
        return buff.readByte();
    }

    public void writeByte(int value) {
        buff.writeByte(value);
    }

    public short readShort() {
        return buff.readShort();
    }

    public void writeShort(int value) {
        buff.writeShort(value);
    }

    public int readInt() {
        return buff.readInt();
    }

    public void writeInt(int value) {
        buff.writeInt(value);
    }

    public long readLong() {
        return buff.readLong();
    }

    public void writeLong(long value) {
        buff.writeLong(value);
    }

    public String readString() {
        int len = readShort();
        return buff.readCharSequence(len, Charset.defaultCharset()).toString();
    }

    public void writeString(String value) {
        writeShort(value.length());
        buff.writeCharSequence(value, Charset.defaultCharset());
    }
buff的类型是Netty的ByteBuf,我希望我对ByteBuf封装的消息包,可以支持ByteBuf所有的方法,但是我真的不想一个一个去实现了,所以就想到了使用动态代理。接下来的一个工具就是为了解决这类问题 (不用付出很大努力)。

只有一个类,用到了JDK的动态代理和CGLIB

/**
 * 动态代理工厂
 */
public class ProxyFactory {

    /**
     * 每个线程每个SuperClass只创建一个Enhancer
     */
    private static final ThreadLocal<Map<Class<?>, Enhancer>> ENHANCER_MAP_HOLDER = new ThreadLocal<>();

    /**
     * 创建代理实例
     *
     * @param superClass    代理类型
     * @param realInstances 实际调用的实例集合(按顺序遍历,调用第一个实现了接口方法的实例)
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T create(Class<T> superClass, Object... realInstances) {
        if (superClass.isInterface()) {
            // 代理的是接口,则使用JDK反射实现
            return (T) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class<?>[]{superClass}, (proxy, method, args) -> invokeRealMethod(realInstances, method, args, superClass));
        }

        // 非接口使用CGLIB实现动态代理
        // 每个线程每个SuperClass只创建一个Enhancer对象(多多少少能节省一些内存吧)
        Map<Class<?>, Enhancer> enhancerMap = ENHANCER_MAP_HOLDER.get();
        if (enhancerMap == null) {
            enhancerMap = new HashMap<>();
            ENHANCER_MAP_HOLDER.set(enhancerMap);
        }

        Enhancer enhancer = enhancerMap.get(superClass);
        if (enhancer == null) {
            enhancer = new Enhancer();
            enhancer.setSuperclass(superClass);
            enhancerMap.put(superClass, enhancer);
        }

        enhancer.setCallback((MethodInterceptor) (o, method, args, methodProxy) -> invokeRealMethod(realInstances, method, args, superClass));

        return (T) enhancer.create();
    }

    /**
     * 调用真实实现了的方法
     *
     * @param realInstances 在这些对象中找到第一个实现了指定方法的,并调用之
     * @param method        代理方法信息
     * @param superClass    代理接口信息
     * @return 方法返回值
     */
    private static Object invokeRealMethod(Object[] realInstances, Method method, Object[] args, Class<?> superClass) throws InvocationTargetException, IllegalAccessException {
        for (Object realInstance : realInstances) {
            // 找到第一个实现了此方法的实例,并调用之
            Method func = getMethod(realInstance, method.getName(), method.getParameterTypes());
            if (func != null) {
                // 方法所属类的访问权限也会影响调用
                func.setAccessible(true);
                return func.invoke(realInstance, args);
            }
        }
        // 没有对应实现,抛出异常
        throw new UnsupportedOperationException("没有实例实现代理类型" + superClass + "的方法:" + method);
    }

    /**
     * 在对象中,找到相同的实现方法
     *
     * @param object
     * @param name           方法名
     * @param parameterTypes 参数类型列表
     * @return 没有实现对应方法,则返回null
     */
    private static Method getMethod(Object object, String name, Class<?>... parameterTypes) {
        try {
            // 找名字、参数一样的方法
            return object.getClass().getMethod(name, parameterTypes);
        } catch (NoSuchMethodException e) {
            // 不存在
            return null;
        }
    }
}

 如注释所说,如果代理的是接口则使用JDK动态代理实现,看别人做的测试在Java8之后JDK动态代理性能很好,优于CGLIB。

不是接口则使用CGLIB,原因是JDK动态代理只能代理接口,此乃无奈之举,CGLIB接口、抽象类、普通类都可以代理。

两种方式实现方式的逻辑是一样的,只不过CGLIB实现对Enhancer对象做了缓存。

主体思路就是通过多个实例来实现一个接口(包括类和抽象类)的所有功能。

接下来是一个实际的使用案例:

/**
 * MsgPack基础接口
 */
interface SimpleMsgPack {

    /**
     * 消息id
     *
     * @return
     */
    int getMsgId();

    /**
     * 读出字符串
     *
     * @return
     */
    String readString();

    /**
     * 写入字符串
     *
     * @param value
     */
    void writeString(String value);

    /**
     * 发送消息<br/>
     * 只能调用一次,buff会被回收
     *
     * @param ctx
     */
    void send(ChannelHandlerContext ctx);
}
/**
 * MsgPack基础实现
 */
class SimpleMsgPackImpl implements SimpleMsgPack {

    /**
     * 消息Id
     */
    private int msgId;
    /**
     * 消息缓冲
     */
    private final ByteBuf buff;

    /**
     * 收到消息包
     *
     * @param in
     */
    SimpleMsgPackImpl(ByteBuf in) {
        this.buff = in;

        // 读取消息头
        in.readInt();// 消息长度
        this.msgId = in.readInt();
    }

    /**
     * 发送消息包
     *
     * @param msgId 消息Id
     */
    SimpleMsgPackImpl(int msgId) {
        this.msgId = msgId;

        // 从buff池取(直接内存)
        this.buff = PooledByteBufAllocator.DEFAULT.directBuffer();
        buff.writeInt(0);
        buff.writeInt(msgId);
    }


    @Override
    public int getMsgId() {
        return this.msgId;
    }

    /**
     * 获取buff
     *
     * @return
     */
    ByteBuf getBuff() {
        return buff;
    }

    @Override
    public String readString() {
        int len = buff.readShort();
        return buff.readCharSequence(len, Charset.defaultCharset()).toString();
    }

    @Override
    public void writeString(String value) {
        buff.writeShort(value.length());
        buff.writeCharSequence(value, Charset.defaultCharset());
    }

    @Override
    public void send(ChannelHandlerContext ctx) {
        int len = this.buff.writerIndex();

        // 修改消息长度字段
        this.buff.writerIndex(0);
        this.buff.writeInt(len);
        this.buff.writerIndex(len);

        ctx.writeAndFlush(this.buff);
    }
}
/**
 * 消息包封装
 */
public abstract class MsgPack extends ByteBuf implements SimpleMsgPack {

    /**
     * 实例化收到消息包
     *
     * @param in 输入Buff
     * @return
     */
    public static MsgPack newInstance(ByteBuf in) {
        // 优先调用自己实现的方法,没实现的委托给ByteBuf
        return ProxyFactory.create(MsgPack.class, new SimpleMsgPackImpl(in), in);
    }

    /**
     * 实例化发送消息包
     *
     * @param msgId 消息Id
     */
    public static MsgPack newInstance(int msgId) {
        SimpleMsgPackImpl msgPack = new SimpleMsgPackImpl(msgId);
        // 优先调用自己实现的方法,没实现的委托给ByteBuf
        return ProxyFactory.create(MsgPack.class, msgPack, msgPack.getBuff());
    }
}
MsgPack msg = MsgPack.newInstance(123);
msg.writeInt(100);
msg.writeLong(1000);
msg.writeByte(1);
msg.writeString("啦啦啦");
msg.send(ctx);

可以看到,咱们只实现了必要实现的方法,就可以使用需要的所有功能,是不是很方便。说是动态代理,实际上是实现了一个装饰器,因为增加了新功能,readString和writeString等。

其实SimpleMsgPack 类可以不要,把其中的抽象方法放到MsgPack中,然后SimpleMsgPackImpl不实现任何接口也不继承任何类,但是还是要实现咱们需要的方法,不过这样做感觉不够优雅,而且动态代理已经为我们节省了太多体力活了,所以多写一个接口也。。。(想不起来合适的词了,到此结束吧)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值