Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for automatic identification of serialization classes and generation of native image configuration #12433

Merged
merged 1 commit into from
May 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.apache.dubbo.config.spring.util.SpringCompatUtils;
import org.apache.dubbo.config.spring6.beans.factory.aot.ReferencedFieldValueResolver;
import org.apache.dubbo.config.spring6.beans.factory.aot.ReferencedMethodArgumentsResolver;
import org.apache.dubbo.config.spring6.utils.AotUtils;
import org.apache.dubbo.rpc.service.Destroyable;
import org.apache.dubbo.rpc.service.EchoService;
import org.apache.dubbo.rpc.service.GenericService;
Expand Down Expand Up @@ -120,7 +121,7 @@
* @since 2.5.7
*/
public class ReferenceAnnotationWithAotBeanPostProcessor extends AbstractAnnotationBeanPostProcessor
implements ApplicationContextAware, BeanRegistrationAotProcessor, BeanFactoryPostProcessor {
implements ApplicationContextAware, BeanRegistrationAotProcessor, BeanFactoryPostProcessor {

/**
* The bean name of {@link ReferenceAnnotationWithAotBeanPostProcessor}
Expand All @@ -135,10 +136,10 @@ public class ReferenceAnnotationWithAotBeanPostProcessor extends AbstractAnnotat
private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());

private final ConcurrentMap<InjectionMetadata.InjectedElement, String> injectedFieldReferenceBeanCache =
new ConcurrentHashMap<>(CACHE_SIZE);
new ConcurrentHashMap<>(CACHE_SIZE);

private final ConcurrentMap<InjectionMetadata.InjectedElement, String> injectedMethodReferenceBeanCache =
new ConcurrentHashMap<>(CACHE_SIZE);
new ConcurrentHashMap<>(CACHE_SIZE);

private ApplicationContext applicationContext;

Expand Down Expand Up @@ -262,8 +263,8 @@ private void processReferenceAnnotatedBeanDefinition(String beanName, AnnotatedB
if (beanClass == null) {
String beanMethodSignature = factoryMethodMetadata.getDeclaringClassName() + "#" + factoryMethodMetadata.getMethodName() + "()";
throw new BeanCreationException("The ReferenceBean is missing necessary generic type, which returned by the @Bean method of Java-config class. " +
"The generic type of the returned ReferenceBean must be specified as the referenced interface type, " +
"such as ReferenceBean<DemoService>. Please check bean method: " + beanMethodSignature);
"The generic type of the returned ReferenceBean must be specified as the referenced interface type, " +
"such as ReferenceBean<DemoService>. Please check bean method: " + beanMethodSignature);
}

// get dubbo reference annotation attributes
Expand Down Expand Up @@ -295,10 +296,10 @@ private void processReferenceAnnotatedBeanDefinition(String beanName, AnnotatedB
if (!StringUtils.isEquals(interfaceName, beanClass.getName()) && beanClass != GenericService.class) {
String beanMethodSignature = factoryMethodMetadata.getDeclaringClassName() + "#" + factoryMethodMetadata.getMethodName() + "()";
throw new BeanCreationException("The 'interfaceClass' or 'interfaceName' attribute value of @DubboReference annotation " +
"is inconsistent with the generic type of the ReferenceBean returned by the bean method. " +
"The interface class of @DubboReference is: " + interfaceName + ", but return ReferenceBean<" + beanClass.getName() + ">. " +
"Please remove the 'interfaceClass' and 'interfaceName' attributes from @DubboReference annotation. " +
"Please check bean method: " + beanMethodSignature);
"is inconsistent with the generic type of the ReferenceBean returned by the bean method. " +
"The interface class of @DubboReference is: " + interfaceName + ", but return ReferenceBean<" + beanClass.getName() + ">. " +
"Please remove the 'interfaceClass' and 'interfaceName' attributes from @DubboReference annotation. " +
"Please check bean method: " + beanMethodSignature);
}

Class interfaceClass = beanClass;
Expand Down Expand Up @@ -352,7 +353,7 @@ public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, C
* @see #postProcessProperties
*/
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
return postProcessProperties(pvs, bean, beanName);
}

Expand All @@ -363,7 +364,7 @@ public PropertyValues postProcessPropertyValues(
*/
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
throws BeansException {
throws BeansException {
try {
AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
prepareInjection(metadata);
Expand All @@ -372,7 +373,7 @@ public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, Str
throw ex;
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getSimpleName()
+ " dependencies is failed", ex);
+ " dependencies is failed", ex);
}
return pvs;
}
Expand Down Expand Up @@ -504,16 +505,16 @@ public String registerReferenceBean(String propertyName, Class<?> injectedType,
// bean name from attribute 'id' or java-config bean, cannot be renamed
if (!renameable) {
throw new BeanCreationException("Already exists another bean definition with the same bean name [" + referenceBeanName + "], " +
"but cannot rename the reference bean name (specify the id attribute or java-config bean), " +
"please modify the name of one of the beans: " +
"prev: " + prevBeanDesc + ", new: " + newBeanDesc + ". " + checkLocation);
"but cannot rename the reference bean name (specify the id attribute or java-config bean), " +
"please modify the name of one of the beans: " +
"prev: " + prevBeanDesc + ", new: " + newBeanDesc + ". " + checkLocation);
}

// the prev bean type is different, rename the new reference bean
int index = 2;
String newReferenceBeanName = null;
while (newReferenceBeanName == null || beanDefinitionRegistry.containsBeanDefinition(newReferenceBeanName)
|| beanDefinitionRegistry.isAlias(newReferenceBeanName)) {
|| beanDefinitionRegistry.isAlias(newReferenceBeanName)) {
newReferenceBeanName = referenceBeanName + "#" + index;
index++;
// double check found same name and reference key
Expand All @@ -524,9 +525,9 @@ public String registerReferenceBean(String propertyName, Class<?> injectedType,
newBeanDesc = newReferenceBeanName + "[" + referenceKey + "]";

logger.warn(CONFIG_DUBBO_BEAN_INITIALIZER, "", "", "Already exists another bean definition with the same bean name [" + referenceBeanName + "], " +
"rename dubbo reference bean to [" + newReferenceBeanName + "]. " +
"It is recommended to modify the name of one of the beans to avoid injection problems. " +
"prev: " + prevBeanDesc + ", new: " + newBeanDesc + ". " + checkLocation);
"rename dubbo reference bean to [" + newReferenceBeanName + "]. " +
"It is recommended to modify the name of one of the beans to avoid injection problems. " +
"prev: " + prevBeanDesc + ", new: " + newBeanDesc + ". " + checkLocation);
referenceBeanName = newReferenceBeanName;
}
attributes.put(ReferenceAttributes.ID, referenceBeanName);
Expand Down Expand Up @@ -664,19 +665,19 @@ private static class AotContribution implements BeanRegistrationAotContribution
@Override
public void applyTo(GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) {
GeneratedClass generatedClass = generationContext.getGeneratedClasses()
.addForFeatureComponent("DubboReference", this.target, type -> {
type.addJavadoc("DubboReference for {@link $T}.", this.target);
type.addModifiers(javax.lang.model.element.Modifier.PUBLIC);
});
.addForFeatureComponent("DubboReference", this.target, type -> {
type.addJavadoc("DubboReference for {@link $T}.", this.target);
type.addModifiers(javax.lang.model.element.Modifier.PUBLIC);
});
GeneratedMethod generateMethod = generatedClass.getMethods().add("apply", method -> {
method.addJavadoc("Apply the dubbo reference.");
method.addModifiers(javax.lang.model.element.Modifier.PUBLIC,
javax.lang.model.element.Modifier.STATIC);
javax.lang.model.element.Modifier.STATIC);
method.addParameter(RegisteredBean.class, REGISTERED_BEAN_PARAMETER);
method.addParameter(this.target, INSTANCE_PARAMETER);
method.returns(this.target);
method.addCode(generateMethodCode(generatedClass.getName(),
generationContext.getRuntimeHints()));
generationContext.getRuntimeHints()));
});
beanRegistrationCode.addInstancePostProcessor(generateMethod.toMethodReference());

