如何为无法更新任何实例变量的方法编写方面或注释?
How to write an aspect or annotation for a method that cannot update any instance variables?
如何为无法更新任何实例变量的方法编写方面或注释?
比如说我有以下 java class
public class Foo {
String name;
int id;
public getName() {
return name;
}
}
现在说我想写一个方面或者一般来说,一些注释称它为 @readOnly
可以强制 getName()
方法不修改 name
或 id
所以如果做
public class Foo {
String name;
int id;
@readOnly
public getName() {
name = "hello world";
id = 7564;
return name;
}
}
像上面那样调用 getName()
应该会导致错误,因为它同时修改了 name
和 id
下面的 class 应该没问题,因为它只是读取实例变量。
public class Foo {
String name;
int id;
@readOnly
public getName() {
return name + "," + id;
}
}
在任何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 {}
样本class/驱动程序申请:
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
方法上的注释,而不检测 class 实体或成员上的注释。如果您也需要,可以扩展它。
在使用 AspectJ 编译器编译您的应用程序时,declare error
语句会直接抛出编译错误。在 Eclipse 中,您会看到类似这样的内容:
如果您还想检测由 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");
}
}
顺便说一句,如果您想查看运行时 pointcut/advice 的运行情况,您需要先编译代码。因此,您要么需要将 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_ReadOnlyGetterAspecte55e852(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_ReadOnlyGetterAspecte55e852(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)
如果你做后者(修复代码),当然你只会看到第二个异常。
如何为无法更新任何实例变量的方法编写方面或注释?
比如说我有以下 java class
public class Foo {
String name;
int id;
public getName() {
return name;
}
}
现在说我想写一个方面或者一般来说,一些注释称它为 @readOnly
可以强制 getName()
方法不修改 name
或 id
所以如果做
public class Foo {
String name;
int id;
@readOnly
public getName() {
name = "hello world";
id = 7564;
return name;
}
}
像上面那样调用 getName()
应该会导致错误,因为它同时修改了 name
和 id
下面的 class 应该没问题,因为它只是读取实例变量。
public class Foo {
String name;
int id;
@readOnly
public getName() {
return name + "," + id;
}
}
在任何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 {}
样本class/驱动程序申请:
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
方法上的注释,而不检测 class 实体或成员上的注释。如果您也需要,可以扩展它。
在使用 AspectJ 编译器编译您的应用程序时,declare error
语句会直接抛出编译错误。在 Eclipse 中,您会看到类似这样的内容:
如果您还想检测由 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");
}
}
顺便说一句,如果您想查看运行时 pointcut/advice 的运行情况,您需要先编译代码。因此,您要么需要将 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_ReadOnlyGetterAspecte55e852(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_ReadOnlyGetterAspecte55e852(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)
如果你做后者(修复代码),当然你只会看到第二个异常。