【问题标题】:Lazy loader, alternative to cglib?懒加载器,cglib 的替代品?
【发布时间】:2018-11-01 23:26:31
【问题描述】:

借助 cglib,我可以使用以下代码创建一个惰性实例化的 BigInteger:

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(BigInteger.class);
enhancer.setCallback(
        new LazyLoader() {

            @Override
            public Object loadObject() throws Exception {
                // expensive computation here
                long totalCount = getTotalCount(totalCountExecutors); 
                return BigInteger.valueOf(totalCount);
            }
        });
totalCount =
        (BigInteger)
                enhancer.create(new Class[] {String.class}, new Object[] {"0"});

但是,上面的 Java 11 引发了警告,并且似乎没有兼容的版本正在制作中,所以我想迁移到其他库。 ByteBuddy 是...庞大的,并希望避免如此大的依赖。是否可以使用 javassist(或任何其他“轻量”库)创建延迟加载代理?

【问题讨论】:

  • 您为什么要这样做?您可以懒惰地创建对象,而无需进行代理和类加载,尤其是考虑到您有 lambda,例如dzone.com/articles/be-lazy-with-java-8
  • @zapl 可能是因为他们需要将某个类型的延迟加载对象传递给库。
  • @ohlec 好的,但是代理并不是你不能用普通代码重新创建的魔法(除非它们做编译器不允许的事情),例如ideone.com/XK2GOH
  • 我有一个 BigDecimal,它的计算成本很高,并且并非代码中的所有后续步骤都会使用(取决于太多因素)。 BigDecimal 没有无参数构造函数,因此不能选择通用代理。哦,但我看到你没有使用 Java 代理,而是创建一个子类......可以工作。
  • @zapl 基本上是一个手动创建的代理。当然,这是可能的,但它需要大量的样板文件。

标签: java javassist cglib


【解决方案1】:

使用 Byte Buddy 会很容易,但如果您必须使用“轻量级”(我的连接在 100 毫秒内下载 3 MB),您可以这样做:

ProxyFactory factory = new ProxyFactory();
factory .setSuperclass(BigInteger.class);
factory .setFilter(new MethodFilter() {
  @Override
  public boolean isHandled(Method m) {
     return !m.getName().equals("finalize");
  }
});

Class<?> clazz = factory.createClass();
MethodHandler handler = new MethodHandler() {

  private volatile Object delegate;

  @Override
  public Object invoke(Object self, Method method, Method proceed,
                       Object[] args) throws Throwable {
     Object delegate = this.delegate;
     if (delegate == null) {
       synchronized (this) {
         delegate = this.delegate;
         if (delegate == null) {
           this.delegate = delegate = loadObject();
         }
       }
     }
     return method.invoke(delegate, args);
 }

  private Object loadObject() throws Exception {
    // expensive computation here
    long totalCount = getTotalCount(totalCountExecutors); 
    return BigInteger.valueOf(totalCount);
  }
};

BigInteger instance = (BigInteger) clazz.newInstance();
((Proxy) instance).setHandler(handler);

但请注意,Javassist 在模块系统方面面临着自己的问题,您可能需要在未来某个时间再次迁移。

