Java 6 - 注释处理器和代码添加

时间:2010-09-18 19:02:25

标签: java properties osgi bytecode code-injection

我编写了一个自定义注释,其中包含属性的元数据和AnnotationProcessor

@SupportedAnnotationTypes({"<package>.Property"})
public class PropertyProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations,
            RoundEnvironment roundEnv) {
        // Get messager object
        Messager messager = processingEnv.getMessager();
        // Iterate through the annotations
        for(TypeElement typeElement : annotations) {
            // Iterate through the annotated elements
            for(Element element : roundEnv.getElementsAnnotatedWith(typeElement)) {
                // Get Property annotation
                Property property = element.getAnnotation(Property.class);

            }
        }
        return false;
    }

}

这是一个问题,我之前使用过Javassist,但它取决于类加载器,我认为它不适合OSGi应用程序。我希望在编译带有Property注释的类时更改生成的字节码。

3 个答案:

答案 0 :(得分:6)

您是否尝试过Google Guice

Google Guice允许您通过拦截方法进行面向方面编程。如果这就是你需要做的全部,你可以实现一个MethodInterceptor,它允许你在运行时覆盖方法。它非常适合隔离交叉问题。

例如,假设您想要阻止某些方法在周末执行,您可以这样注释:

@Property
public class SomeClass {
    public Receipt doSometing() {
        // Do something
    }
}

定义MethodInterceptor:

public class PropertyInterceptor implements MethodInterceptor {
  public Object invoke(MethodInvocation invocation) throws Throwable {
    // For example prevent the classes annotated with @Property
    // from being called on weekends
    Calendar today = new GregorianCalendar();
    if (today.getDisplayName(DAY_OF_WEEK, LONG, ENGLISH).startsWith("S")) {
      throw new IllegalStateException(
          invocation.getMethod().getName() + " not allowed on weekends!");
    }
    return invocation.proceed();
  }
}

然后将拦截器绑定到注释:

public class PropertyModule extends AbstractModule {
  protected void configure() {
        PropertyInterceptor propertyInterceptor = new PropertyInterceptor();        
        bindInterceptor(Matchers.annotatedWith(Property.class), 
        Matchers.any(), propertyInterceptor);
  }
}

答案 1 :(得分:5)

简短的回答是:在注释处理期间,您不应该更改源代码。

我最近遇到的情况是答案不尽如人意(见this question)。我的解决方案是使用内部javac api以编程方式添加我需要的代码。有关详细信息,请参阅my answer to my own question

我从Project Lombok获取灵感,从他们的源代码开始,扔掉了我不需要的一切。我不认为你会找到一个更好的起点。

BTW,Javassist可能无济于事,因为您正在处理源树,而不是字节代码。如果你想使用一个字节码操作库,你可以在编译后静态地或在加载类时动态地执行,但不能在注释处理期间执行,因为这是一个预编译步骤。

答案 2 :(得分:1)

注释处理并不是要改变现有的类 - 它只是用于生成额外的代码/资源(在逐个类的基础上,否则在重新编译修改后的源时会遇到麻烦)。 / p>

前段时间我尝试Spoon来解决类似的问题:我非常喜欢程序处理器的想法(以及IDE集成甚至更多),但当时它并不是很稳定... < / p>

根据您的使用案例,AOP工具(例如:AspectJ)可以比Spoon更好地为您提供服务,当然 - 您可以随时使用源代码生成器或实现完整的DSL(取看看奇妙的Xtext)。

根据你的队友的规模,离职率和“智力惯性” - 你可以更好地承受普通java的痛苦而不是引入新工具/技术,形成同事和整合新的CI系统中的工具。仔细权衡成本/收益。