AOP или APT для переопределения методов из суперклассов
У меня есть большая библиотека компонентов калитки, которые снабжены пользовательской аннотацией@ReferencedResource
или другая аннотация@ReferencedResources
, который имеетReferencedResouce[] value()
параметр, чтобы разрешить несколько аннотаций.
Вот пример кода:
@ReferencedResources({
@ReferencedResource(value = Libraries.MOO_TOOLS, type = ResourceType.JAVASCRIPT),
@ReferencedResource(value = "behaviors/promoteSelectOptions", type = ResourceType.JAVASCRIPT) })
public class PromoteSelectOptionsBehavior extends AbstractBehavior{
...
}
Пока что пользуюсьсклонный проверить, что ссылочные ресурсы действительно существуют. Например.
@ReferencedResource(value = "behaviors/promoteSelectOptions",
type = ResourceType.JAVASCRIPT)
приведет к ошибке компиляции, если файлjs/behaviors/promoteSelectOptions.js
можно найти на пути к классам. Эта часть работает хорошо.
Теперь я также поклонник DRY и хотел бы использовать ту же аннотацию для фактического внедрения ресурсов в объекты при их создании. Используя AspectJ, я реализовал часть этого.
Аннотированные объекты всегда являются экземплярамиСоставная часть или жеAbstractBehavior.
Для компонентов все просто, просто сопоставьте после конструктора. Вот совет, который делает это:
pointcut singleAnnotation() : @within(ReferencedResource);
pointcut multiAnnotation() : @within(ReferencedResources);
after() : execution(Component+.new(..)) && (singleAnnotation() || multiAnnotation()){
final Component component = (Component) thisJoinPoint.getTarget();
final Collection<ReferencedResource> resourceAnnotations =
// gather annotations from cache
this.getResourceAnnotations(component.getClass());
for(final ReferencedResource annotation : resourceAnnotations){
// helper utility that handles the creation of statements like
// component.add(JavascriptPackageResource.getHeaderContribution(path))
this.resourceInjector.inject(component, annotation);
}
}
Однако для поведения мне нужно прикрепить ресурсы к ответу, а не к самому поведению. Вот точки, которые я использую:
pointcut renderHead(IHeaderResponse response) :
execution(* org.apache.wicket.behavior.AbstractBehavior+.renderHead(*))
&& args(response);
И вот совет:
before(final IHeaderResponse response) :
renderHead(response) && (multiAnnotation() || singleAnnotation()) {
final Collection<ReferencedResource> resourceAnnotations =
this.getResourceAnnotations(thisJoinPoint.getTarget().getClass());
for(final ReferencedResource resource : resourceAnnotations){
this.resourceInjector.inject(response, resource);
}
}
Это также хорошо работает, если класс переопределяетrenderHead (ответ) метод, но во многих случаях это просто не нужно, потому что суперкласс уже реализует базовую функциональность, в то время как дочерний класс только добавляет некоторую конфигурацию. Поэтому одним из решений было бы позволить этим классам определить метод, подобный этому:
@Override
public void renderHead(IHeaderResponse response){
super.renderHead(response);
}
Я бы не хотел этого, потому что это мертвый код, но в настоящее время это единственная рабочая опция, которую я вижу, поэтому я ищу другие решения.
РЕДАКТИРОВАТЬ:
Я создал рабочее решение, используя APT и Sun Javac Call. Однако это приводит к следующей проблеме:Запуск APT и AspectJ в одном проекте с использованием maven.
В любом случае, как только у меня будет свободное время, я опубликую ответ на этот вопрос (или его части).