使用 aspectj 拦截带反射的赋值
Using aspectj to intercept an assignment with reflection
当我 运行 带有 tomcat 的应用程序时,它说建议没有被应用,所以我的方面将无法工作。我必须在任何地方配置它吗?我没有做任何事情,所以我不知道什么代码可能有用。
谢谢!
编辑
我刚刚发现如何解决这个问题,即使它说尚未应用方面,当我调用 setter 时它仍然有效,但我在使用反射时遇到问题那。
我有一个方面可以将 setter 拦截到一个工作正常的字段,但是当从示例中的 Gson 库分配值时,它不起作用。
这是方面:
public aspect AnnotationAspect {
pointcut hasAnnotation(Annotation annotation) : @annotation(annotation);
Object around(Annotation annotation, String word) : hasAnnotation(annotation) && set(String *) && args(word) {
Object result = null;
try {
result = proceed(annotation, "intercepted");
} catch (RuntimeException ex) {
throw ex;
}
return result;
}
}
我有这个 class:
public class JavaEntity {
@Annotation
public String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
当我这样做时,它不会拦截赋值:
JavaEntity entity = new Gson().fromJson("{\"name\":\"name\"}", JavaEntity.class);
有办法拦截吗?谢谢!
反射字段访问不能被AspectJ直接拦截。您只能通过以毒攻毒来解决它,即也使用反射:
- 确保 Gson 在 AspectJ 编译器的 inpath 上(即 AspectJ Maven 插件的编织依赖项),以便能够编织到其代码中。
- 拦截对
Field.set(..)
. 的调用
- 在拦截通知中检查要设置哪个字段,通过反射查找其注解以模仿
@annotation(blah)
,决定是否修改其值。
这不完全是通常的 AspectJ 用例,但有可能。
顺便说一句,execution()
连接点没有这种问题,因为它们也会在反射调用方法时触发。不过,call()
会有问题。但这只是供您参考,与这个具体案例没有直接关系。
更新:我的意思是这样的:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<configuration>
<weaveDependencies>
<weaveDependency>
<groupId>Group</groupId>
<artifactId>model</artifactId>
</weaveDependency>
<weaveDependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</weaveDependency>
</weaveDependencies>
</configuration>
</plugin>
@Test
public void testingInterceptWithJavaGson(){
javaEntity = new Gson().fromJson("{\"name\":\"newName\"}", JavaEntity.class);
Assert.assertEquals("intercepted", javaEntity.getName());
}
Object around(Field field, Object obj, Object value) :
within(com.google.gson..*) &&
call(public void Field.set(Object, Object)) &&
target(field) &&
args(obj, value)
{
Object result = null;
System.out.println(thisJoinPoint);
System.out.println(" " + field + " -> " + field.getAnnotation(Annotation.class));
System.out.println(" " + obj);
System.out.println(" " + value);
try {
if (field.getAnnotation(Annotation.class) != null && field.getType() == String.class)
result = proceed(field, obj, "intercepted");
else
result = proceed(field, obj, value);
} catch (RuntimeException ex) {
throw ex;
}
return result;
}
其实你不需要通过args()
绑定第一个参数obj
,我只是用它来记录日志,以便向你展示那里发生了什么。所以这个稍微简单一点的形式也可以:
Object around(Field field, Object value) :
within(com.google.gson..*) &&
call(public void Field.set(Object, Object)) &&
target(field) &&
args(*, value)
{
Object result = null;
System.out.println(thisJoinPoint);
try {
if (field.getAnnotation(Annotation.class) != null && field.getType() == String.class)
result = proceed(field, "intercepted");
else
result = proceed(field, value);
} catch (RuntimeException ex) {
throw ex;
}
return result;
}
我的分支对您现有 GitHub repo are contained in yet another pull request 的更改。
当我 运行 带有 tomcat 的应用程序时,它说建议没有被应用,所以我的方面将无法工作。我必须在任何地方配置它吗?我没有做任何事情,所以我不知道什么代码可能有用。
谢谢!
编辑
我刚刚发现如何解决这个问题,即使它说尚未应用方面,当我调用 setter 时它仍然有效,但我在使用反射时遇到问题那。
我有一个方面可以将 setter 拦截到一个工作正常的字段,但是当从示例中的 Gson 库分配值时,它不起作用。
这是方面:
public aspect AnnotationAspect {
pointcut hasAnnotation(Annotation annotation) : @annotation(annotation);
Object around(Annotation annotation, String word) : hasAnnotation(annotation) && set(String *) && args(word) {
Object result = null;
try {
result = proceed(annotation, "intercepted");
} catch (RuntimeException ex) {
throw ex;
}
return result;
}
}
我有这个 class:
public class JavaEntity {
@Annotation
public String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
当我这样做时,它不会拦截赋值:
JavaEntity entity = new Gson().fromJson("{\"name\":\"name\"}", JavaEntity.class);
有办法拦截吗?谢谢!
反射字段访问不能被AspectJ直接拦截。您只能通过以毒攻毒来解决它,即也使用反射:
- 确保 Gson 在 AspectJ 编译器的 inpath 上(即 AspectJ Maven 插件的编织依赖项),以便能够编织到其代码中。
- 拦截对
Field.set(..)
. 的调用
- 在拦截通知中检查要设置哪个字段,通过反射查找其注解以模仿
@annotation(blah)
,决定是否修改其值。
这不完全是通常的 AspectJ 用例,但有可能。
顺便说一句,execution()
连接点没有这种问题,因为它们也会在反射调用方法时触发。不过,call()
会有问题。但这只是供您参考,与这个具体案例没有直接关系。
更新:我的意思是这样的:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<configuration>
<weaveDependencies>
<weaveDependency>
<groupId>Group</groupId>
<artifactId>model</artifactId>
</weaveDependency>
<weaveDependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</weaveDependency>
</weaveDependencies>
</configuration>
</plugin>
@Test
public void testingInterceptWithJavaGson(){
javaEntity = new Gson().fromJson("{\"name\":\"newName\"}", JavaEntity.class);
Assert.assertEquals("intercepted", javaEntity.getName());
}
Object around(Field field, Object obj, Object value) :
within(com.google.gson..*) &&
call(public void Field.set(Object, Object)) &&
target(field) &&
args(obj, value)
{
Object result = null;
System.out.println(thisJoinPoint);
System.out.println(" " + field + " -> " + field.getAnnotation(Annotation.class));
System.out.println(" " + obj);
System.out.println(" " + value);
try {
if (field.getAnnotation(Annotation.class) != null && field.getType() == String.class)
result = proceed(field, obj, "intercepted");
else
result = proceed(field, obj, value);
} catch (RuntimeException ex) {
throw ex;
}
return result;
}
其实你不需要通过args()
绑定第一个参数obj
,我只是用它来记录日志,以便向你展示那里发生了什么。所以这个稍微简单一点的形式也可以:
Object around(Field field, Object value) :
within(com.google.gson..*) &&
call(public void Field.set(Object, Object)) &&
target(field) &&
args(*, value)
{
Object result = null;
System.out.println(thisJoinPoint);
try {
if (field.getAnnotation(Annotation.class) != null && field.getType() == String.class)
result = proceed(field, "intercepted");
else
result = proceed(field, value);
} catch (RuntimeException ex) {
throw ex;
}
return result;
}
我的分支对您现有 GitHub repo are contained in yet another pull request 的更改。