diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/RegisterForProxyBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/RegisterForProxyBuildStep.java new file mode 100644 index 0000000000000..9b3f27f9923f5 --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/RegisterForProxyBuildStep.java @@ -0,0 +1,34 @@ +package io.quarkus.deployment.steps; + +import java.util.ArrayList; + +import org.jboss.jandex.DotName; + +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.CombinedIndexBuildItem; +import io.quarkus.deployment.builditem.nativeimage.NativeImageProxyDefinitionBuildItem; +import io.quarkus.runtime.annotations.RegisterForProxy; + +public class RegisterForProxyBuildStep { + + @BuildStep + public void build(CombinedIndexBuildItem combinedIndexBuildItem, + BuildProducer proxy) { + for (var annotationInstance : combinedIndexBuildItem.getIndex() + .getAnnotations(DotName.createSimple(RegisterForProxy.class.getName()))) { + var targetsValue = annotationInstance.value("targets"); + var types = new ArrayList(); + if (targetsValue == null) { + var classInfo = annotationInstance.target().asClass(); + types.add(classInfo.name().toString()); + classInfo.interfaceNames().forEach(dotName -> types.add(dotName.toString())); + } else { + for (var type : targetsValue.asClassArray()) { + types.add(type.name().toString()); + } + } + proxy.produce(new NativeImageProxyDefinitionBuildItem(types)); + } + } +} \ No newline at end of file diff --git a/core/runtime/src/main/java/io/quarkus/runtime/annotations/RegisterForProxy.java b/core/runtime/src/main/java/io/quarkus/runtime/annotations/RegisterForProxy.java new file mode 100644 index 0000000000000..fda3b86ecbd31 --- /dev/null +++ b/core/runtime/src/main/java/io/quarkus/runtime/annotations/RegisterForProxy.java @@ -0,0 +1,39 @@ +package io.quarkus.runtime.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation that can be used to force an interface (including its super interfaces) to be registered for dynamic proxy + * generation in native image mode. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Repeatable(RegisterForProxy.List.class) +public @interface RegisterForProxy { + + /** + * Alternative interfaces that should actually be registered for dynamic proxy generation instead of the current interface. + * This allows for interfaces in 3rd party libraries to be registered without modification or writing an + * extension. If this is set then the interface it is placed on is not registered for dynamic proxy generation, so this + * should generally just be placed on an empty interface that is not otherwise used. + */ + Class[] targets() default {}; + + /** + * The repeatable holder for {@link RegisterForProxy}. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + @interface List { + /** + * The {@link RegisterForProxy} instances. + * + * @return the instances + */ + RegisterForProxy[] value(); + } +} \ No newline at end of file diff --git a/docs/src/main/asciidoc/writing-native-applications-tips.adoc b/docs/src/main/asciidoc/writing-native-applications-tips.adoc index c7b6f86c8419a..259f74ded76c2 100644 --- a/docs/src/main/asciidoc/writing-native-applications-tips.adoc +++ b/docs/src/main/asciidoc/writing-native-applications-tips.adoc @@ -234,6 +234,33 @@ The final order of business is to make the configuration file known to the `nati To do that, place the configuration file under the `src/main/resources/META-INF/native-image//` folder. This way they will be automatically parsed by the native build, without additional configuration. +=== Registering for proxy + +Analogous to `@RegisterForReflection`, you can use `@RegisterForProxy` to register interfaces for dynamic proxy: + +[source,java] +---- +@RegisterForProxy +public interface MyInterface extends MySecondInterface { +} +---- + +Note that `MyInterface` and all its super interfaces will be registered. + +Also, in case the interface is in a third-party jar, you can do it by using an empty class that will host the `@RegisterForProxy` for it. + +[source,java] +---- +@RegisterForProxy(targets={MyInterface.class, MySecondInterface.class}) +public class MyReflectionConfiguration { +} +---- + +[WARNING] +==== +Note that the order of the specified proxy interfaces is significant. For more information, see link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/reflect/Proxy.html[Proxy javadoc]. +==== + [[delay-class-init-in-your-app]] === Delaying class initialization