假设有一个简单的类Student
@Data @NoArgsConstructor @AllArgsConstructor
public class Student {
private Integer age;
private String name;
}
在 aop.xml 中使用 Spring AOP 添加日志方面
<aop:config>
<aop:aspect id="log" ref="logging">
<aop:pointcut id="selectAll" expression="execution(* com.tutorial.Student.getName(..))"/>
<aop:before pointcut-ref="selectAll" method="beforeAdvice"/>
<aop:after pointcut-ref="selectAll" method="afterAdvice"/>
</aop:aspect>
</aop:config>
<bean id="student" class="com.tutorial.Student">
<property name="name" value="Zara" />
<property name="age" value="11"/>
</bean>
排除方面字段
public class ExcludeAspects implements ExclusionStrategy {
@Override
public boolean shouldSkipField(FieldAttributes f) {
if(f.getName().startsWith("CGLIB$"))
return true;
return false;
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
}
main,注意第一个 bean 的输出是空的(“{}”):
public static void main(String[] args) {
Gson gson = new GsonBuilder().setPrettyPrinting().addSerializationExclusionStrategy(new ExcludeAspects()).create();
ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
//return "{}"
Student student = (Student) context.getBean("student");
gson.toJson(student);
//works fine
Student student2 = new Student(11,"Zara");
gson.toJson(student2);
}
更新 根据接受的答案,unProxy
对我有用。
最佳答案
您的代码似乎暗示您的方面正在工作,即在执行配置建议之前/之后。如果他们不这样做,那么您在其他地方就会遇到问题。我进一步假设
- 您的方面按设计工作并且您已经检查过,
- 您使用的是 Spring AOP,而不是带有加载时编织的 AspectJ,
- GSON 以某种方式看到了 CGLIB 代理,而不是下面的原始对象。
然后问题可能是 GSON - 我对它的经验为零,以前从未使用过它 - 使用反射来搜索代理类中的字段。但它不会找到任何代理只覆盖方法,但没有字段,因为后者在原始类中(代理的父类)。如果是这样,您需要配置 GSON 在原始类中搜索,而不是在代理类中搜索。那么您也不必排除任何内容。
更新:
我上面的有根据的猜测是正确的。
只是因为我对如何从 CGLIB 代理中获取原始对象感到好奇,所以我在调试器中查看了它。似乎每个代理都有一个公共(public) final方法 getTargetSource
,您可以通过反射调用它:
package com.tutorial;
import org.springframework.aop.TargetSource;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class Application {
public static void main(String[] args) throws Exception {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
Student student = (Student) context.getBean("student");
TargetSource targetSource = (TargetSource)
student
.getClass()
.getMethod("getTargetSource", null)
.invoke(student, null);
System.out.println(gson.toJson(targetSource.getTarget()));
}
}
这对我来说适用于你的代码,但我没有在混合中使用 Lombok(你根本没有提到,我只是在尝试编译你的代码时发现的!)但手动创建的构造函数、getter 和 setter 只是启动并运行。
此外,您不再需要 ExclusionStrategy
。
控制台日志:
{
"age": 11,
"name": "Zara"
}
顺便说一句,众所周知,由于类命名冲突,Lombok 会在与 AspectJ 的连接中引起麻烦,请参阅 my answer here .这也可能会影响 Spring AOP。
我认为您在这里使用了一种不健康的(因为不兼容的)技术组合,如果您找到了解决方案并且不想最终为每个 bean 类编写自定义类型适配器,那将是非常麻烦的。如果删除 Lombok,至少可以从 Spring AOP 切换到 AspectJ with LTW为了摆脱代理问题。 AspectJ 不使用代理,因此 GSON 可能会更好地使用它。
更新 2:
我的第一次更新只是在茶歇期间进行的快速修改。不是 Spring 用户,我也必须先查找 API 文档才能找到接口(interface) Advised
.它包含方法 getTargetSource()
,即:
- 我们可以将 Spring bean(AOP 代理)转换为
Advised
,从而避免丑陋的反射。 - 更进一步,我们可以动态确定给定对象是否是(建议的)代理,即如果您更改或停用您的方面,相同的代码仍然有效。
package com.tutorial;
import org.springframework.aop.framework.Advised;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class Application {
public static void main(String[] args) throws Exception {
try (ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("aop.xml")) {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
Student student = (Student) context.getBean("student");
System.out.println(gson.toJson(unProxy(student)));
}
}
public static Object unProxy(Object object) throws Exception {
return object instanceof Advised
? ((Advised) object).getTargetSource().getTarget()
: object;
}
}
更新 3: 我很好奇,还为我的 IDE 安装了 Lombok。实际上,上面的示例确实与 Gson 和我的小 unProxy(Object)
方法结合使用。所以你很高兴去。 :-)
关于java - 如何使 Lombok + Gson 与 Spring AOP 代理一起工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48961551/