cglib 代理中缺少字段注释 class

Missing field annotation in cglib proxy class

@Service
public class TestService{

    @DynamicReference
    private ITestProvider testProvider;

    public void run() {

    }
}
DynamicReference dynamicRefrence = filed.getAnnotation(DynamicReference.class);
-->NOT NULL

这段代码在这种情况下没问题。但是当我在方法运行中添加@Transactional时,那么@DynamicReference就会丢失

@Service
public class TestService{

    @DynamicReference
    private ITestProvider testProvider;

    @Transactional
    public void run() {

    }
}
DynamicReference dynamicRefrence = filed.getAnnotation(DynamicReference.class);
-->NULL

如何在 cglib 代理 class 中获取字段注释 @DynamicReference

这是获取字段代码:

Object o = this.applicationContext.getBean(beanName);
Class<?> clazz = o.getClass();

for (Field filed : clazz.getDeclaredFields()) {
    DynamicReference dynamicRefrence = filed.getAnnotation(DynamicReference.class);
}

来自Class.getDeclaredFields()

Returns an array of Field objects reflecting all the fieldsdeclared by the class or interface represented by this Class object. This includes public, protected, default(package) access, and private fields, but excludes inherited fields.

在你的例子中,一旦你从 cglib 获得了一个基于 subclass 的代理,该字段将只存在于 superclass 中。根据您的用例,您可能希望收集继承链中具有自定义注释的所有字段。

示例代码:

    Collection<Field> fieldsWithAnnotation = new ArrayList<>();

    Class<?> clazz = // your class

    while(clazz != null) {
        for (Field field : clazz.getDeclaredFields()) {
            DynamicReference dynamicRefrence = field.getAnnotation(DynamicReference.class);
            if(dynamicRefrence != null)
                fieldsWithAnnotation.add(field);
        }
        clazz = clazz.getSuperclass();
    }

编辑:这种方法可以找到带注释的字段。但是,执行 field.set(proxyInstance, value) 实际上会在代理中设置该字段。这对您没有帮助,因为即使代理 subclasses,它仍然使用 delegation 将方法调用转发给实际 class 的包装实例。由于您的目标显然是在此包装实例中设置字段,因此我建议您不要使用自定义 字段注入 而是 setter 注入。您的代码大致如下所示(未经测试):

// in TestService
private ITestProvider testProvider;

@DynamicReference
public void setTestProvider(ITestProvider testProvider) { ... }

// Getting the method
while(clazz != null) {
    for (Method method : clazz.getDeclaredMethods()) {
        DynamicReference dynamicRefrence = method.getAnnotation(DynamicReference.class);
        if(dynamicRefrence != null)
            methodsWithAnnotation.add(method);
    }
    clazz = clazz.getSuperclass();
}

// invoking it
method.invoke(proxyInstance, dependencyInstanceYouWantToSet);

代理应该将方法调用委托给您包装的实例。也许您甚至想保护该方法。

另一种方法是获取代理的回调字段并在该实例上设置该字段,但上面的方法似乎更简洁(有些人可能会说魔法字段注入是邪恶的,你应该始终使用 setter/constructor 为干净的 oop 方法注入)。

编辑 2:也许您还可以重新考虑是否要真正重新发明 DI 框架并利用底层现有的 DI 框架功能。想到使用@Qualifier 或一些自定义注入解析器。参见 this tutorial