Еще в Spring 1.2 была возможность посылать различные event-ы через весь контекст приложения. Я считаю этот вид коммуникации в некоторых местах приложения единственным верным решением.
Опишу кратко как работает этот механизм. Базовым классом для event-а должен быть org.springframework.context.ApplicationEvent
, для рассылки которого используется метод org.springframework.context.ApplicationContext.publishEvent(event)
. Ну а получать event-ы будут те bean-ы, которые реализуют интерфейс org.springframework.context.ApplicationListener
с единственным методом onApplicationEvent(event)
.
Допустим, что у нас есть логика, после выполнения которой другим сервисам необоходимо также сделать какие-то действия. Стандартный вариант решения такой задачи предполагает использование IoC:
public class BussinesServiceImpl implements BussinesService {
@Autowired
private EmailService emailService;
@Autowired
private LoggingService logService;
public void doAction() {
...
emailService.sendEmail(email, body);
logService.recordLog(data);
}
}
Также можно использовать и аспекты. Только вот получать параметры, которые пришли не через аргументы метода, advice-ам будет сложно. Реализовать такой случай с помощью event-ов можно так:
public class BussinesServiceImpl implements BussinesService, ApplicationContextAware {
private ApplicationContext context;
public void doAction() {
...
ActionEvent event = new ActionEvent();
event.setEmail(email);
event.setEmailBody(body);
event.setData(data);
context.publishEvent(event);
}
public void setApplicationContext(ApplicationContext value) {
context = value;
}
}
Соответственно сами сервисы будут выглядеть так:
public class EmailServiceImpl implements EmailService, ApplicationListener {
...
public void onApplicationEvent(ApplicationEvent event) {
if (!(event instanceof ActionEvent)) {
return;
}
ActionEvent actionEvent = (ActionEvent) event;
sendEmail(actionEvent.getEmail(), actionEvent.getEmailBody());
}
}
public class LoggingServiceImpl implements LoggingService, ApplicationListener {
...
public void onApplicationEvent(ApplicationEvent event) {
if (!(event instanceof ActionEvent)) {
return;
}
ActionEvent actionEvent = (ActionEvent) event;
recordLog(actionEvent.getData());
}
}
Какое преимущество дает такой подход? Изоляцию сервиса, от лишних зависимостей. Такая изоляция дает возможность выносить сервисы типа BussinesService
в отдельные модули, которые не потребуют в дальнейшем модификации, если понадобится добавить вызов какого-либо сервиса.
10 комментариев:
Спасибо за действительно полезные статьи :)
Хм, а это не применение ли шаблона "Visitor"? Т.е. вместо добавления новых публичных методов в сервисы, добавляете наследников ActionEvent...
По поводу паттерна Visitor Вы явно ошиблись, он здесь никак себя не проявляет.
А помоему, это похоже на синглетон
А как это будет работать в кластере серверов?
В клястере это работать не будет, т.к. работает лишь в текущем контексте.
Хотел сделать предложение рассказать о том, как заставить Spring работать в кластере серверов, но анонимам это нельзя. Так что пишу здесь.
Что именно нельзя анонимам? Вы же оставили свой комментарий. Про клястера - я подумаю как это можно решить.
Pretty interesting blog you've got here. Thank you for it. I like such themes and anything connected to this matter. I would like to read more on that blog soon.
Best wishes
Darek Wish
Никита, как Вы ухитряетесь создавать свой ActionEvent таким способом
ActionEvent event = new ActionEvent();
?
Ведь метод context.publishEvent принимает ApplicationEvent, а класс, отнаследованный от ApplicationEvent должен обязательно иметь конструктор, принимающий Object.
Отправить комментарий