How to make an aspect that targets all public methods that belong to a class that is marked with specific annotation? In following method1() and method2() should be processed by the aspect and method3() should not be processed by the aspect.
#SomeAnnotation(SomeParam.class)
public class FooServiceImpl extends FooService {
public void method1() { ... }
public void method2() { ... }
}
public class BarServiceImpl extends BarService {
public void method3() { ... }
}
If I put annotations on method level, this aspect will work and match the method calls.
#Around("#annotation(someAnnotation)")
public Object invokeService(ProceedingJoinPoint pjp, SomeAnnotation someAnnotation)
throws Throwable {
// need to have access to someAnnotation's parameters.
someAnnotation.value();
}
I am using Spring and proxy based aspects.
The following should work
#Pointcut("#target(someAnnotation)")
public void targetsSomeAnnotation(#SuppressWarnings("unused") SomeAnnotation someAnnotation) {/**/}
#Around("targetsSomeAnnotation(someAnnotation) && execution(* *(..))")
public Object aroundSomeAnnotationMethods(ProceedingJoinPoint joinPoint, SomeAnnotation someAnnotation) throws Throwable {
... your implementation..
}
This works in Spring Boot 2:
#Around("#within(xyz)")
public Object method(ProceedingJoinPoint joinPoint, SomeAnnotation xyz) throws Throwable {
System.out.println(xyz.value());
return joinPoint.proceed();
}
Note that based on the method argument type (SomeAnnotation xyz), Spring and AspectJ will know which annotation you are looking for, so the xyz does not have to be the name of your annotation.
Using #target and reading the type level annotation with reflection works.
#Around("#target(com.example.SomeAnnotation)")
public Object invokeService(ProceedingJoinPoint pjp) throws Throwable {
Related
I have two classes
public class ParentTestClass {
public void publicMethodOfParent() {
}
}
#Component
#MyAnnotation
public class ChildTestClass extends ParentTestClass {
public void publicMethodOfChild() {
}
}
With Spring AOP I need to wrap:
all calls for all public methods that are annotated with #MyAnnotation if annotation is put on class level
all methods that are annotated with #MyAnnotation if annotation is on the method level.
Here is my pointcut
#Around("(#within(MyAnnotation) && execution(public * *(..))) || #annotation(MyAnnotation)")
public Object myWrapper(ProceedingJoinPoint invocation) throws Throwable {
// ...
}
This works for public methods of ChildTestClass but ParentTestClass#publicMethodOfParent is not wrapped when I make a call childTestClass.publicMethodOfParent() How can I include parent methods?
Following pointcut expression will intercept the parent methods as well
From the documentation
#Pointcut("within(com.app..*) && execution(public * com.app..*.*(..))")
public void publicMethodsInApp() {
}
#Around("(publicMethodsInApp() && #target(MyAnnotation)) || "
+ "(publicMethodsInApp() && #annotation(MyAnnotation))")
public Object myWrapper(ProceedingJoinPoint invocation) throws Throwable {
//..
}
#target: Limits matching to join points (the execution of methods when
using Spring AOP) where the class of the executing object has an
annotation of the given type.
I want to capture the argument of #transactional if it is applied at class level.
for e.g. if #transactional applied at method level like :-
class A {
#transactional(readOnly= true)
public void someMethod(){
// some code...
}
}
then I am able to Intercept and capture the formal argument i.e. readOnly with this code like :-
#Aspect
#Component
#Order(0)
public class ReadOnlyRouteInterceptor {
private static final Logger logger = LoggerFactory.getLogger(ReadOnlyRouteInterceptor.class);
#Around("#annotation(transactional)")
public Object proceed(ProceedingJoinPoint proceedingJoinPoint, Transactional transactional) {
if (transactional.readOnly())
//do something
}
However the above code will not work if #transactional applied at class level as :-
#transactional(readOnly= true)
class A {
public void someMethod(){
// some code...
}
}
Now in order to Intercept the #transactional annotation which is applied at class level I have following code :-
#Pointcut("#within(org.springframework.transaction.annotation.Transactional *)")
public void beanAnnotatedWithTransactional() {}
#Pointcut("execution(public * *(..))")
public void publicMethod() {}
#Pointcut("publicMethod() && beanAnnotatedWithTransactional()")
public void publicMethodInsideAClassMarkedWithATransactional() {}
My actual problem here is I am unable to check the value of readOnly flag if #transactional is applied at class level.
For type level annotation :
#Around("#within(transactional)")
public Object myMethod(ProceedingJoinPoint pjp, Transactional transactional) throws Throwable {
boolean readOnly = transactional.readOnly();
...
return pjp.proceed();
}
For method level annotation:
#Around("execution(public * *(..)) && #annotation(org.springframework.transaction.annotation.Transactional)")
public Object myMethod(ProceedingJoinPoint pjp) throws Throwable {
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
Transactional annotation = method.getAnnotation(org.springframework.transaction.annotation.Transactional.class);
boolean value = annotation. readOnly();
...
return pjp.proceed();
}
Another (cleaner) option for method level :
#Around("#annotation(transactional)")
public Object myMethod(ProceedingJoinPoint pjp, Transactional transactional) throws Throwable {
}
or
With more control over tager :
#Around("execution(#org.springframework.transaction.annotation.Transactional public * *(..)) && #annotation("transactional")
public Object myMethod(ProceedingJoinPoint pjp, Transactional transactional) throws Throwable {
}
You may use the TransactionSynchronizationManager to get reference to the transaction details.
Following code provides the readonly details of the current active transaction.
import org.springframework.transaction.support.TransactionSynchronizationManager;
#Component
#Aspect
public class TestTransactionalAspect {
#Pointcut("#within(org.springframework.transaction.annotation.Transactional)")
public void beanAnnotatedWithTransactional() {}
#Pointcut("execution(public * *(..))")
public void publicMethod() {}
#Around("publicMethod() && beanAnnotatedWithTransactional()")
public void publicMethodInsideAClassMarkedWithATransactional(ProceedingJoinPoint pjp) {
try {
System.out.println("Intercepted "+pjp.toShortString());
if (TransactionSynchronizationManager.isActualTransactionActive()) {
System.out.println("Is transaction readonly : "+TransactionSynchronizationManager.isCurrentTransactionReadOnly());
}
pjp.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
}
}
Update:
Assuming this is not a Spring Boot project , #EnableTransactionManagement is mandatory for your spring application to work with transactions. Data will get persisted to database without transactions as well.
#EnableTransactionManagement annotation has to be used on
#Configuration classes.
Following is a sample code to enable transaction management in spring
#Configuration
#EnableTransactionManagement
public class AppConfig {
#Bean
public FooRepository fooRepository() {
// configure and return a class having #Transactional methods
return new JdbcFooRepository(dataSource());
}
#Bean
public DataSource dataSource() {
// configure and return the necessary JDBC DataSource
}
#Bean
public PlatformTransactionManager txManager() {
return new DataSourceTransactionManager(dataSource());
}
}
Based on the answers above I tried with the following code it is very simple.
/**
* This Aspect advice will be called only if transactional applied at method level
*/
#Around("#annotation(transactional)")
public Object proceedWithMethodLevelAnnotation(ProceedingJoinPoint proceedingJoinPoint,
Transactional transactional) {
if( transactional.readOnly() ) {
//do something
}
/**
* This Aspect advice will be called only if transactional annotation applied at class level
*/
#Around("#within(transactional)")
public Object proceedWithClassLevelAnnotation(ProceedingJoinPoint proceedingJoinPoint,
Transactional transactional)
if( transactional.readOnly() ) {
//do something
}
}
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
I want to have a AspectJ pointcut for methods annotated with #Scheduled. Tried different approaches but nothing worked.
1.)
#Pointcut("execution(#org.springframework.scheduling.annotation.Scheduled * * (..))")
public void scheduledJobs() {}
#Around("scheduledJobs()")
public Object profileScheduledJobs(ProceedingJoinPoint joinPoint) throws Throwable {
LOG.info("testing")
}
2.)
#Pointcut("within(#org.springframework.scheduling.annotation.Scheduled *)")
public void scheduledJobs() {}
#Pointcut("execution(public * *(..))")
public void publicMethod() {}
#Around("scheduledJobs() && publicMethod()")
public Object profileScheduledJobs(ProceedingJoinPoint joinPoint) throws Throwable {
LOG.info("testing")
}
Can anyone suggest any other way to have around/before advice on #Scheduled annotated methods?
The pointcut that you are looking for can be specified as below:
#Aspect
public class SomeClass {
#Around("#annotation(org.springframework.scheduling.annotation.Scheduled)")
public void doIt(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("before");
pjp.proceed();
System.out.println("After");
}
}
I am not sure whether that's all you require or not. So I'm going to post the other parts of the solution as well.
First of all, notice the #Aspect annotation on the class. It is required for the methods in this class to be applied as advice.
Also, you need to make sure that the class that has the #Scheduled method is detectable via scanning. You can do so by annotation that class with #Component annotation. For ex:
#Component
public class OtherClass {
#Scheduled(fixedDelay = 5000)
public void doSomething() {
System.out.println("Scheduled Execution");
}
}
Now, for this to work, the required parts in your spring configuration would be as follows:
<context:component-scan base-package="com.example.mvc" />
<aop:aspectj-autoproxy /> <!-- For #Aspect to work -->
<task:annotation-driven /> <!-- For #Scheduled to work -->
I am trying to define a pointcut, that would catch every method that is annotated with (i.e.) #CatchThis. This is my own annotation.
Moreover, I'd like to have access to the first argument of the method, which will be of Long type. There may be other arguments too, but I don't care about them.
EDIT
This is what I have right now. What I don't know is how to pass the first parameter of the method annotated with #CatchThis.
#Aspect
public class MyAspect {
#Pointcut(value = "execution(public * *(..))")
public void anyPublicMethod() {
}
#Around("anyPublicMethod() && #annotation(catchThis)")
public Object logAction(ProceedingJoinPoint pjp, CatchThis catchThis) throws Throwable {
return pjp.proceed();
}
}
Something like this should do:
#Aspect
public class MyAspect{
#Pointcut(value="execution(public * *(..))")
public void anyPublicMethod() {
}
#Around("anyPublicMethod() && #annotation(catchThis) && args(.., Long ,..)")
public Object logAction(
ProceedingJoinPoint pjp, CatchThis catchThis, Long long)
throws Throwable {
return pjp.proceed();
}
}