当被Spring代理类访问时,Kotlin实例变量为null

我有一个由Spring代理的服务类,如下所示:

@Service @Transactional open class MyService { ... } 

如果我删除了open修饰符,Spring会抱怨它需要代理这个类来应用@Transactional注解调整。

但是,当在代理服务上调用一个函数时会导致问题,该代理服务试图访问一个变量:

 @Service @Transactional open class MyService { protected val internalVariable = ... fun doWork() { internalVariable.execute() // NullPointerException } } 

internalVariable作为其声明的一部分进行分配,没有任何注释(如@Autowired等),并且当我删除 @Transactional注释和Spring代理类的要求时工作正常

为什么当Spring代理/继承我的服务类时,这个变量为null?

我遇到了一个类似的问题,Rafal G&Craig Otis的上述评论帮助了我 – 所以我想提议将以下文字作为答案接受(或将上述评论改为答案,并接受)。

解决方案: 打开方法/字段。

(我遇到了一个类似的情况,这是一个封闭的方法 ,导致了这个问题,但无论是一个领域/方法的解决方案是一样的,我认为一般的原因是一样的…)

说明:

为什么这个解决方案更复杂,肯定与Spring AOP,最终字段/方法,CGLIB代理以及Spring + CGLIB如何处理最终方法(或字段)有关。

Spring使用代理来表示某些对象来处理Aspect Oriented Programming所涉及的某些问题。 这发生在服务和控制器(尤其是@Transactional或其他建议需要AOP解决方案时)。

所以这些bean需要Proxy / Wrapper,Spring有两个选择 – 但是当父类不是接口时,只有CGLIB可用。

当使用CGLIB代理类时,Spring将创建一个类似myService $ EnhancerByCGLIB的子类。 这个增强的类将覆盖一些(如果不是全部)业务方法,以便在您的实际代码中应用横切关注点。

真正的惊喜来了 这个额外的子类不会调用基类的超级方法。 相反,它会创建myService的第二个实例并委托给它。 这意味着你现在有两个对象:你的真实对象和CGLIB增强对象指向(包装)它。

来自: 春天单身豆田没有填充

引用者: Spring AOP CGLIB代理的字段为空

在Kotlin中,类和方法是最终的,除非明确地打开。

Spring / CGLib如何选择在包含目标代理的EnhancerByCGLIB中包装Bean(这样它就可以使用finalized方法/字段)的魔力了。我不知道。 对我来说,不过调试器给了我两种不同的结构。 当父方法打开时 ,它不会创建一个委托(而是使用子类化)而不使用NPE。 但是,当一个特定的方法被关闭, 那么对于那个封闭的方法, Spring / CGLIB使用一个带有委托的包装对象到一个正确初始化的目标委托。 出于某种原因,方法的实际调用是在上下文是未初始化的字段值(NULL)的包装器的情况下完成的,从而导致NPE。 (如果已经调用了实际目标/代理的方法,则不应该有问题)。

Craig能够通过打开属性(而不是方法)来解决这个问题 – 我怀疑它有类似的效果,允许Spring / CGLib不使用委托,或者以某种方式正确使用委托。