Expand All @@ -690,13 +691,13 @@ private CodeBlock generateMethodCode(ClassName targetClassName, RuntimeHints hin
if (!CollectionUtils.isEmpty(this.annotatedInjectionMetadata.getFieldElements())) {
for (AnnotatedInjectElement referenceElement : this.annotatedInjectionMetadata.getFieldElements()) {
code.addStatement(generateMethodStatementForElement(
targetClassName, referenceElement, hints));
targetClassName, referenceElement, hints));
}
}
if (!CollectionUtils.isEmpty(this.annotatedInjectionMetadata.getMethodElements())) {
for (AnnotatedInjectElement referenceElement : this.annotatedInjectionMetadata.getMethodElements()) {
code.addStatement(generateMethodStatementForElement(
targetClassName, referenceElement, hints));
targetClassName, referenceElement, hints));
}
}
code.addStatement("return $L", INSTANCE_PARAMETER);
Expand All @@ -712,6 +713,7 @@ private CodeBlock generateMethodStatementForElement(ClassName targetClassName,

try {
Class<?> c = referenceElement.getInjectedType();
AotUtils.registerSerializationHint(c, hints);
hints.reflection().registerType(TypeReference.of(c), MemberCategory.INVOKE_PUBLIC_METHODS);
hints.proxies().registerJdkProxy(c, EchoService.class, Destroyable.class);
hints.proxies().registerJdkProxy(c, EchoService.class, Destroyable.class, SpringProxy.class, Advised.class, DecoratingProxy.class);
Expand All @@ -721,32 +723,32 @@ private CodeBlock generateMethodStatementForElement(ClassName targetClassName,

if (member instanceof Field) {
return generateMethodStatementForField(
targetClassName, (Field) member, attributes, injectedObject, hints);
targetClassName, (Field) member, attributes, injectedObject, hints);
}
if (member instanceof Method) {
return generateMethodStatementForMethod(
targetClassName, (Method) member, attributes, injectedObject, hints);
targetClassName, (Method) member, attributes, injectedObject, hints);
}
throw new IllegalStateException(
"Unsupported member type " + member.getClass().getName());
"Unsupported member type " + member.getClass().getName());
}

