Spring AOP with aspectj #annotation [duplicate] - model-view-controller

This question already has an answer here:
Emulate annotation inheritance for interfaces and methods with AspectJ
(1 answer)
Closed 1 year ago.
I want to apply annotation with aspectJ. (Use Springboot 1.5.1, Mybatis 2.1.1)
So, I made custom annotation and AspectJ.. and apply it.
/** CustomAnnotation */
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface TestAnnotation {
String value();
}
/** AspectJ configuration */
#Component
#Aspect
public class AuditTrailAspect {
#Autowired
TestDAO dao;
#Around("#annotation(TestAnnotation)")
public Object doSomethingAround(ProceedingJoinPoint joinPoint) throws Throwable {
/* before proceed */
Object result = joinPoint.proceed();
/* after proceed */
return result;
}
}
/** Apply Annoataion at Repository */
#Repository
public interface TestDAO {
#TestAnnotation(value = "test")
int insertSomething(RequestDto dto);
}
(this code made simple, for question)
if pointcut expression apply 'execution' this code works fine in Repository(DAO)..
also if pointcut expression apply '#annotation' this code works other Component(Service.. Controller)
But, Why can't I apply custom annotation in Repository(DAO) with AspectJ?
please help.. Thank you!

Annotations on implemented interfaces cannot be inherited.
#Inherited causes annotations (only on class) to be inherited from superclasses , but no effect for interface implementations.
Note that this meta-annotation type has no effect if the annotated
type is used to annotate anything other than a class. Note also that
this meta-annotation only causes annotations to be inherited from
superclasses; annotations on implemented interfaces have no effect.

Related

#Around Pointcut not getting invoked for custom annotation

The question seems to be duplicate but none of the answers from existing questions worked.
I am creating a custom annotation as below,
#Target({ ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface Translate {
}
I have created an Aspect as,
#Aspect
#Configuration
public class TranslateAspect {
#Around("#annotation(translate)")
public Object translate(ProceedingJoinPoint joinPoint, Translate translate) throws Throwable {
Object result = joinPoint.proceed();
System.out.println(result); //Other logic
return result;
}
}
I tried providing the complete class name with the package also. The entity is getting passed to RestController,
#Entity
#Getter
#Setter
public class Pojo {
#Translate
private String label;
}
But the translate method is not getting invoked whenever the new request is served.
Highly appreciate any help around this.
From the reference documentation : 5.2. Spring AOP Capabilities and Goals
Spring AOP currently supports only method execution join points (advising the execution of methods on Spring beans). Field interception is not implemented, although support for field interception could be added without breaking the core Spring AOP APIs. If you need to advise field access and update join points, consider a language such as AspectJ.
Spring AOP works with spring container managed beans and advicing method executions. The code shared here annotates a field and not the corresponding setter method. PCD defined is for the execution any Spring container managed bean method annotated with #Translate.
A class annotated with #Entity will not register its instance as a Spring managed bean. Do go through this StackOverflow Q&A
Pojo instance is not a Spring managed bean ( also pointed out by João Dias in his answer ).
Try this:
#Aspect
#Configuration
public class TranslateAspect {
#Pointcut("#annotation(com.full.packagename.to.annotation.Translate)")
public void anyTranslatableMethod() {
}
#Around("anyTranslatableMethod()")
public Object translate(ProceedingJoinPoint joinPoint) throws Throwable {
// ...
}
}
A working example here: https://github.com/asgarov1/springTricks/blob/main/aop/src/main/java/com/asgarov/aop/config/LoggingConfiguration.java
Try the following:
#Aspect
#Configuration
public class TranslateAspect {
#Around("#annotation(Translate)")
public Object translate(ProceedingJoinPoint joinPoint) throws Throwable {
Object result = joinPoint.proceed();
System.out.println(result); //Other logic
return result;
}
}
Mind the uppercase "T" in #Around("#annotation(Translate)").
UPDATE
Just noticed you are expecting the aspect to be applied to a class annotated with #Entity. These are Entities that are JPA entities but they are not Spring-managed Beans. Spring AOP only handles Spring-managed Beans so this is simply not possible.

