假设有一个简单的类Student
@Data @NoArgsConstructor @AllArgsConstructor
public class Student {
private Integer age;
private String name;
}
使用Spring AOP在aop.xml中添加日志记录方面
<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
适用于我。
答案 0 :(得分:2)
您可以使用@Expose注释来忽略aop字段。 例如:
Gson gson = new GsonBuilder()。excludeFieldsWithoutExposeAnnotation()。create(); String json = gson.toJson(new Book());
public class book {
@Expose
public String name;
@Expose
public int some;
...
}
答案 1 :(得分:2)
您的代码似乎暗示您的方面正在运行,即在您的配置建议执行之前/之后。如果他们不在,你在其他地方遇到问题。我进一步假设
然后问题可能是GSON - 我对它没有经验,以前从未使用它 - 使用反射来搜索代理类中的字段。但是它不会发现任何代理只会覆盖方法,但是没有字段,因为后者属于原始类(代理的父代)。如果是这样,则需要将GSON配置为在原始类中搜索,而不是在代理类中搜索。那么你也不必排除任何东西。
<强>更新强>
我上面的猜测是正确的。
仅仅因为我对如何从CGLIB代理获取原始对象感到好奇,我在调试器中查看了它。看起来每个代理都有一个公共最终方法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"
}
BTW,由于类命名冲突,Lombok因与AspectJ有关而导致麻烦,请参阅my answer here。这也可能影响Spring AOP。
我认为你在这里使用了一种不健康的(因为不兼容的)技术组合,如果你找到一个解决方案并且不希望最终为每个bean类编写自定义类型适配器,那将非常hacky。如果删除Lombok,至少可以从Spring AOP切换到AspectJ with LTW以摆脱代理问题。 AspectJ不使用代理,因此GSON可能会更好地使用代理。
更新2:
我的第一次更新只是在茶歇期间的快速黑客攻击。不是Spring用户,我首先查找了API文档,以便找到接口Advised
。它包含方法getTargetSource()
,即:
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)
方法有关。所以你很高兴。 :-)
答案 2 :(得分:1)
实施ExclusionStrategy
之类的:
@RequiredArgsConstructor
public class ExcludeListedClasses implements ExclusionStrategy {
@NonNull
private Set<Class<?>> classesToExclude;
@Override
public boolean shouldSkipField(FieldAttributes f) {
return false;
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return classesToExclude.contains(clazz);
}
}
使用类似:
ExclusionStrategy es = new ExcludeListedClasses( new HashSet<Class<?>>() {{
add(Logging.class);
}} );
Gson gson = new GsonBuilder().setPrettyPrinting()
.addSerializationExclusionStrategy(es).create();
也可能出现其他不可序列化的类,按方面添加。只需将这些添加到构建ExcludeListedClasses
时提供的集合中。
与Arun的答案不同的是,这样您就不需要为每个类的每个字段添加@Expose
注释,这些字段可能是不可序列化的字段。
例如,如果要按字段名称跳过序列化,也可以以类似的方式使用方法shouldSkipField(..)
。