Regarding Struts2 Spring AOP Logger - spring

In Strtus2 Action class I am having methods with name like getData(), getDetails(). Along with these I am having several getter/setters in my action class.
I want to use Spring's AOP functionality for logging purpose. I need to maintain Log for getData() and getDetails() method which in turn are calling the service class but I don't want to maintain Log for the getter/setters which are present in the action class.
I don't want to hard code method name in my Aspect. Don't have any clue of how to implement this. Please help me out.
My AOP Logger class is like :
#Before("myPointCut() && allService()")
public void logEntryService(JoinPoint joinPoint) {
System.out.println("Start of " + joinPoint.getSignature().getName() + "() of "+joinPoint.getThis());
start = System.currentTimeMillis();
}
#After("myPointCut() && allService()")
public void logExitService(JoinPoint joinPoint) {
System.out.println("End of " + joinPoint.getSignature().getName() + "() of "+joinPoint.getThis());
start = System.currentTimeMillis();
}
#Pointcut("#annotation(com.ing.trialbal.log.LogThis)")
public void myPointCut() {
System.out.println("*************My PointCut");
}
#Pointcut("execution(* com.ing.trialbal.*.*.*(..))")
public void allService() {
System.out.println("************All service");
}
LogThis.java
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface LogThis {
}
But this has created very tight coupling in my application as I always have to write #Logthis on every method in service and dao for having their logs.

Pointcut bound with #annotation().
Something like
Annotation class
#Target({ ElementType.TYPE, ElementType.METHOD })
#Retention(RetentionPolicy.RUNTIME)
public #interface Loggable {}
AOP Logger
#Pointcut("#annotation(com.Loggable)") // && execution( ... )
Action class
#Loggable
Object getData() {}
#Loggable
Object getDetails() {}

Related

Custom class level annotation in spring boot

I created a custom class level annotation.
Below is the aspect class:
#Aspect
#Slf4j
#Component
public class LoggingAspect {
private String generateLogMessage() {
return ("Entering method");
}
#Before("#within(mypackage.logging.Loggable) || #annotation(mypackage.logging.Loggable)")
public void logMethodEntry(JoinPoint joinPoint) {
String logMessage = generateLogMessage();
log.debug(logMessage);
}
}
Below is my custom annotation:
#Target(value = {ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface Loggable { }
When I use the annotation at class level it works. But if I am calling different methods within the same class the logging is done only once. I want to do the logging for each methods called using the class level #Loggable annotation.
I tried to use it at method level it works. But I want to use it at class level.

Why can't Spring AOP determine if proxy should be made for #target? [duplicate]

When using spring AOP with class level annotations, spring context.getBean seems to always create and return a proxy or interceptor for every class, wether they have the annotation or not.
This behavior is only for class level annotation. For method level annotations, or execution pointcuts, if there is no need for interception, getBean returns a POJO.
Is this a bug? As designed? Or am I doing something wrong?
#Component
#Aspect
public class AspectA {
#Around("#target(myAnnotation)")
public Object process(ProceedingJoinPoint jointPoint, MyAnnotation myAnnotation) throws Throwable {
System.out.println(
"AspectA: myAnnotation target:" + jointPoint.getTarget().getClass().getSimpleName());
System.out.println(" condition:" + myAnnotation.condition());
System.out.println(" key:" + myAnnotation.key());
System.out.println(" value:" + myAnnotation.value());
return jointPoint.proceed();
}
}
#Component("myBean2")
//#MyAnnotation(value="valtest-classLevel2", key="keytest-classLevel2", condition="contest-classLevel2")
public class MyBean2 {
public Integer testAspectCallInt(int i) {
System.out.println("MyBean2.testAspectCallInt(i=" + i + ")");
return i + 1000;
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE, ElementType.METHOD })
public #interface MyAnnotation {
String value() default "";
String key() default "";
String condition() default "";
}
#ComponentScan()
#EnableAspectJAutoProxy
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(Test.class);
MyBean2 bean = (MyBean2) ctx.getBean("myBean2");
System.out.println(bean.getClass()); // prints CGLIB proxy, even when annotation is commented out on class
bean.testAspectCallInt(12); // calling method
}
}
Andy Brown is right, it is by design. The reason is that according to the AspectJ manual pointcut designators such as #args, #this, #target, #within, #withincode, and #annotation (or the subset of those available in Spring AOP) are used to match based on the presence of an annotation at runtime. This is why in the Spring debug log you see that proxies are created for all components which might need aspect functionality.
If you want to avoid that, you can refactor your aspect into something like this at the cost of an uglier pointcut and even uglier reflection in the advice code:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.annotation.Annotation;
#Component
#Aspect
public class AspectA {
#Around("execution(* (#MyAnnotation *).*(..)) || execution(#MyAnnotation * *(..))")
public Object process(ProceedingJoinPoint joinPoint) throws Throwable {
MyAnnotation myAnnotation = null;
for (Annotation annotation : ((MethodSignature) joinPoint.getSignature()).getMethod().getDeclaredAnnotations()) {
if (annotation instanceof MyAnnotation) {
myAnnotation = (MyAnnotation) annotation;
break;
}
}
if (myAnnotation == null) {
myAnnotation = joinPoint.getTarget().getClass().getAnnotationsByType(MyAnnotation.class)[0];
}
System.out.println("AspectA: myAnnotation target:" + joinPoint.getTarget().getClass().getSimpleName());
System.out.println(" condition:" + myAnnotation.condition());
System.out.println(" key:" + myAnnotation.key());
System.out.println(" value:" + myAnnotation.value());
return joinPoint.proceed();
}
}
If neither the bean's class nor any of its methods bears the annotation, no proxy will be created. The advice detects both types of annotation, but prefers a method annotation if both are present.
Update: Instead of this workaround you could of course use full AspectJ from within Spring and avoid proxies altogether.

