Spring aspect call on custom annotation on interface method - java

I have this interface:
public interface FakeTemplate {
#CustomAnnotation
void foo() {
}
}
And this implementation of the interface:
#Component
public FakeImpl implements FakeTemplate {
#Override
public void foo() {
//Do Stuff
}
}
And this aspect:
#Aspect
#Component
public class CustomAspect {
#Before(value = "#annotation(com.fake.CustomAnnotation)")
public void doStuffBefore(JoinPoint joinPoint} {
}
}
I'm using spring with AspectJ enabled using: #EnableAspectJAutoProxy(proxyTargetClass = true)
My issue is that the aspect doStuffBefore method is not being called before the execution of FakeImpl's foo() method. It Does work when I put the #CustomAnnotation on FakeImpl instead of FakeTemplate, but I'd much prefer to put the annotation on FakeTemplate as it's in a separate API package and I've kind of delegated it as the place where I put all my annotations.
I'd also like to ensure that the CustomAnnotation is called on every class that implements FakeTemplate without remembering to put the annotation on all the implementation classes themselves.
Is there any way to get the advice to be called if the annotation is only on the interface class?

Annotation inheritance doesn't work for methods in java.
But you can use other pointcut expression, something like
execution(public * FakeTemplate+.foo(..))

There is sort of a workaround. In your example, you can add an extra 'default' method e.g. named fooFacade to your FakeTemplate interface, annotate that with your #CustomAnnotation, and then delegate to the 'real' foo method:
public interface FakeTemplate {
#CustomAnnotation
default void fooFacade() {
foo();
}
void foo();
}
Now whenever you call fooFacade(), execution of the pointcut on #CustomAnnotation will be triggered.

Related

Is it possible to make a JUnit5 Extension implement an interface that is fulfilled by the extended class?

I would like to write a JUnit5 Extension that extends my test class,
#ExtendWith(MyExtension.class)
public class MyTestClass {
#Test myTest1() {}
#Test myTest2() {}
// ...
}
However, my test class also implements a certain interface, so it looks more like this:
public interface SomeInterface {
SomeClient getSomeClient();
SomeClient getSomeClientAsAdministrator();
}
#ExtendWith(MyExtension.class)
public class MyTestClass implements SomeInterface {
#Test myTest1() {}
#Test myTest2() {}
// ...
SomeClient getSomeClient() {
// ...
}
SomeClient getSomeClientAsAdministrator() {
// ...
}
}
No mysteries so far.
But now, I want those interface implementations to be available to the extension as well, e.g.
public class MyExtension implements BeforeEachCallback, SomeInterface
{
#Override
public void beforeAll(ExtensionContext extensionContext) {
// be able to use getSomeClient();
}
}
How can I set up my classes to achieve this? (Or, what is the inherent flaw or code smell against doing this?)
You need to use the #RegisterExtension annotation which allows you to construct your extension instance manually.
When an extension is registered declaratively via #ExtendWith, it can
typically only be configured via annotations. In contrast, when an
extension is registered via #RegisterExtension, it can be configured
programmatically — for example, in order to pass arguments to the
extension’s constructor, a static factory method, or a builder API.
It sounds like SomeClient is provided from elsewhere (a DI like Spring perhaps) but you need it in MyExtension. Assuming this scenario, you can start with something like:
#ExtendWith(SpringExtension.class)
public class MyTestClass {
#Autowired SomeClient someClient;
#RegisterExtension
MyExtension myExtension = new MyExtension(someClient);
}
One way to achieve that is to use getTestInstance() on the context object:
public class MyExtension implements BeforeEachCallback {
#Override
public void beforeEach(ExtensionContext context) throws Exception {
context.getTestInstance().ifPresent(instance -> {
if (instance instanceof SomeInterface) {
SomeInterface some = (SomeInterface) instance;
System.out.println(some.getSomeClient());
}
});
}
}
What you can see here is two things:
There might not be a test instance object, e.g. in a BeforeAllCallback because test instances are usually created per test.
A cast is required. That means you should check if your test instance really does implement SomeInterface
Having said that, I'm not really sure why you'd want to go down that rather complicated route. What's MyExtension supposed to abstract from?

Aspect method of a parent class of a RestController does not trigger advice logic

I have some construct I'd really love to use an aspect on. I want to let my RestController inherit from a class which yields special logging methods which
log to the standard logback output
fires a http request to a service which does stuff with the log
message as well (done by the aspect)
I created an annotation with which I mark the method I want to aspect so the pointcut cant filter it. Special case is that this method is declared within the parent class of the RestController.
The aspect is not running even tho IntelliJ is marking the method as being used by the aspect, which tells me the pointcut has to be working?
Please see my code and check what I've might missed out to get it to work.
ApplicationClass
#SpringBootApplication
#ComponentScan("com.xetra.experimental")
#EnableAspectJAutoProxy(proxyTargetClass = true)
public class AopTryoutApplication {
public static void main(String[] args) {
SpringApplication.run(AopTryoutApplication.class, args);
}
}
RestController
#RestController
public class Endpoint extends SimpleLogger {
#GetMapping("/endpoint")
public void doStuff(){
log("foo");
}
}
Parent class for RestController
public class SimpleLogger implements EndpointLogger{
#AspectAnnotation
public void log(String msg) {
System.out.println(msg);
}
}
Interface for parent class (heard that aspected methods need interfaces)
public interface EndpointLogger {
void log(String msg);
}
Annotation my aspect should pointcut to
#Inherited
public #interface AspectAnnotation {
}
Spring AOP aspect
#Component
#Aspect
public class TestAspect {
#Pointcut("#annotation(com.xetra.experimental.aoptryout.AspectAnnotation)")
public void methods() {
}
#Before("methods()")
public void beforeMethodExecution(JoinPoint jp) {
System.out.println("Aspect ran!!!!");
}
}
Due to the proxy-based nature of Spring’s AOP framework, calls within the target object are by definition not intercepted.
You can find more here.
The call to log method is not intercepted since it's made from the doStuff method belonging to the same target object.
Now, any call to log method will be intercepted as long it's made externally from another object (not the same target object).
Questions
So if I use SimpleLogger as a component and not a parent class within the Endpoint it will work aye?
Yes, you are right!
Is there any way to get this to work anyway? Like using AspectJ and not Spring AOP?
You can use AspectJ's source weaving to make it work. Here, is a working example.

