我有一个大型检票口组件库,这些组件使用自定义注解@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{
...
}
到目前为止,我使用apt检查引用的资源是否确实存在。例如
@ReferencedResource(value = "behaviors/promoteSelectOptions",
type = ResourceType.JAVASCRIPT)
js/behaviors/promoteSelectOptions.js
除非可以在类路径中找到该文件,否则将导致编译失败。这部分工作得很好。
现在我也是 DRY 的粉丝,我想在创建对象时使用相同的注释将资源实际注入到对象中。使用 AspectJ,我已经实现了其中的一部分。
带注释的对象始终是Component或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(response)方法,这也很有效,但在许多情况下,这不是必需的,因为超类已经实现了基本功能,而子类只添加了一些配置。所以一种解决方案是让这些类定义这样的方法:
@Override
public void renderHead(IHeaderResponse response){
super.renderHead(response);
}
我会讨厌这个,因为这是死代码,但目前这是我看到的唯一可行的选择,所以我正在寻找其他解决方案。
编辑:
我已经使用 APT 和 sun javac 调用创建了一个可行的解决方案。但是,这会导致下一个问题:使用 maven 在同一个项目中运行 APT 和 AspectJ。
无论如何,只要我有空闲时间,我就会发布这个问题(或部分问题)的答案。