异步任务优化の(三) cglib
2019-04-04 本文已影响0人
Yellowtail
网上一顿乱搜之后,看到了一些文章,得到了启发
这是博客
这是源码
思路就是通过 cglib
生成一个代理类,
我们在代理类里把要执行的方法名记下来
大家看到cglib
不要怕,用起来很简单的,实现一个接口就行了
实现
以下实现基本是抄的 https://github.com/benjiman/benjiql
AsyncUtilsV2
里的代码是我自己写的
RecordingObject
cglib
实现类,也就是我们得到 代理类的地方,
基本照抄,获得参数列表功能是我自己加的
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class RecordingObject implements MethodInterceptor {
private String currentPropertyName = "";
private Object[] args;
private Recorder<?> currentMock = null;
@SuppressWarnings("unchecked")
public static <T> Recorder<T> create(Class<T> cls) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(cls);
final RecordingObject recordingObject = new RecordingObject();
enhancer.setCallback(recordingObject);
return new Recorder((T) enhancer.create(), recordingObject);
}
public Object intercept(Object o, Method method, Object[] os, MethodProxy mp) throws Throwable {
if (method.getName().equals("getCurrentPropertyName")) {
return getCurrentPropertyName();
}
//把当前 方法名记录下来
currentPropertyName = method.getName();
args = os;
Class<?> returnType = method.getReturnType();
//在返回值是 void时,快速处理
String name = returnType.getName();
if ("void".equals(name)) {
return null;
}
try {
currentMock = create(returnType);
return currentMock.getObject();
} catch (IllegalArgumentException e) {
return DefaultValues.getDefault(returnType);
}
}
public String getCurrentPropertyName() {
return currentPropertyName + (currentMock == null ? "" : ("." + currentMock.getCurrentPropertyName()));
}
/**
* <br>得到参数值 列表
* @return
* @author YellowTail
* @since 2019-03-28
*/
public Object[] getArgs() {
return args;
}
}
Recorder
中间商
public class Recorder<T> {
private T t;
private RecordingObject recorder;
public Recorder(T t, RecordingObject recorder) {
this.t = t;
this.recorder = recorder;
}
public String getCurrentPropertyName() {
return recorder.getCurrentPropertyName();
}
/**
* <br>得到参数值 列表
* @return
* @author YellowTail
* @since 2019-03-28
*/
public Object[] getArgs() {
return recorder.getArgs();
}
public T getObject() {
return t;
}
}
AsyncUtilsV2
import java.io.UnsupportedEncodingException;
import java.util.function.Consumer;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
/**
* 异步任务 V2
* @author YellowTail
* @since 2019-03-28
* @param <T>
*/
public class AsyncUtilsV2<T> {
private static final Logger LOGGER =LoggerFactory.getLogger(AsyncUtilsV2.class);
private Class<T> cls;
final Recorder<T> recorder;
public AsyncUtilsV2(Class<T> cls) {
this.cls = cls;
this.recorder = RecordingObject.create(cls);
//创建动态代理实例所需的时间
LOGGER.info("send async task, init dynamic proxy gap {}", System.currentTimeMillis() - begin);
LOGGER.info("AsyncUtilsV2 cls is {}", cls);
}
public T getT() {
return recorder.getObject();
}
/**
* <br> 执行一个 <font color="red"> public </font> 方法
* <br> 写法必须是 run(c -> c.genGroupUnit(groupId, agentId)), "c." 不能省略,否则会出问题
* <br> 之所以有这个要求,是因为,此异步任务使用了动态代理,加了“c.” 表示使用了该代理的方法,才能记录下来,否则直接走原生class的方法
* @param c
* @author YellowTail
* @since 2019-03-30
*/
public void run(Consumer<T> c) {
//让方法执行一下,执行的实例是代理实例
long t1 = System.currentTimeMillis();
try {
c.accept(getT());
} catch (Exception e) {
LOGGER.error("Consumer invoke ", e);
}
String methodName = recorder.getCurrentPropertyName();
if (StringUtils.isBlank(methodName)) {
//方法为空,说明调用的是private方法,暂时不支持
}
Object[] args = recorder.getArgs();
//构造实际的消息对象
AsyncMessage asyncMessage = new AsyncMessage();
asyncMessage.setTargetClass(cls);
asyncMessage.setMethodName(methodName);
asyncMessage.setArgs(args);
}
/**
* <br> 要执行的方法在哪个类里,最好使用 xx.class, 不要使用 xxx.getClass(), 因为 spring bean 得到的 getClass() 有些奇怪
* @param cls
* @return
* @author YellowTail
* @since 2019-04-02
*/
public static <T> AsyncUtilsV2<T> from(Class<T> cls) {
return new AsyncUtilsV2<>(cls);
}
@SuppressWarnings("unchecked")
public static <T> AsyncUtilsV2<T> from(T object) {
Class<T> targetClass = (Class<T>) AopUtils.getTargetClass(object);
return new AsyncUtilsV2<>(targetClass);
}
}
调用
// from 参数是 class
AsyncUtilsV2.from(NewCommunityService.class).run(d -> d.incCommentCount("123"));
//from 参数是 spring bean
AsyncUtilsV2.from(unitFollowUpsDAO).run(c -> c.delDocByUnitIdAndGroupId(unit.get_id(), unit.getGroupId()));
实际效果
实际效果还是不错的,因为都是直接调用方法,所以Java 编译器
能够帮我们识别代码重构的问题,
会在编译的时候报错
不足
虽然我们最终是选择的此方案,但是此方案依然有不足的地方
- 不支持
private
方法
因为这个方法的原理就是先获得一个代理类,然后执行这个代理类的方法,private
方法写不出来 -
cglib
性能不行
cglib
创建代理类实例的时候,性能不是很好,它自带缓存,如果命中了,倒挺快的,没有命中就要1-200毫秒
参考
https://benjiweber.co.uk/blog/2013/12/28/typesafe-database-interaction-with-java-8/
https://github.com/benjiman/benjiql
https://colobu.com/2014/10/28/secrets-of-java-8-functional-interface/