Browse Source
This commit adds a central utility to figure out if the application must run with Ahead-Of-Time optimizations. This is mandatory for running in a native image but can be selected on the JVM using the "spring.aot.enabled" property. This commit also introduces a utility that can be used to initialize a context with generated artifacts. This represents the runtime counterpart of ApplicationContextAotGenerator. Closes gh-28474pull/28569/head
4 changed files with 229 additions and 1 deletions
@ -0,0 +1,77 @@
@@ -0,0 +1,77 @@
|
||||
/* |
||||
* Copyright 2002-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.context.aot; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
|
||||
import org.springframework.beans.BeanUtils; |
||||
import org.springframework.context.ApplicationContextInitializer; |
||||
import org.springframework.context.ConfigurableApplicationContext; |
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.util.ClassUtils; |
||||
|
||||
/** |
||||
* Initializes a {@link ConfigurableApplicationContext} using AOT optimizations. |
||||
* |
||||
* @author Stephane Nicoll |
||||
* @since 6.0 |
||||
*/ |
||||
public class ApplicationContextAotInitializer { |
||||
|
||||
private static final Log logger = LogFactory.getLog(ApplicationContextAotInitializer.class); |
||||
|
||||
/** |
||||
* Initialize the specified application context using the specified |
||||
* {@link ApplicationContextInitializer} class names. Each class name is |
||||
* expected to have a default constructor. |
||||
* @param context the context to initialize |
||||
* @param initializerClassNames the application context initializer class names |
||||
*/ |
||||
public void initialize(ConfigurableApplicationContext context, String... initializerClassNames) { |
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug("Initializing ApplicationContext with AOT"); |
||||
} |
||||
for (String initializerClassName : initializerClassNames) { |
||||
if (logger.isTraceEnabled()) { |
||||
logger.trace("Applying " + initializerClassName); |
||||
} |
||||
loadInitializer(initializerClassName, context.getClassLoader()).initialize(context); |
||||
} |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
private ApplicationContextInitializer<ConfigurableApplicationContext> loadInitializer( |
||||
String className, @Nullable ClassLoader classLoader) { |
||||
Object initializer = instantiate(className, classLoader); |
||||
if (!(initializer instanceof ApplicationContextInitializer)) { |
||||
throw new IllegalArgumentException("Not an ApplicationContextInitializer: " + className); |
||||
} |
||||
return (ApplicationContextInitializer<ConfigurableApplicationContext>) initializer; |
||||
} |
||||
|
||||
private static Object instantiate(String className, @Nullable ClassLoader classLoader) { |
||||
try { |
||||
Class<?> type = ClassUtils.forName(className, classLoader); |
||||
return BeanUtils.instantiateClass(type); |
||||
} |
||||
catch (Exception ex) { |
||||
throw new IllegalArgumentException("Failed to instantiate ApplicationContextInitializer: " + className, ex); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,101 @@
@@ -0,0 +1,101 @@
|
||||
/* |
||||
* Copyright 2002-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.context.aot; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition; |
||||
import org.springframework.context.ApplicationContextInitializer; |
||||
import org.springframework.context.support.GenericApplicationContext; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; |
||||
|
||||
/** |
||||
* Tests for {@link ApplicationContextAotInitializer}. |
||||
* |
||||
* @author Stephane Nicoll |
||||
*/ |
||||
class ApplicationContextAotInitializerTests { |
||||
|
||||
private final ApplicationContextAotInitializer initializer = new ApplicationContextAotInitializer(); |
||||
|
||||
@Test |
||||
void initializeInvokeApplicationContextInitializer() { |
||||
GenericApplicationContext context = new GenericApplicationContext(); |
||||
initializer.initialize(context, TestApplicationContextInitializer.class.getName()); |
||||
assertThat(context.getBeanDefinitionNames()).containsExactly("test"); |
||||
} |
||||
|
||||
@Test |
||||
void initializeInvokeApplicationContextInitializersInOrder() { |
||||
GenericApplicationContext context = new GenericApplicationContext(); |
||||
initializer.initialize(context, AnotherApplicationContextInitializer.class.getName(), |
||||
TestApplicationContextInitializer.class.getName()); |
||||
assertThat(context.getBeanDefinitionNames()).containsExactly("another", "test"); |
||||
} |
||||
|
||||
@Test |
||||
void initializeFailWithNonApplicationContextInitializer() { |
||||
GenericApplicationContext context = new GenericApplicationContext(); |
||||
assertThatIllegalArgumentException() |
||||
.isThrownBy(() -> initializer.initialize(context, "java.lang.String")) |
||||
.withMessageContaining("Not an ApplicationContextInitializer: ") |
||||
.withMessageContaining("java.lang.String"); |
||||
} |
||||
|
||||
@Test |
||||
void initializeFailWithApplicationContextInitializerAndNonDefaultConstructor() { |
||||
GenericApplicationContext context = new GenericApplicationContext(); |
||||
assertThatIllegalArgumentException() |
||||
.isThrownBy(() -> initializer.initialize(context, |
||||
ConfigurableApplicationContextInitializer.class.getName())) |
||||
.withMessageContaining("Failed to instantiate ApplicationContextInitializer: ") |
||||
.withMessageContaining(ConfigurableApplicationContextInitializer.class.getName()); |
||||
} |
||||
|
||||
|
||||
static class TestApplicationContextInitializer implements ApplicationContextInitializer<GenericApplicationContext> { |
||||
|
||||
@Override |
||||
public void initialize(GenericApplicationContext applicationContext) { |
||||
applicationContext.registerBeanDefinition("test", new RootBeanDefinition()); |
||||
} |
||||
|
||||
} |
||||
|
||||
static class AnotherApplicationContextInitializer implements ApplicationContextInitializer<GenericApplicationContext> { |
||||
|
||||
@Override |
||||
public void initialize(GenericApplicationContext applicationContext) { |
||||
applicationContext.registerBeanDefinition("another", new RootBeanDefinition()); |
||||
} |
||||
|
||||
} |
||||
|
||||
static class ConfigurableApplicationContextInitializer implements ApplicationContextInitializer<GenericApplicationContext> { |
||||
|
||||
public ConfigurableApplicationContextInitializer(ClassLoader classLoader) { |
||||
} |
||||
|
||||
@Override |
||||
public void initialize(GenericApplicationContext applicationContext) { |
||||
|
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,49 @@
@@ -0,0 +1,49 @@
|
||||
/* |
||||
* Copyright 2002-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.aot; |
||||
|
||||
import org.springframework.core.NativeDetector; |
||||
import org.springframework.core.SpringProperties; |
||||
|
||||
/** |
||||
* Determine if AOT-processed optimizations must be used rather than the |
||||
* regular runtime. Strictly for internal use within the framework. |
||||
* |
||||
* @author Stephane Nicoll |
||||
* @since 6.0 |
||||
*/ |
||||
public abstract class AotDetector { |
||||
|
||||
/** |
||||
* System property that indicates the application should run with AOT |
||||
* generated artifacts. If such optimizations are not available, it is |
||||
* recommended to throw an exception rather than falling back to the |
||||
* regular runtime behavior. |
||||
*/ |
||||
public static final String AOT_ENABLED = "spring.aot.enabled"; |
||||
|
||||
/** |
||||
* Return whether AOT optimizations must be considered at runtime. This |
||||
* is mandatory in a native image but can be triggered on the JVM using |
||||
* the {@value AOT_ENABLED} spring property. |
||||
* @return whether AOT optimizations must be considered |
||||
*/ |
||||
public static boolean useGeneratedArtifacts() { |
||||
return (NativeDetector.inNativeImage() || SpringProperties.getFlag(AOT_ENABLED)); |
||||
} |
||||
|
||||
} |
@ -1,2 +1,3 @@
@@ -1,2 +1,3 @@
|
||||
Args = --initialize-at-build-time=org.springframework.core.NativeDetector \ |
||||
Args = --initialize-at-build-time=org.springframework.aot.AotDetector \ |
||||
--initialize-at-build-time=org.springframework.core.NativeDetector \ |
||||
--initialize-at-build-time=org.springframework.util.unit.DataSize |
||||
|
Loading…
Reference in new issue