如果通过反射设置字段,是否会调用 set() 字段切入点? [英] Does a set() field pointcut get invoked if field is set via reflection?

查看:26
本文介绍了如果通过反射设置字段,是否会调用 set() 字段切入点?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个字段设置切入点,这似乎符合我的预期.其定义如下

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 注释的私有字段属性被设置时,请调用我的 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 {}

使用 main 方法的示例实体类:

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 注释.所有三个字段的 setter 都被调用两次:一次正常,一次通过反射.

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() 字段切入点?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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