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.
Related
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?
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() {
}
I'm relatively new to Spring Boot and dependency injection overall, so please forgive any noob things going on here. I'm building an API and am having trouble when injecting dependencies into a POJO resource (DTO).
When I call the method in the POJO this.numComments = commentSvc.getAllForPhoto(this.getId()); I am getting a NullPointerException. However, when I do this from another spring-managed bean and pass the values into the constructor, it works fine.
After reading around, it looks like I need to do something with aspectJ and Load Time Weaving, but I'm not sure what that would look like in my code.
In essence, my approach looks something like this:
PhotoResource.java (POJO)
public class PhotoResource extends BaseRepresentable {
#Autowired
CommentService commentSvc;
private Long id;
private Integer numComments;
PhotoResource(PhotoEntity entity){
super(entity);
this.setId(entity.getId);
this.numComments = commentSvc.getAllForPhoto(this.getId());
}
}
CommentService.java
#Service
public class CommentService{
public List<CommentResource> getAllForPhoto(Long photoId) {
// code to get all comments for photo
}
}
Application.java
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
Spring won't inject the dependency unless you ask the Spring container to manage the bean. In order for commentSvc to be injected into PhotoResource class, you need to annotate it with #Component or #Bean or #Service, e.g.:
#Component
public class PhotoResource extends BaseRepresentable {
#Autowired
CommentService commentSvc;
}
And make sure the package of this class is included into #ComponentScan packages.
Also, the following won't compile:
#Service
public class CommentService(){
You don't need paranthesis to declare a class, it should be:
#Service
public class CommentService{
I'd like to have multiple #PostConstruct annotated methods in one configuration class, that should be called dependent on the #Profile. You can imagine a code snipped like this:
#Configuration
public class SilentaConfiguration {
private static final Logger LOG = LoggerFactory.getLogger(SilentaConfiguration.class);
#Autowired
private Environment env;
#PostConstruct #Profile("test")
public void logImportantInfomationForTest() {
LOG.info("********** logImportantInfomationForTest");
}
#PostConstruct #Profile("development")
public void logImportantInfomationForDevelopment() {
LOG.info("********** logImportantInfomationForDevelopment");
}
}
However according to the javadoc of #PostConstruct I can only have one method annotated with this annotation. There is an open improvement for that in Spring's Jira https://jira.spring.io/browse/SPR-12433.
How do you solved this requirement? I can always split this configuration class into multiple classes, but maybe you have a better idea/solution.
BTW. The code above runs without problems, however both methods are called regardless of the profile settings.
I solved it with one class per #PostConstruct method. (This is Kotlin but it translates to Java almost 1:1.)
#SpringBootApplication
open class Backend {
#Configuration
#Profile("integration-test")
open class IntegrationTestPostConstruct {
#PostConstruct
fun postConstruct() {
// do stuff in integration tests
}
}
#Configuration
#Profile("test")
open class TestPostConstruct {
#PostConstruct
fun postConstruct() {
// do stuff in normal tests
}
}
}
You can check for profile with Environment within a single #PostContruct.
An if statement would do the trick.
Regards,
Daniel
I use Spring Integration and have a flow described in some #Configuration annotated class (#Bean and #Autowired marked methods), f.e. CustomFlow.class.
If any element in flow throw exception I would like to intercept it with #AfterThrowing advice and do some actions (notification, write smth to DB, etc).
So the question is - how to write proper pointcut to get all beans in this case?
#Aspect
public class LoggingAspect {
#AfterThrowing(
pointcut = "execution(*(..))",
throwing= "error")
public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
//...
}
}
and then the configuration:
<aop:after-throwing method="logAfterThrowing" throwing="error" />