diff --git a/core-processor/src/main/java/io/micronaut/inject/processing/AopIntroductionProxySupportedBeanElementCreator.java b/core-processor/src/main/java/io/micronaut/inject/processing/AopIntroductionProxySupportedBeanElementCreator.java index 9a23eacfe86..7e4b4d985ab 100644 --- a/core-processor/src/main/java/io/micronaut/inject/processing/AopIntroductionProxySupportedBeanElementCreator.java +++ b/core-processor/src/main/java/io/micronaut/inject/processing/AopIntroductionProxySupportedBeanElementCreator.java @@ -43,6 +43,8 @@ protected BeanDefinitionVisitor createBeanDefinitionVisitor() { throw new ProcessingException(classElement, "Cannot apply AOP advice to final class. Class must be made non-final to support proxying: " + classElement.getName()); } aopProxyVisitor = createIntroductionAopProxyWriter(classElement, visitorContext); + aopProxyVisitor.visitTypeArguments(classElement.getAllTypeArguments()); + visitAnnotationMetadata(aopProxyVisitor, classElement.getAnnotationMetadata()); beanDefinitionWriters.add(aopProxyVisitor); MethodElement constructorElement = classElement.getPrimaryConstructor().orElse(null); if (constructorElement != null) { diff --git a/inject-java-test/src/main/groovy/io/micronaut/annotation/processing/test/AbstractTypeElementSpec.groovy b/inject-java-test/src/main/groovy/io/micronaut/annotation/processing/test/AbstractTypeElementSpec.groovy index 574da2da42f..6de6941a871 100644 --- a/inject-java-test/src/main/groovy/io/micronaut/annotation/processing/test/AbstractTypeElementSpec.groovy +++ b/inject-java-test/src/main/groovy/io/micronaut/annotation/processing/test/AbstractTypeElementSpec.groovy @@ -409,6 +409,22 @@ class Test { return (BeanDefinition)classLoader.loadClass(beanFullName).newInstance() } + /** + * Builds the bean definition for an AOP proxy bean. + * @param className The class name + * @param cls The class source + * @return The bean definition + */ + protected BeanDefinition buildSimpleInterceptedBeanDefinition(String className, @Language("java") String cls) { + def classSimpleName = NameUtils.getSimpleName(className) + def beanDefName = (classSimpleName.startsWith('$') ? '' : '$') + classSimpleName + BeanDefinitionVisitor.PROXY_SUFFIX + BeanDefinitionWriter.CLASS_SUFFIX + def packageName = NameUtils.getPackageName(className) + String beanFullName = "${packageName}.${beanDefName}" + + ClassLoader classLoader = buildClassLoader(className, cls) + return (BeanDefinition)classLoader.loadClass(beanFullName).newInstance() + } + /** * Retrieve additional annotation mappers to apply * @param annotationName The annotation name diff --git a/inject-java/src/test/groovy/io/micronaut/inject/beans/BeanDefinitionSpec.groovy b/inject-java/src/test/groovy/io/micronaut/inject/beans/BeanDefinitionSpec.groovy index 41177383ee3..f2973776b1b 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/beans/BeanDefinitionSpec.groovy +++ b/inject-java/src/test/groovy/io/micronaut/inject/beans/BeanDefinitionSpec.groovy @@ -4,6 +4,7 @@ import io.micronaut.annotation.processing.test.AbstractTypeElementSpec import io.micronaut.context.ApplicationContext import io.micronaut.core.annotation.AnnotationUtil import io.micronaut.core.annotation.Order +import io.micronaut.core.reflect.ClassUtils import io.micronaut.core.type.Argument import io.micronaut.core.type.GenericPlaceholder import io.micronaut.inject.BeanDefinition @@ -589,6 +590,84 @@ interface Deserializer { !(deserializerTypeParam instanceof GenericPlaceholder) } + void "test intercepted type arguments"() { + given: + BeanDefinition definition = buildSimpleInterceptedBeanDefinition('test.AImplementationLong', ''' +package test; + +import io.micronaut.aop.Introduction; +import io.micronaut.context.annotation.DefaultScope; +import io.micronaut.context.annotation.Prototype; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import io.micronaut.context.annotation.Requires; +import io.micronaut.context.annotation.Secondary; + +@MyIntroductionBean +@Secondary +class AImplementationLong implements AInterface { + + private Long l1,l2; + + @Override + public void set(Long s, Long s2) { + l1=s; + l2=s2; + } + + @Override + public String get(Long s, Long s2) { + return s.getClass().getName() +","+s.getClass().getName(); + } +} + +@MyIntroductionBean +@Secondary +class AImplementationString implements AInterface { + + private String s1,s2; + + @Override + public void set(String s, String s2) { + s1=s; + this.s2=s2; + } + + @Override + public String get(String s, String s2) { + return s.getClass().getName() +","+s.getClass().getName(); + } +} + +interface AInterface { + + void set(K k, V v); + + String get(K k, V v); +} + +@Introduction +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +@Documented +@DefaultScope(Prototype.class) +@interface MyIntroductionBean { +} + +''') + + when: + def arguments = definition.getTypeArguments(ClassUtils.forName("test.AInterface", definition.getClass().classLoader).orElseThrow()) + then: + arguments[0].type == Long + arguments[1].type == Long + } + void "test repeatable inner type annotation 1"() { when: def ctx = ApplicationContext.builder().properties(["repeatabletest": "true"]).build().start() diff --git a/inject-java/src/test/groovy/io/micronaut/inject/beans/intro/AImplementationLong.java b/inject-java/src/test/groovy/io/micronaut/inject/beans/intro/AImplementationLong.java new file mode 100644 index 00000000000..58bca5cd7ea --- /dev/null +++ b/inject-java/src/test/groovy/io/micronaut/inject/beans/intro/AImplementationLong.java @@ -0,0 +1,23 @@ +package io.micronaut.inject.beans.intro; + +import io.micronaut.context.annotation.Requires; +import io.micronaut.context.annotation.Secondary; + +@Requires(property = "spec", value = "IntroductionInjectionSpec") +@MyIntroductionBean +@Secondary +public class AImplementationLong implements AInterface { + + private Long l1,l2; + + @Override + public void set(Long s, Long s2) { + l1=s; + l2=s2; + } + + @Override + public String get(Long s, Long s2) { + return s.getClass().getName() +","+s.getClass().getName(); + } +} diff --git a/inject-java/src/test/groovy/io/micronaut/inject/beans/intro/AImplementationString.java b/inject-java/src/test/groovy/io/micronaut/inject/beans/intro/AImplementationString.java new file mode 100644 index 00000000000..06528466ea1 --- /dev/null +++ b/inject-java/src/test/groovy/io/micronaut/inject/beans/intro/AImplementationString.java @@ -0,0 +1,23 @@ +package io.micronaut.inject.beans.intro; + +import io.micronaut.context.annotation.Requires; +import io.micronaut.context.annotation.Secondary; + +@Requires(property = "spec", value = "IntroductionInjectionSpec") +@MyIntroductionBean +@Secondary +public class AImplementationString implements AInterface { + + private String s1,s2; + + @Override + public void set(String s, String s2) { + s1=s; + this.s2=s2; + } + + @Override + public String get(String s, String s2) { + return s.getClass().getName() +","+s.getClass().getName(); + } +} diff --git a/inject-java/src/test/groovy/io/micronaut/inject/beans/intro/AInterface.java b/inject-java/src/test/groovy/io/micronaut/inject/beans/intro/AInterface.java new file mode 100644 index 00000000000..0526f4b8b67 --- /dev/null +++ b/inject-java/src/test/groovy/io/micronaut/inject/beans/intro/AInterface.java @@ -0,0 +1,8 @@ +package io.micronaut.inject.beans.intro; + +public interface AInterface { + + void set(K k, V v); + + String get(K k, V v); +} diff --git a/inject-java/src/test/groovy/io/micronaut/inject/beans/intro/IntroductionInjectionSpec.groovy b/inject-java/src/test/groovy/io/micronaut/inject/beans/intro/IntroductionInjectionSpec.groovy new file mode 100644 index 00000000000..4629009f288 --- /dev/null +++ b/inject-java/src/test/groovy/io/micronaut/inject/beans/intro/IntroductionInjectionSpec.groovy @@ -0,0 +1,21 @@ +package io.micronaut.inject.beans.intro + +import io.micronaut.context.ApplicationContext +import io.micronaut.core.type.Argument +import spock.lang.Specification + +class IntroductionInjectionSpec extends Specification { + + void "test injection"() { + def ctx = ApplicationContext.run(["spec": "IntroductionInjectionSpec"]) + def cacheString = ctx.getBean(Argument.of(AInterface, String, String)) + def cacheLong = ctx.getBean(Argument.of(AInterface, Long, Long)) + + expect: + cacheString.get("hola", "mundo") == "java.lang.String,java.lang.String" + cacheLong.get(1L, 2L) == "java.lang.Long,java.lang.Long" + + cleanup: + ctx.close() + } +} diff --git a/inject-java/src/test/groovy/io/micronaut/inject/beans/intro/MyIntroductionBean.java b/inject-java/src/test/groovy/io/micronaut/inject/beans/intro/MyIntroductionBean.java new file mode 100644 index 00000000000..281f70bb91a --- /dev/null +++ b/inject-java/src/test/groovy/io/micronaut/inject/beans/intro/MyIntroductionBean.java @@ -0,0 +1,34 @@ +/* + * Copyright 2017-2020 original 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 io.micronaut.inject.beans.intro; + +import io.micronaut.aop.Introduction; +import io.micronaut.context.annotation.DefaultScope; +import io.micronaut.context.annotation.Prototype; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Introduction +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +@Documented +@DefaultScope(Prototype.class) +public @interface MyIntroductionBean { +}