AOP 或 APT 用于重写超类中的方法
我有一个大型的 wicket 组件库,这些组件使用自定义注释 @ReferencedResource
或另一个注释 @ReferencedResources
进行注释,该注释具有 ReferencedResouce[] value()< /code> 参数允许多个注释。
这是一个示例代码片段:
@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 调用创建了一个工作解决方案。但是,这会导致下一个问题: Running APT and AspectJ in使用 maven 的同一项目。
无论如何,一旦我有空闲时间,我就会发布这个问题(或部分问题)的答案。
I have a large library of wicket components that are annotated with a custom annotation @ReferencedResource
or another annotation @ReferencedResources
, that has a ReferencedResouce[] value()
parameter to allow multiple annotations.
Here is a sample code snippet:
@ReferencedResources({
@ReferencedResource(value = Libraries.MOO_TOOLS, type = ResourceType.JAVASCRIPT),
@ReferencedResource(value = "behaviors/promoteSelectOptions", type = ResourceType.JAVASCRIPT) })
public class PromoteSelectOptionsBehavior extends AbstractBehavior{
...
}
So far, I use apt to check that the referenced resources actually exist. E.g.
@ReferencedResource(value = "behaviors/promoteSelectOptions",
type = ResourceType.JAVASCRIPT)
will cause a compilation failure unless the file js/behaviors/promoteSelectOptions.js
can be found on the class path. This part works nicely.
Now I am also a fan of DRY and I would like to use the same annotation to actually inject the resources into the Objects when they are created. Using AspectJ, I have implemented a part of this.
The annotated Objects are always either instances of Component or AbstractBehavior.
For components, things are easy, just match after the constructor. Here's an advice that does this:
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);
}
}
For behaviors however, I need to attach the resources to a response, not to the behavior itself. Here are the pointcuts I use:
pointcut renderHead(IHeaderResponse response) :
execution(* org.apache.wicket.behavior.AbstractBehavior+.renderHead(*))
&& args(response);
And here is the advice:
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);
}
}
This also works nicely if the class overrides the renderHead(response) method, but in many cases that's just not necessary because a super class already implements the base functionality while the child class only adds some configuration. So one solution would be to let these classes define a method like this:
@Override
public void renderHead(IHeaderResponse response){
super.renderHead(response);
}
I would hate this, because this is dead code, but currently this is the only working option I see, so I am looking for other solutions.
EDIT:
I have created a working solution using APT and sun javac calls. However, this leads to the next problem: Running APT and AspectJ in the same project using maven.
Anyway, as soon as I have some free time, I'll post the answer to this question (or parts of it).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
回答我自己的问题:
这是插入超级调用的相关代码:
这些字段都在 init(env) 或 进程(注释,roundEnv) :
如果具有注释的
AbstractBehavior
子类型不重写renderHead(response)
方法,则调用的逻辑如下:Answering my own question:
Here is the relevant bit of code to insert the super call:
these fields are all initialized in init(env) or process(annotations, roundEnv):
And here is the logic that is called if a subtype of
AbstractBehavior
that has the annotation does not override therenderHead(response)
method: