A story when building an application with a Spring Boot (Freemarker + Doma2) configuration.
It's bad to implement it with a loose understanding, but when I defined a session-scoped bean, I couldn't access properties like hoge.fuga on ftl.
I should have accessed it via Getter like hoge.getFuga (), but I thought I should investigate the cause and make it possible to access the property, and I could do it.
By the way, when I returned it as JSON, it had strange properties, so I tried not to use it.
The session-scoped bean was wrapped in a proxy made with cglib.
Perhaps this proxy has a ThreadLocal variable and is a proxy for a different object for each request.
Just write a Configuration that strips the proxy on output to handle raw objects.
Fortunately, the proxy seems to have an interface called ScopedObject, which I could strip with ScopedObject # getTargetObject.
Click here for support for Freemarker
FreemarkerConfig.java
@Configuration
public class FreemarkerConfig {
  @Autowired
  void configureFreemarkerConfigurer(FreeMarkerConfig config) {
    freemarker.template.Configuration templateConfig = config.getConfiguration();
    templateConfig.setObjectWrapper(new CustomWrapper());
  }
  private static class CustomWrapper extends DefaultObjectWrapper {
    CustomWrapper() {
      super(freemarker.template.Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
    }
    @Override
    protected TemplateModel handleUnknownType(Object obj) throws TemplateModelException {
      if (obj instanceof ScopedObject) {
        return super.handleUnknownType(((ScopedObject) obj).getTargetObject());
      }
      return super.handleUnknownType(obj);
    }
  }
}
Click here for Jackson support
JacksonConfig.java
@Configuration
public class JacksonConfig {
  @Autowired
  public void configureObjectMapperConfig(ObjectMapper objectMapper) {
    objectMapper.registerModule(new ScopedObjectModule());
  }
  private static class ScopedObjectModule extends Module {
    @Getter
    private final String moduleName = "scopedObjectModule";
    @Override
    public Version version() {
      return PackageVersion.VERSION;
    }
    @Override
    public void setupModule(SetupContext context) {
      context.addSerializers(new Serializers.Base() {
        @Override
        public JsonSerializer<?> findSerializer(SerializationConfig config, JavaType type, BeanDescription beanDesc) {
          Class<?> raw = type.getRawClass();
          if (ScopedObject.class.isAssignableFrom(raw)) {
            return new ScopedObjectSerializer(type.getSuperClass());
          }
          return super.findSerializer(config, type, beanDesc);
        }
      });
    }
  }
  @AllArgsConstructor
  private static class ScopedObjectSerializer extends JsonSerializer<ScopedObject> {
    private JavaType type;
    @Override
    public void serialize(ScopedObject value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
      serializers.findValueSerializer(type).serialize(value.getTargetObject(), gen, serializers);
    }
  }
}
        Recommended Posts