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.
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 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) {
Currently we are migrating a Struts 1.1 project into Spring 4.x.
We have successfully converted Action class to Controller and formbean to Model and even we are able to convert struts validation to Spring validation.
But we are facing an issue when we try to add AOP for all the controllers.
The purpose is to add a log for measuring the time taken for all the controller methods.
below is code snippet,
#Component
#Controller
public class LoginController {
#Autowired
private LoginValidator loginValidator;
#InitBinder
private void initBinder(WebDataBinder binder) {
binder.setValidator(loginValidator);
}
#RequestMapping(value = "/login", method = RequestMethod.POST)
public String loginUser(#Valid #ModelAttribute Login form, BindingResult bindingResult) {
System.out.println("Entering loginController.loginUser method");
}
}
We are using the below point-cut to apply aop,
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
#Component
#Aspect
public class Logging {
#Pointcut("execution(* com.controller.*.*(..))")
public void businessLogicMethods() {}
#Around("businessLogicMethods()")
public Object logAround(ProceedingJoinPoint jp) {
System.out.println("around() is running!");
System.out.println(jp.getSignature().getName());
System.out.println(jp.getArgs());
Object obj = null;
try {
obj = jp.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("******");
return obj;
}
}
Either Validation or AOP is working at a time. If AOP is not applied then Validation fired. If aop is applied only AOP is fired.
Can anyone help on this?
Thanks...
I got this working. You have to do 2 changes.
Change No: 1
Your Aspect definition is wrong for some reason.(No idea why). But the below aspect will work.
#Component
#Aspect
public class Logging {
static final Logger LOG = LoggerFactory.getLogger(Logging.class);
#Pointcut("within(#org.springframework.stereotype.Controller *)")
public void controller() {}
#Pointcut("execution(* *(..))")
public void methodPointcut() {}
#Pointcut("within(#org.springframework.web.bind.annotation.RequestMapping *)")
public void requestMapping() {}
#Before("controller() && methodPointcut() && requestMapping()")
public void aroundControllerMethod(JoinPoint joinPoint) throws Throwable {
LOG.info("Invoked: " + niceName(joinPoint));
}
#AfterReturning("controller() && methodPointcut() && requestMapping()")
public void afterControllerMethod(JoinPoint joinPoint) {
LOG.info("Finished: " + niceName(joinPoint));
}
private String niceName(JoinPoint joinPoint) {
return joinPoint.getTarget().getClass()
+ "#" + joinPoint.getSignature().getName()
+ "\n\targs:" + Arrays.toString(joinPoint.getArgs());
}
}
Change No:2
initBinder should be declared as public. Currently you have defined this method as private. Again I am not sure why this work correctly without the aspects.
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.setValidator(loginValidator);
}
The 2 changes will work.
Actually Arun's version should not make a big difference other than being cleaner (as in more general) and more verbose, unless Selvakumar's pointcut is wrong to begin with. Because he is not showing us any package names for aspect or controller, this is speculative, but maybe the controller resides not directly within package com.controller but within some sub-package. In this case the pointcut should rather be
#Pointcut("execution(* com.controller..*(..))")
Please not the double dot syntax .. which roughly means "include any number of sub-package levels".
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 -->