Monday, December 1, 2014

How to inject a logger variable by using annotation in Spring

In development, we need logger almost everywhere. you can inject a bean with annotation like inject other beans
package com.shengwangi.demo;
@Component
public class MyClass {
  @Loggable
  Logger logger;
  public void myMethod() {
    // do some thing
    logger.info("use the logger as usual"); 
  }
}
As you can see, annotation @Loggable is used to inject the logger. @Loggable is an annotation that we defined ourselves. Here is how to do it. (It's not my original idea, borrowed from stackoverflow Q&A here by Redder)

1. Define the annotation

Define the loggable annotation.
package com.shengwangi.demo;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(FIELD)
@Documented
public @interface Loggable {}
Besides the package and import lines there are 3 annotations before the one-line definition of Loggable. @Retention(RUNTIME) means the Annotation will be retained by the compiler and can be used by VM at run time. @Target(FIELD) means it will be used to annotate fields of class. @Documented it will be documented when you run javadoc to create documents.

2. Define LoggableInjector Class

Let's see the code first.
package com.shengwangi.demo;

import java.lang.reflect.Field;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Service;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;

@Service
public class LoggableInjector implements BeanPostProcessor {

  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName)
      throws BeansException {
    return bean;
  }

  @Override
  public Object postProcessBeforeInitialization(final Object bean,
      String beanName) throws BeansException {
    ReflectionUtils.doWithFields(bean.getClass(), new FieldCallback() {
      public void doWith(Field field) throws IllegalArgumentException,
          IllegalAccessException {
        // make the field accessible if defined private
        ReflectionUtils.makeAccessible(field);
        if (field.getAnnotation(Loggable.class) != null) {
          Logger log = LoggerFactory.getLogger(bean.getClass());
          field.set(bean, log);
        }
      }
    });
    return bean;
  }
}
The LoggableInjector implements Spring's BeanPostProcessor interface, which give you the chance to do something just before/after creation of beans. What the LoggableInjector class actually do is after creation of a bean object, find the field annotated by @Loggable, which we defined in step 1, then set the field value to a real logger.

3. Use annotation @Loggable to inject logger

Like we already did in the beginning of this article, using the annotation to inject logger to your beans.
@Loggable Logger logger
One thing need to notice is that the real logger is created after the bean object has been initialized, which means you can NOT use logger in the constructor of the bean. Because at that time, the logger field has not be defined, you'll get NullPointerException Error.

0 comments:

Post a Comment

Powered by Blogger.

About The Author

My Photo
Has been a senior software developer, project manager for 10+ years. Dedicate himself to Alcatel-Lucent and China Telecom for delivering software solutions.

Pages

Unordered List