Spring Boot Bean: value passed in constructor is not null in constructor, but null in CGLIB proxy

279 Views Asked by At

I have the following repository implementation in Kotlin:

@Repository
class SapArticleRepository(jooqDsl: DSLContext, jooqConfiguration: DefaultConfiguration) :
    AbstractSapRepository<TrmSapArticleRecord, TrmSapArticlePojo>(jooqDsl, jooqConfiguration)

with the following base class:

abstract class AbstractSapRepository<R : UpdatableRecord<R?>, TPojo>(
    var dao: DAOImpl<R, TPojo, Long>,
    jooqDsl: DSLContext,
    jooqConfiguration: DefaultConfiguration,
) {
    fun findById(id: Long) = dao.findById(id)
}

When running an Integration test, I get an exception, because dao is null. I checked (by debugging): dao is not null when the constructor is called, but it indeed is when findById is called. I noticed that the object references are not the same (because of CGLIB, the Spring proxy), but I don't know what happens between the constructor call and the time the proxied bean is created.

I tried an abstract function getDaoImpl() an which is implemented in SapArticleRepository (returning the object instance) and the calling that method (instead of accessing dao), but that seems overly complicated. There must be a way to pass the constructor argument/field in a way so it is still present by the time the Bean is used.

Note. TrmSapArticleDao is just a Jooq-generated class.

Edit: I already found Constructor-Injected field is null in Spring CGLIB enhanced bean, but the accepted answer does not seem to answer my question.

1

There are 1 best solutions below

0
tiefenauer On

As always, I found the solution 5 minutes after posting the question...

The problem was that fields passed in the constructor are final in Kotlin by default. CGLIB can't intercept final fields/method and therefore leaves them initialized with null (see this answer on GitHub.

The solution is to make the constructor field open. So above code works when defining the constructor of the abstract class as follows:

abstract class AbstractSapRepository<R : UpdatableRecord<R?>, TPojo>(
    open var dao: DAOImpl<R, TPojo, Long>,
    jooqDsl: DSLContext,
    jooqConfiguration: DefaultConfiguration,
)