Spring AOP - Custom Annotation for Class and Method - java

Existing answers gives nice explanation on how to use Custom Annotation for method execution time logging. I am wondering if there is way to use same annotation for both Class and Method, but Pointcut should be different where it is used.
#LogExecutionTime
public class MyServiceImpl implements MyService {
public void run(){
// logic
}
public void walk(){
// logic
}
private void breather(){
// logic
}
}
If Annotation is used for class all methods inside class should be considered for Execution Time Logging in Aspect Class (like execution(* com.me.package.MyServiceImpl.*(..))). However if the Annotation is only used for single method inside the class, it is should also consider that only method in Aspect Logging Class. (like execution(* com.you.package.YourServiceImpl.forward(..))).
public class YourServiceImpl implements YourService {
#LogExecutionTime
public void forward(){
// logic
}
#LogExecutionTime
public void backward(){
// logic
}
private void upward(){
// logic
}
}
Annotation Class
package com.myproj.core.utils.annotation;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
#Retention(RUNTIME)
public #interface LogExecutionTime {
}
Aspect Class for Annotation (using pointcuts as suggested by #kriegaex)
package com.myproj.core.utils;
import java.time.Duration;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import java.time.Instant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Aspect class to Log Method Execution time
*/
#Aspect
public class MyMethodExecutionLoggingAspect {
private static final Logger LOG = LoggerFactory.getLogger(MyMethodExecutionLoggingAspect.class);
/**
* This method will log Method Execution Time
*
* #param joinPoint
* #return object
* #throws Throwable
*/
#Around("execution(* (#com.myproj.core.utils.annotation.LogExecutionTime *).*(..)) || execution(#com.myproj.core.utils.annotation.LogExecutionTime * *(..))")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
String className = joinPoint.getTarget().getClass().getSimpleName();
String methodName = joinPoint.getSignature().getName();
Instant start = Instant.now();
try {
return joinPoint.proceed();
} finally {
long end = Duration.between(start, Instant.now()).toMillis();
if (end > 0) {
LOG.debug("METHOD EXECUTION TIME LAPSED: {}ms | {}.{}", end, className, methodName);
}
}
}
}
Spring Bean Definition in spring.xml
<bean class="com.myproj.core.utils.MyMethodExecutionLoggingAspect" />

