У меня есть форма, которая на основе собранной информации создает отчет. У меня есть несколько источников для создания отчетов, но форма для них одинакова. Я попытался реализовать шаблон стратегии, используя интерфейс, реализующий службы генератора отчетов, но это привело к тому, что калитка жаловалась на проблемы с сериализацией различных частей генератора отчетов. Я хотел бы решить эту проблему, не дублируя код, содержащийся в форме, но мне не удалось найти информацию о динамической инъекции с помощью @SpringBean.
Вот грубый макет того, что у меня есть
public class ReportForm extends Panel {
private IReportGenerator reportGenerator;
public ReportForm(String id, IReportGenerator reportGenerator) {
super(id);
this.reportGenerator = reportGenerator;
final Form<Void> form = new Form<Void>("form");
this.add(form);
...
form.add(new AjaxButton("button1") {
private static final long serialVersionUID = 1L;
@Override
protected void onSubmit(AjaxRequestTarget target)
{
byte[] report = reportGenerator.getReport(...);
...
}
});
}
}
Если я сделаю это таким образом, калитка попытается сериализовать конкретный экземпляр reportGenerator. Если я аннотирую свойство reportGenerator
с помощью @SpringBean
, я получаю Concrete bean could not be received from the application context for class: IReportGenerator
Обновлено: я переработал реализации IRerportGenerator, чтобы иметь возможность аннотировать их с помощью @Component
, и теперь, когда я использую аннотацию @SpringBean
, я получаю More than one bean of type [IReportGenerator] found, you have to specify the name of the bean (@SpringBean(name = "foo")) or (@Named("foo") if using @javax.inject classes) in order to resolve this conflict.
Именно этого я не хочу делать.
Хорошо, я удаляю дополнительный вопрос и попытаюсь собрать какой-нибудь пример.
Я подозреваю, что вам не хватает конфигурации, которая заставляет Spring создавать сериализуемые прокси-классы для ваших служб.
Аннотация SpringBean работает без проблем, если существует только одна реализация интерфейса. Однако в этом случае у меня есть несколько, и я не знаю, как указать, какой из них использовать динамически. Я знаю, что могу использовать имя с аннотацией SpringBean, но это сложно запрограммировать. Я ищу возможность указать, какую реализацию использовать во время создания экземпляра формы. Я смог передать конкретный экземпляр в форму, но, поскольку он не был проксирован, калитка попыталась сериализовать его, и именно здесь я столкнулся с проблемой.
Ах я вижу. Итак, вам нужен механизм, похожий на квалификатор, который работает в сочетании с Wicket. Я думаю, что могу найти обходной путь, который может сработать для вас
Насколько я могу найти, просматривая документацию и даже источник аннотации калитки @SpringBean
, это невозможно. Самое близкое, что я получил, это явное создание прокси-сервера для компонента Spring на основе переданного класса. Как описано в 13.2.4 Использование прокси из главы проекта wicket-spring в Wicket в действии.
public class ReportForm extends Panel {
private IReportGenerator reportGenerator;
private Class<? extends IReportGenerator> classType;
private static ISpringContextLocator CTX_LOCATOR = new ISpringContextLocator() {
public ApplicationContext getSpringContext() {
return ((MyApplication)MyApplication.get()).getApplicationContext();
}
};
public ReportForm(String id, Class<? extends IReportGenerator> classType) {
super(id);
this.classType = classType;
final Form<Void> form = new Form<Void>("form");
this.add(form);
...
form.add(new AjaxButton("button1") {
private static final long serialVersionUID = 1L;
@Override
protected void onSubmit(AjaxRequestTarget target)
{
byte[] report = getReportGenerator().getReport(...);
...
}
});
}
private <T> T createProxy(Class<T> classType) {
return (T) LazyInitProxyFactory.createProxy(classType, new
SpringBeanLocator(classType, CTX_LOCATOR));
}
private IReportGenerator getReportGenerator() {
if (reportGenerator = null) {
reportGenerator = createProxy(classType);
}
return reportGenerator;
}
}
Я думаю, что поведение, которого вы пытаетесь достичь, может быть реализовано с помощью небольшого обходного пути, путем введения bean-компонента Spring, который содержит все экземпляры IReportGenerator:
@Component
public class ReportGeneratorHolder {
private final List<IReportGenerator> reportGenerators;
@Autowired
public ReportGeneratorHolder(List<IReportGenerator> reportGenerators) {
this.reportGenerators = reportGenerators;
}
public Optional<IReportGenerator> getReportGenerator(Class<? extends IReportGenerator> reportGeneratorClass) {
return reportGenerators.stream()
.filter(reportGeneratorClass::isAssignableFrom)
.findAny();
}
}
Затем вы можете внедрить этот класс на свою страницу Wicket и передать нужный класс в качестве параметра-конструктора. В зависимости от вашей конфигурации Spring вам также может понадобиться ввести интерфейс для этого.
public class ReportForm extends Panel {
@SpringBean
private ReportGeneratorHolder reportGeneratorHolder;
public ReportForm(String id, Class<? extends IReportGenerator> reportGeneratorClass) {
super(id);
IReportGenerator reportGenerator = reportGeneratorHolder
.getReportGenerator(reportGeneratorClass)
.orElseThrow(IllegalStateException::new);
// Form logic omitted for brevity
}
}
После выяснения того, что вы имели в виду под «необходимостью ввести интерфейс», это работает, но теперь я вернулся к проблеме сериализации. Wicket пытается сериализовать исполнителя и терпит неудачу.
Под исполнителем вы подразумеваете экземпляр IReportGenerator
, который вы извлекаете из держателя? Если он назначен переменной в (например) основном классе, и вы ссылаетесь на него из вложенного класса, то я полагаю, что он сериализуется вместе с вложенным классом. Решением в этом случае было бы не обращаться к переменной из внутреннего класса, а снова вызывать метод держателя.
Голосование за закрытие, потому что вы задаете сразу несколько вопросов (почему я не могу внедрить сервисы Spring в Wicket и как мне спроектировать источники отчетов), а в вашем посте отсутствует Минимальный, полный и проверяемый пример. Тем не менее, я думаю, что вы идете в правильном направлении, используя шаблон стратегии, но я подозреваю, что в вашей конфигурации Spring не хватает нескольких настроек для хорошей работы с Wicket. По крайней мере, вы должны включить свою конфигурацию Spring и конфигурацию, которую вы используете для ее интеграции с Wicket.