Skip to content

Commit b1b5f41

Browse files
committed
Change BeanPostProcessor to InstantiationAwareBeanPostProcessor in GrpcClientBeanPostProcessor
1 parent d15b3e9 commit b1b5f41

File tree

1 file changed

+148
-15
lines changed

1 file changed

+148
-15
lines changed

grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/inject/GrpcClientBeanPostProcessor.java

Lines changed: 148 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,27 +19,46 @@
1919

2020
import static java.util.Objects.requireNonNull;
2121

22+
import java.beans.PropertyDescriptor;
23+
import java.lang.annotation.Annotation;
24+
import java.lang.reflect.AccessibleObject;
2225
import java.lang.reflect.Field;
2326
import java.lang.reflect.Member;
2427
import java.lang.reflect.Method;
28+
import java.lang.reflect.Modifier;
2529
import java.util.ArrayList;
2630
import java.util.Collection;
31+
import java.util.LinkedHashSet;
2732
import java.util.List;
2833

34+
import java.util.Map;
35+
import java.util.Set;
36+
import java.util.concurrent.ConcurrentHashMap;
2937
import javax.annotation.PostConstruct;
3038

3139
import org.springframework.beans.BeanInstantiationException;
40+
import org.springframework.beans.BeanUtils;
3241
import org.springframework.beans.BeansException;
3342
import org.springframework.beans.InvalidPropertyException;
43+
import org.springframework.beans.PropertyValues;
3444
import org.springframework.beans.factory.BeanCreationException;
3545
import org.springframework.beans.factory.BeanDefinitionStoreException;
3646
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
47+
import org.springframework.beans.factory.annotation.InjectionMetadata;
3748
import org.springframework.beans.factory.config.BeanPostProcessor;
3849
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
50+
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
51+
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
52+
import org.springframework.beans.factory.support.RootBeanDefinition;
3953
import org.springframework.context.ApplicationContext;
4054
import org.springframework.context.ConfigurableApplicationContext;
4155
import org.springframework.context.annotation.Configuration;
56+
import org.springframework.core.BridgeMethodResolver;
4257
import org.springframework.core.annotation.AnnotationUtils;
58+
import org.springframework.core.annotation.MergedAnnotation;
59+
import org.springframework.core.annotation.MergedAnnotations;
60+
import org.springframework.lang.Nullable;
61+
import org.springframework.util.ClassUtils;
4362
import org.springframework.util.ReflectionUtils;
4463

4564
import com.google.common.collect.Lists;
@@ -51,6 +70,7 @@
5170
import net.devh.boot.grpc.client.nameresolver.NameResolverRegistration;
5271
import net.devh.boot.grpc.client.stubfactory.FallbackStubFactory;
5372
import net.devh.boot.grpc.client.stubfactory.StubFactory;
73+
import org.springframework.util.StringUtils;
5474

