Log controller with AspectJ - java

I have a Spring boot application, and I want to log some info what happens when a Controller method id invoked.
For some reason, my Aspect is not working.
Here is my #Component class annotated with #Aspect:
#Pointcut("within(#org.springframework.stereotype.Controller *)")
public void controller() {
}
#Pointcut("execution(* *.*(..))")
protected void allMethod() {
}
#Before("controller()&& allMethod()")
public void logBefore(JoinPoint joinPoint) {
}
The logBefore method is not called, when any Controller method is called with REST.

Important: As you've stated you are using a Spring Boot setup, my assumption is that you've implemented the Spring AOP module instead of the "actual" AspectJ library. The difference is significant, as the implementation of AOP differs between them. Spring uses the AspectJ annotations to apply proxying, while AspectJ "weaves" the code into your application. In short, Spring AOP might be easier to implement, while AspectJ offers more fine-grained functionality (such as compile-time weaving). A comparison can be found here.
I have tried out the configuration from the code snippet you provided in your post. The advice was invoked after I added several annotations:
#SpringBootApplication
// Be sure to add EnableAspectJAutoProxy and set proxyTargetClass to true
#EnableAspectJAutoProxy(proxyTargetClass = true)
public class DemoApplication {
...
}
// Be sure to add #Aspect and #Component
#Component
#Aspect
public class DemoAop {
private static Logger logger = LoggerFactory.getLogger(DemoAop.class);
#Pointcut("within(#org.springframework.stereotype.Controller *)")
public void controller() {
}
#Pointcut("execution(* *.*(..))")
protected void allMethod() {
}
#Before("controller()&& allMethod()")
public void logBefore(JoinPoint joinPoint) {
logger.info("TEST");
}
}

At runtime your controller is annotated with #RestController but not #Controller.
Simply changing the Pointcut to RestController works:
#Pointcut("within(#org.springframework.web.bind.annotation.RestController *)")
public void controller() {
}

Related

Spring AOP does not intercept JMS MessageListener onMesage() call

I am trying to intercept JMS MessageListener onMessage() method using Spring AOP, but my advice is not getting called. I have created an annotation #LoggingInfo:
#Target (ElementType.METHOD)
#Retention (RetentionPolicy.RUNTIME)
public #interface LoggingInfo {
}
Aspect class : I have also tried with the commented ponitcut definition but still advice is not getting called.
#Aspect
#Component
public class LoggingAspect {
#Pointcut("#annotation(LoggingInfo)")
private void annotationPointCut() {
}
//#Pointcut ("execution(* *(..))”)
//#Pointcut ("execution( void *.onMessage (javax.jms.Message))")
#Pointcut("execution( void javax.jms.MessageListener.onMessage(javax.jms.Message))")
private void onMessagePointCut() {
}
#AfterReturning("onMessagePointCut() && annotationPointCut()")
public void loggingAdvice(JoinPoint jp ) {
System.out.println("logging advice method called successfully");
}
}
MessageReceiver.java
#Component
public class MessageReceiver implements MessageListener {
#Override
#LoggingInfo
public void onMessage(Message message) {
System.out.println("Jms message processed successfully");
}
}
All other configuration to setup the application are done properly and onMessage() is processing the incoming message successfully. Also #ComponentScan annotations is mention in application class to scan all the beans.
Is there anything can be done to achieve this using Spring AOP?

How to wrap all repository calls with an Around aspect

I would like to wrap all the repository calls in my service with an Around aspect for creating some metrics.
All of my JpaRepositories are annotated with org.springframework.stereotype.Repository, so I tried something like this:
#Configuration
#Aspect
public class RepositoryMetrics {
#Around("#annotation(org.springframework.stereotype.Repository)")
public void logQueryTime(ProceedingJoinPoint joinPoint) throws Throwable {
//Some logic here
joinPoint.proceed();
//Some logic here
}
}
But seems like the aspect method never runs. What do I miss?
Assuming you have the JpaRepository classes annotated as follows
#Repository
public interface JpaEmployeeRepository extends CrudRepository<JpaEmployee, Long> {
...
}
Following Aspect would intercept all method calls happening to that class
#Component
#Aspect
public class RepositoryAspect {
#Pointcut("#within(org.springframework.stereotype.Repository)")
public void repositoryAnnotationPointCut() {}
#Around("repositoryAnnotationPointCut()")
public void logQueryTime(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("logged for "+pjp.toLongString());
pjp.proceed();
}
}
Please note that a Configuration class be better left for configuration entries and you may create a separate class for Aspect and annotate the same with #Component as given in the example.
Make sure you have #ComponentScan to auto detect the Aspect and #EnableAspectJAutoProxy on the Configuration class. Something as follows
#Configuration
#EnableAspectJAutoProxy
#ComponentScan(basePackageClasses= {RepositoryAspect.class})
public class AppConfig {
...
}
Update
Following are the reasons , your code didn't work as expected
1.
Pointcut designator #annotation
#annotation: Limits matching to join points where the subject of the
join point (the method being executed in Spring AOP) has the given
annotation.
In this case method is not annotated.
2.
The Configuration class RepositoryMetrics is not annotated with #EnableAspectJAutoProxy . I assume Spring AOP was not enabled in any of the Configuration classes.

Importing a Spring AOP annotation into a separate project

