先看一段让(懒)人崩溃的代码
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不实现任何接口也不继承任何类,但是还是要实现咱们需要的方法,不过这样做感觉不够优雅,而且动态代理已经为我们节省了太多体力活了,所以多写一个接口也。。。(想不起来合适的词了,到此结束吧)。