From 2f41ce6449c60b5a22b87b6a33784c6035499212 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Wed, 3 May 2023 11:41:16 +0200 Subject: [PATCH] Add AOT support for Qualifiers This commit handles AutowiredCandidateQualifier instances, rather than relying on qualifiers being statically defined and meta-annotated with `@Qualifier`. Closes gh-30410 --- ...BeanDefinitionPropertiesCodeGenerator.java | 25 ++++++++++- ...efinitionPropertiesCodeGeneratorTests.java | 43 ++++++++++++++++++- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java index f4dc48e2ed81..648f1d7f37fd 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 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. @@ -19,11 +19,14 @@ import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.util.ArrayDeque; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.function.BiFunction; import java.util.function.BiPredicate; import java.util.function.Function; @@ -40,6 +43,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder; import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.AutowireCandidateQualifier; import org.springframework.beans.factory.support.InstanceSupplier; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.javapoet.CodeBlock; @@ -119,6 +123,7 @@ CodeBlock generateCode(RootBeanDefinition beanDefinition) { addConstructorArgumentValues(code, beanDefinition); addPropertyValues(code, beanDefinition); addAttributes(code, beanDefinition); + addQualifiers(code, beanDefinition); return code.build(); } @@ -180,6 +185,24 @@ private void addPropertyValues(CodeBlock.Builder code, } } + private void addQualifiers(CodeBlock.Builder code, + RootBeanDefinition beanDefinition) { + + Set qualifiers = beanDefinition.getQualifiers(); + if (!qualifiers.isEmpty()) { + for (AutowireCandidateQualifier qualifier : qualifiers) { + Collection arguments = new ArrayList<>(); + arguments.add(CodeBlock.of("$S", qualifier.getTypeName())); + Object qualifierValue = qualifier.getAttribute(AutowireCandidateQualifier.VALUE_KEY); + if (qualifierValue != null) { + arguments.add(generateValue("value", qualifierValue)); + } + code.addStatement("$L.addQualifier(new $T($L))", BEAN_DEFINITION_VARIABLE, + AutowireCandidateQualifier.class, CodeBlock.join(arguments, ", ")); + } + } + } + private CodeBlock generateValue(@Nullable String name, @Nullable Object value) { try { PropertyNamesStack.push(name); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGeneratorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGeneratorTests.java index 2fb541a9696c..60093ff23061 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGeneratorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGeneratorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 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. @@ -16,10 +16,13 @@ package org.springframework.beans.factory.aot; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.BiConsumer; +import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.Supplier; @@ -36,6 +39,7 @@ import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder; import org.springframework.beans.factory.config.RuntimeBeanNameReference; import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.AutowireCandidateQualifier; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.support.ManagedMap; @@ -392,6 +396,43 @@ void attributesWhenSomeFiltered() { }); } + @Test + void qualifiersWhenQualifierHasNoValue() { + this.beanDefinition.addQualifier(new AutowireCandidateQualifier("com.example.Qualifier")); + compile((actual, compiled) -> { + assertThat(actual.getQualifiers()).singleElement().satisfies(isQualifierFor("com.example.Qualifier", null)); + assertThat(this.beanDefinition.getQualifiers()).isEqualTo(actual.getQualifiers()); + }); + } + + @Test + void qualifiersWhenQualifierHasStringValue() { + this.beanDefinition.addQualifier(new AutowireCandidateQualifier("com.example.Qualifier", "id")); + compile((actual, compiled) -> { + assertThat(actual.getQualifiers()).singleElement().satisfies(isQualifierFor("com.example.Qualifier", "id")); + assertThat(this.beanDefinition.getQualifiers()).isEqualTo(actual.getQualifiers()); + }); + } + + @Test + void qualifiersWhenMultipleQualifiers() { + this.beanDefinition.addQualifier(new AutowireCandidateQualifier("com.example.Qualifier", "id")); + this.beanDefinition.addQualifier(new AutowireCandidateQualifier("com.example.Another", ChronoUnit.SECONDS)); + compile((actual, compiled) -> { + List qualifiers = new ArrayList<>(actual.getQualifiers()); + assertThat(qualifiers.get(0)).satisfies(isQualifierFor("com.example.Qualifier", "id")); + assertThat(qualifiers.get(1)).satisfies(isQualifierFor("com.example.Another", ChronoUnit.SECONDS)); + assertThat(qualifiers).hasSize(2); + }); + } + + private Consumer isQualifierFor(String typeName, Object value) { + return qualifier -> { + assertThat(qualifier.getTypeName()).isEqualTo(typeName); + assertThat(qualifier.getAttribute(AutowireCandidateQualifier.VALUE_KEY)).isEqualTo(value); + }; + } + @Test void multipleItems() { this.beanDefinition.setPrimary(true);