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

Java 如何为无法更新任何实例变量的方法编写方面或注释?,java,aop,aspectj,Java,Aop,Aspectj,如何为无法更新任何实例变量的方法编写方面或注释 例如,假设我有以下java类 public class Foo { String name; int id; public getName() { return name; } } 现在假设我想写一个方面,或者一般来说,一些注释称之为say@readOnly,它可以强制getName()方法不修改name或id,如果这样做的话 public class Foo { String name

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

例如,假设我有以下java类

public class Foo {

    String name;
    int id;

    public getName() {
       return name;
    }
}
现在假设我想写一个方面,或者一般来说,一些注释称之为say
@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\u master.app;
导入静态java.lang.annotation.ElementType.FIELD;
导入静态java.lang.annotation.ElementType.METHOD;
导入静态java.lang.annotation.ElementType.TYPE;
导入静态java.lang.annotation.RetentionPolicy.RUNTIME;
导入java.lang.annotation.Retention;
导入java.lang.annotation.Target;
@保留(运行时)
@目标({TYPE,FIELD,METHOD})
public@interface只读{}
示例类/驱动程序应用程序:

package de.scrum\u master.app;
公共类应用程序{
私有int id=1;
私有字符串name=“default”;
@只读
公共int getId(){
返回id;
}
@只读
公共字符串getName(){
name=“你好,世界”;
id=7564;
返回名称;
}
公共字符串getNameWithoutReadOnly(){
name=“你好,世界”;
id=7564;
返回名称;
}
@只读
公共字符串getName(){
修改成员();
返回名称;
}
私有void modifyMembers(){
name=“你好,世界”;
id=7564;
}
公共静态void main(字符串[]args){
应用程序=新应用程序();
application.getId();
请尝试{application.getName();}
catch(异常e){e.printStackTrace(System.out);}
application.getNameWithoutReadOnly();
请尝试{application.getnameinternally();}
catch(异常e){e.printStackTrace(System.out);}
}
}
方面声明编译错误:

以下特性仅检测方法上的
@ReadOnly
注释,而不检测类或成员上的注释。如果你也需要的话,你可以扩展它

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

如果您还希望检测来自getter调用的helper方法的间接写访问,那么还需要使用
cflow()
的动态切入点,但该切入点仅在运行时有效,而不是在编译时,因为它检查调用堆栈。如果你不需要它,就把它取下来

package de.scrum\u master.aspect;
导入de.scrum_master.app.ReadOnly;
公共方面只读etteraspect{
声明错误:
set(**)&&withincode(public*get*())&&withincode(只读):
“禁止从getter中设置成员”;
before():set(**)&&cflow(执行(@ReadOnly public*get*()){
抛出新的IllegalAccessError(“禁止从getter中设置成员”);
}
}
顺便说一句,如果您想看到运行时切入点/建议的作用,您需要先编译代码。因此,您需要将
declare error
减弱为
declare warning
,或者在
getName()
中注释掉导致编译错误的两条语句

如果执行前者,日志输出将为:

java.lang.IllegalAccessError:禁止从getter中设置成员
在de.scrum\u master.aspect.readonlygetterspect.ajc$之前的de.scrum\u master\u aspect\u readonlygetterspect$1$3e55e852(readonlygetterspect.aj:11)
在de.scrum_master.app.Application.getName(Application.java:14)上
位于de.scrum_master.app.Application.main(Application.java:39)
java.lang.IllegalAccessError:禁止从getter中设置成员
在de.scrum\u master.aspect.readonlygetterspect.ajc$之前的de.scrum\u master\u aspect\u readonlygetterspect$1$3e55e852(readonlygetterspect.aj:11)
位于de.scrum_master.app.Application.modifyMembers(Application.java:32)
位于de.scrum_master.app.Application.getname(Application.java:27)
位于de.scrum_master.app.Application.main(Application.java:42)

如果您执行后一种操作(修复代码),当然您只会看到第二个异常。

您需要检查字节码(以及该方法可能递归调用的所有字节码)。我希望我能为此给您+100。非常感谢!!!是的,编译时错误甚至更好!你介意把这个代码上传到别的地方吗?所以我可以看看用gradle设置它需要什么?我对这个框架还是新手。如果你有奖励我的冲动,请在这个问题上悬赏。;-)至于上传代码,它已经完成了,甚至包括了包名。唯一缺少的就是一个Gradle文件。不过,我是一个专业用户。这个示例项目被设置为一个纯Eclipse项目,但我可以向您展示如何使用Maven来构建它。不过,对于格雷德尔,你得问问其他人,我很抱歉。顺便说一句,这是否意味着你问了一个AspectJ问题,却没有自己构建任何AspectJ项目?也许你可以用谷歌搜索一下。