2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Spring Boot でセッションスコープBeanを定義したら動きがおかしかったので調整が必要だった

Posted at

はじめに

Spring Boot (Freemarker + Doma2)の構成でアプリケーションを構築しているときの話。
ゆるふわな理解で実装してるのが悪いんだけど、セッションスコープのBeanを定義したときに、ftl上でhoge.fugaのようにプロパティアクセスできなかった。
hoge.getFuga()のようにGetter経由でアクセスすればよかったんだけど、どうせなら原因究明してプロパティアクセスできるようにしたらいいと思って頑張ったらできた。
ついでにJSONとして返すときも変なプロパティがついてたので、つかないようにした

問題の原因

セッションスコープのBeanはcglibで作られたプロキシに包まれていた。
多分このプロキシがThreadLocalな変数を持っていて、リクエストごとに異なったオブジェクトのプロキシになっているんだろう。

対応方針

出力時にプロキシを剥いて生のオブジェクトを取り扱うようにする、Configurationを書いてやればいい。
幸いにして、プロキシはScopedObjectというインターフェースを持っているようで、ScopedObject#getTargetObjectで剥くことができた。

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);
    }
  }
}

Jacksonへの対応がこちら

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);
    }
  }
}
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?