我有一个大型的检票口组件库,这些检票口组件使用自定义注释@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和sunjavac调用创建了一个可行的解决方案。但是,这会导致下一个问题:使用maven在同一项目中运行APT和AspectJ。
无论如何,只要有空闲时间,我就会发布该问题(或部分问题)的答案。
回答我自己的问题:
这是插入超级调用的相关代码位:
这些字段都在init(env)或process(annotations,roundEnv)中初始化:
private static Filer filer;
private static JavacProcessingEnvironment environment;
private static Messager messager;
private static Types types;
private static JavacElements elementUtils;
private Trees trees;
private TreeMaker treeMaker;
private IdentityHashMap<JCCompilationUnit, Void> compilationUnits;
private Map<String, JCCompilationUnit> typeMap;
如果包含AbstractBehavior
注释的子类型未覆盖该renderHead(response)
方法,则将调用以下逻辑:
private void addMissingSuperCall(final TypeElement element){
final String className = element.getQualifiedName().toString();
final JCClassDecl classDeclaration =
// look up class declaration from a local map
this.findClassDeclarationForName(className);
if(classDeclaration == null){
this.error(element, "Can't find class declaration for " + className);
} else{
this.info(element, "Creating renderHead(response) method");
final JCTree extending = classDeclaration.extending;
if(extending != null){
final String p = extending.toString();
if(p.startsWith("com.myclient")){
// leave it alone, we'll edit the super class instead, if
// necessary
return;
} else{
// @formatter:off (turns off eclipse formatter if configured)
// define method parameter name
final com.sun.tools.javac.util.Name paramName =
elementUtils.getName("response");
// Create @Override annotation
final JCAnnotation overrideAnnotation =
this.treeMaker.Annotation(
Processor.buildTypeExpressionForClass(
this.treeMaker,
elementUtils,
Override.class
),
// with no annotation parameters
List.<JCExpression> nil()
);
// public
final JCModifiers mods =
this.treeMaker.Modifiers(Flags.PUBLIC,
List.of(overrideAnnotation));
// parameters:(final IHeaderResponse response)
final List<JCVariableDecl> params =
List.of(this.treeMaker.VarDef(this.treeMaker.Modifiers(Flags.FINAL),
paramName,
Processor.buildTypeExpressionForClass(this.treeMaker,
elementUtils,
IHeaderResponse.class),
null));
//method return type: void
final JCExpression returnType =
this.treeMaker.TypeIdent(TypeTags.VOID);
// super.renderHead(response);
final List<JCStatement> statements =
List.<JCStatement> of(
// Execute this:
this.treeMaker.Exec(
// Create a Method call:
this.treeMaker.Apply(
// (no generic type arguments)
List.<JCExpression> nil(),
// super.renderHead
this.treeMaker.Select(
this.treeMaker.Ident(
elementUtils.getName("super")
),
elementUtils.getName("renderHead")
),
// (response)
List.<JCExpression> of(this.treeMaker.Ident(paramName)))
)
);
// build code block from statements
final JCBlock body = this.treeMaker.Block(0, statements);
// build method
final JCMethodDecl methodDef =
this.treeMaker.MethodDef(
// public
mods,
// renderHead
elementUtils.getName("renderHead"),
// void
returnType,
// <no generic parameters>
List.<JCTypeParameter> nil(),
// (final IHeaderResponse response)
params,
// <no declared exceptions>
List.<JCExpression> nil(),
// super.renderHead(response);
body,
// <no default value>
null);
// add this method to the class tree
classDeclaration.defs =
classDeclaration.defs.append(methodDef);
// @formatter:on turn eclipse formatter on again
this.info(element,
"Created renderHead(response) method successfully");
}
}
}
}
我正在制作一个定制的盔甲,在我的盔甲课程中我得到了这个错误: ArmorE类型的方法getArmorTexture(ItemStack,Entity,int,int)必须重写或实现超类型方法 为什么我会犯这个错误? 这是我的代码: 装甲等级: 主类的一部分:
构建基于链表的队列实现。由于以下两个错误,无法运行应用程序: 错误#1:错误:队列。ListIterator不是抽象的,并且不会覆盖迭代器中的抽象方法next(),其中Integer是一个类型变量:Integer扩展类队列中声明的对象。列表迭代器 错误#2:错误:队列中的next()。ListIterator无法在迭代器中实现next(),返回类型int与Integer不兼容,其中E、Intege
我有3个类,配置如下:Class1有几个方法,其中一些是抽象的: 接下来,来自同一个包的class 2扩展了class 1,覆盖了它的抽象方法并添加了一些其他方法: 最后,另一个包中的class3扩展了class2并覆盖了class1中的一个方法: 我正在使用Eclipse,并且它在编码时不会检测到任何错误。我正在使用Maven Build构建项目,但构建失败,说thisIsTheMethod o
问题内容: 为什么我们不能覆盖子类中超类的实例变量? 问题答案: 因为如果您更改数据成员的实现,则很有可能会破坏超类(想象将超类的数据成员从float更改为String)。
我有一个抽象类,割草机,它覆盖了toString()方法。草坪拖拉机类扩展了割草机。它覆盖了toString()方法,并调用super.toString()作为方法的第一行。然后我有一个商业类,它扩展了劳恩拖拉机并覆盖了toString(),也在第一行调用super.toString()。当实例化一个商业对象(声明为割草机对象)并调用它的toString()时,我希望它打印割草机和草坪拖拉机的两个
问题内容: 说我有一个像这样的课程: 它具有一个带有扩展它的类的层次结构: 然后在其他地方,我有一堂课利用了这些东西: 我是否可以指定A的子类,以便使用更具体的“某物”类覆盖setSomething方法?这就是我想要做的: 目前,我正在A类中执行以下操作: 如果SuperSomething的类型不适合这些类,则B类和C类在checkClass方法中引发异常。 编辑:我已经尝试使用上述确切的方法签名