Your sample code with #annotation() does not make sense because you do not specify a method annotation type. It should be something like #annotation(fully.qualified.AnnotationType) instead.
For matching class annotations you want to use #within(fully.qualified.AnnotationType), as described here.
So you could use a pointcut like
#annotation(fully.qualified.AnnotationType)
|| #within(fully.qualified.AnnotationType)
Alternatively, according to my answer here, you could also use the more cryptic version
execution(* (#fully.qualified.AnnotationType *).*(..))
|| execution(#fully.qualified.AnnotationType * *(..))

Related

Proxy for abstract class without changing the usage

I have an abstract class (database mapping) implementing an interface where default implementations are injected at runtime (this is part of another library and cannot be changed).
I want to override one of the default implementation via a proxy (as that seems like the way to override this).
public abstract class Table1 implements Storable<Table1>
{
#Sequence("ID_SEQUENCE")
#Alias("ID")
public abstract String getID();
public abstract void setID(String ID);
#Alias("NAME")
public abstract String getAvailabilityZone();
public abstract void setAvailabilityZone(String value);
}
public interface Storable<S extends Storable<S>> {
//a bunch of method definition.
boolean tryLoad() throws Exception;
}
Let's say I want to override tryLoad() method to do my own things instead of what the generated code provides. Given the nature of the library, it is not something I can achieve by simple #Override.
The simple way this is currently used is as following:
public void method() {
Table1 t = Repository.storageFor(Table1.class).prepare();
t.setName( "temp" );
if (!t.tryLoad())
t.tryInsert();
}
I want to proxy tryLoad() without making changes in all the methods across the whole codebase - that would be to get proxied instance instead of actual one and perform the operation on that.
Is there any recommended way to achieve this?
Thanks!
I woke up last night and felt bored, so despite your lack of feedback I created a little Carbonado showcase project and shared it on GitHub. I made three commits:
Initial commit with Maven project already prepared for AspectJ and a JUnit test for me to find out how Carbonado actually works, because I had never used it before.
Add failing unit test for behaviour of tryLoad() expected to be provided by aspect.
Add aspect to make unit test pass. Aspect hooks into tryLoad() and auto-creates non-existent record. I do not know if I guessed right what you actually wanted to achieve, but if it was a different thing, just change the aspect implementation.
Sample code
Carbonado storable:
package de.scrum_master.app;
import com.amazon.carbonado.Nullable;
import com.amazon.carbonado.PrimaryKey;
import com.amazon.carbonado.Storable;
#PrimaryKey("ID")
public interface StoredMessage extends Storable<StoredMessage> {
long getID();
void setID(long id);
#Nullable String getMessage();
void setMessage(String message);
}
Aspect:
package de.scrum_master.aspect;
import com.amazon.carbonado.Storable;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
#Aspect
public class CarbonadoAspect {
#Around("call(boolean tryLoad()) && target(storable)")
public boolean tryInsertIfNotFound(ProceedingJoinPoint thisJoinPoint, Storable storable) throws Throwable {
System.out.println(thisJoinPoint);
if ((boolean) thisJoinPoint.proceed())
return true;
System.out.println("Not found: " + storable + " -> inserting");
return storable.tryInsert();
}
}
JUnit test:
package de.scrum_master.app;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.amazon.carbonado.FetchException;
import com.amazon.carbonado.PersistException;
import com.amazon.carbonado.Repository;
import com.amazon.carbonado.RepositoryException;
import com.amazon.carbonado.Storage;
import com.amazon.carbonado.SupportException;
import com.amazon.carbonado.repo.map.MapRepositoryBuilder;
import de.scrum_master.app.StoredMessage;
public class CarbonadoTest {
private Repository repo;
private Storage<StoredMessage> storage;
StoredMessage message;
#Before
public void setUp() throws Exception {
repo = MapRepositoryBuilder.newRepository();
storage = repo.storageFor(StoredMessage.class);
message = storage.prepare();
}
#After
public void tearDown() throws Exception {
repo.close();
repo = null;
storage = null;
message = null;
}
// (...)
#Test
public void aspectCreatesNonExistentRecord() throws SupportException, RepositoryException {
message.setID(1);
// Without the aspect this would be false
assertTrue(message.tryLoad());
assertEquals(message.getID(), 1);
assertEquals(message.getMessage(), null);
}
}
Enjoy!

springboot logging with aspectj getting IllegalArgumentException: error at ::0 formal unbound in pointcut

I want to create an aspectJ component within springboot project, that prints log messages wherever #Loggable annotation is present, methods or class, or maybe both (method will be considered).
Loggable annotation:
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE, ElementType.METHOD })
public #interface Loggable {
boolean duration() default false;
}
Aspectj class:
#Aspect
#Component
public class LogInterceptorAspect {
#Pointcut("execution(public * ((#Loggable *)+).*(..)) && within(#Loggable *)")
public boolean loggableDefinition(Loggable loggable) {
return loggable.duration();
}
#Around("loggableDefinition(withDuration)")
public void log(ProceedingJoinPoint joinPoint, boolean withDuration) throws Throwable {
getLogger(joinPoint).info("start {}", joinPoint.getSignature().getName());
StopWatch sw = new StopWatch();
Object returnVal = null;
try {
sw.start();
returnVal = joinPoint.proceed();
} finally {
sw.stop();
}
getLogger(joinPoint).info("return value: {}, duration: {}", returnVal, sw.getTotalTimeMillis()));
}
private Logger getLogger(JoinPoint joinPoint) {
return LoggerFactory.getLogger(joinPoint.getSignature().getDeclaringType());
}
}
With the above code I get
java.lang.IllegalArgumentException: error at ::0 formal unbound in pointcut
What's wrong?
Basically the formal parameter is unbound on the PointCut.
Here is an alternate working example based on the approach detailed in this post: #AspectJ Class level Annotation Advice with Annotation as method argument
I modified your approach slightly to avoid the problem for a couple of reasons :
simplified the initial PointCut and gave it a single responsibility
gave it a descriptive name indicating its purpose
made it more reusable by removing the dependency on Loggable
kept it close in implementation to most of the sample documentation available
broke the Advice into two simpler methods each with a single responsibility that is easy to comprehend
simplified the expressions by removing fancy operators
injected the annotation directly into the Advice where its used rather than attempting to pass from the PointCut which feels like an unnecessary complexity
kept it close in implementation to most of the sample documentation available
added the start of a unit test to verify the expected behavior so that changes can be made responsibly to the PointCut and Advice expressions (you should complete it)
When working with PointCut/Advice Expressions, I generally try to go for the simplest, clearest solutions possible and unit test them thoroughly to ensure the behavior I expect is what I get. The next person to look at your code will appreciate it.
Hope this helps.
package com.spring.aspects;
import static org.junit.Assert.assertEquals;
import org.aspectj.lang.JoinPoint;
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.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.StopWatch;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = AspectInjectAnnotationTest.TestContext.class)
public class AspectInjectAnnotationTest {
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE, ElementType.METHOD })
public #interface Loggable {
boolean duration() default false;
}
#Aspect
public static class LogInterceptorAspect {
#Pointcut("execution(public * *(..))")
public void anyPublicMethod() {
}
#Around("anyPublicMethod() && #annotation(loggable)")
public Object aroundLoggableMethods(ProceedingJoinPoint joinPoint, Loggable loggable) throws Throwable {
return log(joinPoint, loggable);
}
#Around("(anyPublicMethod() && !#annotation(AspectInjectAnnotationTest.Loggable)) && #within(loggable)")
public Object aroundPublicMethodsOnLoggableClasses(ProceedingJoinPoint joinPoint, Loggable loggable)
throws Throwable {
return log(joinPoint, loggable);
}
public Object log(ProceedingJoinPoint joinPoint, Loggable loggable) throws Throwable {
getLogger(joinPoint).info("start [{}], duration [{}]", joinPoint.getSignature().getName(),
loggable.duration());
StopWatch sw = new StopWatch();
Object returnVal = null;
try {
sw.start();
returnVal = joinPoint.proceed();
} finally {
sw.stop();
}
getLogger(joinPoint).info("return value: [{}], duration: [{}]", returnVal, sw.getTotalTimeMillis());
return returnVal;
}
private Logger getLogger(JoinPoint joinPoint) {
return LoggerFactory.getLogger(joinPoint.getSignature().getDeclaringType());
}
}
// class level annotation - should only proxy public methods
#Loggable(duration = true)
public static class Service1 {
// public - should be proxied
public String testS1M1(String test) {
return testProtectedM(test);
}
// public - should be proxied
public String testS1M2(String test) {
return testProtectedM(test);
}
// protected - should not be proxied
protected String testProtectedM(String test) {
return testPrivateM(test);
}
// private - should not be proxied
private String testPrivateM(String test) {
return test;
}
}
// no annotation - class uses method level
public static class Service2 {
#Loggable
public String testS2M1(String test) {
return protectedMethod(test);
}
// no annotation - should not be proxied
public String testS2M2(String test) {
return protectedMethod(test);
}
// protected - should not be proxied
protected String protectedMethod(String test) {
return testPrivate(test);
}
// private - should not be proxied
private String testPrivate(String test) {
return test;
}
}
// annotation - class and method level - make sure only call once
#Loggable
public static class Service3 {
#Loggable
public String testS3M1(String test) {
return test;
}
}
// context configuration for the test class
#Configuration
#EnableAspectJAutoProxy
public static class TestContext {
// configure the aspect
#Bean
public LogInterceptorAspect loggingAspect() {
return new LogInterceptorAspect();
}
// configure a proxied beans
#Bean
public Service1 service1() {
return new Service1();
}
// configure a proxied bean
#Bean
public Service2 service2() {
return new Service2();
}
// configure a proxied bean
#Bean
public Service3 service3() {
return new Service3();
}
}
#Autowired
private Service1 service1;
#Autowired
private Service2 service2;
#Autowired
private Service3 service3;
#Test
public void aspectShouldLogAsExpected() {
// observe the output in the log, but craft this into specific
// unit tests to assert the behavior you are expecting.
assertEquals("service-1-method-1", service1.testS1M1("service-1-method-1")); // expect logging
assertEquals("service-1-method-2", service1.testS1M2("service-1-method-2")); // expect logging
assertEquals("service-2-method-1", service2.testS2M1("service-2-method-1")); // expect logging
assertEquals("service-2-method-2", service2.testS2M2("service-2-method-2")); // expect no logging
assertEquals("service-3-method-1", service3.testS3M1("service-3-method-1")); // expect logging once
}
}

