如何使Lombok + Gson与Spring AOP代理一起使用 [英] How to make Lombok + Gson work with Spring AOP proxies

查看:135
本文介绍了如何使Lombok + Gson与Spring AOP代理一起使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设有一个简单的类Student

@Data @NoArgsConstructor @AllArgsConstructor
public class Student {
    private Integer age;
    private String name;
}

在aop.xml中使用Spring AOP添加日志记录方面

Add a logging aspect With Spring AOP in 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;
    }
}

主要,请注意第一个bean的输出为空("{}"):

main,note the output of the first bean is empty ("{}"):

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对我有用.

Update According to the accepted answer, unProxy works for me.

推荐答案

您的代码似乎暗示您的各个方面都有效,即在配置建议得到执行之前/之后.如果没有,那么您在其他地方也会遇到问题.我进一步假设

Your code seems to imply that your aspects are working, i.e. before/after advices from your configuration get executed. If they don't, you have problems in other places. I am further assuming that

  • 您的方面已按设计工作,并且您已经检查了
  • 您正在使用Spring AOP,而不是使用具有加载时编织功能的AspectJ,
  • GSON以某种方式看到了CGLIB代理,而不是其下面的原始对象.

那么问题可能是GSON-我对它的使用经验为零,以前从未使用过-使用反射来搜索代理类中的字段.但是它找不到任何内容,因为代理仅覆盖方法,但没有字段,因为后者在原始类中(代理的父级).如果是这样,则需要将GSON配置为在原始类中搜索,而不是在代理类中搜索.然后,您也将不必排除任何内容.

Then the problem could be that GSON - I have zero experience with it, never used it before - uses reflection in order to search for fields in the proxy class. But it will not find any as the proxy only overrides methods, but does not have fields because the latter are in the original class (parent to the proxy). If this is true, you need to configure GSON to search in the original class, not in the proxy class. Then you also would not have to exclude anything.

更新:

我上面的有根据的猜测是正确的.

My educated guess above was correct.

仅仅因为我对如何从CGLIB代理获取原始对象感到好奇,所以我在调试器中对其进行了研究.似乎每个代理都有一个公共的final方法getTargetSource,您可以通过反射来调用它:

Just because I was curious about how to get the original object from a CGLIB proxy, I looked at it in a debugger. It seems like every proxy has a public final method getTargetSource which you can invoke via reflection:

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起床并运行.

This works for me with your code, but I did not use Lombok (which you did not mention at all, I just found out when trying to compile your code!) in the mix but manually created constructors, getters and setters just to get up and running.

此外,您不再需要ExclusionStrategy.

控制台日志:

{
  "age": 11,
  "name": "Zara"
}

顺便说一句,由于类命名冲突,已知Lombok会导致与AspectJ关联出现问题,请参见我的回答此处.这也可能会影响Spring AOP.

BTW, Lombok is known to cause trouble in connection with AspectJ because of class naming conflicts, see my answer here. This might also affect Spring AOP.

我认为您在这里使用了不健康的(因为不兼容)技术组合,如果您找到一种解决方案并且不想最终为每个bean类编写自定义类型适配器,那将是非常棘手的事情.如果删除Lombok,至少可以从Spring AOP切换到

I think that you use an unhealthy (because incompatible) mix of technologies here, if you find a solution and do not want to end up writing custom type adapters for each bean class it will be quite hacky. If you remove Lombok, at least you can switch from Spring AOP to AspectJ with LTW in order to get rid of the proxy problem. AspectJ does not use proxies, so GSON might work better with it.

更新2:

我的第一次更新只是茶歇期间的快速破解.由于不是Spring用户,我也不得不先查找API文档才能找到接口

My first update was just a quick hack during a tea break. Not being a Spring user, I had too look up the API doc first in order to find interface Advised. It contains method getTargetSource(), i.e.:

  • 我们可以将Spring bean(AOP代理)转换为Advised,从而避免难看的反射.
  • 再进一步一步,我们可以动态确定给定对象是否是(建议的)代理,即,如果您更改或停用了方面,相同的代码仍然可以使用.
  • We can cast our Spring bean (AOP proxy) to Advised and thus avoid ugly reflection.
  • Going one step further, we can dynamically determine if a given object is an (advised) proxy or not, i.e. if you change or deactivate your aspect, the same code will still work.
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)方法结合使用.所以你很好. :-)


Update 3: I was curious and also installed Lombok for my IDE. Actually the sample from above does work in connection with Gson and my little unProxy(Object) method. So you are good to go. :-)

这篇关于如何使Lombok + Gson与Spring AOP代理一起使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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