private CodeBlock generateMethodStatementForField(ClassName targetClassName,
Field field, AnnotationAttributes attributes, Object injectedObject, RuntimeHints hints) {

hints.reflection().registerField(field);
CodeBlock resolver = CodeBlock.of("$T.$L($S)",
ReferencedFieldValueResolver.class,
"forRequiredField", field.getName());
ReferencedFieldValueResolver.class,
"forRequiredField", field.getName());
CodeBlock shortcutResolver = CodeBlock.of("$L.withShortcut($S)", resolver, injectedObject);
AccessControl accessControl = AccessControl.forMember(field);

if (!accessControl.isAccessibleFrom(targetClassName)) {
return CodeBlock.of("$L.resolveAndSet($L, $L)", shortcutResolver,
REGISTERED_BEAN_PARAMETER, INSTANCE_PARAMETER);
REGISTERED_BEAN_PARAMETER, INSTANCE_PARAMETER);
}
return CodeBlock.of("$L.$L = $L.resolve($L)", INSTANCE_PARAMETER,
field.getName(), shortcutResolver, REGISTERED_BEAN_PARAMETER);
field.getName(), shortcutResolver, REGISTERED_BEAN_PARAMETER);
}

private CodeBlock generateMethodStatementForMethod(ClassName targetClassName,
Expand Down Expand Up @@ -774,9 +776,9 @@ private CodeBlock generateMethodStatementForMethod(ClassName targetClassName,
} else {
hints.reflection().registerMethod(method, ExecutableMode.INTROSPECT);
CodeBlock arguments = new AutowiredArgumentsCodeGenerator(this.target,
method).generateCode(method.getParameterTypes());
method).generateCode(method.getParameterTypes());
CodeBlock injectionCode = CodeBlock.of("args -> $L.$L($L)",
INSTANCE_PARAMETER, method.getName(), arguments);
INSTANCE_PARAMETER, method.getName(), arguments);
code.add(".resolve($L, $L)", REGISTERED_BEAN_PARAMETER, injectionCode);
}
return code.build();
Expand Down Expand Up @@ -830,7 +832,7 @@ private void registerHints(RuntimeHints runtimeHints) {
private void registerProxyIfNecessary(RuntimeHints runtimeHints, DependencyDescriptor dependencyDescriptor) {
if (this.candidateResolver != null) {
Class<?> proxyClass =
this.candidateResolver.getLazyResolutionProxyClass(dependencyDescriptor, null);
this.candidateResolver.getLazyResolutionProxyClass(dependencyDescriptor, null);
if (proxyClass != null) {
ClassHintUtils.registerProxyIfNecessary(proxyClass, runtimeHints);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.apache.dubbo.config.spring.ServiceBean;
import org.apache.dubbo.config.spring.beans.factory.annotation.ServiceAnnotationPostProcessor;
import org.apache.dubbo.config.spring.schema.AnnotationBeanDefinitionParser;
import org.apache.dubbo.config.spring6.utils.AotUtils;
import org.springframework.aot.generate.GenerationContext;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.TypeReference;
Expand Down Expand Up @@ -86,7 +87,8 @@ public DubboServiceBeanRegistrationAotContribution(Class<?> cl) {
@Override
public void applyTo(GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) {
generationContext.getRuntimeHints().reflection().registerType(TypeReference.of(cl),
MemberCategory.INVOKE_PUBLIC_METHODS);
MemberCategory.INVOKE_PUBLIC_METHODS);
AotUtils.registerSerializationHint(cl, generationContext.getRuntimeHints());

}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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
*
* http://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.apache.dubbo.config.spring6.utils;

import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.TypeReference;

import java.util.Arrays;


public class AotUtils {

private AotUtils(){

}

public static void registerSerializationHint(Class<?> injectType, RuntimeHints hints) {
Arrays.stream(injectType.getMethods()).forEach((method) -> {
Arrays.stream(method.getParameterTypes()).forEach((cl) -> {
hints.serialization().registerType(TypeReference.of(cl));
});

hints.serialization().registerType(TypeReference.of(method.getReturnType()));
});
}
}