Skip to content

Commit

Permalink
Interceptor should intercept non-abstract methods for abstract classes
Browse files Browse the repository at this point in the history
  • Loading branch information
dstepanov committed May 24, 2024
1 parent e6c018e commit a432689
Show file tree
Hide file tree
Showing 10 changed files with 322 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -109,20 +109,20 @@ public static AnnotationMetadata getElementAnnotationMetadata(MemberElement memb
return memberElement.getAnnotationMetadata();
}

protected boolean visitIntrospectedMethod(BeanDefinitionVisitor visitor, ClassElement typeElement, MethodElement methodElement) {
protected boolean visitIntrospectedMethod(BeanDefinitionVisitor visitor, ClassElement classElement, MethodElement methodElement) {
AopProxyWriter aopProxyWriter = (AopProxyWriter) visitor;

final AnnotationMetadata resolvedTypeMetadata = typeElement.getAnnotationMetadata();
final AnnotationMetadata resolvedTypeMetadata = classElement.getAnnotationMetadata();
final boolean resolvedTypeMetadataIsAopProxyType = InterceptedMethodUtil.hasDeclaredAroundAdvice(resolvedTypeMetadata);

if (methodElement.isAbstract()
|| resolvedTypeMetadataIsAopProxyType
|| InterceptedMethodUtil.hasDeclaredAroundAdvice(methodElement.getAnnotationMetadata())) {
addToIntroduction(aopProxyWriter, typeElement, methodElement, false);
addToIntroduction(aopProxyWriter, classElement, methodElement, false);
return true;
} else if (!methodElement.isAbstract() && methodElement.hasDeclaredStereotype(Executable.class)) {
} else if (methodElement.hasDeclaredStereotype(Executable.class)) {
aopProxyWriter.visitExecutableMethod(
typeElement,
classElement,
methodElement,
visitorContext
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,26 +69,30 @@ protected AopProxyWriter getAroundAopProxyVisitor(BeanDefinitionVisitor visitor,

@Override
protected boolean visitPropertyReadElement(BeanDefinitionVisitor visitor, PropertyElement propertyElement, MethodElement readElement) {
if (readElement.isAbstract() && visitIntrospectedMethod(visitor, classElement, readElement)) {
if (intercept(visitor, readElement)) {
return true;
}
return super.visitPropertyReadElement(visitor, propertyElement, readElement);
}

@Override
protected boolean visitPropertyWriteElement(BeanDefinitionVisitor visitor, PropertyElement propertyElement, MethodElement writeElement) {
if (writeElement.isAbstract() && visitIntrospectedMethod(visitor, classElement, writeElement)) {
if (intercept(visitor, writeElement)) {
return true;
}
return super.visitPropertyWriteElement(visitor, propertyElement, writeElement);
}

@Override
protected boolean visitMethod(BeanDefinitionVisitor visitor, MethodElement methodElement) {
if (methodElement.isAbstract() && visitIntrospectedMethod(visitor, classElement, methodElement)) {
if (intercept(visitor, methodElement)) {
return true;
}
return super.visitMethod(visitor, methodElement);
}

private boolean intercept(BeanDefinitionVisitor visitor, MethodElement methodElement) {
return !methodElement.isFinal() && visitIntrospectedMethod(visitor, classElement, methodElement);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright 2017-2019 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
*
* 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 io.micronaut.aop.introduction

import io.micronaut.annotation.processing.test.AbstractTypeElementSpec

class MyAbstractRepoSpec extends AbstractTypeElementSpec {

void "test abstract interceptor method"() {
given:
def context = buildContext("""
package test;
import io.micronaut.aop.introduction.Tx;
import io.micronaut.aop.introduction.RepoDef;
import io.micronaut.aop.introduction.DeleteByIdCrudRepo;
import io.micronaut.context.annotation.Executable;
@Tx
@RepoDef
abstract class MyAbstractRepo4 implements DeleteByIdCrudRepo<Integer> {
public String findById(Integer id) {
return "ABC";
}
}
""")

when:
def beanDef1 = context.getBeanDefinition(context.classLoader.loadClass("test.MyAbstractRepo4"))
def findById = beanDef1.getRequiredMethod("findById", Integer)
then:
findById

cleanup:
context.close()
}

void "test default interceptor method"() {
given:
def context = buildContext("""
package test;
import io.micronaut.aop.introduction.Tx;
import io.micronaut.aop.introduction.RepoDef;
import io.micronaut.aop.introduction.DeleteByIdCrudRepo;
@Tx
@RepoDef
interface MyDefaultRepo extends DeleteByIdCrudRepo<Integer> {
default String findById(Integer id) {
return "ABC";
}
}
""")

when:
def beanDef1 = context.getBeanDefinition(context.classLoader.loadClass("test.MyDefaultRepo"))
def findById = beanDef1.getRequiredMethod("findById", Integer)
then:
findById

cleanup:
context.close()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* 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.aop.introduction;

import jakarta.validation.constraints.NotNull;

@Tx
@RepoDef
public interface MyRepo3 extends DeleteByIdCrudRepo<Integer> {

@Override
void deleteById(@NotNull Integer id);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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.aop.introduction;

@Tx
@RepoDef
public abstract class MyRepo4 implements DeleteByIdCrudRepo<Integer> {

public String findById(Integer id) {
return "ABC";
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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.aop.introduction;

@Tx
@RepoDef
public interface MyRepo5 extends DeleteByIdCrudRepo<Integer> {

default String findById(Integer id) {
return "ABC";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,49 @@ class MyRepoIntroductionSpec extends Specification {
MyRepoIntroducer.EXECUTED_METHODS.clear()
}

void "test tx interface repo methods"() {
when:
def bean = applicationContext.getBean(MyRepo3)
bean.deleteById(1)
then:
MyRepoIntroducer.EXECUTED_METHODS.size() == 1
MyRepoIntroducer.EXECUTED_METHODS.clear()
TxInterceptor.EXECUTED_METHODS.size() == 1
TxInterceptor.EXECUTED_METHODS.clear()
}

void "test tx abstract repo methods"() {
given:
def bean = applicationContext.getBean(MyRepo4)
when:
bean.deleteById(1)
then:
MyRepoIntroducer.EXECUTED_METHODS.size() == 1
MyRepoIntroducer.EXECUTED_METHODS.clear()
TxInterceptor.EXECUTED_METHODS.size() == 1
TxInterceptor.EXECUTED_METHODS.clear()
when:
bean.findById(1)
then:
TxInterceptor.EXECUTED_METHODS.size() == 1
TxInterceptor.EXECUTED_METHODS.clear()
}

void "test tx default repo methods"() {
given:
def bean = applicationContext.getBean(MyRepo5)
when:
bean.deleteById(1)
then:
MyRepoIntroducer.EXECUTED_METHODS.size() == 1
MyRepoIntroducer.EXECUTED_METHODS.clear()
TxInterceptor.EXECUTED_METHODS.size() == 1
TxInterceptor.EXECUTED_METHODS.clear()
when:
bean.findById(1)
then:
TxInterceptor.EXECUTED_METHODS.size() == 1
TxInterceptor.EXECUTED_METHODS.clear()
}

}
38 changes: 38 additions & 0 deletions inject-java/src/test/groovy/io/micronaut/aop/introduction/Tx.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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.aop.introduction;

// tag::imports[]

import io.micronaut.aop.Around;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
// end::imports[]

// tag::annotation[]
@Documented
@Retention(RUNTIME) // <1>
@Target({TYPE, METHOD}) // <2>
@Around // <3>
public @interface Tx {
}
// end::annotation[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* 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.aop.introduction;


import io.micronaut.aop.InterceptedMethod;
import io.micronaut.aop.InterceptorBean;
import io.micronaut.aop.MethodInterceptor;
import io.micronaut.aop.MethodInvocationContext;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.convert.ConversionService;
import jakarta.inject.Singleton;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

@Singleton
@InterceptorBean(Tx.class)
public class TxInterceptor implements MethodInterceptor<Object, Object> {

public static final List<Method> EXECUTED_METHODS = new ArrayList<>();

private final ConversionService conversionService;

public TxInterceptor(ConversionService conversionService) {
this.conversionService = conversionService;
}

@Override
public int getOrder() {
return 0;
}

@Nullable
@Override
public Object intercept(MethodInvocationContext<Object, Object> context) {
EXECUTED_METHODS.add(context.getExecutableMethod().getTargetMethod());
InterceptedMethod interceptedMethod = InterceptedMethod.of(context, conversionService);
try {
return interceptedMethod.handleResult(
interceptedMethod.interceptResult()
);
} catch (Exception e) {
return interceptedMethod.handleException(e);
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ abstract class AbstractClass : AbstractSuperClass() {

abstract fun test(name: String): String

fun nonAbstract(name: String): String {
open fun nonAbstract(name: String): String {
return test(name)
}
}

0 comments on commit a432689

Please sign in to comment.