美文网首页异步任务
异步任务优化の(三) cglib

异步任务优化の(三) cglib

作者: Yellowtail | 来源:发表于2019-04-04 10:46 被阅读0次

    网上一顿乱搜之后,看到了一些文章,得到了启发
    这是博客
    这是源码

    思路就是通过 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 编译器 能够帮我们识别代码重构的问题,
    会在编译的时候报错

    不足

    虽然我们最终是选择的此方案,但是此方案依然有不足的地方

    1. 不支持private方法
      因为这个方法的原理就是先获得一个代理类,然后执行这个代理类的方法,private方法写不出来
    2. 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/

    相关文章

      网友评论

        本文标题:异步任务优化の(三) cglib

        本文链接:https://www.haomeiwen.com/subject/uuffiqtx.html