在多种类型上声明ITD静态方法

时间:2015-03-04 17:21:44

标签: aspectj

我想在我的App类的每个子类型上声明一个静态方法(比如void main(String..args))。

public aspect Aspects pertypewithin(App+) {

    protected Class appClass;

    after() : staticinitialization(App+) && !staticinitialization(App) {
        StaticPart point = thisJoinPointStaticPart;
        Signature signature = point.getSignature();
        Class declaringType = signature.getDeclaringType();
        this.appClass = declaringType;
    }

    public static void App.main(String...args) {
        // how do i make this appear on every subtype of App, not just App
    }

}

AspectJ可以实现吗?

1 个答案:

答案 0 :(得分:1)

将一组非静态方法添加到多个类的通常模式是在一个方面中定义一个接口+实现方法,并使用declare parents以使目标类实现接口。

不幸的是,这对于main之类的静态方法不起作用,因为无法通过接口定义静态方法。另一方面,如果您有课程MyApp extends App,则可以致电java -cp ... MyApp,即会自动使用其父main方法。

现在让我们假设由于某种原因,您希望生成的main方法的内容在某种程度上不同。在这种情况下,您确实需要为每个类生成一个方法。为此,您可以使用AspectJ 1.8.2中引入的新功能,并在发行说明中进行了描述:annotation processing support。这是一些自洽的示例代码。我是从命令行编译的,因为从Eclipse开始我无法根据AspectJ维护者Andy Clement's description运行它。

好的,我们假设我们在名为java-src的基目录中有两个项目目录。目录布局如下:

java-src
    SO_AJ_ITD_AddMainMethodToAllSubclasses_AJ
        src\de\scrum_master\app\App.java
        src\de\scrum_master\app\BarApp.java
        src\de\scrum_master\app\FooApp.java
        compile_run.bat
    SO_AJ_ITD_AddMainMethodToAllSubclasses_APT
        src\de\scrum_master\app\EntryPoint.java
        src\de\scrum_master\aspect\EntryPointProcessor.java
        src\META-INF\services\javax.annotation.processing.Processor

在项目SO_AJ_ITD_AddMainMethodToAllSubclasses_AJ中,我们有 Java类:

package de.scrum_master.app;

@EntryPoint
public class App {
    public void doSomething() {
        System.out.println("Doing something");
    }
}
package de.scrum_master.app;

public class FooApp extends App {
    public void doFoo() {
        System.out.println("Doing foo");
    }

    public int add(int a, int b) {
        return a + b;
    }
}
package de.scrum_master.app;

public class BarApp extends App {
    public void doBar() {
        System.out.println("Doing bar");
    }

    public int multiply(int a, int b) {
        return a * b;
    }
}

在项目SO_AJ_ITD_AddMainMethodToAllSubclasses_APT中,我们有标记注释,我们META-INF的{​​{1}}目录的注释处理器和处理器描述文件:

请注意:应用于课程时,注释为processor.jar。这对于使注释处理器找到它很重要。

@Inherited

注释处理器为每个带注释的类创建一个方面。每个方面都做到以下几点:

  • 添加package de.scrum_master.app; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Inherited public @interface EntryPoint {} 方法
  • 实例化目标类的对象
  • 始终在目标实例
  • 上调用基本方法main
  • 在目标类中搜索没有参数的其他简单非静态方法,并调用这些方法,以便显示一些奇特和动态的东西,并使得结果方面看起来有点不同。
doSomething()

