Разрабатывая приложения используя IoC-контейнер Spring думаю каждый задумывался, а как же «правильнее и красивее» создать логгер. В данной публикации хочу привести несколько примеров решения данной задачи.
Решение 1
Получаем логгер напрямую через LoggerFactory:
@Component public class MyBean { private static final Logger log = LoggerFactory.getLogger("application"); ... }
Данное решение является классическим, безусловно работающим, но нарушает саму идеологию IoC, ведь нам хочется, что бы работу по созданию логгера выполнил сам контейнер.
Решение 2
Получаем логгер из контейнера при помощи Autowired:
@Component public class MyBean { @Autowired private Logger log; ... }
Для этого в конфигурации Spring объявляем Bean:
@EnableAutoConfiguration @ComponentScan public class ApplicationConfiguration { @Bean public Logger logger(){ return LoggerFactory.getLogger("application"); } ... }
В данном решении задача по созданию логгера возложена на сам контейнер и укладывается в идеологию IoC, но что же делать, если логгеров в приложении должно быть больше одного?
Решение 3
Объявляем каждый логгер в виде отдельного Bean:
@EnableAutoConfiguration @ComponentScan public class ApplicationConfiguration { @Bean @Primary public Logger logger(){ return LoggerFactory.getLogger("application"); } @Bean(name = "loggerBean") public Logger loggerBean(){ return LoggerFactory.getLogger("loggerBean"); } ... }
Получаем нужный логгер используя соответствующий Qualifier:
@Component public class MyBean { @Autowired private Logger log; @Autowired @Qualifier("loggerBean") private Logger log2; ... }
Данное решение является достаточным в большинстве случаев, и использует только готовые средства контейнера. Одним из минусов данного решения является то, что при добавлении нового логгера всегда придется объявлять новый Bean. Есть ли более универсальный способ?
Решение 4
Получаем логгер из контейнера при помощи специальной аннотации, назовем ее Logging:
@Component public class MyBean { @Logging private Logger log; @Logging("loggerBean") private Logger log2; ... }
Для это собственно необходимо объявить аннотацию:
@Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface Logging { String value(); }
Данная аннотация будет указывать контейнеру на то, что необходим логгер с именем переданным в параметр value. Если данный параметр не указан, то логгер будет получен по классу компонента, в нашем случае MyBean.
Отлично, но контейнер не умеет обрабатывать нашу аннотацию. Давайте его научим, для этого создадим процессор:
public class LoggingAnnotationProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) { Class clazz = bean.getClass(); do { for (Field field : clazz.getDeclaredFields()) { Logging annotation = field.getAnnotation(Logging.class); if (annotation!= null) { boolean accessible = field.isAccessible(); field.setAccessible(true); try { if(!annotation.value().isEmpty()){ field.set(bean, LoggerFactory.getLogger(annotation.value())); } else { field.set(bean, LoggerFactory.getLogger(clazz)); } } catch (IllegalAccessException e) { LoggerFactory.getLogger(this.getClass()).error(e.getMessage(), e); } field.setAccessible(accessible); } } clazz = clazz.getSuperclass(); } while (clazz != null); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) { return bean; } }
И объявим процессор в конфигурации Spring:
@EnableAutoConfiguration @ComponentScan public class ApplicationConfiguration { @Bean public LoggingAnnotationProcessor loggingAnnotationProcessor(){ return new LoggingAnnotationProcessor(); } ... }
Данное решение является более универсальным, но необходимо дополнительно объявить аннотацию и написать для нее процессор.
Заключение
Друзья, предлагай в комментариях ваши варианты решения данной задачи, буду рад очень рад!
ссылка на оригинал статьи https://habrahabr.ru/post/276729/