From 3524401bf1654af20b5b77913cb4ebc0ee036a20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Deleuze?= Date: Mon, 15 Feb 2021 16:33:41 +0100 Subject: [PATCH] Introduce a NativeDetector utility class With the introduction of the -H:+InlineBeforeAnalysis native image compiler flag in GraalVM 21.0.0, it is now possible to use an utility method and get code removal at build time. This flag will be enabled as of Spring Native 0.9.0. closes gh-25795 --- .../aop/framework/DefaultAopProxyFactory.java | 12 ++---- .../AbstractAutowireCapableBeanFactory.java | 11 +----- .../ConfigurationClassPostProcessor.java | 12 ++---- .../support/AbstractApplicationContext.java | 18 +++------ .../core/DefaultParameterNameDiscoverer.java | 12 ++---- .../springframework/core/NativeDetector.java | 39 +++++++++++++++++++ .../core/SerializableTypeWrapper.java | 11 +----- 7 files changed, 58 insertions(+), 57 deletions(-) create mode 100644 spring-core/src/main/java/org/springframework/core/NativeDetector.java diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAopProxyFactory.java b/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAopProxyFactory.java index d84df2f4c9..3e8c838704 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAopProxyFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAopProxyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2021 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. @@ -20,6 +20,7 @@ import java.io.Serializable; import java.lang.reflect.Proxy; import org.springframework.aop.SpringProxy; +import org.springframework.core.NativeDetector; /** * Default {@link AopProxyFactory} implementation, creating either a CGLIB proxy @@ -47,17 +48,10 @@ import org.springframework.aop.SpringProxy; @SuppressWarnings("serial") public class DefaultAopProxyFactory implements AopProxyFactory, Serializable { - /** - * Whether this environment lives within a native image. - * Exposed as a private static field rather than in a {@code NativeImageDetector.inNativeImage()} static method due to https://github.com/oracle/graal/issues/2594. - * @see ImageInfo.java - */ - private static final boolean IN_NATIVE_IMAGE = (System.getProperty("org.graalvm.nativeimage.imagecode") != null); - @Override public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { - if (!IN_NATIVE_IMAGE && + if (!NativeDetector.inNativeImage() && (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) { Class targetClass = config.getTargetClass(); if (targetClass == null) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java index 4997a7337a..ff76dcdd37 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java @@ -73,6 +73,7 @@ import org.springframework.beans.factory.config.TypedStringValue; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.MethodParameter; import org.springframework.core.NamedThreadLocal; +import org.springframework.core.NativeDetector; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.PriorityOrdered; import org.springframework.core.ResolvableType; @@ -123,14 +124,6 @@ import org.springframework.util.StringUtils; public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { - /** - * Whether this environment lives within a native image. - * Exposed as a private static field rather than in a {@code NativeImageDetector.inNativeImage()} static method due to https://github.com/oracle/graal/issues/2594. - * @see ImageInfo.java - */ - private static final boolean IN_NATIVE_IMAGE = (System.getProperty("org.graalvm.nativeimage.imagecode") != null); - - /** Strategy for creating bean instances. */ private InstantiationStrategy instantiationStrategy; @@ -184,7 +177,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac ignoreDependencyInterface(BeanNameAware.class); ignoreDependencyInterface(BeanFactoryAware.class); ignoreDependencyInterface(BeanClassLoaderAware.class); - if (IN_NATIVE_IMAGE) { + if (NativeDetector.inNativeImage()) { this.instantiationStrategy = new SimpleInstantiationStrategy(); } else { diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java index 449c48fa87..326a5bab3a 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 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. @@ -52,6 +52,7 @@ import org.springframework.context.ApplicationStartupAware; import org.springframework.context.EnvironmentAware; import org.springframework.context.ResourceLoaderAware; import org.springframework.context.annotation.ConfigurationClassEnhancer.EnhancedConfiguration; +import org.springframework.core.NativeDetector; import org.springframework.core.Ordered; import org.springframework.core.PriorityOrdered; import org.springframework.core.env.Environment; @@ -104,13 +105,6 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo private static final String IMPORT_REGISTRY_BEAN_NAME = ConfigurationClassPostProcessor.class.getName() + ".importRegistry"; - /** - * Whether this environment lives within a native image. - * Exposed as a private static field rather than in a {@code NativeImageDetector.inNativeImage()} static method due to https://github.com/oracle/graal/issues/2594. - * @see ImageInfo.java - */ - private static final boolean IN_NATIVE_IMAGE = (System.getProperty("org.graalvm.nativeimage.imagecode") != null); - private final Log logger = LogFactory.getLog(getClass()); @@ -427,7 +421,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef); } } - if (configBeanDefs.isEmpty() || IN_NATIVE_IMAGE) { + if (configBeanDefs.isEmpty() || NativeDetector.inNativeImage()) { // nothing to enhance -> return immediately enhanceConfigClasses.end(); return; diff --git a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java index e87edab5f6..86ca92ef6f 100644 --- a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 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. @@ -66,6 +66,7 @@ import org.springframework.context.event.SimpleApplicationEventMulticaster; import org.springframework.context.expression.StandardBeanExpressionResolver; import org.springframework.context.weaving.LoadTimeWeaverAware; import org.springframework.context.weaving.LoadTimeWeaverAwareProcessor; +import org.springframework.core.NativeDetector; import org.springframework.core.ResolvableType; import org.springframework.core.SpringProperties; import org.springframework.core.annotation.AnnotationUtils; @@ -164,13 +165,6 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader */ private static final boolean shouldIgnoreSpel = SpringProperties.getFlag("spring.spel.ignore"); - /** - * Whether this environment lives within a native image. - * Exposed as a private static field rather than in a {@code NativeImageDetector.inNativeImage()} static method due to https://github.com/oracle/graal/issues/2594. - * @see ImageInfo.java - */ - private static final boolean IN_NATIVE_IMAGE = (System.getProperty("org.graalvm.nativeimage.imagecode") != null); - static { // Eagerly load the ContextClosedEvent class to avoid weird classloader issues @@ -711,7 +705,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this)); // Detect a LoadTimeWeaver and prepare for weaving, if found. - if (!IN_NATIVE_IMAGE && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { + if (!NativeDetector.inNativeImage() && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); // Set a temporary ClassLoader for type matching. beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); @@ -752,7 +746,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor) - if (!IN_NATIVE_IMAGE && beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { + if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } @@ -943,7 +937,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader publishEvent(new ContextRefreshedEvent(this)); // Participate in LiveBeansView MBean, if active. - if (!IN_NATIVE_IMAGE) { + if (!NativeDetector.inNativeImage()) { LiveBeansView.registerApplicationContext(this); } } @@ -1054,7 +1048,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader logger.debug("Closing " + this); } - if (!IN_NATIVE_IMAGE) { + if (!NativeDetector.inNativeImage()) { LiveBeansView.unregisterApplicationContext(this); } diff --git a/spring-core/src/main/java/org/springframework/core/DefaultParameterNameDiscoverer.java b/spring-core/src/main/java/org/springframework/core/DefaultParameterNameDiscoverer.java index 283e5ba0f3..f4dca0f996 100644 --- a/spring-core/src/main/java/org/springframework/core/DefaultParameterNameDiscoverer.java +++ b/spring-core/src/main/java/org/springframework/core/DefaultParameterNameDiscoverer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 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. @@ -39,15 +39,9 @@ package org.springframework.core; */ public class DefaultParameterNameDiscoverer extends PrioritizedParameterNameDiscoverer { - /** - * Whether this environment lives within a native image. - * Exposed as a private static field rather than in a {@code NativeImageDetector.inNativeImage()} static method due to https://github.com/oracle/graal/issues/2594. - * @see ImageInfo.java - */ - private static final boolean IN_NATIVE_IMAGE = (System.getProperty("org.graalvm.nativeimage.imagecode") != null); - public DefaultParameterNameDiscoverer() { - if (KotlinDetector.isKotlinReflectPresent() && !IN_NATIVE_IMAGE) { + // TODO Remove this conditional inclusion when upgrading to Kotlin 1.5, see https://youtrack.jetbrains.com/issue/KT-44594 + if (KotlinDetector.isKotlinReflectPresent() && !NativeDetector.inNativeImage()) { addDiscoverer(new KotlinReflectionParameterNameDiscoverer()); } addDiscoverer(new StandardReflectionParameterNameDiscoverer()); diff --git a/spring-core/src/main/java/org/springframework/core/NativeDetector.java b/spring-core/src/main/java/org/springframework/core/NativeDetector.java new file mode 100644 index 0000000000..f2f191747e --- /dev/null +++ b/spring-core/src/main/java/org/springframework/core/NativeDetector.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2021 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.core; + +/** + * A common delegate for detecting a GraalVM native image environment. + * + *

Requires using the {@code -H:+InlineBeforeAnalysis} native image compiler flag in order to allow code removal at + * build time. + * + * @author Sebastien Deleuze + * @since 5.3.4 + */ +public abstract class NativeDetector { + + // See https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/ImageInfo.java + private static final boolean imageCode = (System.getProperty("org.graalvm.nativeimage.imagecode") != null); + + /** + * Returns {@code true} if invoked in the context of image building or during image runtime, else {@code false}. + */ + public static boolean inNativeImage() { + return imageCode; + } +} diff --git a/spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java b/spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java index 8a3a18e209..7d09fe2e06 100644 --- a/spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java +++ b/spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 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. @@ -59,13 +59,6 @@ final class SerializableTypeWrapper { private static final Class[] SUPPORTED_SERIALIZABLE_TYPES = { GenericArrayType.class, ParameterizedType.class, TypeVariable.class, WildcardType.class}; - /** - * Whether this environment lives within a native image. - * Exposed as a private static field rather than in a {@code NativeImageDetector.inNativeImage()} static method due to https://github.com/oracle/graal/issues/2594. - * @see ImageInfo.java - */ - private static final boolean IN_NATIVE_IMAGE = (System.getProperty("org.graalvm.nativeimage.imagecode") != null); - static final ConcurrentReferenceHashMap cache = new ConcurrentReferenceHashMap<>(256); @@ -116,7 +109,7 @@ final class SerializableTypeWrapper { // No serializable type wrapping necessary (e.g. for java.lang.Class) return providedType; } - if (IN_NATIVE_IMAGE || !Serializable.class.isAssignableFrom(Class.class)) { + if (NativeDetector.inNativeImage() || !Serializable.class.isAssignableFrom(Class.class)) { // Let's skip any wrapping attempts if types are generally not serializable in // the current runtime environment (even java.lang.Class itself, e.g. on GraalVM native images) return providedType;