java-如何使Lombok Gson与Spring AOP代理一起使用

假设有一个简单的班级学生

@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代理获取原始对象感到好奇,所以在调试器中对其进行了研究.似乎每个代理都有一个公共的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文档才能找到接口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)方法结合使用.所以你很好.

上一篇:android-如何使用Gson解析JSON对象内的多个JSON数组?


下一篇:java-如何将json字符串反序列化为对象