diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/aot/BasePathAwareControllerAotProcessor.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/aot/BasePathAwareControllerAotProcessor.java new file mode 100644 index 000000000..525153d71 --- /dev/null +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/aot/BasePathAwareControllerAotProcessor.java @@ -0,0 +1,34 @@ +/* + * Copyright 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.data.rest.webmvc.aot; + +import org.springframework.data.rest.webmvc.BasePathAwareController; +import org.springframework.hateoas.aot.ControllerMethodReturnTypeAotProcessor; + +/** + * Extension of {@link ControllerMethodReturnTypeAotProcessor} to also pick up controllers defined using + * {@link BasePathAwareController}. + * + * @author Christoph Strobl + * @author Oliver Drotbohm + * @since 4.0 + */ +class BasePathAwareControllerAotProcessor extends ControllerMethodReturnTypeAotProcessor { + + protected BasePathAwareControllerAotProcessor() { + super(BasePathAwareController.class); + } +} diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/aot/ProjectionProxyAotProcessor.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/aot/ProjectionProxyAotProcessor.java new file mode 100644 index 000000000..7d3d93cef --- /dev/null +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/aot/ProjectionProxyAotProcessor.java @@ -0,0 +1,93 @@ +/* + * Copyright 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.data.rest.webmvc.aot; + +import java.util.HashSet; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.aop.SpringProxy; +import org.springframework.beans.factory.aot.BeanRegistrationAotContribution; +import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor; +import org.springframework.beans.factory.support.RegisteredBean; +import org.springframework.core.DecoratingProxy; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.data.projection.TargetAware; +import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; +import org.springframework.data.rest.core.config.Projection; +import org.springframework.data.util.AnnotatedTypeScanner; +import org.springframework.util.ClassUtils; +import org.springframework.util.ObjectUtils; + +/** + * {@link BeanRegistrationAotProcessor} to register proxy hints for projection interfaces. + * + * @author Oliver Drotbohm + * @since 4.0 + */ +class ProjectionProxyAotProcessor implements BeanRegistrationAotProcessor { + + private static final Logger LOGGER = LoggerFactory.getLogger(ProjectionProxyAotProcessor.class); + + private static Class[] ADDITIONAL_INTERFACES = new Class[] { // + TargetAware.class, // + SpringProxy.class, // + DecoratingProxy.class + }; + + private Set packagesSeen = new HashSet<>(); + + @Override + public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) { + + if (!ClassUtils.isAssignable(RepositoryFactoryBeanSupport.class, registeredBean.getBeanClass())) { + return null; + } + + var holder = registeredBean.getMergedBeanDefinition() // + .getConstructorArgumentValues() // + .getIndexedArgumentValue(0, String.class); + + var repositoryInterface = (String) holder.getValue(); + var packageToScan = ClassUtils.getPackageName(repositoryInterface); + + LOGGER.debug("Detecting projection interfaces in {}", packageToScan); + + if (packagesSeen.contains(packageToScan)) { + return null; + } + + packagesSeen.add(packageToScan); + + return (context, code) -> { + + var classLoader = registeredBean.getBeanFactory().getBeanClassLoader(); + var proxies = context.getRuntimeHints().proxies(); + + var scanner = new AnnotatedTypeScanner(Projection.class); + scanner.setResourceLoader(new DefaultResourceLoader(classLoader)); + scanner.findTypes(packageToScan) + .forEach(it -> { + + LOGGER.debug("Registering proxy config for projection interface {}.", it.getName()); + + proxies.registerJdkProxy(ObjectUtils.addObjectToArray(ADDITIONAL_INTERFACES, it, 0)); + }); + + }; + } +} diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/aot/package-info.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/aot/package-info.java new file mode 100644 index 000000000..bb0648156 --- /dev/null +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/aot/package-info.java @@ -0,0 +1,2 @@ +/** AOT & native configuration for rest.webmvc */ +package org.springframework.data.rest.webmvc.aot; diff --git a/spring-data-rest-webmvc/src/main/resources/META-INF/spring/aot.factories b/spring-data-rest-webmvc/src/main/resources/META-INF/spring/aot.factories new file mode 100644 index 000000000..d606d29af --- /dev/null +++ b/spring-data-rest-webmvc/src/main/resources/META-INF/spring/aot.factories @@ -0,0 +1,3 @@ +org.springframework.beans.factory.aot.BeanRegistrationAotProcessor=\ + org.springframework.data.rest.webmvc.aot.BasePathAwareControllerAotProcessor,\ + org.springframework.data.rest.webmvc.aot.ProjectionProxyAotProcessor