This is hopefully just a lack of understanding on how spring "cglib" support is supposed to work (in quotes because it's not cglib anymore right?) or how exactly a reflective access needs exports
instead of just opens
. Sorry if this isn't simply an understanding problem vs an issue which could be fixed to allow my module to work without adding an exports ... to spring...
// © Copyright 2024 Caleb Cushing
// SPDX-License-Identifier: AGPL-3.0-or-later
package com.xenoterracide.jpa.transaction;
import com.xenoterracide.jpa.annotation.TransactionScope;
import com.xenoterracide.jpa.util.Constants;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import org.jspecify.annotations.NonNull;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.support.SimpleTransactionScope;
/**
* The type Transaction bean post processor.
*/
@Configuration
class TransactionBeanPostProcessor implements BeanFactoryPostProcessor {
private static final ZoneId UTC = ZoneId.of("UTC");
@Bean
@TransactionScope
Instant instantNow() {
return Instant.now();
}
@Bean
@TransactionScope
ZonedDateTime zonedDateTimeNow(@NonNull Instant instantNow) {
return instantNow.atZone(UTC);
}
/**
* Offset date time now offset date time.
*
* @param zonedDateTimeNow the zoned date time now
* @return the offset date time
*/
@Bean
@TransactionScope
OffsetDateTime offsetDateTimeNow(@NonNull ZonedDateTime zonedDateTimeNow) {
return zonedDateTimeNow.toOffsetDateTime();
}
@Override
public void postProcessBeanFactory(@NonNull ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.registerScope(Constants.TRANSACTION_SCOPE, new SimpleTransactionScope());
}
}
import org.jspecify.annotations.NullMarked;
/**
* JPA utilities.
*/
@NullMarked module com.xenoterracide.jpa {
exports com.xenoterracide.jpa;
exports com.xenoterracide.jpa.annotation;
exports com.xenoterracide.jpa.util;
opens com.xenoterracide.jpa to org.hibernate.orm.core, spring.core;
opens com.xenoterracide.jpa.transaction to spring.core;
requires java.base;
requires org.apache.commons.lang3;
requires spring.data.commons;
requires spring.beans;
requires spring.context;
requires spring.tx;
requires org.hibernate.orm.envers;
requires static transitive org.jspecify;
requires static com.xenoterracide.tools.java;
requires static jakarta.annotation;
requires transitive jakarta.persistence;
requires transitive jakarta.validation;
requires transitive com.xenoterracide.model;
}
Adding this fixes it.
exports com.xenoterracide.jpa.transaction to spring.beans, spring.context;
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.xenoterracide.jpa.transaction.TransactionBeanPostProcessor$$SpringCGLIB$$0]: Is the constructor accessible?
at spring.beans@6.1.11/org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) ~[spring-beans-6.1.11.jar:?]
at spring.beans@6.1.11/org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205) ~[spring-beans-6.1.11.jar:?]
at spring.context@6.1.11/org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:202) ~[spring-context-6.1.11.jar:?]
at spring.context@6.1.11/org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:789) ~[spring-context-6.1.11.jar:?]
at spring.context@6.1.11/org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:607) ~[spring-context-6.1.11.jar:?]
at spring.boot@3.3.2/org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.3.2.jar:?]
at spring.boot@3.3.2/org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.3.2.jar:?]
at spring.boot@3.3.2/org.springframework.boot.SpringApplication.run(SpringApplication.java:335) ~[spring-boot-3.3.2.jar:?]
at spring.boot.test@3.3.2/org.springframework.boot.test.context.SpringBootContextLoader.lambda$loadContext$3(SpringBootContextLoader.java:137) ~[spring-boot-test-3.3.2.jar:?]
at spring.core@6.1.11/org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58) ~[spring-core-6.1.11.jar:?]
at spring.core@6.1.11/org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46) ~[spring-core-6.1.11.jar:?]
at spring.boot@3.3.2/org.springframework.boot.SpringApplication.withHook(SpringApplication.java:1463) ~[spring-boot-3.3.2.jar:?]
at spring.boot.test@3.3.2/org.springframework.boot.test.context.SpringBootContextLoader$ContextLoaderHook.run(SpringBootContextLoader.java:553) ~[spring-boot-test-3.3.2.jar:?]
at spring.boot.test@3.3.2/org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:137) ~[spring-boot-test-3.3.2.jar:?]
at spring.boot.test@3.3.2/org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:108) ~[spring-boot-test-3.3.2.jar:?]
at spring.test@6.1.11/org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:225) ~[spring-test-6.1.11.jar:?]
at spring.test@6.1.11/org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:152) ~[spring-test-6.1.11.jar:?]
... 130 more
Caused by: java.lang.IllegalAccessException: class org.springframework.beans.BeanUtils (in module spring.beans) cannot access class com.xenoterracide.jpa.transaction.TransactionBeanPostProcessor$$SpringCGLIB$$0 (in module com.xenoterracide.jpa) because module com.xenoterracide.jpa does not export com.xenoterracide.jpa.transaction to module spring.beans
Caused by: java.lang.IllegalAccessException: class org.springframework.beans.BeanUtils (in module spring.beans) cannot access class com.xenoterracide.jpa.transaction.TransactionBeanPostProcessor$$SpringCGLIB$$0 (in module com.xenoterracide.jpa) because module com.xenoterracide.jpa does not export com.xenoterracide.jpa.transaction to module spring.beans
at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:394) ~[?:?]
at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:714) ~[?:?]
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:495) ~[?:?]
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486) ~[?:?]
at spring.beans@6.1.11/org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:195) ~[spring-beans-6.1.11.jar:?]
at spring.beans@6.1.11/org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:94) ~[spring-beans-6.1.11.jar:?]
at spring.beans@6.1.11/org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1331) ~[spring-beans-6.1.11.jar:?]
at spring.beans@6.1.11/org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1222) ~[spring-beans-6.1.11.jar:?]
at spring.beans@6.1.11/org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562) ~[spring-beans-6.1.11.jar:?]
at spring.beans@6.1.11/org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.11.jar:?]
at spring.beans@6.1.11/org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) ~[spring-beans-6.1.11.jar:?]
at spring.beans@6.1.11/org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.11.jar:?]
at spring.beans@6.1.11/org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) ~[spring-beans-6.1.11.jar:?]
at spring.beans@6.1.11/org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205) ~[spring-beans-6.1.11.jar:?]
at spring.context@6.1.11/org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:202) ~[spring-context-6.1.11.jar:?]
at spring.context@6.1.11/org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:789) ~[spring-context-6.
1.11.jar:?]
at spring.context@6.1.11/org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:607) ~[spring-context-6.1.11.jar:?]
at spring.boot@3.3.2/org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.3.2.jar:?]
at spring.boot@3.3.2/org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.3.2.jar:?]
at spring.boot@3.3.2/org.springframework.boot.SpringApplication.run(SpringApplication.java:335) ~[spring-boot-3.3.2.jar:?]
at spring.boot.test@3.3.2/org.springframework.boot.test.context.SpringBootContextLoader.lambda$loadContext$3(SpringBootContextLoader.java:137) ~[spring-boot-test-3.3.2.jar:?]
at spring.core@6.1.11/org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58) ~[spring-core-6.1.11.jar:?]
at spring.core@6.1.11/org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46) ~[spring-core-6.1.11.jar:?]
at spring.boot@3.3.2/org.springframework.boot.SpringApplication.withHook(SpringApplication.java:1463) ~[spring-boot-3.3.2.jar:?]
at spring.boot.test@3.3.2/org.springframework.boot.test.context.SpringBootContextLoader$ContextLoaderHook.run(SpringBootContextLoader.java:553) ~[spring-boot-test-3.3.2.jar:?]
at spring.boot.test@3.3.2/org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:137) ~[spring-boot-test-3.3.2.jar:?]
at spring.boot.test@3.3.2/org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:108) ~[spring-boot-test-3.3.2.jar:?]
at spring.test@6.1.11/org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:225) ~[spring-test-6.1.11.jar:?]
at spring.test@6.1.11/org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:152) ~[spring-test-6.1.11.jar:?]
... 130 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionBeanPostProcessor' defined in URL [jar:file:///home/xeno/IdeaProjects/spring-app-commons/module/jpa/build/libs/jpa.jar!/com/xenoterracide/jpa/transaction/TransactionBeanPostProcessor.class]: class org.springframework.context.annotation.ConfigurationClassEnhancer$BeanFactoryAwareMethodInterceptor (in module spring.context) cannot access class com.xenoterracide.jpa.transaction.TransactionBeanPostProcessor$$SpringCGLIB$$0 (in module com.xenoterracide.jpa) because module com.xenoterracide.jpa does not export com.xenoterracide.jpa.transaction to module spring.context
at spring.boot.test@3.3.2/org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:108) ~[spring-boot-test-3.3.2.jar:?]
at spring.test@6.1.11/org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:225) ~[spring-test-6.1.11.jar:?]
at spring.test@6.1.11/org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:152) ~[spring-test-6.1.11.jar:?]
... 130 more
Caused by: java.lang.IllegalAccessException: class org.springframework.context.annotation.ConfigurationClassEnhancer$BeanFactoryAwareMethodInterceptor (in module spring.context) cannot access class com.xenoterracide.jpa.transaction.TransactionBeanPostProcessor$$SpringCGLIB$$0 (in module com.xenoterracide.jpa) because module com.xenoterracide.jpa does not export com.xenoterracide.jpa.transaction to module spring.context
at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:394) ~[?:?]
org.springframework:spring-core:6.1.11
------------------------------------------------------------
Gradle 8.9
------------------------------------------------------------
Build time: 2024-07-11 14:37:41 UTC
Revision: d536ef36a19186ccc596d8817123e5445f30fef8
Kotlin: 1.9.23
Groovy: 3.0.21
Ant: Apache Ant(TM) version 1.10.13 compiled on January 4 2023
Launcher JVM: 21.0.4 (Eclipse Adoptium 21.0.4+7-LTS)
Daemon JVM: /home/xeno/.asdf/installs/java/temurin-21.0.4+7.0.LTS (no JDK specified, using current Java home)
OS: Linux 6.6.41-1-MANJARO amd64
Comment From: sbrannen
Hi @xenoterracide,
For starters, it's still CGLIB just rather our port of CGLIB which we now maintain.
Regarding the exception you've encountered, ConfigurationClassEnhancer$BeanFactoryAwareMethodInterceptor
likely signals that there was an issue related to a dynamic subclass of your @Configuration
class.
To avoid that, try @Configuration(proxyBeanMethods = false)
and let us know if that solves your problem.
As a side note, we don't recommend that a @Configuration
class implement BeanFactoryPostProcessor
. Instead, a @Configuration
class should contain a static
@Bean
method that returns an instance of your BeanFactoryPostProcessor
.
Comment From: xenoterracide
As a side note, we don't recommend that a @Configuration class implement BeanFactoryPostProcessor. Instead, a @Configuration class should contain a static @Bean method that returns an instance of your BeanFactoryPostProcessor.
for some reason I never considered that, or using statics...
For starters, it's still CGLIB just rather our port of CGLIB which we now maintain.
well now I wonder if bytebuddy has the same issue
To avoid that, try @Configuration(proxyBeanMethods = false) and let us know if that solves your problem.
It does, although I think I prefer the export
because I can solve that in 1 place instead of N, or in theory at least less places (since I have N libraries). I have @SpringBootApplication(proxyBeanMethods = false)
so I figured I wouldn't have to do it again. For some, unknown, reason I presumed that would be a default; like the component scanning package is for the whole app by default.
I still don't understand why the export
helps here. Might be worth documenting these options.
Comment From: xenoterracide
well now I wonder if bytebuddy has the same issue
and why I don't seem to have it with hibernate, which I would think has to accomplish something similar with its magic.
Comment From: xenoterracide
I think this is just a duplicate of #32671 re-open if you disagree