vendredi 6 juillet 2012

Injecter un logger dans les spring beans

L'utilisation d'un logger est très importante dans tout projet, pour ma part j'utilise la bibliothèque LogBack (j'expliquerais dans un autre billet en détail les raisons qui m'ont motivé a le choisir).
Il nous faut donc une instance du logger dans chaque classe comme dans le code suivant :
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class UserService {
  
  private static Logger log = LoggerFactory.getLogger(UserService.class);
  
  public void create(String nom) {
        logger.debug("User with name " + args + " is created");
  }
}

Il serait interessant de pouvoir injecter une instance du logger avec Spring en utilisant les annotations.
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;

@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.FIELD)  
@Documented
public @interface Log {
    
}
Puis on définit un Spring bean processor
import java.lang.reflect.Field;
import org.slf4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;

import org.slf4j.LoggerFactory;

public class LoggerInjector implements BeanPostProcessor {

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

    @Override
    public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
        ReflectionUtils.doWithFields(bean.getClass(), new FieldCallback() {

            @Override
            public void doWith(Field field) throws IllegalArgumentException,
                    IllegalAccessException {
                 
                ReflectionUtils.makeAccessible(field);
                if (field.getAnnotation(Log.class) != null) {
                    Logger log = LoggerFactory.getLogger(bean.getClass());
                    field.set(bean, log);
                }
            }
        });
        return bean;
    }
}

Ansi nous pouvons injecter un logger en utilisant l'annotation @Log
@Service
public class UserService {
  
   @Log
   Logger log;
  
  public void create(String nom) {
        log.debug("User with name " + args + " is created");
  }
}

Dans le fichier de configuration de Spring nous activons le component-scan en replaçant "le_nom_du_package" par le nom du package ou sont définie les Spring beans (par exemple : com.monapp )
 

<context:component-scan base-package="le_nom_du_package"/>