Where to write Common code in spring boot

I want to write common code which should be execute before every method,
Where can I place this code in spring.
Thanks in advance.
What you ask is not trivial but Aspect Oriented Programming (AoP) is one way to achieve that. This description assumes that you are somewhat familiar with the Proxy class, the InvocationHandler interface and the Interceptor pattern in general. As I said, not a totally trivial matter.
Define the logic that you what to be executed before every method, or some method or whatever. Usually it is some kind of Interceptor, this is an example:
public class TimeProfilerInterceptor implements MethodInterceptor {
#Getter
private final TimeStatistics statistics = new TimeStatistics();
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
StopWatch watch = new StopWatch();
try {
watch.start();
Object returnValue = invocation.proceed();
return returnValue;
}
finally {
// etc...
}
}
}
Define a place where your logic is wired to your methods. In this example, the place is a Spring component that extends AbstractBeanFactoryAwareAdvisingPostProcessor and implements InitializingBean. The afterPropertiesSet method is called by Spring once the initialization of the bean is done. The method uses the Advice class from spring-aop to identify the pointcuts i.e. the methods that must be wrapped by the interceptor. In this case, it is an annotation based pointcut, meaning that it matches every method that has a certain custom annotation on it (TimeProfiled).
#Component
public class TimeProfiledAnnotationPostProcessor
extends AbstractBeanFactoryAwareAdvisingPostProcessor
implements InitializingBean {
#Autowired
TimeProfilerInterceptor timeProfilerInterceptor;
#Override
public void afterPropertiesSet() throws Exception {
this.setProxyTargetClass(true);
Advice advice = timeProfilerInterceptor;
Pointcut pointcut = new AnnotationMatchingPointcut(null, TimeProfiled.class);
this.advisor = new DefaultPointcutAdvisor(pointcut, advice);
}
}
Define your custom annotation to use where needed.
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface TimeProfiled {
}
Tell Spring to initiate the wrapping mechanism at startup via the following annotation upon a Spring Configuration or a SpringBootApplication:
#ComponentScan(basePackageClasses = TimeProfiledAnnotationPostProcessor.class)
#EnableAspectJAutoProxy(proxyTargetClass = true)
You can change the pointcut so that it matches other methods with other criteria, there is an entire syntax to do that, a world in itself, this is just a small example...
You should have a look at Spring AOP. With Spring AOP you can write Aspects which can be common code which is executed before/after a method. The following example is a simple Aspect:
#Aspect
public class EmployeeAspect {
#Before("execution(public String getName())")
public void getNameAdvice(){
System.out.println("Executing Advice on getName()");
}
#Before("execution(* your.package.name.*.get*())")
public void getAllAdvice(){
System.out.println("Service method getter called");
}
}
Within the #Before() annotation you can specify the exact method which is surrounded with the Aspect or you use the wildcard * to specify more methods. For this, you should be familiar with Pointcut expressions.

Can #ContextConfiguration in a custom annotation be merged?

