AspectJ - 继承的'父级'类的切入点

时间:2016-09-25 21:48:19

标签: java inheritance aspectj

目标:从我的任何static filter()课程中调用班级的@Entity方法。返回List<Object。获得正确的类调用者类型。 例如,当我从filter()类调用User时,我希望将User作为调用者类(而不是拥有原始静态方法的类)。

filter()方法位于ORMEntity

public static List<Object> filter(){
    System.out.println("Called filter method!");
    return Collections.emptyList();
}

我写了这个方面:

public privileged aspect OrmAspect {

//Extends ORMEntity when class marked with @Entity
declare parents : (@Entity *) extends ORMEntity;

//Getting filter() calls from anywhere
//This pointing to ORMEntity.filter()
pointcut staticFilter() : call(* *.filter());

before() : staticFilter(){
    System.out.println(">>"+thisJoinPoint);
}

然后我可以从我的主要方法中编码:

User.filter();

User是一个带有@Entity注释的简单bean。

这就是这样的:

  1. 编译器请参阅filter()类中的User方法。确定。
  2. filter()的切入点仅在ORMEntity.filter()上,即使User extends ORMEntity(然后是filter()方法)也是如此。不行。
  3. filter()加入点之前的输出:

    System.out.println(">>"+thisJoinPoint.getSignature().getDeclaringType());
    

    ORMEntity而不是我期望的User

    如何让User类继承static filter()方法? 或者喜欢使用AspectJ在declare parent上进行切入点?

2 个答案:

答案 0 :(得分:0)

似乎不可能。

需要隐藏User类中的静态ORMEntity filter()方法。 但静态方法在编译时解决。

我看到的唯一解决方案是在.java文件中生成静态filter()方法,就像lombok可以用getter / setter一样。

答案 1 :(得分:0)

正如我们在您自己的回答中所讨论的那样,我怀疑您的应用程序设计是否真的有意义,但是对于它的价值,我已经使用AspectJ's annotation processing capability introduced in version 1.8.2为您准备了一个解决方案。这个解决方案是我在another StackOverflow answer中为更复杂的案例描述的简化版本。

这是我的Eclipse布局,包含两个源文件夹和一个执行两阶段编译过程的Windows批处理文件,

  • 首先编译负责为每个带注释的类创建一个方面的APT处理器
  • 然后在下一个AspectJ编译步骤中将此处理器实际应用于Java源代码。

以下是我的目录布局的屏幕截图:

Eclipse directory layout

如您所见,Eclipse无法直接编译类Application,您确实需要使用批处理文件。

标记注释:

注意,此类必须存储在 src_apt 中才能显示 稍后注释处理器EntityProcessor

package de.scrum_master.app;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface Entity {}

两个带注释的样本实体:

package de.scrum_master.app;

@Entity
public class User {}
package de.scrum_master.app;

@Entity
public class Group {}

驱动程序应用程序:

此应用程序依赖于APT在实际查看所使用的静态方法之前完成其工作。

package de.scrum_master.app;

public class Application {
    public static void main(String[] args) {
        User.filter();
        Group.filter();
    }
}

Aspect打印方法签名:

这是原始方面的简化版本,只有打印方法签名,不会声明任何父级或静态方法。这只是为了以后获得更好的日志输出:

package de.scrum_master.aspect;

public aspect LogAspect {
    pointcut staticFilter() :
        call(public static * filter());

    before() : staticFilter(){
        System.out.println(thisJoinPoint);
    }
}

注释处理器:

此注释处理器搜索使用@Entity注释的类,并创建一个方面,为每个类引入静态方法filter()

package de.scrum_master.app;

import java.io.*;
import java.util.*;

import javax.tools.*;
import javax.annotation.processing.*;
import javax.lang.model.*;
import javax.lang.model.element.*;

@SupportedAnnotationTypes(value = { "*" })
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class EntityProcessor extends AbstractProcessor {
    private Filer filer;

    @Override
    public void init(ProcessingEnvironment env) {
        filer = env.getFiler();
    }

    @Override
    public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
        env.getElementsAnnotatedWith(Entity.class).stream()
            .filter(annotatedClass -> annotatedClass.getKind() == ElementKind.CLASS)
            .forEach(annotatedClass -> {
                String packageName = annotatedClass.getEnclosingElement().toString().substring(8);
                String className = annotatedClass.getSimpleName().toString();
                String aspectName = "ORMAspect_" + className;
                String aspectSource = createAspectSource(packageName, className,aspectName);
                writeAspectSourceToDisk(packageName, aspectName, aspectSource);
            });
        return true;
    }

    private String createAspectSource(String packageName, String className, String aspectName) {
        StringBuilder aspectSource = new StringBuilder()
            .append("package " + packageName + ";\n\n")
            .append("import java.util.Collections;\n")
            .append("import java.util.List;\n\n")
            .append("public aspect " + aspectName + " {\n")
            .append("    public static List<Object> " + className + ".filter() {\n")
            .append("        System.out.println(\"Called filter method!\");\n")
            .append("        return Collections.emptyList();\n")
            .append("    }\n")
            .append("}\n");
        return aspectSource.toString();
    }

    private void writeAspectSourceToDisk(String packageName, String aspectName, String aspectSource) {
        try {
            JavaFileObject file = filer.createSourceFile(packageName + "." + aspectName);
            file.openWriter().append(aspectSource).close();
            System.out.println("Generated aspect " + packageName + "." + aspectName);
        } catch (IOException ioe) {
            // Message "already created" can appear if processor runs more than once
            if (!ioe.getMessage().contains("already created"))
                ioe.printStackTrace();
        }
    }
}

