This is small sample from bigger project, trying to replicate issue that we see.
Implementation:
@Component
public class TestService {
public void test(String target, String key, Object message) {
System.out.println("Wrong method");
}
public void test(String target, Object message, SomeProcessor processor) {
System.out.println("Correct method");
}
}
Wrapper:
@Aspect
@Component
public class TestServiceAspect {
@Around(value = "execution(* com.example.TestService.test(..))"
" && args(target, key, message)",
argNames = "pjp,target,key,message"))
public Object traceTest(ProceedingJoinPoint pjp,
String target, String key, Object message) throws Throwable {
System.out.println("Wrong wrapper");
return pjp.proceed();
}
@Around(value = "execution(* com.example.TestService.test(..))"
" && args(target, message, processor)",
argNames = "pjp,target,message,processor")
public Object traceTest(ProceedingJoinPoint pjp,
String target, Object message, SomeProcessor processor) throws Throwable {
System.out.println("Correct wrapper");
return pjp.proceed();
}
}
Calling code:
@RestController
public class Tester {
private final TestService tst;
public Tester(TestService tst) {
this.tst = tst;
}
@RequestMapping(value = "/test", method = RequestMethod.POST)
public void testHandler() {
System.out.println("START");
tst.test("foo", (Object)"hello", new SomeProcessorImpl());
System.out.println("END");
}
}
We get following output:
START
Wrong wrapper
Correct wrapper
Correct method
END
Why is it OK for second pointcut ("Wrong wrapper") to match (is it correct at all)? I know why it happens, because they internally check if type can be coerced into expected type. Based on runtime information, framework will detect that passed object ("hello") can be cast into String and SomeProcessor can be converted to Object. But does it still make right thing to do?
CodePudding user response:
Just make your pointcuts more precise, not just the bound parameter types. Binding parameters is only necessary, if you actually want to use them in the advice methods.
Here is an MCVE for native AspectJ, which should work the same way in Spring AOP. I was just lazy to set up a Spring project with @Component
and @Service
stuff.
package de.scrum_master.app;
public interface SomeProcessor {}
package de.scrum_master.app;
public class SomeProcessorImpl implements SomeProcessor {}
package de.scrum_master.app;
public class TestService {
public void test(String target, String key, Object message) {
System.out.println("Wrong method");
}
public void test(String target, Object message, SomeProcessor processor) {
System.out.println("Correct method");
}
}
package de.scrum_master.app;
public class Tester {
private final TestService tst;
public Tester(TestService tst) {
this.tst = tst;
}
public void testHandler() {
System.out.println("START");
tst.test("foo", (Object) "hello", new SomeProcessorImpl());
// tst.test("foo", "hello", (Object) new SomeProcessorImpl());
System.out.println("END");
}
public static void main(String[] args) {
new Tester(new TestService()).testHandler();
}
}
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import de.scrum_master.app.SomeProcessor;
@Aspect
public class TestServiceAspect {
@Around("execution(* de.scrum_master.app.TestService.test(String, String, Object)) && args(target, key, message)")
public Object traceTest(ProceedingJoinPoint pjp, String target, String key, Object message) throws Throwable {
System.out.println(pjp " -> wrong wrapper");
return pjp.proceed();
}
@Around("execution(* de.scrum_master.app.TestService.test(String, Object, SomeProcessor)) && args(target, message, processor)")
public Object traceTest(ProceedingJoinPoint pjp, String target, Object message, SomeProcessor processor)
throws Throwable {
System.out.println(pjp " -> correct wrapper");
return pjp.proceed();
}
}
Console output when running the driver application:
START
execution(void de.scrum_master.app.TestService.test(String, Object, SomeProcessor)) -> correct wrapper
Correct method
END