Oleg Atamanenko
Сегодня я хочу рассказать, как можно сделать инициализацию логгера в классе с использованием аннотаций и BeanPostProcessor
Очень часто мы инициализируем логгер следующим образом:
public class MyClass {
private static final Logger LOG = LoggerFactory.getLogger(MyClass.class);
}
Я покажу, как сделать, чтобы можно было писать вот так:
@Log
private Logger LOG;
Первым делом нам нужно объявить аннотацию:
@Retention(RUNTIME)
@Target(FIELD)
@Documented
public @interface Log {
String category() default "";
}
А вторым делом, написать собственный BeanPostProcessor, который бы устанавливал нам логгер:
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
@Component
public class LoggerPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
@Override
public Object postProcessBeforeInitialization(final Object bean, final String beanName) {
ReflectionUtils.doWithFields(bean.getClass(), new FieldProcessor(bean, beanName), new LoggerFieldFilter());
return bean;
}
private static class FieldProcessor implements ReflectionUtils.FieldCallback {
private final Object bean;
private final String beanName;
private FieldProcessor(Object bean, String beanName) {
this.bean = bean;
this.beanName = beanName;
}
@Override
public void doWith(Field field) throws IllegalAccessException {
Log loggerAnnot = field.getAnnotation(Log.class);
// Sanity check if annotation is on the field with correct type.
if (field.getType().equals(org.slf4j.Logger.class)) {
// As user can override logger category - check if it was done.
String loggerCategory = loggerAnnot.category();
if (StringUtils.isBlank(loggerCategory)) {
// use default category instead.
loggerCategory = bean.getClass().getName();
}
Logger logger = LoggerFactory.getLogger(loggerCategory);
ReflectionUtils.makeAccessible(field);
field.set(bean, logger);
} else {
throw new IllegalArgumentException(
"Unable to set logger on field '" + field.getName() + "' in bean '" + beanName +
"': field should have class " + Logger.class.getName());
}
}
}
private static class LoggerFieldFilter implements ReflectionUtils.FieldFilter {
@Override
public boolean matches(Field field) {
Log logger = field.getAnnotation(Log.class);
return null != logger;
}
}
}
Если вы используете не sfl4j, а, например, log4j, или commons-logging, то нужно немного поправить код внутри метода doWith
Попутно, данный код показывает пример использования класса org.springframework.util.ReflectionUtils.