When I defined a session scope bean in Spring Boot, it behaved strangely and needed to be adjusted.

Introduction

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.

Cause of a problem

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.

Correspondence policy

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

When I defined a session scope bean in Spring Boot, it behaved strangely and needed to be adjusted.
When internationalizing is supported by Spring Boot, a specific locale is not translated and I am addicted to it
Until you create a Spring Boot project in Intellij and push it to Github
How to add a classpath in Spring Boot
I want to issue a connection when a database is created using Spring and MyBatis
What I was addicted to when developing a Spring Boot application with VS Code
A memo that I was addicted to when making batch processing with Spring Boot
A site that was easy to understand when I was a beginner when I started learning Spring Boot
How to create a Spring Boot project in IntelliJ
I made a Restful server and client in Spring.
Get a list of other sessions of the same user when using Redis Session in Spring Boot (2 series). Also discard it.
Session was a cookie designed to be erased when the browser was closed and was a method for exchanging it: Rails Tutorial Note-What is a Rails Session?
What I fixed when updating to Spring Boot 1.5.12 ・ What I was addicted to
How to call and use API in Java (Spring Boot)
Create a Spring Boot web app that uses IBM Cloudant and deploy it to Cloud Foundry
RSocket is supported in Spring Boot 2.2, so give it a try
Fitted in Spring Boot using a bean definition file named application.xml
What I did in the migration from Spring Boot 1.4 series to 2.0 series
I wrote a route search program in TDD and refactored it
What I did in the migration from Spring Boot 1.5 series to 2.0 series
How to display characters entered in Spring Boot on a browser and reference links [Introduction to Spring Boot / For beginners]
What I did when I was addicted to the error "Could not find XXX in any of the sources" when I added a Gem and built it