advice is not executing if the annotations contains parameters

I'm trying to get some advice to execute.
When I use annotation without parameters its do execute but when the annotation includes parameters it's not.
#Aspect
class a{
#Pointcut("execution(#com.annotations.AnnotationName* *(..))")
void someMethod() {}
#Around("someMethod()")
public Object aroundSomeMethod(ProceedingJoinPoint pjp) throws Throwable
{
// some code
}
}
Annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface AnnotationName
{
public String someString();
public boolean someBoolean();
}
The use of the annotation:
#AnnotationName(
someString= "string",
someBoolean = false
)
private void mycode()
{//code }
Following aspect code would advice a target method annotated with #AnnotationName
#Component
#Aspect
public class SomeMethodAspect {
#Pointcut("#annotation(annotationName) && within(so.qn69016852..*)")
private void someMethod(AnnotationName annotationName) {}
#Around("someMethod(annotationName)")
public Object aroundSomeMethod(ProceedingJoinPoint pjp,AnnotationName annotationName) throws Throwable
{
System.out.println(annotationName.someString());
System.out.println(annotationName.someBoolean());
return pjp.proceed();
}
}
Couple of corrections/observations .
Spring AOP cannot advice a private method of a Spring bean. The mycode() method should be in a bean and ideally public. ( Refer )
The Aspect should also be a spring bean. This can be achieved by annotating the aspect with #Component
Remember to limit the scope : https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#writing-good-pointcuts
You may also go through this answer from #kriegaex to understand why an #annotation has a global scope.
Update :
The code shared by OP also works with modifying a typo ( a space between the AnnotationName and * in the pointcut expression ) . The observations shared earlier holds good here as well.
#Component
#Aspect
public class SomeMethodAspect {
#Pointcut("execution(#so.qn69016852.anno.AnnotationName * so.qn69016852..*.*(..))")
private void someMethod() {}
#Around("someMethod() && #annotation(annotationName)")
public Object aroundSomeMethod(ProceedingJoinPoint pjp,AnnotationName annotationName) throws Throwable
{
System.out.println(annotationName.someBoolean());
System.out.println(annotationName.someString());
return pjp.proceed();
}
}

Java method introspection with Spring AOP

I use spring-aop to make some treatments on my services methods. The methods on wich the treatment must occur are annotated with #History. Moreover, the annotation can have some params like "comment". Here is on exemple :
#Service
public class MyServiceImpl implements IMyService {
#Override
#History(comment = "my comment")
public void myMethod() {...}
}
public interface IMyService {
void create();
}
And, I have a aspect defined like this :
#Aspect
#Component
public class MyHistoryAspect {
#AfterReturning(pointcut = "execution(* my.service.package.*(..)) && #annotation(history)", returning = "result")
public void myTreatment(JoinPoint joinPoint, History history, Object result) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
...
}
}
Now, my problem : when I use reflection to find out the value of "comment" in my aspect, I can't find it. The reason : the method is the method signature of IMyService, not the method signature of MyServiceImpl. And if I put my annotation on the interface instead of the service, my Aspect is never reached.
Am I missing something or is it the normal behavior of spring aop ?
Thank you

Spring AspectJ, pointcut before method execution where method OR class is annotated

