すきま風

勉強したことのメモとか

Configuration annotationのproxyBeanMethodsとBean Lite Modeについての備忘録

記事の概略

  • SpringのBeanにはBean Lite Modeというものがある
  • Spring 5.2から@Componentの代わりに@Configuration(proxyBeanMethods = false)を指定することでBean Lite Modeにできる
  • CGLIB Proxyが不要ならパフォーマンスがよくなる
  • https://github.com/spring-projects/spring-boot/issues/9068 を読めば大体わかる


以下は自分の勉強の備忘録になります。上のissueが完璧なのであんまり読む価値ない。


@Configuration#proxyBeanMethods

Spring Sleuthのコードを読んでいたら見慣れない@Configurationの書き方を見つけました。

https://github.com/spring-cloud/spring-cloud-sleuth/blob/v2.2.1.RELEASE/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfiguration.java#L71

@Configuration(proxyBeanMethods = false)

Spring5.2から追加されたもので、falseを指定することでCGLIB Proxyを利用したAspectJ系の処理が動かなくなります。 Bean Lite Modeと呼ばれるものらしいです。

Bean Lite Mode

初めて聞いた単語だったので、下の解説を読んで少し勉強しました。

Bean (Spring Framework 5.2.3.BUILD-SNAPSHOT API)

@Configurationの代わりに@Componentを利用することで通常のBeanではなくBean Lite Modeで登録できるようです。 普通のBeanとの違いはCGLIB proxyを利用しないことです。このため inter-bean referencesが利用できなくなります。 (Bean間参照、であっているだろうか)

デモプログラム

BeanとBean Lite Modeの違いを確認するデモを用意しました。

@SpringBootApplication
class DemoApplication

fun main(args: Array<String>) {
    runApplication<DemoApplication>(*args)
}

// ここのアノテーションを切り替えるとBean Lite Modeになる
@Configuration
// @Component
// @Configuration(proxyBeanMethods = false)
class FooBarConfiguration {
    @Bean
    fun foo(): Foo {
        val id = UUID.randomUUID().toString()
        println("create: $id")

        return Foo(id)
    }

    @Bean
    fun bar(): Bar {
        println(foo())

        return Bar()
    }
}

data class Foo(val value: String)

class Bar

@Configuration

# log output
create: 2264edc9-1a5f-4bfc-8945-e808b12eae61
Foo(value=2264edc9-1a5f-4bfc-8945-e808b12eae61)

Beanに登録したクラスを参照先で取得します。 @Configurationの場合、BeanがCGLIBでWrapされ、method callをインターセプトしてBeanインスタンスをコンテキストから返します。 そのためinter-bean referencesが利用可能になっています。

@Component (Bean Lite Mode)

# log output
create: 47224697-69bb-4b5e-8a15-a925e71eae27
create: 098e25bc-5e99-4cd6-8b0a-22a57dde0d13
Foo(value=098e25bc-5e99-4cd6-8b0a-22a57dde0d13)

Bean Lite Modeでは、Spring Containerにより通常のFactory Methodとして処理されます。つまり、CGLIB Proxyを利用しません。 そのため、inter-bean referencesは機能せず、普通にmethod callして再取得、という形になっています。ちなみにこのコードを書くとIntelliJが警告してくれます。

@Configuration(proxyBeanMethods = false)

@Componentの場合と同じになります。

proxyBeanMethods = false をつけると何が良いのか

spring-bootのissueを見たところ、パフォーマンスが良くなるみたいです。

Use @Configuration(proxyBeanMethods=false) wherever possible · Issue #9068 · spring-projects/spring-boot · GitHub

ブログを書き終わったあとでこのissueをみつけたのですが、 ここの情報だけで何もかも解決していたので自分の書いた記事の価値とは一体、ウゴゴゴという虚無に襲われました。

まとめ

Bean Lite Modeを利用したい場合、Spring 5.2以降なら @Configuration(proxyBeanMethods = false) を利用するのが良さそうです。 @Component@Configuration についてはかなりいい加減な知識で使っていましたが、少しだけ中身を知ることができました。

記事を書いた後の感想

もうちょっとまともに英語読めるようにならないとダメダナ 😑

参考

[spring] @Configuration と @Component は違う、あるいは @Bean lite mode について - tokuhirom's blog