APT的处理器描述文件package de.scrum_master.aspect; import java.io.*; import javax.tools.*; import java.util.*; import javax.annotation.processing.*; import javax.lang.model.*; import javax.lang.model.element.*; import de.scrum_master.app.EntryPoint; @SupportedAnnotationTypes(value = { "*" }) @SupportedSourceVersion(SourceVersion.RELEASE_7) public class EntryPointProcessor extends AbstractProcessor { private Filer filer; @Override public void init(ProcessingEnvironment env) { filer = env.getFiler(); } @Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) { // Discover elements marked with @EntryPoint for (Element element : env.getElementsAnnotatedWith(EntryPoint.class)) { // Skip non-class elements if (element.getKind() != ElementKind.CLASS) continue; String packageName = element.getEnclosingElement().toString().substring(8); String className = element.getSimpleName().toString(); String aspectName = "MainMethodAspect_" + className; // For each marked class, create an aspect adding a 'main' method String aspectSource = createAspectSource(element, packageName, className, aspectName); writeAspectSourceToDisk(element, packageName, aspectName, aspectSource); } return true; } private String createAspectSource(Element element, String packageName, String className, String aspectName) { String variableName = className.substring(0, 1).toLowerCase() + className.substring(1); StringBuilder aspectSource = new StringBuilder() .append("package " + packageName + ";\n\n") .append("public aspect " + aspectName + " {\n") .append(" public static void " + className + ".main(String[] args) {\n") .append(" " + className + " " + variableName + " = new " + className + "();\n") .append(" " + variableName + ".doSomething();\n"); for (Element childElement : element.getEnclosedElements()) { // Skip everything which is not a non-static method if (childElement.getKind() != ElementKind.METHOD || childElement.getModifiers().contains(Modifier.STATIC)) continue; ExecutableElement method = (ExecutableElement) childElement; // Skip methods with parameters or named 'doSomething' if (!method.getParameters().isEmpty() || method.getSimpleName().toString().equals("doSomething")) continue; // Add call to found method aspectSource.append(" " + variableName + "." + method.getSimpleName() + "();\n"); } aspectSource .append(" }\n") .append("}\n"); return aspectSource.toString(); } private void writeAspectSourceToDisk(Element element, String packageName, String aspectName, String aspectSource) { try { JavaFileObject file = filer.createSourceFile(packageName + "." + aspectName, element); file.openWriter().append(aspectSource).close(); System.out.println("Generated aspect " + packageName + "." + aspectName + " to advise " + element); } catch (IOException ioe) { // Message "already created" can appear if processor runs more than once if (!ioe.getMessage().contains("already created")) ioe.printStackTrace(); } } } 如下所示:

src\META-INF\services\javax.annotation.processing.Processor

如何编译和运行:最后,但并非最不重要的是(Windows)批处理文件de.scrum_master.aspect.EntryPointProcessor

  • 编译APT项目并将其打包成JAR,
  • 编译其他项目,包括APM方面代码生成,最后
  • 运行三个Java类中的每一个,测试他们的SO_AJ_ITD_AddMainMethodToAllSubclasses_AJ\compile_run.bat方法是否实际按预期工作:
main

控制台输出:如果运行批处理文件,输出应如下所示:

@echo off

set SRC_PATH=C:\Users\Alexander\Documents\java-src
set ASPECTJ_HOME=C:\Program Files\Java\AspectJ

echo Building annotation processor
cd "%SRC_PATH%\SO_AJ_ITD_AddMainMethodToAllSubclasses_APT"
rmdir /s /q bin
del /q processor.jar
call "%ASPECTJ_HOME%\bin\ajc.bat" -8 -sourceroots src -d bin -cp "c:\Program Files\Java\AspectJ\lib\aspectjrt.jar"
jar -cvf processor.jar -C src META-INF -C bin .

echo.
echo Generating aspects and building project
cd "%SRC_PATH%\SO_AJ_ITD_AddMainMethodToAllSubclasses_AJ"
rmdir /s /q bin .apt_generated
call "%ASPECTJ_HOME%\bin\ajc.bat" -8 -sourceroots src -d bin -s .apt_generated -cp "c:\Program Files\Java\AspectJ\lib\aspectjrt.jar";..\SO_AJ_ITD_AddMainMethodToAllSubclasses_APT\processor.jar

echo.
echo Running de.scrum_master.app.App
java -cp bin;"c:\Program Files\Java\AspectJ\lib\aspectjrt.jar" de.scrum_master.app.App

echo.
echo Running de.scrum_master.app.FooApp
java -cp bin;"c:\Program Files\Java\AspectJ\lib\aspectjrt.jar" de.scrum_master.app.FooApp

echo.
echo Running de.scrum_master.app.BarApp
java -cp bin;"c:\Program Files\Java\AspectJ\lib\aspectjrt.jar" de.scrum_master.app.BarApp

如果您查看Building annotation processor Manifest wurde hinzugefügt Eintrag META-INF/ wird ignoriert META-INF/services/ wird hinzugefügt(ein = 0) (aus = 0)(0 % gespeichert) META-INF/services/javax.annotation.processing.Processor wird hinzugefügt(ein = 43) (aus = 45)(-4 % verkleinert) de/ wird hinzugefügt(ein = 0) (aus = 0)(0 % gespeichert) de/scrum_master/ wird hinzugefügt(ein = 0) (aus = 0)(0 % gespeichert) de/scrum_master/app/ wird hinzugefügt(ein = 0) (aus = 0)(0 % gespeichert) de/scrum_master/app/EntryPoint.class wird hinzugefügt(ein = 430) (aus = 253)(41 % verkleinert) de/scrum_master/aspect/ wird hinzugefügt(ein = 0) (aus = 0)(0 % gespeichert) de/scrum_master/aspect/EntryPointProcessor.class wird hinzugefügt(ein = 5782) (aus = 2617)(54 % verkleinert) Generating aspects and building project Generated aspect de.scrum_master.app.MainMethodAspect_App to advise de.scrum_master.app.App Generated aspect de.scrum_master.app.MainMethodAspect_BarApp to advise de.scrum_master.app.BarApp Generated aspect de.scrum_master.app.MainMethodAspect_FooApp to advise de.scrum_master.app.FooApp Running de.scrum_master.app.App Doing something Running de.scrum_master.app.FooApp Doing something Doing foo Running de.scrum_master.app.BarApp Doing something Doing bar 下的注释处理器生成的文件,您会发现三个类看起来像这样(我只显示其中一个作为示例):< / p>

SO_AJ_ITD_AddMainMethodToAllSubclasses_AJ\.apt_generated

很抱歉这个长篇答案,但除了创建一个GitHub回购并指向那里之外,我不得不提及它以使其可重现。

享受!