AspectJ #Before annotation issue - java

I'm having some issues with AspectJ implementation!
I want to make a log method for methods with the #MyAnnotation annotation.
MyAnnotation.java :
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface MyAnnotation{ }
MyAspect.java :
#Aspect
public class MyAspect {
private static Logger logger = Logger.getLogger(MyAspect.class.getName());
#Pointcut("#annotation(com.utils.aop.annotations.MyAnnotation)")
public void logMyAspect() {
}
#Before("logMyAspect()")
public void logMethod(JoinPoint jp) {
String methodName = jp.getSignature().getName();
logger.info("Executing method: " + methodName);
}
}
I'm using my #MyAnnotation before some of the service method of my project:
#RolesAllowed({ "DEV", "GUI", "API" })
#POST
#Path("/getList")
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
#MyAnnotation
public Response getList(#Context final ContainerRequestContext requestContext,
FilterAndSortObject filterAndSortObject,
#QueryParam("offset") final int offset,
#QueryParam("limit") final int limit)
{
...
}
I've also seen that I should use #EnableAspectJAutoProxy in my configuration class :
#Configuration
#EnableAspectJAutoProxy
public class ServletContextClass implements ServletContextListener {
final static Logger logger = Logger.getLogger(ServletContextClass.class);
#Override
public void contextInitialized(final ServletContextEvent sce) {
...
}
...
}
However it doesn't seem to work. It doesn't log anything!
I used a breakpoint in the logMethod(JoinPoint jp) as well checking the result, without any success!
Does anyone have any idea why this doesn't work?

You don't have to separate the pointcut and the handler method; in fact, I'm sure this is what causes your issue. The following aspect should work just fine:
#Aspect
public class MyAspect {
private static Logger logger = Logger.getLogger(MyAspect.class.getName());
#Before("#annotation(com.utils.aop.annotations.MyAnnotation)")
public void logMyAspect(JoinPoint jp) {
String methodName = jp.getSignature().getName();
logger.info("Executing method: " + methodName);
}
}
You can also inspect your annotation values, in case it takes parameters:
#Before("#annotation(a)")
public void logMyAspect(JoinPoint jp, MyAnnotation a) {
// conditional logging based on annotation contents
}

Related

How do I get the method in the annotation parameter in an Aspect and get the result of the method execution

this is annotations Code:
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Inherited
#Documented
public #interface PreVisit {
String value();
}
this use in the Controller #PreVisit("#pv.hasAccess('xxxxxx')")
#PreVisit("#pv.hasAccess('xxxxxx')")
#RequestMapping(value = "getUser")
public User getUser(Integer userId) {...some code...}
this is pv.hasAccess('xxxxxx') code:
#Service("pv")
public class PageVisit{
public boolean hasAccess(String par){
//return false or true;
}
}
My question:
In Aspect, how do you get methods in annotation parameters and get the result of execution
this is Aspect file codeļ¼š
#Aspect
#Component
public class PreVisitAspect {
#Around("#annotation(PreVisit)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
//How do I get the result of the method execution in the annotation parameter here
//boolean pvResult=#pv.hasAccess('xxxxxx'); pvResult=false or true
//Do something use the pvResult
}
}
You can second argument type PreVisit and you can access the annotation values in the method.
#Aspect
#Component
public class PreVisitAspect {
#Around("#annotation(PreVisit)")
public Object around(ProceedingJoinPoint joinPoint, PreVisit preVisit) throws Throwable {
//How do I get the result of the method execution in the annotation parameter here
//boolean pvResult=#pv.hasAccess('xxxxxx');
//Do something use the pvResult
String value = preVisit.value();
}
}
If you want to use the PageVisit#hasAccess(String), then inject PageVisit into your aspect and invoke the method.
For this you must modify you controller method as below.
#PreVisit("xxxxxx")
#RequestMapping(value = "getUser")
public User getUser(Integer userId) {...some code...}
and your aspect will be.
#Aspect
#Component
public class PreVisitAspect {
#Autowired
private PageVisit pv;
#Around("#annotation(PreVisit)")
public Object around(ProceedingJoinPoint joinPoint, PreVisit preVisit) throws Throwable {
//How do I get the result of the method execution in the annotation parameter here
//boolean pvResult=#pv.hasAccess('xxxxxx');
//Do something use the pvResult
String value = preVisit.value();
boolean hasAccess = pv.hasAccess(value);
}
}
I'm the questioner
I have solved this using Java reflection,as follows:
#Aspect
#Component
public class PreVisitAspect {
#Autowired
private PageVisit pv;
#Around("#annotation(preVisit)")
public Object around(ProceedingJoinPoint joinPoint, PreVisit preVisit) throws Throwable {
String value = preVisit.value();
//String value="#pas.hasAccess('xxxxxx')";
if(value.startsWith("#")){
String beanName=value.substring(value.indexOf("#")+1,value.indexOf("."));
String methodName=value.substring(value.indexOf(".")+1,value.indexOf("("));
String paramsStr=value.substring(value.indexOf("(")+2,value.lastIndexOf(")")-1);
Object[] paramsArr=paramsStr.split("','");
logger.info("beanName:"+beanName);
logger.info("methodName:"+methodName);
logger.info("paramsStr:"+paramsStr);
ServletContext servletContext = request.getSession().getServletContext();
ApplicationContext appContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
Method method = ReflectionUtils.findMethod(appContext.getBean(beanName).getClass(), methodName,new Class[]{String.class} );
Boolean result = (Boolean)ReflectionUtils.invokeMethod(method, appContext.getBean(beanName),paramsArr);
logger.info(result.toString());
}
.....other Code.....
}
}
tks #Karthikeyan Vaithilingam

