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

Signed-off-by: crazyhzm <crazyhzm@gmail.com>
This commit is contained in:
huazhongming 2023-05-31 16:05:54 +08:00 committed by GitHub
parent 30daba2d74
commit 4604b8074a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 83 additions and 38 deletions

View File

@ -35,6 +35,7 @@ import org.apache.dubbo.config.spring.reference.ReferenceBeanSupport;
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;
@ -120,7 +121,7 @@ import static org.springframework.util.StringUtils.hasText;
* @since 2.5.7
*/
public class ReferenceAnnotationWithAotBeanPostProcessor extends AbstractAnnotationBeanPostProcessor
implements ApplicationContextAware, BeanRegistrationAotProcessor, BeanFactoryPostProcessor {
implements ApplicationContextAware, BeanRegistrationAotProcessor, BeanFactoryPostProcessor {
/**
* The bean name of {@link ReferenceAnnotationWithAotBeanPostProcessor}
@ -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;
@ -262,8 +263,8 @@ public class ReferenceAnnotationWithAotBeanPostProcessor extends AbstractAnnotat
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
@ -295,10 +296,10 @@ public class ReferenceAnnotationWithAotBeanPostProcessor extends AbstractAnnotat
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;
@ -352,7 +353,7 @@ public class ReferenceAnnotationWithAotBeanPostProcessor extends AbstractAnnotat
* @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);
}
@ -363,7 +364,7 @@ public class ReferenceAnnotationWithAotBeanPostProcessor extends AbstractAnnotat
*/
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
throws BeansException {
throws BeansException {
try {
AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
prepareInjection(metadata);
@ -372,7 +373,7 @@ public class ReferenceAnnotationWithAotBeanPostProcessor extends AbstractAnnotat
throw ex;
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getSimpleName()
+ " dependencies is failed", ex);
+ " dependencies is failed", ex);
}
return pvs;
}
@ -504,16 +505,16 @@ public class ReferenceAnnotationWithAotBeanPostProcessor extends AbstractAnnotat
// 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
@ -524,9 +525,9 @@ public class ReferenceAnnotationWithAotBeanPostProcessor extends AbstractAnnotat
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);
@ -664,19 +665,19 @@ public class ReferenceAnnotationWithAotBeanPostProcessor extends AbstractAnnotat
@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());
@ -690,13 +691,13 @@ public class ReferenceAnnotationWithAotBeanPostProcessor extends AbstractAnnotat
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);
@ -712,6 +713,7 @@ public class ReferenceAnnotationWithAotBeanPostProcessor extends AbstractAnnotat
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);
@ -721,14 +723,14 @@ public class ReferenceAnnotationWithAotBeanPostProcessor extends AbstractAnnotat
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,
@ -736,17 +738,17 @@ public class ReferenceAnnotationWithAotBeanPostProcessor extends AbstractAnnotat
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,
@ -774,9 +776,9 @@ public class ReferenceAnnotationWithAotBeanPostProcessor extends AbstractAnnotat
} 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();
@ -830,7 +832,7 @@ public class ReferenceAnnotationWithAotBeanPostProcessor extends AbstractAnnotat
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);
}

View File

@ -22,6 +22,7 @@ import org.apache.dubbo.config.annotation.Service;
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;
@ -86,7 +87,8 @@ public class ServiceAnnotationWithAotPostProcessor extends ServiceAnnotationPost
@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());
}
}

View File

@ -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()));
});
}
}