Merge branch '6.2.x'
# Conflicts: # spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java # spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCreatorUtils.java
This commit is contained in:
@@ -133,6 +133,11 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||||||
* System property that instructs Spring to enforce strict locking during bean creation,
|
* System property that instructs Spring to enforce strict locking during bean creation,
|
||||||
* rather than the mix of strict and lenient locking that 6.2 applies by default. Setting
|
* rather than the mix of strict and lenient locking that 6.2 applies by default. Setting
|
||||||
* this flag to "true" restores 6.1.x style locking in the entire pre-instantiation phase.
|
* this flag to "true" restores 6.1.x style locking in the entire pre-instantiation phase.
|
||||||
|
* <p>By default, the factory infers strict locking from the encountered thread names:
|
||||||
|
* If additional threads have names that match the thread prefix of the main bootstrap thread,
|
||||||
|
* they are considered external (multiple external bootstrap threads calling into the factory)
|
||||||
|
* and therefore have strict locking applied to them. This inference can be turned off through
|
||||||
|
* explicitly setting this flag to "false" rather than leaving it unspecified.
|
||||||
* @since 6.2.6
|
* @since 6.2.6
|
||||||
* @see #preInstantiateSingletons()
|
* @see #preInstantiateSingletons()
|
||||||
*/
|
*/
|
||||||
@@ -156,8 +161,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||||||
private static final Map<String, Reference<DefaultListableBeanFactory>> serializableFactories =
|
private static final Map<String, Reference<DefaultListableBeanFactory>> serializableFactories =
|
||||||
new ConcurrentHashMap<>(8);
|
new ConcurrentHashMap<>(8);
|
||||||
|
|
||||||
/** Whether lenient locking is allowed in this factory. */
|
/** Whether strict locking is enforced or relaxed in this factory. */
|
||||||
private final boolean lenientLockingAllowed = !SpringProperties.getFlag(STRICT_LOCKING_PROPERTY_NAME);
|
private @Nullable final Boolean strictLocking = SpringProperties.checkFlag(STRICT_LOCKING_PROPERTY_NAME);
|
||||||
|
|
||||||
/** Optional id for this factory, for serialization purposes. */
|
/** Optional id for this factory, for serialization purposes. */
|
||||||
private @Nullable String serializationId;
|
private @Nullable String serializationId;
|
||||||
@@ -208,6 +213,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||||||
|
|
||||||
private volatile boolean preInstantiationPhase;
|
private volatile boolean preInstantiationPhase;
|
||||||
|
|
||||||
|
private @Nullable volatile String mainThreadPrefix;
|
||||||
|
|
||||||
private final NamedThreadLocal<PreInstantiation> preInstantiationThread =
|
private final NamedThreadLocal<PreInstantiation> preInstantiationThread =
|
||||||
new NamedThreadLocal<>("Pre-instantiation thread marker");
|
new NamedThreadLocal<>("Pre-instantiation thread marker");
|
||||||
|
|
||||||
@@ -1030,7 +1037,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Bean intended to be initialized in main bootstrap thread
|
// Bean intended to be initialized in main bootstrap thread.
|
||||||
if (this.preInstantiationThread.get() == PreInstantiation.BACKGROUND) {
|
if (this.preInstantiationThread.get() == PreInstantiation.BACKGROUND) {
|
||||||
throw new BeanCurrentlyInCreationException(beanName, "Bean marked for mainline initialization " +
|
throw new BeanCurrentlyInCreationException(beanName, "Bean marked for mainline initialization " +
|
||||||
"but requested in background thread - enforce early instantiation in mainline thread " +
|
"but requested in background thread - enforce early instantiation in mainline thread " +
|
||||||
@@ -1041,8 +1048,28 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected @Nullable Boolean isCurrentThreadAllowedToHoldSingletonLock() {
|
protected @Nullable Boolean isCurrentThreadAllowedToHoldSingletonLock() {
|
||||||
return (this.lenientLockingAllowed && this.preInstantiationPhase ?
|
if (this.preInstantiationPhase) {
|
||||||
this.preInstantiationThread.get() != PreInstantiation.BACKGROUND : null);
|
// We only differentiate in the preInstantiateSingletons phase.
|
||||||
|
PreInstantiation preInstantiation = this.preInstantiationThread.get();
|
||||||
|
if (preInstantiation != null) {
|
||||||
|
// A Spring-managed thread:
|
||||||
|
// MAIN is allowed to lock (true) or even forced to lock (null),
|
||||||
|
// BACKGROUND is never allowed to lock (false).
|
||||||
|
return switch (preInstantiation) {
|
||||||
|
case MAIN -> (Boolean.TRUE.equals(this.strictLocking) ? null : true);
|
||||||
|
case BACKGROUND -> false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (Boolean.FALSE.equals(this.strictLocking) ||
|
||||||
|
(this.strictLocking == null && !getThreadNamePrefix().equals(this.mainThreadPrefix))) {
|
||||||
|
// An unmanaged thread (assumed to be application-internal) with lenient locking,
|
||||||
|
// and not part of the same thread pool that provided the main bootstrap thread
|
||||||
|
// (excluding scenarios where we are hit by multiple external bootstrap threads).
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Traditional behavior: forced to always hold a full lock.
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1060,6 +1087,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||||||
|
|
||||||
this.preInstantiationPhase = true;
|
this.preInstantiationPhase = true;
|
||||||
this.preInstantiationThread.set(PreInstantiation.MAIN);
|
this.preInstantiationThread.set(PreInstantiation.MAIN);
|
||||||
|
this.mainThreadPrefix = getThreadNamePrefix();
|
||||||
try {
|
try {
|
||||||
for (String beanName : beanNames) {
|
for (String beanName : beanNames) {
|
||||||
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
|
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
|
||||||
@@ -1072,6 +1100,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
this.mainThreadPrefix = null;
|
||||||
this.preInstantiationThread.remove();
|
this.preInstantiationThread.remove();
|
||||||
this.preInstantiationPhase = false;
|
this.preInstantiationPhase = false;
|
||||||
}
|
}
|
||||||
@@ -1166,6 +1195,12 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String getThreadNamePrefix() {
|
||||||
|
String name = Thread.currentThread().getName();
|
||||||
|
int numberSeparator = name.lastIndexOf('-');
|
||||||
|
return (numberSeparator >= 0 ? name.substring(0, numberSeparator) : name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------
|
//---------------------------------------------------------------------
|
||||||
// Implementation of BeanDefinitionRegistry interface
|
// Implementation of BeanDefinitionRegistry interface
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
|||||||
// Thread-safe exposure is still guaranteed, there is just a risk of collisions
|
// Thread-safe exposure is still guaranteed, there is just a risk of collisions
|
||||||
// when triggering creation of other beans as dependencies of the current bean.
|
// when triggering creation of other beans as dependencies of the current bean.
|
||||||
if (logger.isInfoEnabled()) {
|
if (logger.isInfoEnabled()) {
|
||||||
logger.info("Creating singleton bean '" + beanName + "' in thread \"" +
|
logger.info("Obtaining singleton bean '" + beanName + "' in thread \"" +
|
||||||
Thread.currentThread().getName() + "\" while other thread holds " +
|
Thread.currentThread().getName() + "\" while other thread holds " +
|
||||||
"singleton lock for other beans " + this.singletonsCurrentlyInCreation);
|
"singleton lock for other beans " + this.singletonsCurrentlyInCreation);
|
||||||
}
|
}
|
||||||
@@ -441,12 +441,16 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether the current thread is allowed to hold the singleton lock.
|
* Determine whether the current thread is allowed to hold the singleton lock.
|
||||||
* <p>By default, any thread may acquire and hold the singleton lock, except
|
* <p>By default, all threads are forced to hold a full lock through {@code null}.
|
||||||
* background threads from {@link DefaultListableBeanFactory#setBootstrapExecutor}.
|
* {@link DefaultListableBeanFactory} overrides this to specifically handle its
|
||||||
* @return {@code false} if the current thread is explicitly not allowed to hold
|
* threads during the pre-instantiation phase: {@code true} for the main thread,
|
||||||
* the lock, {@code true} if it is explicitly allowed to hold the lock but also
|
* {@code false} for managed background threads, and configuration-dependent
|
||||||
* accepts lenient fallback behavior, or {@code null} if there is no specific
|
* behavior for unmanaged threads.
|
||||||
* indication (traditional behavior: always holding a full lock)
|
* @return {@code true} if the current thread is explicitly allowed to hold the
|
||||||
|
* lock but also accepts lenient fallback behavior, {@code false} if it is
|
||||||
|
* explicitly not allowed to hold the lock and therefore forced to use lenient
|
||||||
|
* fallback behavior, or {@code null} if there is no specific indication
|
||||||
|
* (traditional behavior: forced to always hold a full lock)
|
||||||
* @since 6.2
|
* @since 6.2
|
||||||
*/
|
*/
|
||||||
protected @Nullable Boolean isCurrentThreadAllowedToHoldSingletonLock() {
|
protected @Nullable Boolean isCurrentThreadAllowedToHoldSingletonLock() {
|
||||||
|
|||||||
@@ -77,7 +77,6 @@ import org.springframework.beans.testfixture.beans.NestedTestBean;
|
|||||||
import org.springframework.beans.testfixture.beans.SideEffectBean;
|
import org.springframework.beans.testfixture.beans.SideEffectBean;
|
||||||
import org.springframework.beans.testfixture.beans.TestBean;
|
import org.springframework.beans.testfixture.beans.TestBean;
|
||||||
import org.springframework.beans.testfixture.beans.factory.DummyFactory;
|
import org.springframework.beans.testfixture.beans.factory.DummyFactory;
|
||||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
@@ -1419,7 +1418,6 @@ class DefaultListableBeanFactoryTests {
|
|||||||
lbf.registerBeanDefinition("rod", bd);
|
lbf.registerBeanDefinition("rod", bd);
|
||||||
RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class);
|
RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class);
|
||||||
lbf.registerBeanDefinition("rod2", bd2);
|
lbf.registerBeanDefinition("rod2", bd2);
|
||||||
lbf.setParameterNameDiscoverer(new DefaultParameterNameDiscoverer());
|
|
||||||
|
|
||||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
|
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
|
||||||
.isThrownBy(() -> lbf.autowire(ConstructorDependency.class, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false))
|
.isThrownBy(() -> lbf.autowire(ConstructorDependency.class, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false))
|
||||||
@@ -1490,7 +1488,6 @@ class DefaultListableBeanFactoryTests {
|
|||||||
RootBeanDefinition bd = new RootBeanDefinition(ConstructorDependenciesBean.class);
|
RootBeanDefinition bd = new RootBeanDefinition(ConstructorDependenciesBean.class);
|
||||||
bd.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
|
bd.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
|
||||||
lbf.registerBeanDefinition("bean", bd);
|
lbf.registerBeanDefinition("bean", bd);
|
||||||
lbf.setParameterNameDiscoverer(new DefaultParameterNameDiscoverer());
|
|
||||||
|
|
||||||
ConstructorDependenciesBean bean = lbf.getBean(ConstructorDependenciesBean.class);
|
ConstructorDependenciesBean bean = lbf.getBean(ConstructorDependenciesBean.class);
|
||||||
Object spouse1 = lbf.getBean("spouse1");
|
Object spouse1 = lbf.getBean("spouse1");
|
||||||
@@ -1508,7 +1505,6 @@ class DefaultListableBeanFactoryTests {
|
|||||||
bd.setAttribute(GenericBeanDefinition.PREFERRED_CONSTRUCTORS_ATTRIBUTE,
|
bd.setAttribute(GenericBeanDefinition.PREFERRED_CONSTRUCTORS_ATTRIBUTE,
|
||||||
ConstructorDependenciesBean.class.getConstructors());
|
ConstructorDependenciesBean.class.getConstructors());
|
||||||
lbf.registerBeanDefinition("bean", bd);
|
lbf.registerBeanDefinition("bean", bd);
|
||||||
lbf.setParameterNameDiscoverer(new DefaultParameterNameDiscoverer());
|
|
||||||
|
|
||||||
ConstructorDependenciesBean bean = lbf.getBean(ConstructorDependenciesBean.class);
|
ConstructorDependenciesBean bean = lbf.getBean(ConstructorDependenciesBean.class);
|
||||||
Object spouse1 = lbf.getBean("spouse1");
|
Object spouse1 = lbf.getBean("spouse1");
|
||||||
@@ -1526,7 +1522,6 @@ class DefaultListableBeanFactoryTests {
|
|||||||
bd.setAttribute(GenericBeanDefinition.PREFERRED_CONSTRUCTORS_ATTRIBUTE,
|
bd.setAttribute(GenericBeanDefinition.PREFERRED_CONSTRUCTORS_ATTRIBUTE,
|
||||||
ConstructorDependenciesBean.class.getConstructor(TestBean.class));
|
ConstructorDependenciesBean.class.getConstructor(TestBean.class));
|
||||||
lbf.registerBeanDefinition("bean", bd);
|
lbf.registerBeanDefinition("bean", bd);
|
||||||
lbf.setParameterNameDiscoverer(new DefaultParameterNameDiscoverer());
|
|
||||||
|
|
||||||
ConstructorDependenciesBean bean = lbf.getBean(ConstructorDependenciesBean.class);
|
ConstructorDependenciesBean bean = lbf.getBean(ConstructorDependenciesBean.class);
|
||||||
Object spouse = lbf.getBean("spouse1");
|
Object spouse = lbf.getBean("spouse1");
|
||||||
|
|||||||
@@ -16,6 +16,9 @@
|
|||||||
|
|
||||||
package org.springframework.context.annotation;
|
package org.springframework.context.annotation;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.Timeout;
|
import org.junit.jupiter.api.Timeout;
|
||||||
|
|
||||||
@@ -67,7 +70,7 @@ class BackgroundBootstrapTests {
|
|||||||
@Test
|
@Test
|
||||||
@Timeout(10)
|
@Timeout(10)
|
||||||
@EnabledForTestGroups(LONG_RUNNING)
|
@EnabledForTestGroups(LONG_RUNNING)
|
||||||
void bootstrapWithStrictLockingThread() {
|
void bootstrapWithStrictLockingFlag() {
|
||||||
SpringProperties.setFlag(DefaultListableBeanFactory.STRICT_LOCKING_PROPERTY_NAME);
|
SpringProperties.setFlag(DefaultListableBeanFactory.STRICT_LOCKING_PROPERTY_NAME);
|
||||||
try {
|
try {
|
||||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(StrictLockingBeanConfig.class);
|
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(StrictLockingBeanConfig.class);
|
||||||
@@ -79,6 +82,42 @@ class BackgroundBootstrapTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Timeout(10)
|
||||||
|
@EnabledForTestGroups(LONG_RUNNING)
|
||||||
|
void bootstrapWithStrictLockingInferred() throws InterruptedException {
|
||||||
|
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||||
|
ctx.register(InferredLockingBeanConfig.class);
|
||||||
|
ExecutorService threadPool = Executors.newFixedThreadPool(2);
|
||||||
|
threadPool.submit(() -> ctx.refresh());
|
||||||
|
Thread.sleep(500);
|
||||||
|
threadPool.submit(() -> ctx.getBean("testBean2"));
|
||||||
|
Thread.sleep(1000);
|
||||||
|
assertThat(ctx.getBean("testBean2", TestBean.class).getSpouse()).isSameAs(ctx.getBean("testBean1"));
|
||||||
|
ctx.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Timeout(10)
|
||||||
|
@EnabledForTestGroups(LONG_RUNNING)
|
||||||
|
void bootstrapWithStrictLockingTurnedOff() throws InterruptedException {
|
||||||
|
SpringProperties.setFlag(DefaultListableBeanFactory.STRICT_LOCKING_PROPERTY_NAME, false);
|
||||||
|
try {
|
||||||
|
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||||
|
ctx.register(InferredLockingBeanConfig.class);
|
||||||
|
ExecutorService threadPool = Executors.newFixedThreadPool(2);
|
||||||
|
threadPool.submit(() -> ctx.refresh());
|
||||||
|
Thread.sleep(500);
|
||||||
|
threadPool.submit(() -> ctx.getBean("testBean2"));
|
||||||
|
Thread.sleep(1000);
|
||||||
|
assertThat(ctx.getBean("testBean2", TestBean.class).getSpouse()).isNull();
|
||||||
|
ctx.close();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
SpringProperties.setProperty(DefaultListableBeanFactory.STRICT_LOCKING_PROPERTY_NAME, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Timeout(10)
|
@Timeout(10)
|
||||||
@EnabledForTestGroups(LONG_RUNNING)
|
@EnabledForTestGroups(LONG_RUNNING)
|
||||||
@@ -128,6 +167,24 @@ class BackgroundBootstrapTests {
|
|||||||
ctx.close();
|
ctx.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Timeout(10)
|
||||||
|
@EnabledForTestGroups(LONG_RUNNING)
|
||||||
|
void bootstrapWithCustomExecutorAndStrictLocking() {
|
||||||
|
SpringProperties.setFlag(DefaultListableBeanFactory.STRICT_LOCKING_PROPERTY_NAME);
|
||||||
|
try {
|
||||||
|
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CustomExecutorBeanConfig.class);
|
||||||
|
ctx.getBean("testBean1", TestBean.class);
|
||||||
|
ctx.getBean("testBean2", TestBean.class);
|
||||||
|
ctx.getBean("testBean3", TestBean.class);
|
||||||
|
ctx.getBean("testBean4", TestBean.class);
|
||||||
|
ctx.close();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
SpringProperties.setProperty(DefaultListableBeanFactory.STRICT_LOCKING_PROPERTY_NAME, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
static class UnmanagedThreadBeanConfig {
|
static class UnmanagedThreadBeanConfig {
|
||||||
@@ -220,6 +277,27 @@ class BackgroundBootstrapTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
static class InferredLockingBeanConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public TestBean testBean1() {
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
}
|
||||||
|
catch (InterruptedException ex) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
return new TestBean("testBean1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public TestBean testBean2(ConfigurableListableBeanFactory beanFactory) {
|
||||||
|
return new TestBean((TestBean) beanFactory.getSingleton("testBean1"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
static class CircularReferenceAgainstMainThreadBeanConfig {
|
static class CircularReferenceAgainstMainThreadBeanConfig {
|
||||||
|
|
||||||
@@ -377,13 +455,13 @@ class BackgroundBootstrapTests {
|
|||||||
|
|
||||||
@Bean(bootstrap = BACKGROUND) @DependsOn("testBean3")
|
@Bean(bootstrap = BACKGROUND) @DependsOn("testBean3")
|
||||||
public TestBean testBean1(TestBean testBean3) throws InterruptedException {
|
public TestBean testBean1(TestBean testBean3) throws InterruptedException {
|
||||||
Thread.sleep(3000);
|
Thread.sleep(6000);
|
||||||
return new TestBean();
|
return new TestBean();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(bootstrap = BACKGROUND) @Lazy
|
@Bean(bootstrap = BACKGROUND) @Lazy
|
||||||
public TestBean testBean2() throws InterruptedException {
|
public TestBean testBean2() throws InterruptedException {
|
||||||
Thread.sleep(3000);
|
Thread.sleep(6000);
|
||||||
return new TestBean();
|
return new TestBean();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -527,15 +527,26 @@ public class ReflectUtils {
|
|||||||
c = lookup.defineClass(b);
|
c = lookup.defineClass(b);
|
||||||
}
|
}
|
||||||
catch (LinkageError | IllegalAccessException ex) {
|
catch (LinkageError | IllegalAccessException ex) {
|
||||||
throw new CodeGenerationException(ex) {
|
if (ex instanceof LinkageError) {
|
||||||
@Override
|
// Could be a ClassLoader mismatch with the class pre-existing in a
|
||||||
public String getMessage() {
|
// parent ClassLoader -> try loadClass before giving up completely.
|
||||||
return "ClassLoader mismatch for [" + contextClass.getName() +
|
try {
|
||||||
"]: JVM should be started with --add-opens=java.base/java.lang=ALL-UNNAMED " +
|
c = contextClass.getClassLoader().loadClass(className);
|
||||||
"for ClassLoader.defineClass to be accessible on " + loader.getClass().getName() +
|
|
||||||
"; consider co-locating the affected class in that target ClassLoader instead.";
|
|
||||||
}
|
}
|
||||||
};
|
catch (ClassNotFoundException cnfe) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (c == null) {
|
||||||
|
throw new CodeGenerationException(ex) {
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return "ClassLoader mismatch for [" + contextClass.getName() +
|
||||||
|
"]: JVM should be started with --add-opens=java.base/java.lang=ALL-UNNAMED " +
|
||||||
|
"for ClassLoader.defineClass to be accessible on " + loader.getClass().getName() +
|
||||||
|
"; consider co-locating the affected class in that target ClassLoader instead.";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Throwable ex) {
|
catch (Throwable ex) {
|
||||||
throw new CodeGenerationException(ex);
|
throw new CodeGenerationException(ex);
|
||||||
|
|||||||
@@ -117,7 +117,18 @@ public final class SpringProperties {
|
|||||||
* @param key the property key
|
* @param key the property key
|
||||||
*/
|
*/
|
||||||
public static void setFlag(String key) {
|
public static void setFlag(String key) {
|
||||||
localProperties.put(key, Boolean.TRUE.toString());
|
localProperties.setProperty(key, Boolean.TRUE.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Programmatically set a local flag to the given value, overriding
|
||||||
|
* an entry in the {@code spring.properties} file (if any).
|
||||||
|
* @param key the property key
|
||||||
|
* @param value the associated boolean value
|
||||||
|
* @since 6.2.6
|
||||||
|
*/
|
||||||
|
public static void setFlag(String key, boolean value) {
|
||||||
|
localProperties.setProperty(key, Boolean.toString(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -130,4 +141,19 @@ public final class SpringProperties {
|
|||||||
return Boolean.parseBoolean(getProperty(key));
|
return Boolean.parseBoolean(getProperty(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the flag for the given property key, returning {@code null}
|
||||||
|
* instead of {@code false} in case of no actual flag set.
|
||||||
|
* @param key the property key
|
||||||
|
* @return {@code true} if the property is set to the string "true"
|
||||||
|
* (ignoring case), {@code} false if it is set to any other value,
|
||||||
|
* {@code null} if it is not set at all
|
||||||
|
* @since 6.2.6
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static Boolean checkFlag(String key) {
|
||||||
|
String flag = getProperty(key);
|
||||||
|
return (flag != null ? Boolean.valueOf(flag) : null);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2024 the original author or authors.
|
* Copyright 2002-2025 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -85,7 +85,7 @@ public abstract class StatementCreatorUtils {
|
|||||||
|
|
||||||
private static final Map<Class<?>, Integer> javaTypeToSqlTypeMap = new HashMap<>(64);
|
private static final Map<Class<?>, Integer> javaTypeToSqlTypeMap = new HashMap<>(64);
|
||||||
|
|
||||||
static @Nullable Boolean shouldIgnoreGetParameterType;
|
static @Nullable Boolean shouldIgnoreGetParameterType = SpringProperties.checkFlag(IGNORE_GETPARAMETERTYPE_PROPERTY_NAME);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
javaTypeToSqlTypeMap.put(boolean.class, Types.BOOLEAN);
|
javaTypeToSqlTypeMap.put(boolean.class, Types.BOOLEAN);
|
||||||
@@ -114,11 +114,6 @@ public abstract class StatementCreatorUtils {
|
|||||||
javaTypeToSqlTypeMap.put(java.sql.Timestamp.class, Types.TIMESTAMP);
|
javaTypeToSqlTypeMap.put(java.sql.Timestamp.class, Types.TIMESTAMP);
|
||||||
javaTypeToSqlTypeMap.put(Blob.class, Types.BLOB);
|
javaTypeToSqlTypeMap.put(Blob.class, Types.BLOB);
|
||||||
javaTypeToSqlTypeMap.put(Clob.class, Types.CLOB);
|
javaTypeToSqlTypeMap.put(Clob.class, Types.CLOB);
|
||||||
|
|
||||||
String flag = SpringProperties.getProperty(IGNORE_GETPARAMETERTYPE_PROPERTY_NAME);
|
|
||||||
if (flag != null) {
|
|
||||||
shouldIgnoreGetParameterType = Boolean.valueOf(flag);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user