AOP for inner and private methods Java

I am trying to log the execution time for methods annotated with custom interface.
I am using Spring AOP.
But this does not seems to work for inner methods.
I think it is the limitation in Spring AOP
#Aspect
public class BusinessProfiler {
private static Log log = LogFactory.getLog(BusinessProfiler.class);
#Around("execution(* *(..)) && #annotation(TimeLog)")
public Object profile(ProceedingJoinPoint point) throws Throwable {
long start = System.currentTimeMillis();
Object result = point.proceed();
String format =
String.format("%s#%s: took [%s msec]", point.getTarget().getClass().getSimpleName(),
MethodSignature.class.cast(point.getSignature()).getMethod().getName(),
System.currentTimeMillis() - start);
log.info(format);
return result;
}
}
Are there any alternatives than Spring AOP
If you think about the way AOP annotations are dealt with by Spring this will be clear:
Spring takes your class and wraps it in a proxy with the extra code generated on the fly by the AOP annotation added. So only code called via the proxy (i.e from outside your class will be included).
Example
#Service
public class Foo {
public void doSomething() {
doSomethingInternal();
}
public void doSomethingInternal() {
}
}
If from another Spring bean I do this:
#Service
public class Bar {
#Autowired
private Foo foo;
public void execute() {
foo.doSomething();
}
}
Only doSomething will be called via the proxy which wraps your class, not doSomethingInternal, that will be called by your class.

How to make pointcut expressions advise methods of beans that extend classes that are defined in third-party library?

I have a Spring bean defined like this:
package org.behrang.sample;
import foo.AbstractThirdPartyClass;
#Component
public class SampleBean extends AbstractThirdPartyClass<Input, Output> {
#Override
public Optional<Output> process(Input input) {
}
}
The AbstractThirdPartyClass class is defined in a third-party library named foo.
I want to implement an advice that applies to all methods in the
org.behrang.sample package, so I have implemented something like this:
#Aspect
#Component
public class SampleAspect {
#Before("execution(public * org.behrang.sample..*.*(..))")
public void sampleBefore(JoinPoint joinPoint) {
}
}
However this is not advising SampleBean::process(Input). If I remove the extends AbstractThirdPartyClass<Input, Output>
part, then the process method is advised.
Is there an elegant way to workaround this problem? For example, I can define an interface in org.behrang.sample with one method like this:
public interface Sampler<I, O> {
public Optional<O> process(I input);
}
And make the SampleBean implement it too. But this is way too ugly and anti-DRY.
Also I have enabled AOP using #EnableAspectJAutoProxy(proxyTargetClass = true) as manu beans defined in this project are not implementing any interfaces.

AspectJ ITDs: implementing a generic interface

I want my class to implement an interface, but I want to provide the implementation of the methods using ITD in an aspect. Is this possible?
Interface:
public interface CloningService<T> {
public T clone(T object);
}
Default implementation:
public class DefaultCloningServiceImpl implements CloningService<T> {
public T clone(T object) {
// implementation of the clone method
}
}
Specific implementation:
public class PersonService implements CloningService<Person> {
// no code (!)
}
The class PersonService would declare that it implements the CloningService interface, but the actual implementation of the methods would be provided in DefaultCloningServiceImpl and an aspect would introduce them to PersonService.
I followed the example on Eclipse.com and I tried to use #DeclareParents to achieve the above functionality. However, I was getting a compiler error from AspectJ, which had to do with generics. It's as if the #DeclareParents annotation did not expect the generics to be used...
Thank you.
I'd recommend that you use code style aspectj to solve this rather than annotation style.
This could be done simply by having an aspect like this:
aspect CloningServiceAspect {
declare parents : PersonService extends DefaultCloningServiceImpl<Object>;
}
To make this more general and attached to an annotation, you can do something like this:
aspect CloningServiceAspect {
declare parents : (#CloningService *) extends DefaultCloningServiceImpl<Object>;
}
And if you wanted to package this up into a standalone jar, just make sure to add all code that you want to weave adds this jar to its aspect path (if using compile-time weaving).
I found the solution! It involves using the #DeclareMixin annotation from AspectJ to mix the default implementation of the clone() method:
#Aspect
public class CloningServiceAspect {
#DeclareMixin(value = "(#CloningService *)")
public static CloningService<?> createImplementation() {
return new DefaultCloningServiceImpl<Object>();
}
}
And then my service is annotated with #CloningService instead of implementing the interface:
#CloningService
public class PersonService {
// no code
}

Categories

Resources