AspectJ - Pointcut at specified method with a param annotated with class level annotation

In an aspect, i'd like stop at a specified method. This method has one parameter which is annotated with a class level annotation:
The annotation is:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface Auditable {}
The parameter is an object of a class annotated like:
#Auditable
public class User {}
The method I like to inspect:
public Object findSingleResultByExample(final Object entity) {}
This aspect is not working:
#AfterReturning(value="execution(* org.wtp.repository.GenericDao.find*(#org.wtp.aspects.Auditable (*)))",
argNames = "joinPoint, result",
returning = "result")
private void auditFindAnnotation(final JoinPoint joinPoint, final Object result) {}
First of all, your advice method must be public, not private. Please change that into
public void auditFindAnnotation(...)
It is not working because your pointcut intercepts methods with an #Auditable parameter annotation. Your sample method does not have such an annotation, though. It would work if the method signature was like this:
public Object findSingleResultByExample(final #Auditable Object entity) {}
BTW, then the #Target(ElementType.TYPE) restriction must be removed or extended in order for the code to still compile.
But I guess what you want is not to match on parameter annotations, but on type annotations. Then your pointcut would look like this (no parentheses around * this time):
execution(* org.wtp.repository.GenericDao.find*(#org.wtp.aspects.Auditable *))
But again, this does not match your sample method because its parameter type is not User or Auditable but just Object and the latter does not carry the annotation. You can see the difference if you overload your find* method and do something like this:
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface Auditable {}
package de.scrum_master.app;
#Auditable
public class User {}
package de.scrum_master.app;
import java.util.ArrayList;
public class Application {
public Object findSingleResultByExample(final Object entity) {
return entity;
}
public Object findSingleResultByExample(final User entity) {
return entity;
}
public static void main(String[] args) {
Application application = new Application();
application.findSingleResultByExample("foo");
application.findSingleResultByExample(new User());
application.findSingleResultByExample(new ArrayList<String>());
}
}
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
#Aspect
public class AuditAspect {
#AfterReturning(
value = "execution(* de.scrum_master.app..find*(#de.scrum_master.app.Auditable *))",
argNames = "thisJoinPoint, result",
returning = "result"
)
public void auditFindAnnotation(final JoinPoint thisJoinPoint, final Object result) {
System.out.println(thisJoinPoint + " -> " + result);
}
}
The console log then looks like this:
execution(Object de.scrum_master.app.Application.findSingleResultByExample(User)) -> de.scrum_master.app.User#4a574795
Update: In order to get the whole thing working without changing or overloading any method signatures, you would have to make your pointcut match all calls and dynamically determine the type and its annotations from withing the aspect via reflection (not so nice, but possible). Feel free to ask questions if you do not understand this idea.

AspectJ - Multiple #annotation Pointcut

I can't make a pointcut with "||" operator and multiple annotations. I'm trying to create a Pointcut for some JBehave annotations (#Given, #Then, #When).
This works fine:
#Pointcut("#annotation(given)")
public void jBehaveGivenPointcut(Given given) { }
and if I create and advice arount it, it also works.
What would be the syntax for making the Pointcut for the three annotations? Since I had used the logical OR operator in other pointcuts I would assume it is something like:
#Pointcut("#annotation(given) || #annotation(then) || #annotation(when) ")
public void jBehaveGivenPointcut(Given given, Then then, When when) { }
but it doesn't work, I get an inconsist binding exception. I tried other combinations but couldn't find the one that does the trick.
What you want cannot be done this way because, as the error message says, the annotation binding is inconsistent: You cannot bind all three annotations at the same time because they are in (possibly mutually exclusive) OR parts of the pointcut, i.e. usually only of of them can be bound (unless you assign multiple annotations to the same method). Your expectation might be that AspectJ can deal with this inconsistency by just assigning null to the other two if one is bound, but this is not how the compiler works right now.
I can offer a workaround which involves reflection instead of using the #annotation() binding.
Driver application:
package de.scrum_master.app;
import org.jbehave.core.annotations.Given;
import org.jbehave.core.annotations.Then;
import org.jbehave.core.annotations.When;
public class Application {
public static void main(String[] args) {
doGiven("foo");
doSomething("bar");
doWhen(11);
doSomethingElse(22);
doThen();
}
#Given("an input value") public static void doGiven(String string) {}
#When("I do something") public static void doWhen(int i) {}
#Then("I should obtain a result") public static boolean doThen() { return true; }
public static void doSomething(String string) {}
public static void doSomethingElse(int i) {}
}
Aspect:
package de.scrum_master.aspect;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
#Aspect
public class JBehaveInterceptor {
#Pointcut("execution(#org.jbehave.core.annotations.* * *(..))")
public void jBehavePointcut() {}
#Before("jBehavePointcut()")
public void jBehaveAdvice(JoinPoint.StaticPart thisJoinPointStaticPart) {
Method method = ((MethodSignature) thisJoinPointStaticPart.getSignature()).getMethod();
for (Annotation jBehaveAnnotation : method.getAnnotations()) {
if (jBehaveAnnotation.annotationType().getPackage().getName().equals("org.jbehave.core.annotations"))
System.out.println(thisJoinPointStaticPart + " -> " + jBehaveAnnotation);
}
}
}
As you can see, the pointcut only intercepts methods annotated by org.jbehave.core.annotations.* which narrows down pointcut matching considerably - to more than just #Given, #When, #Then, but maybe that is even what you want because JBehave offers more annotations than just those.
In the advice we loop over all method annotations because there might be more than just the JBehave ones. If any annotation package name matches the corresponding JBehave package, we do something (in this case print the annotation to standard output).
I hope this solves your problem. I cannot extend the AspectJ language for you, this is the best I can think of. Anyway, this yields the following output:
execution(void de.scrum_master.app.Application.doGiven(String)) -> #org.jbehave.core.annotations.Given(priority=0, value=an input value)
execution(void de.scrum_master.app.Application.doWhen(int)) -> #org.jbehave.core.annotations.When(priority=0, value=I do something)
execution(boolean de.scrum_master.app.Application.doThen()) -> #org.jbehave.core.annotations.Then(priority=0, value=I should obtain a result)
This works:
#Component
#Aspect
#RequiredArgsConstructor
public class PermissionAspect {
#Pointcut("#annotation(org.springframework.web.bind.annotation.GetMapping)")
public void getMapping(){}
#Pointcut("#annotation(org.springframework.web.bind.annotation.PostMapping)")
public void postMapping(){}
#Pointcut("#annotation(org.springframework.web.bind.annotation.PutMapping)")
public void putMapping(){}
#Pointcut("#annotation(org.springframework.web.bind.annotation.DeleteMapping)")
public void deleteMapping(){}
#Around("(execution(* xx.*.controller.*.*(..))) && (getMapping() || postMapping() || putMapping() || deleteMapping())")
Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

#AspectJ Class level Annotation Advice with Annotation as method argument

How can I get the annotation to be passed as the argument for the Advice defined for
class-level annotation? Is it possible?
From the post here I am able to get the point cut that identifies all the public method in the class which is marked by a specific Annotation. I am able to get the advice applied as well. However, I don’t know how to get the annotation variable passed as argument in above case.
For a method-level annotation, I am able to get the pointcut and advice in which I can get the annotation passed as argument, but I don’t know how to achieve the same for class-level annotation.
The below code works, but I need to get the annotation as the argument for the advice “LogExecutionTimeByClass” in below program and I couldn’t able to get appropriate advice or pointcut for the same.
Annotation:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface LogExecutionTime {
String level();
}
Aspect:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
#Aspect
public class LogTimeAspect {
/*
* Pointcut to match all the public methods.
*/
#Pointcut("execution(public * *(..))")
public void publicMethod() {}
/*
* Advice for the public methods that are marked with Annotation "LogExecutionTime" and it works as expected no issue.
*/
#Around("publicMethod() && #annotation(annotation) ")
public Object LogExecutionTimeByMethod(final ProceedingJoinPoint joinPoint,final LogExecutionTime annotation) throws Throwable
{
System.out.println("Invoking the method " +joinPoint.getSignature() +" by LogExecutionTimeByMethod Advice");
return joinPoint.proceed();
}
/*
* Pointcut to match all the public methods that are defined under the Class marked with Annotation LogExecutionTime.
*/
#Pointcut("within(#LogExecutionTime *)")
public void beanAnnotatedWithMonitor() {}
#Pointcut("publicMethod() && beanAnnotatedWithMonitor()")
public void publicMethodInsideAClassMarkedWithAtMonitor() {}
/*
* Below Advice works but I need the LogExecutionTime annotation as an argument to below method. (similar to the advice "LogExecutionTimeByMethod"
* defined above)
*/
#Around("publicMethodInsideAClassMarkedWithAtMonitor()")
public Object LogExecutionTimeByClass(final ProceedingJoinPoint joinPoint) throws Throwable
{
System.out.println("Invoking the method " +joinPoint.getSignature() +" by LogExecutionTimeByClass Advice");
//System.out.println("Invoked by " + annotation.value()); //Need the Annotation Variable here as well...
return joinPoint.proceed();
}
/*
*/
}
Annotated Class:
#LogExecutionTime(level="Class_Level_Invocation")
public class Operator {
#LogExecutionTime(level="Method_Level_Invocation")
public void operate() throws InterruptedException {
Thread.sleep(1000);
}
public void operate1() throws InterruptedException {
Thread.sleep(1000);
}
}
Main Program:
public class AspectJMain {
public static void main(String[] args) throws InterruptedException {
Operator op = new Operator();
op.operate();
op.operate1();
}
}
Output:
Invoking the method void Operator.operate() by LogExecutionTimeByMethod Advice
Invoking the method void Operator.operate() by LogExecutionTimeByClass Advice
Invoking the method void Operator.operate1() by LogExecutionTimeByClass Advice
Please note that using Spring is is not an option. I have to use AspectJ compiler.
I compiled my classes and packaged them as jar and use ApsectJ compiler to woven the aspect using below command.
ajc -inpath core.jar -outjar ..\lib\core_woven.jar -1.5
Any pointer would be helpful.
The solution is actually quite simple. I am writing my code in native AspectJ style, I prefer it for clarity. You will be easily able to adjust it to #AspectJ annotation style:
public aspect LogTimeAspect {
pointcut publicMethod() : execution(public * *(..));
before(LogExecutionTime logAnn) : publicMethod() && #annotation(logAnn) {
System.out.println(thisJoinPointStaticPart + " -> " + logAnn.level());
}
before(LogExecutionTime logAnn) : publicMethod() && #within(logAnn) {
System.out.println(thisJoinPointStaticPart + " -> " + logAnn.level());
}
}
The output is as follows:
execution(void Operator.operate()) -> Method_Level_Invocation
execution(void Operator.operate()) -> Class_Level_Invocation
execution(void Operator.operate1()) -> Class_Level_Invocation
As you can see,
there is no need for around() advice, before() is sufficient unless you want to manipulate any parameters or block the captured method executions,
you can bind the annotations in question via #annotation() or #within() to named parameters if you just use the correct syntax.
Enjoy! :-)
Update: Here is the #AspectJ version of the aspect for your convenience and because you seemed to have problems adapting my solution from the native syntax:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
#Aspect
public class LogTimeAspect {
#Pointcut("execution(public * *(..))")
public void publicMethod() {}
#Around("publicMethod() && #annotation(logAnn)")
public Object LogExecutionTimeByMethod(ProceedingJoinPoint joinPoint, LogExecutionTime logAnn) throws Throwable {
System.out.println(joinPoint + " -> " + logAnn.level());
return joinPoint.proceed();
}
#Around("publicMethod() && #within(logAnn)")
public Object LogExecutionTimeByClass(ProceedingJoinPoint joinPoint, LogExecutionTime logAnn) throws Throwable {
System.out.println(joinPoint + " -> " + logAnn.level());
return joinPoint.proceed();
}
}
The results will be identical to my original version.

Categories