I'm trying to get the value of an annotation via Spring Aop AspectJ-style, where the annotation can be on the class OR the method. I tried a lot of different things, but I can only get it to work when the annotation is on the method. I'd really like to annotate ONCE on the class - but advice all the methods of the class - and access the value of the class annotation in the advice. Here's where I've ended up:
Annotation:
#Inherited
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {
String value() default "";
}
Aspect:
#Aspect
public class MyAspect {
#Pointcut("execution(#com.myco.MyAnnotation * com.myco.somepackage..*.*(..))")
public void atExecution() { }
#Before("atExecution() && #annotation(myAnnotation)")
public void myAdvice(JoinPoint joinPoint, MyAnnotation myAnnotation) {
...
}
}
Any thoughts? Thanks.
Short answer
While you can formulate a pointcut that will match both directly annotated methods and methods of annotated types at the same time, you cannot make a pointcut and/or advice where you bind the value of the annotation (i.e. use the annotation value in the advice code).
#Aspect
public class MyAspect {
#Pointcut("execution(#com.myco.MyAnnotation * com.myco.somepackage..*.*(..))")
public void atExecutionOfAnnotatedMethod() {}
#Pointcut("execution(* (#com.myco.MyAnnotation com.myco.somepackage..*).*(..))")
public void atExecutionOfMethodsOfAnnotatedClass() {}
#Before("atExecutionOfAnnotatedMethod() && #annotation(myAnnotation)")
public void myAdviceForMethodAnnotation(JoinPoint joinPoint, MyAnnotation myAnnotation) {
System.out.println("myAdviceForMethodAnnotation: " + myAnnotation.value());
}
#Before("atExecutionOfMethodsOfAnnotatedClass() && #this(myAnnotation)")
public void myAdviceForTypeAnnotation(JoinPoint joinPoint, MyAnnotation myAnnotation) {
System.out.println("myAdviceForTypeAnnotation: " + myAnnotation.value());
}
// /* the following pointcut will result in "inconsistent binding" errors */
// #Pointcut("(atExecutionOfAnnotatedMethod() && #annotation(myMethodAnnotation)) || (atExecutionOfMethodsOfAnnotatedClass() && #this(myTypeAnnotation))")
// public void combinedPointcut(MyAnnotation myMethodAnnotation, MyAnnotation myTypeAnnotation) {}
}
Some detail
To combine the two separate pointcuts (atExecutionOfAnnotatedMethod and atExecutionOfMethodsOfAnnotatedClass) we would have to use the OR (||) construct. Since the OR construct doesn't guarantee that either of the two annotation bindings will be present at advice execution, they will both result in a compile error (inconsistent binding).
You can still handle both cases in separate advices, you may also delegate the actual advice code to a common method to avoid duplication. In that case you'll need to take care of the case where both the type and the method is annotated with #MyAnnotation because that would match both pointcuts and would result in your method doubly advised by both advices, hence your common advice handling code will execute twice.
Combining the two
If you need to combine the two cases while defending against doubly advising the target code, you need to set up a precedence between the method level annotation and the class level annotation. Based on the principle of specificity, I'd suggest to go on the route where the method level annotation takes precedence over the class level one. Your aspect would look like this:
#Aspect
public class MyCombinedAspect {
#Pointcut("execution(#com.myco.MyAnnotation * com.myco.somepackage..*.*(..))")
public void atExecutionOfAnnotatedMethod() {}
#Pointcut("execution(* (#com.myco.MyAnnotation com.myco.somepackage..*).*(..))")
public void atExecutionOfMethodsOfAnnotatedClass() {}
#Before("atExecutionOfAnnotatedMethod() && !atExecutionOfMethodsOfAnnotatedClass() && #annotation(myAnnotation)")
public void myAdviceForMethodAnnotation(JoinPoint joinPoint, MyAnnotation myAnnotation) {
handleBeforeExecution(joinPoint, myAnnotation);
}
#Before("atExecutionOfMethodsOfAnnotatedClass() && !atExecutionOfAnnotatedMethod() && #this(myAnnotation)")
public void myAdviceForTypeAnnotation(JoinPoint joinPoint, MyAnnotation myAnnotation) {
handleBeforeExecution(joinPoint, myAnnotation);
}
#Before("atExecutionOfMethodsOfAnnotatedClass() && atExecutionOfAnnotatedMethod() && #annotation(myMethodAnnotation)")
public void myAdviceForDoublyAnnotated(JoinPoint joinPoint, MyAnnotation myMethodAnnotation) {
handleBeforeExecution(joinPoint, myMethodAnnotation);
}
protected void handleBeforeExecution(JoinPoint joinPoint, MyAnnotation myAnnotation) {
System.out.println(myAnnotation.value());
}

Resources