Skip to content

Commit

Permalink
Merge pull request #32730 from mkouba/arc-interceptors-subclasses
Browse files Browse the repository at this point in the history
ArC - support multiple interceptor methods in a class hierarchy and around invoke method declared on target class
  • Loading branch information
manovotn authored Apr 20, 2023
2 parents 1739785 + 1adf1b7 commit 3fe819c
Show file tree
Hide file tree
Showing 31 changed files with 1,584 additions and 150 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ public class BeanInfo implements InjectionTargetInfo {

private final String targetPackageName;

private final List<MethodInfo> aroundInvokes;

BeanInfo(AnnotationTarget target, BeanDeployment beanDeployment, ScopeInfo scope, Set<Type> types,
Set<AnnotationInstance> qualifiers, List<Injection> injections, BeanInfo declaringBean, DisposerInfo disposer,
boolean alternative, List<StereotypeInfo> stereotypes, String name, boolean isDefaultBean, String targetPackageName,
Expand Down Expand Up @@ -143,6 +145,7 @@ public class BeanInfo implements InjectionTargetInfo {
this.lifecycleInterceptors = Collections.emptyMap();
this.forceApplicationClass = forceApplicationClass;
this.targetPackageName = targetPackageName;
this.aroundInvokes = isInterceptor() || isDecorator() ? List.of() : Beans.getAroundInvokes(implClazz, beanDeployment);
}

@Override
Expand Down Expand Up @@ -360,8 +363,10 @@ public boolean hasAroundInvokeInterceptors() {
}

boolean isSubclassRequired() {
return !interceptedMethods.isEmpty() || !decoratedMethods.isEmpty()
|| lifecycleInterceptors.containsKey(InterceptionType.PRE_DESTROY);
return !interceptedMethods.isEmpty()
|| !decoratedMethods.isEmpty()
|| lifecycleInterceptors.containsKey(InterceptionType.PRE_DESTROY)
|| !aroundInvokes.isEmpty();
}

/**
Expand Down Expand Up @@ -445,6 +450,18 @@ public List<DecoratorInfo> getBoundDecorators() {
return bound;
}

/**
*
* @return the list of around invoke interceptor methods declared in the hierarchy of a bean class
*/
List<MethodInfo> getAroundInvokes() {
return aroundInvokes;
}

boolean hasAroundInvokes() {
return !aroundInvokes.isEmpty();
}

public DisposerInfo getDisposer() {
return disposer;
}
Expand Down Expand Up @@ -602,8 +619,8 @@ private Map<MethodInfo, InterceptionInfo> initInterceptedMethods(List<Throwable>
}
}

Set<MethodInfo> finalMethods = Methods.addInterceptedMethodCandidates(beanDeployment, target.get().asClass(),
candidates, classLevelBindings, bytecodeTransformerConsumer, transformUnproxyableClasses);
Set<MethodInfo> finalMethods = Methods.addInterceptedMethodCandidates(this, candidates, classLevelBindings,
bytecodeTransformerConsumer, transformUnproxyableClasses);
if (!finalMethods.isEmpty()) {
String additionalError = "";
if (finalMethods.stream().anyMatch(KotlinUtils::isNoninterceptableKotlinMethod)) {
Expand All @@ -620,7 +637,7 @@ private Map<MethodInfo, InterceptionInfo> initInterceptedMethods(List<Throwable>
for (Entry<MethodKey, Set<AnnotationInstance>> entry : candidates.entrySet()) {
List<InterceptorInfo> interceptors = beanDeployment.getInterceptorResolver()
.resolve(InterceptionType.AROUND_INVOKE, entry.getValue());
if (!interceptors.isEmpty()) {
if (!interceptors.isEmpty() || !aroundInvokes.isEmpty()) {
interceptedMethods.put(entry.getKey().method, new InterceptionInfo(interceptors, entry.getValue()));
}
}
Expand Down Expand Up @@ -651,7 +668,8 @@ private Map<MethodInfo, DecorationInfo> initDecoratedMethods() {
ClassInfo classInfo = target.get().asClass();
addDecoratedMethods(candidates, classInfo, classInfo, bound,
new SubclassSkipPredicate(beanDeployment.getAssignabilityCheck()::isAssignableFrom,
beanDeployment.getBeanArchiveIndex(), beanDeployment.getObserverAndProducerMethods()));
beanDeployment.getBeanArchiveIndex(), beanDeployment.getObserverAndProducerMethods(),
beanDeployment.getAnnotationStore()));

Map<MethodInfo, DecorationInfo> decoratedMethods = new HashMap<>(candidates.size());
for (Entry<MethodKey, DecorationInfo> entry : candidates.entrySet()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ public List<Resource> generateResources(ReflectionRegistration reflectionRegistr
}

SubclassGenerator subclassGenerator = new SubclassGenerator(annotationLiterals, applicationClassPredicate,
generateSources, refReg, existingClasses);
generateSources, refReg, existingClasses, privateMembers);

ObserverGenerator observerGenerator = new ObserverGenerator(annotationLiterals, applicationClassPredicate,
privateMembers, generateSources, refReg, existingClasses, observerToGeneratedName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,35 @@ static List<MethodInfo> getCallbacks(ClassInfo beanClass, DotName annotation, In
return callbacks;
}

static List<MethodInfo> getAroundInvokes(ClassInfo beanClass, BeanDeployment deployment) {
AnnotationStore store = deployment.getAnnotationStore();
List<MethodInfo> methods = new ArrayList<>();

List<MethodInfo> allMethods = new ArrayList<>();
ClassInfo aClass = beanClass;
while (aClass != null) {
int aroundInvokesFound = 0;
for (MethodInfo method : aClass.methods()) {
if (Modifier.isStatic(method.flags())) {
continue;
}
if (store.hasAnnotation(method, DotNames.AROUND_INVOKE)) {
InterceptorInfo.addInterceptorMethod(allMethods, methods, method);
if (++aroundInvokesFound > 1) {
throw new DefinitionException(
"Multiple @AroundInvoke interceptor methods declared on class: " + aClass);
}
}
allMethods.add(method);
}
DotName superTypeName = aClass.superName();
aClass = superTypeName == null || DotNames.OBJECT.equals(superTypeName) ? null
: getClassByName(deployment.getBeanArchiveIndex(), superTypeName);
}
Collections.reverse(methods);
return methods.isEmpty() ? List.of() : List.copyOf(methods);
}

static void analyzeType(Type type, BeanDeployment beanDeployment) {
if (type.kind() == Type.Kind.PARAMETERIZED_TYPE) {
for (Type argument : type.asParameterizedType().arguments()) {
Expand Down
Loading

0 comments on commit 3fe819c

Please sign in to comment.