当被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不使用委托,或者以某种方式正确使用委托。