问题描述
我有一个字段设置切入点,这似乎和我期望的一样.其定义如下
I have a field set pointcut, which seems to do as I expect. Its defined as follows
before(Object newval): set(@Serviced private * *.*) && args(newval)
以上内容旨在捕获:每当设置有@Serviced注释的私有字段属性时,请调用my before建议.
The above is meant to capture: whenever a private field attribute, annotated with @Serviced, is set call my before advice.
除了我的代码中通过Java反射(即通过java.lang.reflect.Field.set(....)"设置与上述变量匹配的变量的一种情况外,一切似乎都可以正常工作.
Everything seems to work fine, except for the one case in my code that sets a variable matching the above via java reflection ( ie via java.lang.reflect.Field.set(....).
有什么主意可以捕捉到这个场景"吗?
Any idea's how I can catch that "set" also?
谢谢
推荐答案
您已经注意到,set()
切入点不能拦截反射场的变化.但是,如果您控制(即可以将方面编织)调用Field.set*(..)
方法的代码,则可以通过使用反射来解决该问题.这是一个完整的可编译代码示例,说明了该解决方案:
As you have noticed, the set()
pointcut cannot intercept reflective field changes. But if you control (i.e. can weave aspects into) the code calling the Field.set*(..)
methods, you can work around that issue by also using reflection. Here is a complete, compileable code sample illustrating the solution:
示例注释:
package de.scrum_master.app;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Serviced {}
使用主要方法对实体类进行采样:
package de.scrum_master.app;
public class Person {
@Serviced private int id;
@Serviced private String name;
private String country;
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getCountry() { return country; }
public void setCountry(String country) { this.country = country; }
public void setIdReflective(int id) throws Exception {
Person.class.getDeclaredField("id").setInt(this, id);
}
public void setNameReflective(String name) throws Exception {
Person.class.getDeclaredField("name").set(this, name);
}
public void setCountryReflective(String country) throws Exception {
Person.class.getDeclaredField("country").set(this, country);
}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", country=" + country + "]";
}
public static void main(String[] args) throws Exception {
Person person = new Person();
person.setId(11);
person.setName("Tin Man");
person.setCountry("Oz");
System.out.println("Before reflective setters: " + person);
person.setIdReflective(22);
person.setNameReflective("Cowardly Lion");
person.setCountryReflective("The Land of Oz");
System.out.println("After reflective setters: " + person);
}
}
如您所见,三个私有字段中只有两个具有@Serviced
批注.对于所有三个字段,将两次调用设置程序:一次是正常调用,一次是通过反射调用.
As you can see, only two out of three private fields have the @Serviced
annotation. Setters are called for all three fields twice: once normally and once via reflection.
方面可拦截法向和反射场变化:
package de.scrum_master.aspect;
import de.scrum_master.app.Serviced;
import java.lang.reflect.Field;
public aspect ServicedFieldChangeInterceptor {
before(Object newValue):
set(@Serviced private * *) && args(newValue)
{
System.out.println(thisJoinPointStaticPart + " -> " + newValue);
}
before(Object newValue, Field field):
call(public void Field.set*(Object, *)) && args(*, newValue) && target(field)
{
if (field.getAnnotation(Serviced.class) == null)
return;
System.out.println(thisJoinPointStaticPart + " -> " + field + ", " + newValue);
}
}
运行Person.main
时的示例输出:
Sample output when running Person.main
:
set(int de.scrum_master.app.Person.id) -> 11
set(String de.scrum_master.app.Person.name) -> Tin Man
Before reflective setters: Person [id=11, name=Tin Man, country=Oz]
call(void java.lang.reflect.Field.setInt(Object, int)) -> private int de.scrum_master.app.Person.id, 22
call(void java.lang.reflect.Field.set(Object, Object)) -> private java.lang.String de.scrum_master.app.Person.name, Cowardly Lion
After reflective setters: Person [id=22, name=Cowardly Lion, country=The Land of Oz]
输出清楚地表明,两个建议仅对用@Serviced
注释的字段做某事"(在这种情况下,将信息打印到标准输出中),而其他字段则被跳过.虽然set()
切入点是静态应用的,但是反射切入点需要动态确定目标字段是否具有匹配的注释.
The output clearly shows that both advice only "do something" (in this case print information to standard output) for fields annotated with @Serviced
, whereas other fields are skipped. While the set()
pointcut applies statically, the reflective one needs to determine if the target field has a matching annotation dynamically.
这篇关于如果通过反射设置字段,是否会调用set()字段切入点?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!