5575
/**
5676
* This {@link BeanPostProcessor} searches for fields and methods in beans that are annotated with {@link GrpcClient}
@@ -59,7 +79,7 @@
5979
* @author Michael ([email protected])
6080
* @author Daniel Theuke ([email protected])
6181
*/
62-
public class GrpcClientBeanPostProcessor implements BeanPostProcessor {
82+
public class GrpcClientBeanPostProcessor implements InstantiationAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor {
6383

6484
private final ApplicationContext applicationContext;
6585

@@ -72,14 +92,20 @@ public class GrpcClientBeanPostProcessor implements BeanPostProcessor {
7292
// For bean registration via @GrpcClientBean
7393
private ConfigurableListableBeanFactory configurableBeanFactory;
7494

95+
private final Set<Class<? extends Annotation>> grpcClientAnnotationTypes = new LinkedHashSet<>(4);
96+
97+
private final Map<String, InjectionMetadata> injectionMetadataCache = new ConcurrentHashMap<>(256);
98+
7599
/**
76-
* Creates a new GrpcClientBeanPostProcessor with the given ApplicationContext.
100+
* Creates a new GrpcClientBeanPostProcessor with the given ApplicationContext
101+
* for GrpcClient standard {@link GrpcClient @GrpcClient} annotation.
77102
*
78103
* @param applicationContext The application context that will be used to get lazy access to the
79104
* {@link GrpcChannelFactory} and {@link StubTransformer}s.
80105
*/
81106
public GrpcClientBeanPostProcessor(final ApplicationContext applicationContext) {
82107
this.applicationContext = requireNonNull(applicationContext, "applicationContext");
108+
this.grpcClientAnnotationTypes.add(GrpcClient.class);
83109
}
84110

85111
@PostConstruct
@@ -119,19 +145,18 @@ private void initGrpClientConstructorInjections() {
119145
}
120146

121147
@Override
122-
public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
123-
Class<?> clazz = bean.getClass();
124-
do {
125-
processFields(clazz, bean);
126-
processMethods(clazz, bean);
127-
128-
if (isAnnotatedWithConfiguration(clazz)) {
129-
processGrpcClientBeansAnnotations(clazz);
130-
}
131-
132-
clazz = clazz.getSuperclass();
133-
} while (clazz != null);
134-
return bean;
148+
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
149+
InjectionMetadata metadata = findGrpcClientMetadata(beanName, bean.getClass(), pvs);
150+
try {
151+
metadata.inject(bean, beanName, pvs);
152+
}
153+
catch (BeanCreationException ex) {
154+
throw ex;
155+
}
156+
catch (Throwable ex) {
157+
throw new BeanCreationException(beanName, "Injection of gRPC client stub failed", ex);
158+
}
159+
return pvs;
135160
}
136161

137162
/**
@@ -400,4 +425,112 @@ private boolean isAnnotatedWithConfiguration(final Class<?> clazz) {
400425
return configurationAnnotation != null;
401426
}
402427

428+
@Override
429+
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
430+
InjectionMetadata metadata = findGrpcClientMetadata(beanName, beanType, null);
431+
metadata.checkConfigMembers(beanDefinition);
432+
}
433+
434+
private InjectionMetadata findGrpcClientMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
435+
// Fall back to class name as cache key, for backwards compatibility with custom callers.
436+
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
437+
// Quick check on the concurrent map first, with minimal locking.
438+
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
439+
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
440+
synchronized (this.injectionMetadataCache) {
441+
metadata = this.injectionMetadataCache.get(cacheKey);
442+
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
443+
if (metadata != null) {
444+
metadata.clear(pvs);
445+
}
446+
metadata = buildGrpcClientMetadata(clazz);
447+
this.injectionMetadataCache.put(cacheKey, metadata);
448+
}
449+
}
450+
}
451+
return metadata;
452+
}
453+
454+
private InjectionMetadata buildGrpcClientMetadata(Class<?> clazz) {
455+
if (!AnnotationUtils.isCandidateClass(clazz, this.grpcClientAnnotationTypes)) {
456+
return InjectionMetadata.EMPTY;
457+
}
458+
459+
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
460+
Class<?> targetClass = clazz;
461+
462+
do {
463+
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
464+
465+
ReflectionUtils.doWithLocalFields(targetClass, field -> {
466+
MergedAnnotation<?> ann = findGrpcClientAnnotation(field);
467+
if (ann != null) {
468+
if (Modifier.isStatic(field.getModifiers())) {
469+
throw new IllegalStateException("GrpcClient annotation is not supported on static fields: " + field);
470+
}
471+
currElements.add(new GrpcClientMemberElement(field, null));
472+
}
473+
});
474+
475+
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
476+
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
477+
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
478+
return;
479+
}
480+
MergedAnnotation<?> ann = findGrpcClientAnnotation(bridgedMethod);
481+
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
482+
if (Modifier.isStatic(method.getModifiers())) {
483+
throw new IllegalStateException("GrpcClient annotation is not supported on static method: " + method);
484+
}
485+
if (method.getParameterCount() == 0) {
486+
throw new IllegalStateException("GrpcClient annotation should only be used on methods with parameters: " + method);
487+
}
488+
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
489+
currElements.add(new GrpcClientMemberElement(method, pd));
490+
}
491+
});
492+
493+
elements.addAll(0, currElements);
494+
targetClass = targetClass.getSuperclass();
495+
}
496+
while (targetClass != null && targetClass != Object.class);
497+
498+
return InjectionMetadata.forElements(elements, clazz);
499+
}
500+
501+
private MergedAnnotation<?> findGrpcClientAnnotation(AccessibleObject ao) {
502+
MergedAnnotations annotations = MergedAnnotations.from(ao);
503+
for (Class<? extends Annotation> type : this.grpcClientAnnotationTypes) {
504+
MergedAnnotation<?> annotation = annotations.get(type);
505+
if (annotation.isPresent()) {
506+
return annotation;
507+
}
508+
}
509+
return null;
510+
}
511+
512+
/**
513+
* Class representing injection information about an annotated member.
514+
*/
515+
private class GrpcClientMemberElement extends InjectionMetadata.InjectedElement {
516+
517+
public GrpcClientMemberElement(Member member, @Nullable PropertyDescriptor pd) {
518+
super(member, pd);
519+
}
520+
521+
@Override
522+
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
523+
Class<?> clazz = bean.getClass();
524+
do {
525+
processFields(clazz, bean);
526+
processMethods(clazz, bean);
527+
528+
if (isAnnotatedWithConfiguration(clazz)) {
529+
processGrpcClientBeansAnnotations(clazz);
530+
}
531+
532+
clazz = clazz.getSuperclass();
533+
} while (clazz != null);
534+
}
535+
}
403536
}

0 commit comments

Comments
 (0)