I am working on custom Spring Boot starters. In a test starter what I wanted do to is to implement a composed annotation, which would add additional #Configuration classes to the ApplicationContext (and possibly use this annotation in a TestExecutionListener). ex:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#ContextConfiguration(classes = AdditionalTestConfiguration.class)
public #interface ComposedAnnotation {
}
And use that in my Spring Boot integration test:
#RunWith(SpringJUnit4ClassRunner.class)
#WebIntegrationTest
#SpringApplicationConfiguration(Application.class)
#ComposedAnnotation
public class SomeTest {
}
No inheritance is involved. Unfortunately, it does not seem to work. I doubt it's a Spring Boot thing, rather Spring testing framework itself.
Is there any way I can achieve expected result?
You're right: this is not an issue with Spring Boot. But it's also not an issue with spring-test.
Rather, it's the intended behavior of Spring in general. For details, check out my answer to this question: #ActiveProfiles in meta annotation and on test class not working
In summary, you cannot achieve this with two #ContextConfiguration annotations declared on an individual test class (either directly or as meta-annotations).
However, I just came up with a trick that will allow you to achieve this. Specifically, you can create an ApplicationContextInitializer (ACI) that registers one or more #Configuration classes. In your composed annotation, you can then register this ACI to register the always present #Configuration classes. And when the composed annotation is actually used, it can declare additional #Configuration classes like normal.
I just submitted a working example in this commit.
Basically, the code would look something like this:
#ContextConfiguration(loader = AnnotationConfigContextLoader.class, initializers = FooConfigInitializer.class)
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface ComposedContextConfiguration {
#AliasFor(annotation = ContextConfiguration.class, attribute = "classes")
Class<?>[] value() default {};
}
public class FooConfigInitializer implements ApplicationContextInitializer<GenericApplicationContext> {
#Override
public void initialize(GenericApplicationContext applicationContext) {
new AnnotatedBeanDefinitionReader(applicationContext).register(FooConfig.class);
}
}
And you can use it like this:
#RunWith(SpringRunner.class)
#ComposedContextConfiguration(BarConfig.class)
public class InitializerConfiguredViaMetaAnnotationTests { /* ... */ }
Your ApplicationContext will then be loaded from FooConfig and BarConfig.
The above examples obviously do not use Spring Boot, but the same principles should also be applicable to #SpringApplicationConfiguration.
Regards,
Sam (author of the Spring TestContext Framework)

Spring AspectJ Custom Annotation for Logging

I have defined a custom annotation as below.
package com.xyz;
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface Loggable {
String message() default "Log Message";
}
My aspect class contains the below method:
#Around(value = "#annotation(com.xyz.Loggable)")
public void logAround(ProceedingJoinPoint joinPoint) throws Throwable {
// come code here
}
My service interface is as below.
public interface Service {
#Loggable
public void method1();
}
My implementation is as below.
public class ServiceImpl implements Service {
public void method1() {
// some code here
}
}
With this setup, My advice is not getting triggered. (however it gets triggered if i move the #Loggable annotation to method1() in ServiceImpl class).
I would like to keep the annotation defined at interface level instead of method implementations. Is there a way to get this work ?
No, that is not possible (yet?).
Annotations can only be inherited among Classes and even then only if they are themselves annotated with the meta-Annotation #Inherited:
http://docs.oracle.com/javase/8/docs/api/java/lang/annotation/Inherited.html
It is not possible to have annotations on Interfaces be inherited to their implementing classes.
This is also explained in the AspectJ documentation: http://www.eclipse.org/aspectj/doc/released/adk15notebook/annotations.html#annotation-inheritance
#Inherited annotations are not inherited when used to annotate anything other than a type. A type that implements one or more interfaces never inherits any annotations from the interfaces it implements.

advice for annotation on either class or method

I have the following problem:
I have create annotation for Security:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.TYPE})
#Inherited
#Documented
public #interface Security {
Role[] roles();
}
I am now annotating classes and methods with this annotation. In some cases the class is annotated with this annotation and specific method within this class is also annotated with #Security with different roles.
How can create #Before advice that will catch either methods annotated with #Security ot methods within classes annotated with #Security, and also to get the more specific definition in case there are annotation on both the class and method.
I obviously need also the content of the annotation as well (the roles).
Is it possible?
Is it also possible in cases that I have inhertience between classes that all of them have the #Security annotation to get the most specific definition?
Yosi
This should work (native AspectJ code, but you can translate to #Aspect if you prefer that):
pointcut secureMethods() : execution(#Security * *(..));
pointcut secureTypes() : execution(* (#Security *).*(..));
Object around(Security security) : secureMethods() && #annotation(security) {
apply(security);
return proceed(security);
}
Object around(Security security) : secureTypes() && !secureMethods() && #target(security) {
apply(security);
return proceed(security);
}

Resources