cannot find annotation when apply AspectJ AOP on annotation

when I use a aspect on an annotation, I cannot use AnnotationUtils.getAnnotation,
//here, I cannot not find PulsarListener
PulsarListener annotation = AnnotationUtils.getAnnotation(method, PulsarListener.class);
and when i remove #Aspect ,then it's ok.
#Target({ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface PulsarListener {
String[] topics() default {};
}
#Aspect
#Slf4j
#Component
#Order(0)
public class MDCAspect {
#Around("#annotation(PulsarListener)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
try {
String requestUUID = MDC.get("requestUUID");
if (StringUtils.isEmpty(requestUUID)) {
String uid = ObjectId.get().toHexString();
MDC.put("requestUUID", uid);
}
return joinPoint.proceed();
} finally {
MDC.clear();
}
}
}
#Component
#Slf4j
public class PulsarConsumer {
#PulsarListener(topics = "${topics}")
public void listen(Message<byte[]> receive) {
//doSomething
}
}
public class PulsarPostProcessor implements BeanPostProcessor {
#Value("${pulsar.service.url}")
private String pulsar_service_url;
#Autowired
private ApplicationContext applicationContext;
#Override
public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
Method[] methods = bean.getClass().getDeclaredMethods();
for (Method method : methods) {
//here , i canot not found PulsarListener
//here is the problem
PulsarListener annotation = AnnotationUtils.getAnnotation(method, PulsarListener.class);
if (annotation != null) {
if (log.isDebugEnabled()) {
log.debug("bean :{},method:{}", beanName, method.getName());
}
}
}
}

AOP for annotation is not working in Spring Boot

