Java,反射将字段转换为 JAXBElement

Java, reflection convert field into JAXBElement

我正在尝试执行 SOAPMessage 的日志记录。 这个对象包含包装器 类 和 JAXBElements,我正在做这样的事情

@Before("soapRequest()")
public void logBefore(JoinPoint joinPoint) {

    Object[] signatureArgs = joinPoint.getArgs();
    System.out.println("\n\n\n");
    for (Object signatureArg : signatureArgs) {
        StringBuilder sb = new StringBuilder();

        try {

            Field[] aClassFields = signatureArg.getClass().getDeclaredFields();
            sb.append(signatureArg.getClass().getSimpleName() + " [ ");
            for (Field f : aClassFields) {
                f.setAccessible(true);
                String fName = f.getName();

                String value = "";
                if(f.get(signatureArg) instanceof JAXBElement) {
                    log.info("is instance of");
                    JAXBElement val = (JAXBElement) f.get(signatureArg);
                    log.info(val.toString());
                    value = val.getValue().toString();
                } else {
                    value = f.get(signatureArg).toString();
                }

                sb.append("(" + f.getType() + ") " + fName + " = " + value + ", ");
            }
            sb.append("]");
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println(sb.toString());
    }
}

但是这一行抛出 NPE:

if(f.get(signatureArg) instanceof JAXBElement) {
                        log.info("is instance of");
                        JAXBElement val = (JAXBElement) f.get(signatureArg);
                        log.info(val.toString());
                        value = val.getValue().toString();
                    }

如何检查该字段是否是 JAXBElement 的实例并从中提取值?

实际上我认为您的 NPE 出现在 then 代码块的这行代码中:

value = f.get(signatureArg).toString();

如果字段值为 null,则会发生这种情况,因为在 null 上您无法调用 toString()。顺便说一句,这应该发生在任何 null 字段上,而不仅仅是 JAXBElement。你不需要 toString(),你可以删除它,因为当你打印任何对象时,它会在适用的地方自动使用它的 toString() 表示。

在我看来,您的代码也比必要的更复杂,并且通过一些重组和重命名变量,根本不再需要 then 块。这是我的 MCVE 纯 Java + AspectJ(没有 Spring)给你的:

package de.scrum_master.app;

import javax.xml.bind.JAXBElement;

public class Container {
  private String name;
  private JAXBElement jaxbElement;

  public Container(String name, JAXBElement jaxbElement) {
    this.name = name;
    this.jaxbElement = jaxbElement;
  }
}
package de.scrum_master.app;

import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;

public class Application {
  public void doSomething(int number, String text, Container myContainer) {}

  public static void main(String[] args) {
    Application application = new Application();
    application.doSomething(11, "foo", new Container("bar", new JAXBElement(new QName("local"), String.class, "dummy")));
    application.doSomething(11, "foo", new Container("bar", null));
  }
}
package de.scrum_master.aspect;

import java.lang.reflect.Field;

import javax.xml.bind.JAXBElement;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class MyAspect {
  @Pointcut("execution(* doSomething(..))")
  private void soapRequest() {}

  @Before("soapRequest()")
  public void logBefore(JoinPoint joinPoint) {
    System.out.println(joinPoint);

    for (Object methodArg : joinPoint.getArgs()) {
      StringBuilder sb = new StringBuilder();
      try {
        sb.append(methodArg.getClass().getSimpleName() + " [ ");
        for (Field field : methodArg.getClass().getDeclaredFields()) {
          field.setAccessible(true);
          String fieldName = field.getName();
          Object value = field.get(methodArg);
          if (value instanceof JAXBElement) {
            System.out.println("  -> is instance of");
            JAXBElement jaxbElement = (JAXBElement) value;
            System.out.println("  -> " + jaxbElement);
            value = jaxbElement.getValue();
          }
          // Un-comment this in order to see the NPE
          //else {
          //  value = field.get(methodArg).toString();
          //}
          sb.append("(" + field.getType() + ") " + fieldName + " = " + value + ", ");
        }
        sb.append("]");
      } catch (Exception e) {
        e.printStackTrace();
      }
      System.out.println("  " + sb);
    }
  }
}

控制台日志如下所示:

execution(void de.scrum_master.app.Application.doSomething(int, String, Container))
  Integer [ (int) MIN_VALUE = -2147483648, (int) MAX_VALUE = 2147483647, (class java.lang.Class) TYPE = int, (class [C) digits = [C@8efb846, (class [C) DigitTens = [C@2a84aee7, (class [C) DigitOnes = [C@a09ee92, (class [I) sizeTable = [I@30f39991, (int) value = 11, (int) SIZE = 32, (int) BYTES = 4, (long) serialVersionUID = 1360826667806852920, ]
  String [ (class [C) value = [C@4a574795, (int) hash = 0, (long) serialVersionUID = -6849794470754667710, (class [Ljava.io.ObjectStreamField;) serialPersistentFields = [Ljava.io.ObjectStreamField;@f6f4d33, (interface java.util.Comparator) CASE_INSENSITIVE_ORDER = java.lang.String$CaseInsensitiveComparator@23fc625e, ]
  -> is instance of
  -> javax.xml.bind.JAXBElement@4f023edb
  Container [ (class java.lang.String) name = bar, (class javax.xml.bind.JAXBElement) jaxbElement = dummy, ]
execution(void de.scrum_master.app.Application.doSomething(int, String, Container))
  Integer [ (int) MIN_VALUE = -2147483648, (int) MAX_VALUE = 2147483647, (class java.lang.Class) TYPE = int, (class [C) digits = [C@8efb846, (class [C) DigitTens = [C@2a84aee7, (class [C) DigitOnes = [C@a09ee92, (class [I) sizeTable = [I@30f39991, (int) value = 11, (int) SIZE = 32, (int) BYTES = 4, (long) serialVersionUID = 1360826667806852920, ]
  String [ (class [C) value = [C@4a574795, (int) hash = 0, (long) serialVersionUID = -6849794470754667710, (class [Ljava.io.ObjectStreamField;) serialPersistentFields = [Ljava.io.ObjectStreamField;@f6f4d33, (interface java.util.Comparator) CASE_INSENSITIVE_ORDER = java.lang.String$CaseInsensitiveComparator@23fc625e, ]
  Container [ (class java.lang.String) name = bar, (class javax.xml.bind.JAXBElement) jaxbElement = null, ]

看到了吗?你的错误消失了。取消注释 else 块以便看到它重新出现,然后从该行中删除 .toString() 并且它再次消失。也许它可以帮助您更好地理解您的错误。

顺便说一句,我觉得日志输出看起来有点丑。您是否还注意到您也打印了静态字段?您可能应该过滤掉它们。但我不想更改您的更多代码,因为我仍然希望您能识别它。


没有 JAXBElement 的额外调试日志记录且没有 try - catch 但声明的异常的简短版本将是:

package de.scrum_master.aspect;

import java.lang.reflect.Field;

import javax.xml.bind.JAXBElement;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class MyAspect {
  @Pointcut("execution(* doSomething(..))")
  private void soapRequest() {}

  @Before("soapRequest()")
  public void logBefore(JoinPoint joinPoint) throws Throwable {
    System.out.println(joinPoint);
    for (Object methodArg : joinPoint.getArgs()) {
      StringBuilder sb = new StringBuilder();
      sb.append(methodArg.getClass().getSimpleName() + " [ ");
      for (Field field : methodArg.getClass().getDeclaredFields()) {
        field.setAccessible(true);
        String fieldName = field.getName();
        Object value = field.get(methodArg);
        if (value instanceof JAXBElement)
          value = ((JAXBElement) value).getValue();
        sb.append("(" + field.getType() + ") " + fieldName + " = " + value + ", ");
      }
      sb.append("]");
      System.out.println("  " + sb);
    }
  }
}