注释处理器的服务描述符:

这是 META-INF / services / javax.annotation.processing.Processor 的内容:

de.scrum_master.app.EntityProcessor

执行两阶段编译的批处理文件:

这个批处理文件完成了我在答案开头所描述的内容。请确保根据需要调整变量SRC_PATHASPECTJ_HOME

@echo off

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

echo Building annotation processor
cd "%SRC_PATH%"
rmdir /s /q bin
del /q processor.jar
set CLASSPATH=%ASPECTJ_HOME%\lib\aspectjrt.jar
call "%ASPECTJ_HOME%\bin\ajc.bat" -1.8 -sourceroots src_apt -d bin
jar -cvf processor.jar -C src_apt META-INF -C bin .

echo.
echo Generating aspects and building project
rmdir /s /q bin .apt_generated
set CLASSPATH=%ASPECTJ_HOME%\lib\aspectjrt.jar;processor.jar
call "%ASPECTJ_HOME%\bin\ajc.bat" -1.8 -sourceroots src -d bin -s .apt_generated -inpath processor.jar -processor de.scrum_master.app.EntityProcessor -showWeaveInfo

echo.
echo Running de.scrum_master.app.Application
java -cp bin;"%ASPECTJ_HOME%\lib\aspectjrt.jar" de.scrum_master.app.Application

运行批处理文件时的控制台日志:

C:\Users\Alexander\Documents\java-src\SO_AJ_ITDStaticMethods>compile_run.bat
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 = 36) (aus = 38)(-5 % 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/Entity.class wird hinzugefügt(ein = 293) (aus = 200)(31 % verkleinert)
de/scrum_master/app/EntityProcessor.class wird hinzugefügt(ein = 5679) (aus = 2476)(56 % verkleinert)

Generating aspects and building project
Generated aspect de.scrum_master.app.ORMAspect_Group
Generated aspect de.scrum_master.app.ORMAspect_User

Running de.scrum_master.app.Application
call(List de.scrum_master.app.User.filter())
Called filter method!
call(List de.scrum_master.app.Group.filter())
Called filter method!

Etvoilà!最后4行显示了您想要看到的内容:在带注释的目标类中显式声明的静态方法。享受!