2017-07-21 3 views
1

異なる注釈付きメソッドを置き換えるのにbytebuddyを使用しています。これらのように:あなたが見ることができるように、我々はからいくつかの情報が必要Bytebuddy:インターセプトされた固定値でメソッドを取得

public String interceptString(@Origin Method method) { 
    // fetch current value from DB 
    return db.fetchString(method);  
} 

new ByteBuddy().subclass(Example.class) 
.method(ElementMatchers.isAnnotatedWith(Setting.class) 
.intercept(MethodDelegation.to(interceptors)).make().load(...) 

interceptorsは以下ました:現時点では

public class Example{ 

    @Setting 
    public String foo(){ 
     return "hello"; 
    } 
    @Setting 
    public String bar(){ 
     return "world"; 
    } 
} 

、我々はMethodDelegationを使用しますオリジナルのメソッドを使用して、データベースから正しいデータを取得します。これは機能していますが、

データベースからの値は1回だけ必要です(アプリケーションの起動時)。その後、値は実際には動的ではありません。パフォーマンス上の理由から、MethodDelegationをFixedValueに変更して、各メソッド/設定ごとにDBへの呼び出しを1つだけ行い、その後のすべての呼び出しで「キャッシュされた」固定値を使用します。我々はDBからデータを解決してフェッチする方法が必要になりますので

通常、我々は

//... 
.method(ElementMatchers.isAnnotatedWith(Setting.class) 
.intercept(FixedValue.value(getValue())) 

private Object getValue(){ 
    Method method = ??? 
    return db.fetchString(method); 
    } 

のようなものを使用することになり、これが不足しています。だから最終的には質問があります:

ここでは、インターセプトされたメソッドに固定値を渡す可能性がありますか?

答えて

1

問題を解決する方法の1つは、クラスにキャッシュを追加することです。基本的に、値を格納し、最初のアクセス時に値をデータベースから取得するフィールドを追加します。

あなたが望むものに近いコード例が必要です。

// Wrapper arround ConcurrentHashMap for the cached values 
// An instance of that class will be a field in the subclass of Example 
public static class ConfigCache { 
    private Map<String, Object> values = new ConcurrentHashMap<>(); 

    public Object computeIfAbsent(String key, Function<String, Object> mappingFunction){ 
     return values.computeIfAbsent(key, mappingFunction); 
    } 
} 

public static void main(String[] args) throws Exception { 
    Class<? extends Example> clazz = new ByteBuddy().subclass(Example.class) 
      // Add a field to the class 
      .defineField(CONFIG_CACHE, ConfigCache.class, Visibility.PUBLIC, FieldManifestation.FINAL) 
      // Add a constructor that initializes the field 
      .defineConstructor(Modifier.PUBLIC).withParameter(ConfigCache.class).intercept(new FieldAssignConstructor()) 
      .method(ElementMatchers.nameStartsWith("get").or(ElementMatchers.nameStartsWith("is"))) 
      .intercept(MethodDelegation.to(Stack.class)) // 
      .make() 
      .load(ByteBuddyEnhancer.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION).getLoaded(); 

    Example example = clazz.getConstructor(ConfigCache.class).newInstance(new ConfigCache()); 
    example.getX(); 
} 

// Use @FieldValue to access fields of a class 
@RuntimeType 
public static Object intercept(@Origin Method method, @FieldValue(CONFIG_CACHE) ConfigCache cache){ 
    return cache.computeIfAbsent(method.getName(), key -> { 
     // Do whatever you want here 
     System.out.println("Computing for " + key); 
     return null; 
    }); 
} 

private static final String CONFIG_CACHE = "configCache"; 

コンストラクタの実装:

private static final class FieldAssignConstructor implements Implementation { 
    @Override 
    public InstrumentedType prepare(InstrumentedType instrumentedType) { 
     return instrumentedType; 
    } 

    @Override 
    public ByteCodeAppender appender(Target implementationTarget) { 
     return new ByteCodeAppender() { 

      @Override 
      public Size apply(MethodVisitor methodVisitor, Context instrumentationContext, 
        MethodDescription instrumentedMethod) { 

       StackManipulation.Size size = new StackManipulation.Compound(
         MethodVariableAccess.REFERENCE 
           .loadFrom(0), 
         MethodInvocation.invoke(new TypeDescription.ForLoadedType(Example.class) 
           .getDeclaredMethods().filter(ElementMatchers.isConstructor() 
             .and(ElementMatchers.takesArguments(0))) 
           .getOnly()), 
         MethodVariableAccess.REFERENCE.loadFrom(0), // 
         MethodVariableAccess.REFERENCE.loadFrom(1), // 
         FieldAccess 
           .forField(implementationTarget.getInstrumentedType().getDeclaredFields() 
             .filter(ElementMatchers.named(CONFIG_CACHE)).getOnly()) 
           .write(), 
         MethodReturn.VOID).apply(methodVisitor, instrumentationContext); 
       return new Size(size.getMaximalSize(), instrumentedMethod.getStackSize()); 
      } 
     }; 
    } 
} 
関連する問題