【讨论】:

    【解决方案2】:

    正如@zapl 建议的那样,这是完全没有任何第三方库的实现。我花了 3 分钟在 Eclispe 中创建它:我创建了一个类存根,然后使用“生成委托方法...”,然后使用“生成 Getters 和 Setters...”。所有荣誉都归于@zapl。

    import java.math.BigInteger;
    
    public class MyBigInt extends BigInteger {
    
        private BigInteger delegate;
    
        public MyBigInt() {
            super("0");
        }
    
        private BigInteger getDelegate() {
            if (delegate == null) {
                delegate = computeMeHere();
            }
            return delegate;
        }
    
        public byte byteValue() {
            return getDelegate().byteValue();
        }
    
        public short shortValue() {
            return getDelegate().shortValue();
        }
    
        public BigInteger nextProbablePrime() {
            return getDelegate().nextProbablePrime();
        }
    
        public BigInteger add(BigInteger val) {
            return getDelegate().add(val);
        }
    
        public BigInteger subtract(BigInteger val) {
            return getDelegate().subtract(val);
        }
    
        public BigInteger multiply(BigInteger val) {
            return getDelegate().multiply(val);
        }
    
        public BigInteger divide(BigInteger val) {
            return getDelegate().divide(val);
        }
    
        public BigInteger[] divideAndRemainder(BigInteger val) {
            return getDelegate().divideAndRemainder(val);
        }
    
        public BigInteger remainder(BigInteger val) {
            return getDelegate().remainder(val);
        }
    
        public BigInteger pow(int exponent) {
            return getDelegate().pow(exponent);
        }
    
        public BigInteger gcd(BigInteger val) {
            return getDelegate().gcd(val);
        }
    
        public BigInteger abs() {
            return getDelegate().abs();
        }
    
        public BigInteger negate() {
            return getDelegate().negate();
        }
    
        public int signum() {
            return getDelegate().signum();
        }
    
        public BigInteger mod(BigInteger m) {
            return getDelegate().mod(m);
        }
    
        public BigInteger modPow(BigInteger exponent, BigInteger m) {
            return getDelegate().modPow(exponent, m);
        }
    
        public BigInteger modInverse(BigInteger m) {
            return getDelegate().modInverse(m);
        }
    
        public BigInteger shiftLeft(int n) {
            return getDelegate().shiftLeft(n);
        }
    
        public BigInteger shiftRight(int n) {
            return getDelegate().shiftRight(n);
        }
    
        public BigInteger and(BigInteger val) {
            return getDelegate().and(val);
        }
    
        public BigInteger or(BigInteger val) {
            return getDelegate().or(val);
        }
    
        public BigInteger xor(BigInteger val) {
            return getDelegate().xor(val);
        }
    
        public BigInteger not() {
            return getDelegate().not();
        }
    
        public BigInteger andNot(BigInteger val) {
            return getDelegate().andNot(val);
        }
    
        public boolean testBit(int n) {
            return getDelegate().testBit(n);
        }
    
        public BigInteger setBit(int n) {
            return getDelegate().setBit(n);
        }
    
        public BigInteger clearBit(int n) {
            return getDelegate().clearBit(n);
        }
    
        public BigInteger flipBit(int n) {
            return getDelegate().flipBit(n);
        }
    
        public int getLowestSetBit() {
            return getDelegate().getLowestSetBit();
        }
    
        public int bitLength() {
            return getDelegate().bitLength();
        }
    
        public int bitCount() {
            return getDelegate().bitCount();
        }
    
        public boolean isProbablePrime(int certainty) {
            return getDelegate().isProbablePrime(certainty);
        }
    
        public int compareTo(BigInteger val) {
            return getDelegate().compareTo(val);
        }
    
        public boolean equals(Object x) {
            return getDelegate().equals(x);
        }
    
        public BigInteger min(BigInteger val) {
            return getDelegate().min(val);
        }
    
        public BigInteger max(BigInteger val) {
            return getDelegate().max(val);
        }
    
        public int hashCode() {
            return getDelegate().hashCode();
        }
    
        public String toString(int radix) {
            return getDelegate().toString(radix);
        }
    
        public String toString() {
            return getDelegate().toString();
        }
    
        public byte[] toByteArray() {
            return getDelegate().toByteArray();
        }
    
        public int intValue() {
            return getDelegate().intValue();
        }
    
        public long longValue() {
            return getDelegate().longValue();
        }
    
        public float floatValue() {
            return getDelegate().floatValue();
        }
    
        public double doubleValue() {
            return getDelegate().doubleValue();
        }
    
        public long longValueExact() {
            return getDelegate().longValueExact();
        }
    
        public int intValueExact() {
            return getDelegate().intValueExact();
        }
    
        public short shortValueExact() {
            return getDelegate().shortValueExact();
        }
    
        public byte byteValueExact() {
            return getDelegate().byteValueExact();
        }
    }
    

    【讨论】:

    • 最后,这是我正在寻找的最佳匹配,没有额外的图书馆包袱。
    【解决方案3】:

    您可以使用 Javassist 库 (http://www.javassist.org) 吗?

    例如,Hibernate 大量使用 Javassist 代理来管理延迟加载: https://docs.jboss.org/hibernate/orm/4.2/javadocs/org/hibernate/proxy/pojo/javassist/JavassistLazyInitializer.html

    你可能会从他们那里得到启发?

    【讨论】:

    • 您可能正在做某事,但代码似乎严重依赖于 Hibernate,而我在类路径中没有。
    • 我的意思是:不要这样使用它,而是看看 Hibernate 是如何做的(使用 javassist),并尝试做同样的事情(但当然不嵌入 Hibernate).. .
    • 我真正的意思是,100 个奖励积分应该比一个通用指针更有价值;-)
    • 仅供参考:Hibernate 使用 Byte Buddy 有一段时间了。
    猜你喜欢
    • 2011-01-16
    • 1970-01-01
    • 1970-01-01
    • 2020-05-27
    • 2016-04-02
    • 2018-09-04
    • 2013-12-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多