I use mybatis. My question is how can Spring AOP matches annotation on interface method? Because I want to put some param in annotation and then handle them in afterReturning method.
my annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface CacheClear {
String key() default "";
}
in mapper class:
#CacheClear
List<BizInst> selectAllBizInsts();
and in my aspect:
when use "execute..." it works
#AfterReturning("execution(public * com.dao.*.select*(..))")
public void doAfterReturning(){
System.out.println("after returning");
}
but when use "#annotation(...)" it doesn't work
#AfterReturning("#annotation(com.annotation.CacheClear)")
public void doAfterReturning(){
System.out.println("after returning");
}
You can do something like that for selecting your public dao methods annotated with CacheClear annotation:
#Pointcut("execution(#com.yourPackage.CacheClear * *(..))")
public void methodAnnotatedWithCacheClear( ) {}
#Pointcut("execution(public * com.dao.*.select*(..))")
public void publicDAOMethod() {}
#AfterReturning(pointcut = "methodAnnotatedWithCacheClear() && publicDAOMethod()", returning = "result")
public void doStuff(JoinPoint joinPoint, Object result) {
Related
There is a #annotation tag in Spring AOP Aspectj and we can easily annotation object. E.g:
Custom Annotation:
public #interface Authorize
#Before("#annotation(authorize)")
public void adviseAnnotatedMethods(JoinPoint joinPoint, Authorize authorize) {
System.out.println(authorize);
}
I wonder whether there is a #class tag like #annotation?
#Before("#class(customClass)")
public void adviseAnnotatedMethods(JoinPoint joinPoint, CustomClass customClass {
System.out.println(customClass);
}
Actually, in here, my aim is to reach CustomClass object. I know that I can reach it from jointPoint.getArgs() as checking (object instanceOf CustomClass). But I wonder there is a easy way to do it?
Update: full code
Exact Code:
I have a controller method:
#GetMapping("user/waiting/{accountId}/{userId}")
public ResponseEntity<?> getWaitingItems(#Custom CustomUser user) {
...
}
I have an annotation like below:
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.PARAMETER, ElementType.TYPE})
public #interface Custom {}
CustomUser:
public class CustomUser {
private Long accountId;
private Long userId;
private User user;
}
Aspectj methods:
#Pointcut("execution(* *(.., #Custom (*), ..))")
void annotatedCustom() {}
#Before("annotatedCustom()")
public void adviseMethodsOfAnnotatedClass(JoinPoint joinPoint) {
CustomUser cu = getArg(joinPoint);
...handle it.
}
private CustomClass getArg(JoinPoint joinPoint) {
for (Object a : joinPoint.getArgs()) {
if (a instanceof CustomClass) {
return (CustomClass) a;
}
}
return null;
}
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 have an annotation.
#Target(value = {ElementType.METHOD, ElementType.TYPE})
#Retention(value = RetentionPolicy.RUNTIME)
#Inherited
#Documented
public #interface MyCustomAnnotation{
}
My Aspect class is like that
#Component
#Aspect
public class MyCustomAsspect{
#AfterReturning(
pointcut="#annotation(MyCustomAnnotation)",
returning="retVal")
public void publishMessage(JoinPoint jp, Object retVal) throws Throwable {
}
}
My Service class is
#Service
public class ServiceClass{
#MyCustomAnnotation
public Object someMethod(){
return new Object();
}
}
Above are mentioned classes i am not sure why my aspect not working. I am new to Spring AOP . Please help me it shall be very thankful.
Issue is due to pointcut declaration. As spring documentation says
#annotation - limits matching to join points where the subject of the
join point (method being executed in Spring AOP) has the given
annotation
So I order to make this work
#Aspect
public class MyCustomAsspect{
#AfterReturning(
pointcut="execution(public * *(..)) and #annotation(MyCustomAnnotation)",
returning="retVal")
public void publishMessage(JoinPoint jp, Object retVal) throws Throwable {
}
}
I have problem with AspectJ. I added arguments to annotation before which Aspect will be woven and as a result it doesn't work.
Annotation interface:
#Target({ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface Logged {
Event event();
System system();
}
My Aspect:
#Aspect
#Component
public class Aspect {
#Pointcut("#annotation(Logged) && args(event, system)")
public void invoke(Event event, System system) { }
#Around("invoke(event, system)")
public void aspectMethod (ProceedingJoinPoint, Event event, System system) {
System.out.println(event + " " + system);
}
}
Event and System are Enums.
and added annotation before some method like that:
#Logged(event = Event.USER_LOGGED, system = System.WIN)
someTestingMethod();
It works only when I leave Aspect as:
#Aspect
#Component
public class Aspect {
#Pointcut("#annotation(Logged)")
public void invoke() { }
#Around("invoke()")
public void aspectMethod (ProceedingJoinPoint) {
System.out.println("Hey");
}
}
I don't know how to pass arguments into Aspect with annotation.
The basic solution is to bind the annotation:
#Aspect
class MyAspect {
#Pointcut("execution(* *(..)) && #annotation(l)")
public void invoke(Logged l) {}
#Around("invoke(l)")
public void aspectMethod (ProceedingJoinPoint pjp, Logged l) {
java.lang.System.out.println(l.event()+" "+l.system());
}
}
I've used the execution() pointcut to select only methods (so we want annotated methods) otherwise it will bind other users of the annotation (on fields/types/etc). As someone pointed out, args is for binding method parameters, rather than annotations.
I want to do something with a field BEFORE it gets the value assigned with a setter. I created the annotation and the aspect, but I don't know how to get the setter. This is my code:
#Aspect
public class AnnotationAspect{
#Pointcut("#annotation(annotationVariableName)")
public void annotationPointCutDefinition(Annotation annotationVariableName){
}
#Pointcut("execution(* *(..))")
public void atExecution(){}
#Around("annotationPointCutDefinition(annotationVariableName) && atExecution()")
public Object aroundAdvice(ProceedingJoinPoint joinPoint, Annotation annotationVariableName) throws Throwable {
Object returnObject = null;
returnObject = joinPoint.proceed();
return returnObject;
}
}
The annotation #Annotation is applied to a field in scala, so the method is "field =" (I don't know if this can change something).
Can anyone help me?
Thanks!
EDIT
This is the annotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface Annotation{
}
And an example code:
class Test{
#Annotation
var testing: String =_
}