I have myannotation and whenever my method(which has myannotation) is executed then AOP should be called but which is not working in my spring boot controller.But which is working for methods which has other annotations.Please help me to understand about what happens.
Update: MyAnnotation
#Retention(RUNTIME)
#Target({ METHOD, CONSTRUCTOR })
public #interface MyAnnotation {
}
#Aspect
#Component
public class AnnotationAspect {
private static final String POINTCUT_METHOD1 = "#annotation(com.somepackage.MyAnnotation)";
#Around(POINTCUT_METHOD1)
public Object weaveJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
try {
System.out.println("Beforeee " + joinPoint);
joinPoint.proceed();
} finally {
System.out.println("Afterrr " + joinPoint);
}
return null;
}
}
Scenario 1:(Working)
#Controller
#RequestMapping("user")
public class ArticleController {
#GetMapping("article/{id}")
#MyAnnotation // here it is
public ResponseEntity<String> getArticleById(#PathVariable("id") Integer id)
{
return new ResponseEntity<String>(dummyMethod(), HttpStatus.OK);
}
public String dummyMethod() {
System.out.println("Dummy method with MyAnnotation");
return "HelloWorld!";
}
}
Log:(Working)
Beforeee execution(ResponseEntity com.mypackage.getArticleById(Integer))
Dummy method with MyAnnotation
Afterrr execution(ResponseEntity com.mypackage.getArticleById(Integer))
Scenario 2:(Not Working)
#Controller
#RequestMapping("user")
public class ArticleController {
#GetMapping("article/{id}")
public ResponseEntity<String> getArticleById(#PathVariable("id") Integer id)
{
return new ResponseEntity<String>(dummyMethod(), HttpStatus.OK);
}
#MyAnnotation //here it is
public String dummyMethod() {
System.out.println("Dummy method with MyAnnotation");
return "HelloWorld!";
}
}
Log:(Not Working)
Dummy method with MyAnnotation
Scenario 3: (Not Working)
#Service
public class ArticleService {
#MyAnnotation //here it is
public String dummyMethod() {
System.out.println("Dummy method with MyAnnotation");
return "HelloWorld!";
}
}
It might not work because you call dummyMethod() from the same class. Try moving dummyMethod() to another service class. The reason is that calls within the same class does not go though the Spring proxy. The call to getArticleById() is proxied and will be handled by AOP but dummyMethod() might as well be a private method.

Spring Boot AOP

I am having some issues trying to get my advice to execute. I tried several different pointcuts to no avail. The "#EnableAspectJProxy" seems to be working and detects my aspect. Any advice is appreciated.
I am using spring-boot-aop-starter.
#Aspect
#Component
public class ExecutionTimeLogger {
private Logger logger;
public ExecutionTimeLogger() {
logger = LoggerFactory.getLogger(getClass());
logger.info("HEY");
}
#Pointcut("within(#org.springframework.stereotype.Controller *)")
public void controller() {}
#Pointcut("execution(* edu.x.y.z.server.web.controller.*.*(*))")
public void methodPointcut() {}
#Pointcut("within(#org.springframework.web.bind.annotation.RequestMapping *)")
public void requestMapping() {}
#Around("controller() && methodPointcut() && requestMapping()")
public Object profile(ProceedingJoinPoint pjp) throws Throwable {
StopWatch sw = new StopWatch();
String name = pjp.getSignature().getName();
try {
sw.start();
return pjp.proceed();
} finally {
sw.stop();
logger.info("STOPWATCH: " + sw.getTime() + " - " + name);
}
}
}
I am trying to match any method that is within my package and is annotated with the #RequestMapping annotation. I have tried the very generic match any and all methods without any luck too.
Here is a sample of a method that the advice should be applied to:
#RequestMapping(value = "/analysis", method = RequestMethod.GET)
#ApiOperation(value = "Get analyses available for the current user")
JsonModelAndView getAllAnalyses(HttpServletRequest request)
I managed to get this resolved. I ended up creating a small spring application to test the use case with the specific pointcuts to remove other potential barriers. I found that my pointcuts needed some adjusting.
#Aspect
#Component
public class ExecutionTimeLogger {
private Logger logger;
public ExecutionTimeLogger() {
logger = LoggerFactory.getLogger(getClass());
logger.info("HEY");
}
#Pointcut("#annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void requestMapping() {}
#Pointcut("execution(* edu.x.y.z.server.web.controller.*Controller.*(..))")
public void methodPointcut() {}
#Around("requestMapping() && methodPointcut()")
public Object profile(ProceedingJoinPoint pjp) throws Throwable {
StopWatch sw = new StopWatch();
String name = pjp.getSignature().getName();
try {
sw.start();
return pjp.proceed();
} finally {
sw.stop();
logger.info("STOPWATCH: " + sw.getTime() + " - " + name);
}
}
}
As you can see the big difference was the annotation pointcut.
You might want to set proxyTargetClass=true (assuming your controllers do not have an interface). Use your own #EnableASpectJAutoProxy or set spring.aop.proxyTargetClass=true.

#AspectJ pointcut for all methods of a class with specific annotation

I want to monitor all public methods of all Classes with specified annotation (say #Monitor) (note: Annotation is at class level). What could be a possible pointcut for this?
Note: I am using #AspectJ style Spring AOP.
You should combine a type pointcut with a method pointcut.
These pointcuts will do the work to find all public methods inside a class marked with an #Monitor annotation:
#Pointcut("within(#org.rejeev.Monitor *)")
public void beanAnnotatedWithMonitor() {}
#Pointcut("execution(public * *(..))")
public void publicMethod() {}
#Pointcut("publicMethod() && beanAnnotatedWithMonitor()")
public void publicMethodInsideAClassMarkedWithAtMonitor() {}
Advice the last pointcut that combines the first two and you're done!
If you're interested, I have written a cheat sheet with #AspectJ style here with a corresponding example document here.
Using annotations, as described in the question.
Annotation: #Monitor
Annotation on class, app/PagesController.java:
package app;
#Controller
#Monitor
public class PagesController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public #ResponseBody String home() {
return "w00t!";
}
}
Annotation on method, app/PagesController.java:
package app;
#Controller
public class PagesController {
#Monitor
#RequestMapping(value = "/", method = RequestMethod.GET)
public #ResponseBody String home() {
return "w00t!";
}
}
Custom annotation, app/Monitor.java:
package app;
#Component
#Target(value = {ElementType.METHOD, ElementType.TYPE})
#Retention(value = RetentionPolicy.RUNTIME)
public #interface Monitor {
}
Aspect for annotation, app/MonitorAspect.java:
package app;
#Component
#Aspect
public class MonitorAspect {
#Before(value = "#within(app.Monitor) || #annotation(app.Monitor)")
public void before(JoinPoint joinPoint) throws Throwable {
LogFactory.getLog(MonitorAspect.class).info("monitor.before, class: " + joinPoint.getSignature().getDeclaringType().getSimpleName() + ", method: " + joinPoint.getSignature().getName());
}
#After(value = "#within(app.Monitor) || #annotation(app.Monitor)")
public void after(JoinPoint joinPoint) throws Throwable {
LogFactory.getLog(MonitorAspect.class).info("monitor.after, class: " + joinPoint.getSignature().getDeclaringType().getSimpleName() + ", method: " + joinPoint.getSignature().getName());
}
}
Enable AspectJ, servlet-context.xml:
<aop:aspectj-autoproxy />
Include AspectJ libraries, pom.xml:
<artifactId>spring-aop</artifactId>
<artifactId>aspectjrt</artifactId>
<artifactId>aspectjweaver</artifactId>
<artifactId>cglib</artifactId>
Something like that:
#Before("execution(* com.yourpackage..*.*(..))")
public void monitor(JoinPoint jp) {
if (jp.getTarget().getClass().isAnnotationPresent(Monitor.class)) {
// perform the monitoring actions
}
}
Note that you must not have any other advice on the same class before this one, because the annotations will be lost after proxying.
Use
#Before("execution(* (#YourAnnotationAtClassLevel *).*(..))")
public void beforeYourAnnotation(JoinPoint proceedingJoinPoint) throws Throwable {
}
it should be enough to mark your aspect method like this:
#After("#annotation(com.marcot.CommitTransaction)")
public void after() {
have a look at this for a step by step guide on this.
You can also define the pointcut as
public pointcut publicMethodInsideAClassMarkedWithAtMonitor() : execution(public * (#Monitor *).*(..));
The simplest way seems to be :
#Around("execution(#MyHandling * com.exemple.YourService.*(..))")
public Object aroundServiceMethodAdvice(final ProceedingJoinPoint pjp)
throws Throwable {
// perform actions before
return pjp.proceed();
// perform actions after
}
It will intercept execution of all methods specifically annotated with '#MyHandling' in 'YourService' class. To intercept all methods without exception, just put the annotation directly on the class.
No matter of the private / public scope here, but keep in mind that spring-aop cannot use aspect for method calls in same instance (typically private ones), because it doesn't use the proxy class in this case.
We use #Around advice here, but it's basically the same syntax with #Before, #After or any advice.
By the way, #MyHandling annotation must be configured like this :
#Retention(RetentionPolicy.RUNTIME)
#Target( { ElementType.METHOD, ElementType.TYPE })
public #interface MyHandling {
}
I share with you a code that can be useful, it is to create an annotation that can be used either in a class or a method.
#Target({TYPE, METHOD})
#Retention(RUNTIME)
#Documented
public #interface AnnotationLogger {
/**
* It is the parameter is to show arguments in the method or the class.
*/
boolean showArguments() default false;
}
#Aspect
#Component
public class AnnotationLoggerAspect {
#Autowired
private Logger logger;
private static final String METHOD_NAME = "METHOD NAME: {} ";
private static final String ARGUMENTS = "ARGS: {} ";
#Before(value = "#within(com.org.example.annotations.AnnotationLogger) || #annotation(com.org.example.annotations.AnnotationLogger)")
public void logAdviceExecutionBefore(JoinPoint joinPoint){
CodeSignature codeSignature = (CodeSignature) joinPoint.getSignature();
AnnotationLogger annotationLogger = getAnnotationLogger(joinPoint);
if(annotationLogger!= null) {
StringBuilder annotationLoggerFormat = new StringBuilder();
List<Object> annotationLoggerArguments = new ArrayList<>();
annotationLoggerFormat.append(METHOD_NAME);
annotationLoggerArguments.add(codeSignature.getName());
if (annotationLogger.showArguments()) {
annotationLoggerFormat.append(ARGUMENTS);
List<?> argumentList = Arrays.asList(joinPoint.getArgs());
annotationLoggerArguments.add(argumentList.toString());
}
logger.error(annotationLoggerFormat.toString(), annotationLoggerArguments.toArray());
}
}
private AnnotationLogger getAnnotationLogger(JoinPoint joinPoint) {
AnnotationLogger annotationLogger = null;
try {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = joinPoint.getTarget().getClass().
getMethod(signature.getMethod().getName(), signature.getMethod().getParameterTypes());
if (method.isAnnotationPresent(AnnotationLogger.class)){
annotationLogger = method.getAnnotation(AnnotationLoggerAspect.class);
}else if (joinPoint.getTarget().getClass().isAnnotationPresent(AnnotationLoggerAspect.class)){
annotationLogger = joinPoint.getTarget().getClass().getAnnotation(AnnotationLoggerAspect.class);
}
return annotationLogger;
}catch(Exception e) {
return annotationLogger;
}
}
}
From Spring's AnnotationTransactionAspect:
/**
* Matches the execution of any public method in a type with the Transactional
* annotation, or any subtype of a type with the Transactional annotation.
*/
private pointcut executionOfAnyPublicMethodInAtTransactionalType() :
execution(public * ((#Transactional *)+).*(..)) && within(#Transactional *);
You could use Spring's PerformanceMonitoringInterceptor and programmatically register the advice using a beanpostprocessor.
#Target({ ElementType.TYPE, ElementType.METHOD })
#Retention(RetentionPolicy.RUNTIME)
#Inherited
#Documented
public #interface Monitorable
{
}
public class PerformanceMonitorBeanPostProcessor extends ProxyConfig implements BeanPostProcessor, BeanClassLoaderAware, Ordered,
InitializingBean
{
private Class<? extends Annotation> annotationType = Monitorable.class;
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
private Advisor advisor;
public void setBeanClassLoader(ClassLoader classLoader)
{
this.beanClassLoader = classLoader;
}
public int getOrder()
{
return LOWEST_PRECEDENCE;
}
public void afterPropertiesSet()
{
Pointcut pointcut = new AnnotationMatchingPointcut(this.annotationType, true);
Advice advice = getInterceptor();
this.advisor = new DefaultPointcutAdvisor(pointcut, advice);
}
private Advice getInterceptor()
{
return new PerformanceMonitoringInterceptor();
}
public Object postProcessBeforeInitialization(Object bean, String beanName)
{
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName)
{
if(bean instanceof AopInfrastructureBean)
{
return bean;
}
Class<?> targetClass = AopUtils.getTargetClass(bean);
if(AopUtils.canApply(this.advisor, targetClass))
{
if(bean instanceof Advised)
{
((Advised)bean).addAdvisor(this.advisor);
return bean;
}
else
{
ProxyFactory proxyFactory = new ProxyFactory(bean);
proxyFactory.copyFrom(this);
proxyFactory.addAdvisor(this.advisor);
return proxyFactory.getProxy(this.beanClassLoader);
}
}
else
{
return bean;
}
}
}

Categories