如何为无法更新任何实例变量的方法编写外观或注释?

时间:2019-01-06 12:20:46

标签: java aop aspectj

如何为无法更新任何实例变量的方法编写外观或注释?

例如,我有以下java类

public class Foo {

    String name;
    int id;

    public getName() {
       return name;
    }
}

现在说我想写一个方面,或者一般来说,有些注解称它为@readOnly,它可以强制执行getName()方法而不修改nameid,所以如果可以

public class Foo {

    String name;
    int id;

    @readOnly
    public getName() {
       name = "hello world";
       id = 7564;
       return name;
    }
}

任何对getName()的调用都将导致错误,因为它同时修改了nameid

下面的类应该很好,因为它只读取实例变量。

public class Foo {

    String name;
    int id;

    @readOnly
    public getName() {
       return name + "," + id;
    }
}

1 个答案:

答案 0 :(得分:1)

在任何get*()方法中有任何成员写访问权限时,直接抛出编译错误怎么办?

标记注释:

package de.scrum_master.app;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target({ TYPE, FIELD, METHOD })
public @interface ReadOnly {}

示例类/驱动程序应用程序

package de.scrum_master.app;

public class Application {
  private int id = 1;
  private String name = "default";

  @ReadOnly
  public int getId() {
    return id;
  }

  @ReadOnly
  public String getName() {
    name = "hello world";
    id = 7564;
    return name;
  }

  public String getNameWithoutReadOnly() {
    name = "hello world";
    id = 7564;
    return name;
  }

  @ReadOnly
  public String getNameIndirectly() {
    modifyMembers();
    return name;
  }

  private void modifyMembers() {
    name = "hello world";
    id = 7564;
  }

  public static void main(String[] args) {
    Application application = new Application();
    application.getId();
    try { application.getName(); }
    catch (Exception e) { e.printStackTrace(System.out); }
    application.getNameWithoutReadOnly();
    try { application.getNameIndirectly(); }
    catch (Exception e) { e.printStackTrace(System.out); }
  }
}

声明编译错误:

以下方面仅在方法而非类或成员上检测到@ReadOnly注释。您也可以扩展它。

在使用AspectJ编译器编译应用程序时,declare error语句直接引发编译错误。在Eclipse中,您会看到以下内容:

AspectJ declared compile errors

如果您还想从getter调用的辅助方法中检测间接写访问,则还需要使用cflow()的动态切入点,但是该切入点仅在运行时有效,而不是在编译时有效,因为它会检查调用栈。如果不需要它,只需将其删除。

package de.scrum_master.aspect;

import de.scrum_master.app.ReadOnly;

public aspect ReadOnlyGetterAspect {
  declare error :
    set(* *) && withincode(public * get*()) && @withincode(ReadOnly) :
      "Setting members from within a getter is forbidden";

  before() : set(* *) && cflow(execution(@ReadOnly public * get*())) {
    throw new IllegalAccessError("Setting members from within a getter is forbidden");
  }
}

顺便说一句,如果您想查看运行时切入点/建议,则需要先编译代码。因此,您需要将declare error削弱为declare warning或注释掉引起getName()中编译错误的两条语句。

如果使用前者,则日志输出将是:

java.lang.IllegalAccessError: Setting members from within a getter is forbidden
    at de.scrum_master.aspect.ReadOnlyGetterAspect.ajc$before$de_scrum_master_aspect_ReadOnlyGetterAspect$1$3e55e852(ReadOnlyGetterAspect.aj:11)
    at de.scrum_master.app.Application.getName(Application.java:14)
    at de.scrum_master.app.Application.main(Application.java:39)
java.lang.IllegalAccessError: Setting members from within a getter is forbidden
    at de.scrum_master.aspect.ReadOnlyGetterAspect.ajc$before$de_scrum_master_aspect_ReadOnlyGetterAspect$1$3e55e852(ReadOnlyGetterAspect.aj:11)
    at de.scrum_master.app.Application.modifyMembers(Application.java:32)
    at de.scrum_master.app.Application.getNameIndirectly(Application.java:27)
    at de.scrum_master.app.Application.main(Application.java:42)

如果您选择后者(修复代码),那么您当然只会看到第二个异常。