I am new to Spring and AOP. I am trying this simple thing where I have created a custom annotation which when placed before any method should execute some code.
This is the annotation I created
// Declares a custom annotation that validates json
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface JsonSchemaAnnotation {
}
Next I created the Spring Aspect class which holds the logic
#Aspect
public class UpdateUIMetadataInterceptor {
#Pointcut("execution(public * com.fico.cardinal.cm.*.*(..))")
public void anyPublicMethod() {
System.out.println("Running");
}
#Before("anyPublicMethod() && #annotation(jsonSchemaAnnotation)")
public void validateJson(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("Running");
}
}
And this is my simple test class
public class ValidationTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring/configuration.xml");
String jsondata = "{\"id\": \"EXPENSE_REPORT\",\"properties\": {\"transactionType\": \"EXPENSE_REPORT\"},\"sections\": []} ]}";
ValidationTest test = new ValidationTest();
test.jsonValidationTest("dummy", jsondata);
((AbstractApplicationContext) context).close();
}
#JsonSchemaAnnotation
public void jsonValidationTest(String dummy, String jsondata) {
System.out.println("Success");
}
The problem is my spring aop never gets triggered. I have included a bean in my configuration.xml
<aop:aspectj-autoproxy>
<aop:include name="UpdateUIMetadataInterceptor" />
</aop:aspectj-autoproxy>
<bean id="updateUI" class="com.fico.cardinal.cm.interceptor.UpdateUIMetadataInterceptor" />
Can anyone point out what I am missing?
You have several problems with your code:
You should create your ValidationTest object as a bean managed by Spring and not using new
<aop:include name="UpdateUIMetadataInterceptor" /> should be <aop:include name="updateUI"/>; you can actually just stick with <aop:aspectj-autoproxy/> for simplicity here
ProceedingJoinPoint is not supported for before aspects, so remove it; you can use JoinPoint instead if you need access to arguments
JsonSchemaAnnotation jsonSchemaAnnotation parameter should be present for validateJson method of your aspect, as pointed out by frant.hartm
I think you need either fully qualified name or a parameter in the method:
FQN:
#Before("anyPublicMethod() && #annotation(your.package.JsonSchemaAnnotation)")
public void validateJson(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("Running");
}
Parameter:
#Before("anyPublicMethod() && #annotation(jsonSchemaAnnotation)")
public void validateJson(ProceedingJoinPoint pjp, JsonSchemaAnnotation jsonSchemaAnnotation ) throws Throwable {
System.out.println("Running");
}
Source: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-pointcuts
(and you also need to use the bean, as Dmitry Kuskov pointed out
Related
I'm using #KafkaListener and I need a dynamic topic name so I use the SpEL '__listener' in order to do that
#PostConstruct
public void init() {
myProps= generateTopicDynamically();
}
#KafkaListener(topics = "#{__listener.myProps}")
public void listenerKafka(#Payload MyObject myObject) {
//Do something with my event
}
It works perfectly well.
The main issue is when I want to add another annotation that trigger some Aspect programmation
#MyCustomAnnotationToRecordPerformance
#KafkaListener(topics = "#{__listener.myProps}")
public void listenerKafka(#Payload MyObject myObject)
and here the aspect class
#Aspect
#Configuration
#Slf4j
public class MyCustomAnnotationToRecordPerformanceAspect {
#Pointcut("#annotation(MyCustomAnnotationToRecordPerformance)")
public void annotationMyCustomAnnotationToRecordPerformance() {
}
#Around("annotationMyCustomAnnotationToRecordPerformance()")
public Object doSomething(final ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
return proceedingJoinPoint.proceed();
}
}
I have this issue because Spring try to resolve __listener before #PostConstruct has been called.
Caused by: java.lang.IllegalArgumentException: #KafKaListener can't resolve 'null' as a String
at org.springframework.kafka.annotation.KafkaListenerAnnotationBeanPostProcessor.resolveAsString(KafkaListenerAnnotationBeanPostProcessor.java:648)
at org.springframework.kafka.annotation.KafkaListenerAnnotationBeanPostProcessor.resolveTopics(KafkaListenerAnnotationBeanPostProcessor.java:520)
at org.springframework.kafka.annotation.KafkaListenerAnnotationBeanPostProcessor.processListener(KafkaListenerAnnotationBeanPostProcessor.java:419)
at org.springframework.kafka.annotation.KafkaListenerAnnotationBeanPostProcessor.processKafkaListener(KafkaListenerAnnotationBeanPostProcessor.java:370)
at org.springframework.kafka.annotation.KafkaListenerAnnotationBeanPostProcessor.postProcessAfterInitialization(KafkaListenerAnnotationBeanPostProcessor.java:298)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:431)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:595)
... 41 common frames omitted
I tried to debug it
We can see lot of CGLIB reference, so bean has been already proxified, but all properties are null. So I supposed Autowired and PostConstruct method has not been called yet
For now, I tried to delay the processor that manage #KafkaListener, but I was not able to find where I can change that without have to redefine fully Kafka configuration
#EnableKafka import KafkaListenerConfigurationSelector that is DeferredImportSelector.
Here the comment on this class
A {#link DeferredImportSelector} implementation with the lowest order to import a {#link KafkaBootstrapConfiguration} as late as possible.
So I supposed it already delay as late as possible based on the comment
I test it with #Transactional, and I have the same issue.
#Transactional
#KafkaListener(topics = "#{__listener.myProps}")
public void listenerKafka(#Payload MyObject myObject)
Do have any idea about it?
The only alternative I see for now is split my class in 2 and create 2 beans.
KafkaListener method call the other bean. But I found very strange to have to do that.
Thanks in advance for you help.
I just tested it with #Transactional and it works as expected for me - I have confirmed that we already have a CGLIB proxy by the time we get to the #KafkaListener annotation BPP...
#SpringBootApplication
#EnableTransactionManagement
public class So69817946Application {
public static void main(String[] args) {
SpringApplication.run(So69817946Application.class, args);
}
#Bean
public NewTopic topic() {
return TopicBuilder.name("so69817946").partitions(1).replicas(1).build();
}
}
#Component
class listener {
public String getTopic() {
return "so69817946";
}
#Transactional
#KafkaListener(id = "so69817946", topics = "#{__listener.topic}")
public void listen(String in) {
System.out.println(in);
}
}
#Component
class TM extends AbstractPlatformTransactionManager {
#Override
protected Object doGetTransaction() throws TransactionException {
return new Object();
}
#Override
protected void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException {
}
#Override
protected void doCommit(DefaultTransactionStatus status) throws TransactionException {
}
#Override
protected void doRollback(DefaultTransactionStatus status) throws TransactionException {
}
}
so69817946: partitions assigned: [so69817946-0]
And I can see the transaction interceptor in the call stack.
So, yes, an MCVE would be helpful.
Thanks to the help of Gary, I found the solution.
Once we have aspect, the class is proxified and properties became null in the CGLIB object.
We need to call getter in order to have the value from original object, not the proxified one
SpEL is able to read public getter that will be executed on the original object, and not the CGLIB one
So the solution was simply to create a public getter for my private
public String getMyProps(){
return this.myProps;
}
Thanks all.
After adding Spring AOP to my Spring Boot project, the following aspect produces a NullPointerException on an autowired service component in my controllers:
#Aspect
#Component
#Slf4j
public class LogRequestAspect {
#Around("#annotation(org.springframework.web.bind.annotation.RequestMapping) && execution(public * *(..))")
public Object log(final ProceedingJoinPoint joinPoint) throws Throwable {
final HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.currentRequestAttributes())
.getRequest();
final Object proceed = joinPoint.proceed();
log.info(
"{} {} from {},{}",
request.getMethod(),
request.getRequestURI(),
request.getRemoteAddr(),
request.getHeader("X-Forwarded-For"));
return proceed;
}
}
Example controller:
#RestController
public class AController {
#Autowired
AService aService;
#RequestMapping("/doSomething")
private List<Map<String, Object>> doSomething() {
return aService.doSomething();
}
}
Example service:
#Service
public class AService {
public List<Map<String, Object>> doSomething() {
List<Map<String, Object>> results = new ArrayList<>();
return results;
}
}
Example configuration:
#EnableAspectJAutoProxy
#SpringBootApplication
public class Application implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void run(String... strings) {
}
}
As soon as i remove the aspect, everything works perfectly.
Any idea what i'm missing here?
Spring AOP, by default, works using proxies. IN this case a class based proxy is being used because no interface has been implemented. A class based proxy extends the actual class and overrides all the methods to apply the interceptors/aspects.
However a private method cannot be overriden in a subclass and as such your controller method will be invoked on the proxy instead of the proxied object. The proxy never has anything injected and hence the aService field is always null on there.
To fix make the method public or protected so that a subclass can override the method and eventually the method will be called on the proxied instance instead of the proxy.
I'm trying out a simple custom Spring annotation, but it seems like Spring isn't executing anything when i slap the annotation on a method...anyone have any ideas? I see no logging at all. Maybe i need some aop dependency?
#Aspect
#Component
public class LethargicLoggerAspect {
private final Logger log = LoggerFactory.getLogger(getClass());
#Around("#annotation(LethargicLogger)")
public Object logSlowExecutionTime(ProceedingJoinPoint
proceedingJoinPoint) throws
Throwable {
log.error("HIIIIIIIIII david");
Object proceed = proceedingJoinPoint.proceed();
return proceed;
}
}
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface LethargicLogger {
}
It looks good, you need to add package to the #Around annotation.
#Around("#annotation(com.example.package.LethargicLogger)")
public Object logSlowExecutionTime(ProceedingJoinPoint
proceedingJoinPoint) throws
Throwable {
}
I have service that has several overloaded methods, for example:
MyService.execute(Long id);
MyService.execute(Collection collection);
And i need to intercept only execution of 'MyService.execute(Long id)' via AOP like:
#Aspect
#Component
public class AopInterseptor{
#After("execution(* my.Service.MyService.execute(..))")
public void intercept(JoinPoint joinPoint) throws Exception {
// Do stuff
}
}
Is it possible to do so?
What about:
#Aspect
#Component
public class AopInterseptor{
#After("execution(* my.Service.MyService.execute(Long))")
public void intercept(JoinPoint joinPoint) throws Exception
{
// Do stuff
}
}
This Poincut designator matches only if there is only one param with type Long given in the method call.
I was trying to create an Aspectj pointcut on method annotation but I failed all the time with different approaches. I'm using aspectj autoproxy (I have no other weaving configured in my spring context). My classes look like this:
public interface Intf
{
#SomeAnnotation
void method1() throws SomeExc;
}
public class Impl implements Intf
{
#Override
public void method1() throws SomeExc
{
//...
}
}
#Aspect
public class MyAspect
{
#AfterThrowing(
pointcut = "execution(* *(..)) && #annotation(SomeAnnotation)",
throwing = "error")
public void afterThrowing(JoinPoint jp, Throwable error)
{
System.err.println(error.getMessage());
}
}
#Component
public class Usage
{
#Autowired
Intf intf;
public void doStuff()
{
intf.method1();
}
}
So I'm wondering why the aspectj won't create the pointcut. I managed to make it work using execution(* *(..) throws SomeExc) which does the job for me but I still want to know what I did wrong.
Also since method1 is defined in an interface and I specify the annotation on implementing class, is there a way to make it work this way? Other proxying mechanisms like transaction management/security works this way in other parts of spring right? And if I'm using interface proxying would specifying the pointcut on implementing class create the pointcut? (I guess not since I'm not using cglib)
try to add #Component to MyAspect class
#Component
#Aspect
public class MyAspect {
...
simply mark your aspect method with
#After("#annotation(package.SomeAnnotation)")
Have a look at this for a step by step guide