java - 如何为无法更新任何实例变量的方法编写方面或注释?
问题描述
如何为无法更新任何实例变量的方法编写方面或注释?
例如说我有以下java类
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
像下面这样的类应该没问题,因为它只是读取实例变量。
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 {}
示例类/驱动程序应用程序:
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); }
}
}
方面声明编译错误:
The following aspect only detects @ReadOnly
annotations on methods, not on classes or members. You can extend it if you also need that.
The declare error
statement directly throws compile errors when compiling your application with the AspectJ compiler. In Eclipse you would see something like this:
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");
}
}
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.
推荐阅读
- c# - DbContext 不包含配置的定义 - EF Core
- php - PHP:尝试在多维数组中按值查找键,但不能正常工作
- ruby-on-rails - 如何推理枚举的级别
- git - 忽略整个目录树中所有扩展名为 .aux、.log 的文件
- excel - Excel VBA中的合并和总计
- c# - 使用 ASP.Net Core DI 的策略模式
- visual-studio - 使用 Microsoft.VisualStudio.TestTools.UnitTesting;
- angular6 - 通过点击事件上的选择器将数据从父级传递给子级
- java - 初始化复杂过滤器时出错。从 Kotlin 运行 ffmpeg 时出现无效参数错误
- node.js - 如何让函数在 Node Js 和 MySQL 中同时退出触发?