I have two projects. One is a Spring Boot service, and the other is a library which is making use of Spring AOP. I have created a custom annotation to be executed whenever a method is annotated with this custom annotation. As I want this annotation to be used in lots of services, it lives inside the library. My code looks something like this:
Service:
#MyCustomAnnotation
public void doSomething() {
log.info("Do something is invoked!");
}
#EnableAspectJAutoProxy
public class ApplicationConfig {
...
}
Library:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface MyCustomAnnotation {
...
}
#Aspect
public class ContinueTraceFromSpringKafkaEventAspect {
#Pointcut("#annotation(MyCustomAnnotation)")
public void executeMyCustomAnnotation() { }
#Before("executeMyCustomAnnotation()")
public void beforeAnnotation(JoinPoint joinPoint) {
log.info("Before annotation");
}
#After("executeMyCustomAnnotation()")
public void afterAnnotation(JoinPoint joinPoint) throws Throwable {
log.info("After annotation");
}
}
The annotation would successfully execute when the code lived inside the service but, ever since extracting it to a library (and having the library on the classpath of the service via Maven dependency), it doesn't execute - I suspect the pointcut expression needs amending.
Any ideas?

Spring AOP adviced methods do not work. My configuration seems ok

I'm trying to create a demo AOP application but it just does not work right.
I read through all tutorials and got it working with #RestController but as I tried it with a plain java spring driven application I just can't get it to work. Please review my files and tell me where my mistake lies in.
Application Class
#SpringBootApplication
#ComponentScan("com.xetra.experimental")
#EnableAspectJAutoProxy
public class AoptryoutnowebApplication {
public static void main(String[] args) {
SpringApplication.run(AoptryoutnowebApplication.class, args);
DefaultClassToAspectImpl defaultClassToAspectImpl = new DefaultClassToAspectImpl();
defaultClassToAspectImpl.doStuff();
}
}
ClassToAspect Interface
public interface ClassToAspect {
void doStuff();
}
ClassToAspect Implementation
#Component
public class DefaultClassToAspectImpl implements ClassToAspect {
#FooAnnotation
public void doStuff(){
System.out.println("DoStuff!");
}
}
Annotation for Pointcut
public #interface FooAnnotation {
}
Aspect Class
#Aspect
public class FooAspect {
#Pointcut("#annotation(FooAnnotation)")
public void methods(){
}
#Before("methods()")
public void doAspect(){
System.out.println("FooAspect before");
}
}
Try this:
replace #EnableAspectJAutoProxy with #EnableAspectJAutoProxy(proxyTargetClass = false)
change pointcut to
#Pointcut("execution (* your.package..*.*(..)) && #annotation(fooAnnotation))")
The problem is you are using a non Spring managed instance by doing new DefaultClassToAspectImpl(). Spring AOP only works for Spring managed beans, because by default Spring AOP is proxy based.
So instead of doing new DefaultClassToAspectImpl() you should be obtaining the instance from the ApplicationContext. When using Spring Boot the SpringApplication.run call returns an ApplicationContext. Here you can use one of the getBean methods to obtain the instance you want.
ApplicationContext ctx = SpringApplication.run(AoptryoutnowebApplication.class, args);
ClassToAspect bean = getBean(ClassToAspect.class);
bean.doStuff();
This way you get the Spring managed

Full AspectJ support in Spring

I'd like to weave an advice on a method that is NOT part of a Spring bean (Spring Boot 1.4.4.RELEASE) :
#Component
#Aspect
...
#Around("execution(public * com.netflix.appinfo.InstanceInfo.getId())")
I added aspectjrt and spring-instrument (??) dependencies
I added #EnableAspectJAutoProxy and #EnableLoadTimeWeaving(aspectjWeaving = AspectJWeaving.ENABLED) annotations
I added VM arguments:
-javaagent:d:\.m2\repository\org\springframework\spring-instrument\4.3.6.RELEASE\spring-instrument-4.3.6.RELEASE.jar
-javaagent:d:\.m2\repository\org\aspectj\aspectjweaver\1.8.9\aspectjweaver-1.8.9.jar
The bean is handled (postconstruct log) but the execution isn't intercepted.
Does anyone has a clue on something I could miss ? Thx in advance
Ok, here is the trick for those interested, a singleton pattern is handling access to a singleton for both LTW and Spring, so it can be injected with Spring dependencies after being weaved by LTW:
#Configuration
#Aspect
public class MyAspect {
#Value("${mycompany.property}")
private String myKey;
#Around("execution(public * com.mycompany.NotASpringean.getProperty())")
public String weave(ProceedingJoinPoint jp) throws Throwable {
String value = (String) jp.proceed();
// transform the value thx to injected myKey value
return value;
}
#Bean("post-construct-aspect")
public MyAspect init() {
return MyAspect.aspectOf(); // get existing instance via factory method
}
private static MyAspect instance = new MyAspect();
/** Singleton pattern used by LTW then Spring */
public static MyAspect aspectOf() {
return instance;
}
}
Try using the #Pointcut annotation too like this:
#Pointcut("execution(public * com.netflix.appinfo.InstanceInfo.getId())")
public void pointcut() {}
#Around("pointcut()")
public Object whatever(ProceedingJoinPoint joinPoint) throws {...}

Categories

Resources