Custom annotation as Interceptor for a method logging
Solution 1
Based in your answers of my comments, you will not be able to do this with just annotations. You can, of course, create your annotations and create some reflective code that will detected then and execute some code, but this will not change your code too much, because you will need to call the parser
method before you call your methods and I think that will not help you too much, since you will need to call the parser method before each call.
If you need the behavior that you mentioned (automatic call), you will need to combine your annotations with some AOP framework like Spring (plain Java) or AspectJ (AspectJ code). With then, you can set pointcuts and everytime this point is reached, some code may be executed. You can configure then to execute some code before and/or after method execution.
If the first scenario is sufficient, you can do something like:
Logger: enum
public enum Logger {
INFO,
DEBUG;
}
LogMethodCall: annotation
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention( RetentionPolicy.RUNTIME ) // the annotation will be available during runtime
@Target( ElementType.METHOD ) // this can just used in methods
public @interface LogMethodCall {
Logger logLevel() default Logger.INFO;
}
Person: annotated class
public class Person {
// will use the default log level (INFO)
@LogMethodCall
public void foo( int a ) {
System.out.println( "foo! " + a );
}
@LogMethodCall( logLevel = Logger.DEBUG )
public void bar( int b ) {
System.out.println( "bar! " + b );
}
}
Utils: class with the log static method (this will perform the "parsing")
public class Utils {
public static void log( Object o, String methodName ) {
// gets the object class
Class klass = o.getClass();
// iterate over its methods
for ( Method m : klass.getMethods() ) {
// verify if the method is the wanted one
if ( m.getName().equals( methodName ) ) {
// yes, it is
// so, iterate over its annotations
for ( Annotation a : m.getAnnotations() ) {
// verify if it is a LogMethodCall annotation
if ( a instanceof LogMethodCall ) {
// yes, it is
// so, cast it
LogMethodCall lmc = ( LogMethodCall ) a;
// verify the log level
switch ( lmc.logLevel() ) {
case INFO:
System.out.println( "performing info log for \"" + m.getName() + "\" method" );
break;
case DEBUG:
System.out.println( "performing debug log for \"" + m.getName() + "\" method" );
break;
}
}
}
// method encountered, so the loop can be break
break;
}
}
}
}
AnnotationProcessing: class with code to test the annotation processing
public class AnnotationProcessing {
public static void main(String[] args) {
Person p = new Person();
Utils.log( p, "foo" );
p.foo( 2 );
Utils.log( p, "bar" );
p.bar( 3 );
}
}
Of course, you will need to improve my code to fit your needs. It is just a start point.
More about annotations:
- http://docs.oracle.com/javase/1.5.0/docs/guide/language/annotations.html
- http://docs.oracle.com/javase/tutorial/java/javaOO/annotations.html
- http://tutorials.jenkov.com/java-reflection/annotations.html
More about AOP:
- http://en.wikipedia.org/wiki/Aspect-oriented_programming
- http://static.springsource.org/spring/docs/3.0.x/reference/aop.html
- http://www.eclipse.org/aspectj/
Solution 2
Use Spring AOP along with Java Annotation. Spring AOP negates the requirement for writing a util class for parsing of Java classes using Java Reflection.
Example -
-
Custom Annotation -
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface A { boolean startA() default false; boolean endA() default false; }
-
Aspect-
@Aspect public class AAspect { @Pointcut(value = "execution(* *.*(..))") public void allMethods() { LOGGER.debug("Inside all methods"); } @Before("allMethods() && @annotation(A)") public void startAProcess(JoinPoint pjp, A a) throws Throwable { if (a.startA()) { //Do something } }
-
Enable AspectJ -
@Configuration @EnableAspectJAutoProxy public class AConfig { }
-
Use in code -
@A(startA = true, endA = true) public void setUp(){ //Do something- logic }
Solution 3
As already suggested, AOP and annotations is the best option. I would recommend to use a ready-made mechanism from jcabi-aspects (I'm a developer):
@Loggable(Loggable.DEBUG)
public String load(URL url) {
return url.openConnection().getContent();
}
All method calls will be logged to SLF4J.
Bharat Sinha
Principal Software Engineer at Entertainment Partners
Updated on August 29, 2020Comments
-
Bharat Sinha over 3 years
Java Gurus,
I am pretty new for
annotations
and haven't searched for this a lot, so please bear with me...I would like to implement a
Custom Annotation
which willintercept
a method call; to start with something very basic it can just print the methods name and parameters so that I could avoid thelogger
statement.A sample call like this:
public MyAppObject findMyAppObjectById(Long id) throws MyCustomException { log.debug("in findMyAppObjectById(" + id + ")"); //.... }
can be converted into:
@LogMethodCall(Logger.DEBUG) public MyAppObject findMyAppObjectById(Long id) throws MyCustomException { //.... }
Could I get some hints about this?
-
Bharat Sinha over 11 yearsI will; as soon as I get into office... Thanks a lot for all the pain you've taken from me for writing such a nice answer!!!
-
steve cook over 7 yearsthis should really be the accepted answer. much simpler than above.
-
Josh M. almost 6 yearsIn this example, if I call one of the methods in the
Person
class, does it log every time? From looking at the code, it seems the only place any logging would occur is in theAnnotationProcessing.main()
method. Where is the code to execute logging for every method call in thePerson
class? (I may misunderstand the requirements/solution -- thanks). -
davidbuzatto over 5 years@JoshM. I'm really late to answer you! No, it isn't automatic, it's just a starting point for OP if this manual scenario is sufficient for him. To have automatic behavior he will need some AOP framework or write his own.
-
VLAZ about 5 years@stevecook but this requires to use Spring AOP. If you are using core Java, then the above is what you end up with.