如何为无法更新任何实例变量的方法编写方面或注释? [英] How to write an aspect or annotation for a method that cannot update any instance variables?

查看:99
本文介绍了如何为无法更新任何实例变量的方法编写方面或注释?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

How to write an aspect or annotation for a method that cannot update any instance variables?

例如,我有以下java类

Say for example I have the following java class

public class Foo {

    String name;
    int id;

    public getName() {
       return name;
    }
}

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

Now say I want to write an aspect or in general, some annotation call it say @readOnly which can enforce getName() method to not modify either name or id so if do

public class Foo {

    String name;
    int id;

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

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

Any invocation of getName() like above should result in an error since it modifies both name and id

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

The class like below should be just fine since it just doing read of instance variables.

public class Foo {

    String name;
    int id;

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

推荐答案

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

How about directly throwing a compile error when there is any member write access in any get*() method?

标记注释:

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批注.您也可以扩展它.

The following aspect only detects @ReadOnly annotations on methods, not on classes or members. You can extend it if you also need that.

当使用AspectJ编译器编译应用程序时,declare error语句直接引发编译错误.在Eclipse中,您会看到类似这样的内容:

The declare error statement directly throws compile errors when compiling your application with the AspectJ compiler. In Eclipse you would see something like this:

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

If you also want to detect indirect write access from helper methods called by a getter, you also need the dynamic pointcut with cflow(), but that one only works at runtime, not at compile time, because it inspects the callstack. If you do not need it, just remove it.

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()编译错误的两条语句.

BTW, if you want to see the runtime pointcut/advice in action, you need to make the code compile first. So you either need to weaken declare error into declare warning or comment out the two statements causing the compile errors in getName().

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

If you do the former, your log output will be:

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)

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

If you do the latter (fix the code), of course you will only see the second exception.

这篇关于如何为无法更新任何实例变量的方法编写方面或注释?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