[Kotlin + Spring] Maven configuration

Posted at 2019.05.26 11:55 | Posted in Java+/Trouble Shooting

Problem

Kotlin 1.3 이상을 사용하면서 스프링 부트 메인 클래스는 open 키워드를 넣어줘야 하는 가벼운(?) 이슈가 있었었다...

이번에는 spring-data + Mongodb를 공부하면서 다시 이 이슈가 등장하였다.

Kotlin + Spring 하면서 코틀린의 class 가 final로 되는 부분과 스프링에서 빈을 처리하기위해 클래스를 까발리는(?) 부분에서 문제가 생긴다. final class 는 조작이 안되기 때문에!?

> mvn spring-boot:run

[INFO] Scanning for projects...
[INFO]
[INFO] ---------------------< com.microservices:chapter5 >---------------------
[INFO] Building chapter5 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] >>> spring-boot-maven-plugin:2.1.5.RELEASE:run (default-cli) > test-compile @ chapter5 >>>
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ chapter5 ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ chapter5 ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- kotlin-maven-plugin:1.3.31:compile (compile) @ chapter5 ---
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (compile) @ chapter5 ---
[INFO] Changes detected - recompiling the module!
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:testResources (default-testResources) @ chapter5 ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory D:\dev\projects\kotlin-micro-service\chapter5\src\test\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ chapter5 ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to D:\dev\projects\kotlin-micro-service\chapter5\target\test-classes
[INFO]
[INFO] --- kotlin-maven-plugin:1.3.31:test-compile (test-compile) @ chapter5 ---
[WARNING] No sources found skipping Kotlin compile
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (testCompile) @ chapter5 ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to D:\dev\projects\kotlin-micro-service\chapter5\target\test-classes
[INFO]
[INFO] <<< spring-boot-maven-plugin:2.1.5.RELEASE:run (default-cli) < test-compile @ chapter5 <<<
[INFO]
[INFO]
[INFO] --- spring-boot-maven-plugin:2.1.5.RELEASE:run (default-cli) @ chapter5 ---

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.5.RELEASE)

0000-00-00 00:00:46.552  INFO 5708 --- [           main] c.m.chapter5.Chapter5ApplicationKt       : Starting Chapter5ApplicationKt on ANTOP-GRAM with PID 5708 (D:\dev\projects\kotlin-micro-service\chapter5\target\classes started by antop in D:\dev\projects\kotlin-m
icro-service\chapter5)
0000-00-00 00:00:46.556  INFO 5708 --- [           main] c.m.chapter5.Chapter5ApplicationKt       : No active profile set, falling back to default profiles: default
0000-00-00 00:00:46.739  WARN 5708 --- [kground-preinit] o.s.h.c.j.Jackson2ObjectMapperBuilder    : For Jackson Kotlin classes support please add "com.fasterxml.jackson.module:jackson-module-kotlin" to the classpath
0000-00-00 00:00:47.090  INFO 5708 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data repositories in DEFAULT mode.
0000-00-00 00:00:47.126  INFO 5708 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 29ms. Found 0 repository interfaces.
0000-00-00 00:00:47.132  INFO 5708 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data repositories in DEFAULT mode.
0000-00-00 00:00:47.137  INFO 5708 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 4ms. Found 0 repository interfaces.
0000-00-00 00:00:48.799  INFO 5708 --- [           main] org.mongodb.driver.cluster               : Cluster created with settings {hosts=[localhost:27017], mode=SINGLE, requiredClusterType=UNKNOWN, serverSelectionTimeout='30000 ms', maxWaitQueueSize=500}
0000-00-00 00:00:49.003  INFO 5708 --- [           main] org.mongodb.driver.cluster               : Cluster created with settings {hosts=[localhost:27017], mode=SINGLE, requiredClusterType=UNKNOWN, serverSelectionTimeout='30000 ms', maxWaitQueueSize=500}
0000-00-00 00:00:49.056  INFO 5708 --- [localhost:27017] org.mongodb.driver.connection            : Opened connection [connectionId{localValue:2, serverValue:16}] to localhost:27017
0000-00-00 00:00:49.063  INFO 5708 --- [localhost:27017] org.mongodb.driver.cluster               : Monitor thread successfully connected to server with description ServerDescription{address=localhost:27017, type=STANDALONE, state=CONNECTED, ok=true, version=Server
Version{versionList=[4, 0, 9]}, minWireVersion=0, maxWireVersion=7, maxDocumentSize=16777216, logicalSessionTimeoutMinutes=30, roundTripTimeNanos=4861938}
0000-00-00 00:00:49.491  INFO 5708 --- [localhost:27017] org.mongodb.driver.connection            : Opened connection [connectionId{localValue:1, serverValue:17}] to localhost:27017
0000-00-00 00:00:49.493  INFO 5708 --- [localhost:27017] org.mongodb.driver.cluster               : Monitor thread successfully connected to server with description ServerDescription{address=localhost:27017, type=STANDALONE, state=CONNECTED, ok=true, version=Server
Version{versionList=[4, 0, 9]}, minWireVersion=0, maxWireVersion=7, maxDocumentSize=16777216, logicalSessionTimeoutMinutes=30, roundTripTimeNanos=2452559}
0000-00-00 00:00:49.643  INFO 5708 --- [ntLoopGroup-2-2] org.mongodb.driver.connection            : Opened connection [connectionId{localValue:3, serverValue:18}] to localhost:27017
0000-00-00 00:00:49.643  INFO 5708 --- [ntLoopGroup-2-3] org.mongodb.driver.connection            : Opened connection [connectionId{localValue:4, serverValue:19}] to localhost:27017
0000-00-00 00:00:49.645  INFO 5708 --- [ntLoopGroup-2-4] org.mongodb.driver.connection            : Opened connection [connectionId{localValue:5, serverValue:20}] to localhost:27017
0000-00-00 00:00:49.652  WARN 5708 --- [           main] onfigReactiveWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating be
an with name 'customerHandler' defined in file [D:\dev\projects\kotlin-micro-service\chapter5\target\classes\com\microservices\chapter5\CustomerHandler.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework
.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'customerServiceImpl': Unsatisfied dependency expressed through field 'customerRepository'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean
 with name 'customerRepository' defined in file [D:\dev\projects\kotlin-micro-service\chapter5\target\classes\com\microservices\chapter5\CustomerRepository.class]: Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigExcepti
on: Could not generate CGLIB subclass of class com.microservices.chapter5.CustomerRepository: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class com.m
icroservices.chapter5.CustomerRepository
0000-00-00 00:00:49.665  WARN 5708 --- [ntLoopGroup-2-3] org.mongodb.driver.connection            : Got socket exception on connection [connectionId{localValue:4, serverValue:19}] to localhost:27017. All connections to localhost:27017 will be closed.
0000-00-00 00:00:49.665  WARN 5708 --- [ntLoopGroup-2-4] org.mongodb.driver.connection            : Got socket exception on connection [connectionId{localValue:5, serverValue:20}] to localhost:27017. All connections to localhost:27017 will be closed.
0000-00-00 00:00:49.665  WARN 5708 --- [ntLoopGroup-2-2] org.mongodb.driver.connection            : Got socket exception on connection [connectionId{localValue:3, serverValue:18}] to localhost:27017. All connections to localhost:27017 will be closed.
0000-00-00 00:00:49.672 ERROR 5708 --- [ntLoopGroup-2-2] org.mongodb.driver.connection            : Callback onResult call produced an error

java.lang.IllegalStateException: state should be: open
        at com.mongodb.assertions.Assertions.isTrue(Assertions.java:70) ~[mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.DefaultServer.invalidate(DefaultServer.java:125) ~[mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.DefaultServer.handleThrowable(DefaultServer.java:163) ~[mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.DefaultServer.access$600(DefaultServer.java:45) ~[mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.DefaultServer$DefaultServerProtocolExecutor$2.onResult(DefaultServer.java:223) ~[mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:49) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.CommandProtocolImpl$1.onResult(CommandProtocolImpl.java:83) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.DefaultConnectionPool$PooledConnection$1.onResult(DefaultConnectionPool.java:461) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.UsageTrackingInternalConnection$2.onResult(UsageTrackingInternalConnection.java:111) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:49) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.InternalStreamConnection$2$1.onResult(InternalStreamConnection.java:361) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.InternalStreamConnection$2$1.onResult(InternalStreamConnection.java:356) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback.onResult(InternalStreamConnection.java:603) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback.onResult(InternalStreamConnection.java:593) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.InternalStreamConnection$5.failed(InternalStreamConnection.java:500) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.connection.netty.NettyStream.readAsync(NettyStream.java:232) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.connection.netty.NettyStream.handleReadResponse(NettyStream.java:266) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.connection.netty.NettyStream.access$600(NettyStream.java:66) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.connection.netty.NettyStream$2$1.operationComplete(NettyStream.java:153) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.connection.netty.NettyStream$2$1.operationComplete(NettyStream.java:150) [mongodb-driver-core-3.8.2.jar:na]
        at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:502) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:476) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:415) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.concurrent.DefaultPromise.setValue0(DefaultPromise.java:540) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.concurrent.DefaultPromise.setSuccess0(DefaultPromise.java:529) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.concurrent.DefaultPromise.trySuccess(DefaultPromise.java:101) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.DefaultChannelPromise.trySuccess(DefaultChannelPromise.java:84) [netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.AbstractChannel$CloseFuture.setClosed(AbstractChannel.java:1183) [netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.AbstractChannel$AbstractUnsafe.doClose0(AbstractChannel.java:769) [netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.AbstractChannel$AbstractUnsafe.close(AbstractChannel.java:745) [netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.AbstractChannel$AbstractUnsafe.close(AbstractChannel.java:616) [netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.nio.NioEventLoop.closeAll(NioEventLoop.java:730) [netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:509) [netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:906) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at java.lang.Thread.run(Thread.java:748) [na:1.8.0_191-1-ojdkbuild]

0000-00-00 00:00:49.672 ERROR 5708 --- [ntLoopGroup-2-3] org.mongodb.driver.connection            : Callback onResult call produced an error

java.lang.IllegalStateException: state should be: open
        at com.mongodb.assertions.Assertions.isTrue(Assertions.java:70) ~[mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.DefaultServer.invalidate(DefaultServer.java:125) ~[mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.DefaultServer.handleThrowable(DefaultServer.java:163) ~[mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.DefaultServer.access$600(DefaultServer.java:45) ~[mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.DefaultServer$DefaultServerProtocolExecutor$2.onResult(DefaultServer.java:223) ~[mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:49) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.CommandProtocolImpl$1.onResult(CommandProtocolImpl.java:83) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.DefaultConnectionPool$PooledConnection$1.onResult(DefaultConnectionPool.java:461) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.UsageTrackingInternalConnection$2.onResult(UsageTrackingInternalConnection.java:111) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:49) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.InternalStreamConnection$2$1.onResult(InternalStreamConnection.java:361) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.InternalStreamConnection$2$1.onResult(InternalStreamConnection.java:356) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback.onResult(InternalStreamConnection.java:603) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback.onResult(InternalStreamConnection.java:593) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.InternalStreamConnection$5.failed(InternalStreamConnection.java:500) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.connection.netty.NettyStream.readAsync(NettyStream.java:232) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.connection.netty.NettyStream.handleReadResponse(NettyStream.java:266) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.connection.netty.NettyStream.access$600(NettyStream.java:66) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.connection.netty.NettyStream$2$1.operationComplete(NettyStream.java:153) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.connection.netty.NettyStream$2$1.operationComplete(NettyStream.java:150) [mongodb-driver-core-3.8.2.jar:na]
        at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:502) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:476) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:415) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.concurrent.DefaultPromise.setValue0(DefaultPromise.java:540) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.concurrent.DefaultPromise.setSuccess0(DefaultPromise.java:529) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.concurrent.DefaultPromise.trySuccess(DefaultPromise.java:101) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.DefaultChannelPromise.trySuccess(DefaultChannelPromise.java:84) [netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.AbstractChannel$CloseFuture.setClosed(AbstractChannel.java:1183) [netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.AbstractChannel$AbstractUnsafe.doClose0(AbstractChannel.java:769) [netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.AbstractChannel$AbstractUnsafe.close(AbstractChannel.java:745) [netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.AbstractChannel$AbstractUnsafe.close(AbstractChannel.java:616) [netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.nio.NioEventLoop.closeAll(NioEventLoop.java:730) [netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:509) [netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:906) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at java.lang.Thread.run(Thread.java:748) [na:1.8.0_191-1-ojdkbuild]

0000-00-00 00:00:49.672 ERROR 5708 --- [ntLoopGroup-2-4] org.mongodb.driver.connection            : Callback onResult call produced an error

java.lang.IllegalStateException: state should be: open
        at com.mongodb.assertions.Assertions.isTrue(Assertions.java:70) ~[mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.DefaultServer.invalidate(DefaultServer.java:125) ~[mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.DefaultServer.handleThrowable(DefaultServer.java:163) ~[mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.DefaultServer.access$600(DefaultServer.java:45) ~[mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.DefaultServer$DefaultServerProtocolExecutor$2.onResult(DefaultServer.java:223) ~[mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:49) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.CommandProtocolImpl$1.onResult(CommandProtocolImpl.java:83) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.DefaultConnectionPool$PooledConnection$1.onResult(DefaultConnectionPool.java:461) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.UsageTrackingInternalConnection$2.onResult(UsageTrackingInternalConnection.java:111) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:49) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.InternalStreamConnection$2$1.onResult(InternalStreamConnection.java:361) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.InternalStreamConnection$2$1.onResult(InternalStreamConnection.java:356) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback.onResult(InternalStreamConnection.java:603) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback.onResult(InternalStreamConnection.java:593) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.internal.connection.InternalStreamConnection$5.failed(InternalStreamConnection.java:500) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.connection.netty.NettyStream.readAsync(NettyStream.java:232) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.connection.netty.NettyStream.handleReadResponse(NettyStream.java:266) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.connection.netty.NettyStream.access$600(NettyStream.java:66) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.connection.netty.NettyStream$2$1.operationComplete(NettyStream.java:153) [mongodb-driver-core-3.8.2.jar:na]
        at com.mongodb.connection.netty.NettyStream$2$1.operationComplete(NettyStream.java:150) [mongodb-driver-core-3.8.2.jar:na]
        at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:502) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:476) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:415) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.concurrent.DefaultPromise.setValue0(DefaultPromise.java:540) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.concurrent.DefaultPromise.setSuccess0(DefaultPromise.java:529) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.concurrent.DefaultPromise.trySuccess(DefaultPromise.java:101) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.DefaultChannelPromise.trySuccess(DefaultChannelPromise.java:84) [netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.AbstractChannel$CloseFuture.setClosed(AbstractChannel.java:1183) [netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.AbstractChannel$AbstractUnsafe.doClose0(AbstractChannel.java:769) [netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.AbstractChannel$AbstractUnsafe.close(AbstractChannel.java:745) [netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.AbstractChannel$AbstractUnsafe.close(AbstractChannel.java:616) [netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.nio.NioEventLoop.closeAll(NioEventLoop.java:730) [netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:509) [netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:906) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) [netty-common-4.1.36.Final.jar:4.1.36.Final]
        at java.lang.Thread.run(Thread.java:748) [na:1.8.0_191-1-ojdkbuild]

0000-00-00 00:00:51.886  INFO 5708 --- [           main] ConditionEvaluationReportLoggingListener :

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
0000-00-00 00:00:51.892 ERROR 5708 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'customerHandler' defined in file [D:\dev\projects\kotlin-micro-service\chapter5\target\classes\com\microservices\chapter5\CustomerHandler.class]: Unsatisfied dependency
 expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'customerServiceImpl': Unsatisfied dependency expressed through field 'customerRepository'; nested except
ion is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customerRepository' defined in file [D:\dev\projects\kotlin-micro-service\chapter5\target\classes\com\microservices\chapter5\CustomerRepository.class]: Initialization of
bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class com.microservices.chapter5.CustomerRepository: Common causes of this problem include using a final class or a non-visible class; nested
 exception is java.lang.IllegalArgumentException: Cannot subclass final class com.microservices.chapter5.CustomerRepository
        at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:769) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:218) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1341) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1187) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:843) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877) ~[spring-context-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549) ~[spring-context-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.refresh(ReactiveWebServerApplicationContext.java:67) ~[spring-boot-2.1.5.RELEASE.jar:2.1.5.RELEASE]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775) [spring-boot-2.1.5.RELEASE.jar:2.1.5.RELEASE]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) [spring-boot-2.1.5.RELEASE.jar:2.1.5.RELEASE]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:316) [spring-boot-2.1.5.RELEASE.jar:2.1.5.RELEASE]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260) [spring-boot-2.1.5.RELEASE.jar:2.1.5.RELEASE]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248) [spring-boot-2.1.5.RELEASE.jar:2.1.5.RELEASE]
        at com.microservices.chapter5.Chapter5ApplicationKt.main(Chapter5Application.kt:12) [classes/:na]
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_191-1-ojdkbuild]
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_191-1-ojdkbuild]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_191-1-ojdkbuild]
        at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_191-1-ojdkbuild]
        at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run(AbstractRunMojo.java:558) [spring-boot-maven-plugin-2.1.5.RELEASE.jar:2.1.5.RELEASE]
        at java.lang.Thread.run(Thread.java:748) [na:1.8.0_191-1-ojdkbuild]
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'customerServiceImpl': Unsatisfied dependency expressed through field 'customerRepository'; nested exception is org.springframework.beans.factory.BeanCreation
Exception: Error creating bean with name 'customerRepository' defined in file [D:\dev\projects\kotlin-micro-service\chapter5\target\classes\com\microservices\chapter5\CustomerRepository.class]: Initialization of bean failed; nested exception is org.springframework.
aop.framework.AopConfigException: Could not generate CGLIB subclass of class com.microservices.chapter5.CustomerRepository: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Can
not subclass final class com.microservices.chapter5.CustomerRepository
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:374) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1411) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1248) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1168) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:857) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:760) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ... 25 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customerRepository' defined in file [D:\dev\projects\kotlin-micro-service\chapter5\target\classes\com\microservices\chapter5\CustomerRepository.class]: Initialization
 of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class com.microservices.chapter5.CustomerRepository: Common causes of this problem include using a final class or a non-visible class; ne
sted exception is java.lang.IllegalArgumentException: Cannot subclass final class com.microservices.chapter5.CustomerRepository
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1248) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1168) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:593) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ... 39 common frames omitted
Caused by: org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class com.microservices.chapter5.CustomerRepository: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lan
g.IllegalArgumentException: Cannot subclass final class com.microservices.chapter5.CustomerRepository
        at org.springframework.aop.framework.CglibAopProxy.getProxy(CglibAopProxy.java:208) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:110) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor.postProcessAfterInitialization(AbstractAdvisingBeanPostProcessor.java:92) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:429) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1782) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ... 48 common frames omitted
Caused by: java.lang.IllegalArgumentException: Cannot subclass final class com.microservices.chapter5.CustomerRepository
        at org.springframework.cglib.proxy.Enhancer.generateClass(Enhancer.java:657) ~[spring-core-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.cglib.transform.TransformingClassGenerator.generateClass(TransformingClassGenerator.java:33) ~[spring-core-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25) ~[spring-core-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.aop.framework.CglibAopProxy$ClassLoaderAwareUndeclaredThrowableStrategy.generate(CglibAopProxy.java:1007) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:358) ~[spring-core-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.cglib.proxy.Enhancer.generate(Enhancer.java:582) ~[spring-core-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:110) ~[spring-core-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:108) ~[spring-core-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54) ~[spring-core-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_191-1-ojdkbuild]
        at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61) ~[spring-core-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.cglib.core.internal.LoadingCache.get(LoadingCache.java:34) ~[spring-core-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:134) ~[spring-core-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:319) ~[spring-core-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:569) ~[spring-core-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.cglib.proxy.Enhancer.createClass(Enhancer.java:416) ~[spring-core-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.aop.framework.ObjenesisCglibAopProxy.createProxyClassAndInstance(ObjenesisCglibAopProxy.java:57) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.aop.framework.CglibAopProxy.getProxy(CglibAopProxy.java:205) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ... 53 common frames omitted

[WARNING]
java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke (Method.java:498)
    at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run (AbstractRunMojo.java:558)
    at java.lang.Thread.run (Thread.java:748)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'customerHandler' defined in file [D:\dev\projects\kotlin-micro-service\chapter5\target\classes\com\microservices\chapter5\CustomerHandler.class]: Unsatisfied
 dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'customerServiceImpl': Unsatisfied dependency expressed through field 'customerRepository'; ne
sted exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customerRepository' defined in file [D:\dev\projects\kotlin-micro-service\chapter5\target\classes\com\microservices\chapter5\CustomerRepository.class]: Initial
ization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class com.microservices.chapter5.CustomerRepository: Common causes of this problem include using a final class or a non-visible cl
ass; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class com.microservices.chapter5.CustomerRepository
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray (ConstructorResolver.java:769)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor (ConstructorResolver.java:218)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor (AbstractAutowireCapableBeanFactory.java:1341)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance (AbstractAutowireCapableBeanFactory.java:1187)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean (AbstractAutowireCapableBeanFactory.java:555)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean (AbstractAutowireCapableBeanFactory.java:515)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0 (AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton (DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean (AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean (AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons (DefaultListableBeanFactory.java:843)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization (AbstractApplicationContext.java:877)
    at org.springframework.context.support.AbstractApplicationContext.refresh (AbstractApplicationContext.java:549)
    at org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.refresh (ReactiveWebServerApplicationContext.java:67)
    at org.springframework.boot.SpringApplication.refresh (SpringApplication.java:775)
    at org.springframework.boot.SpringApplication.refreshContext (SpringApplication.java:397)
    at org.springframework.boot.SpringApplication.run (SpringApplication.java:316)
    at org.springframework.boot.SpringApplication.run (SpringApplication.java:1260)
    at org.springframework.boot.SpringApplication.run (SpringApplication.java:1248)
    at com.microservices.chapter5.Chapter5ApplicationKt.main (Chapter5Application.kt:12)
    at sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke (Method.java:498)
    at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run (AbstractRunMojo.java:558)
    at java.lang.Thread.run (Thread.java:748)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'customerServiceImpl': Unsatisfied dependency expressed through field 'customerRepository'; nested exception is org.springframework.beans.factory.BeanCreation
Exception: Error creating bean with name 'customerRepository' defined in file [D:\dev\projects\kotlin-micro-service\chapter5\target\classes\com\microservices\chapter5\CustomerRepository.class]: Initialization of bean failed; nested exception is org.springframework.
aop.framework.AopConfigException: Could not generate CGLIB subclass of class com.microservices.chapter5.CustomerRepository: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Can
not subclass final class com.microservices.chapter5.CustomerRepository
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject (AutowiredAnnotationBeanPostProcessor.java:596)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject (InjectionMetadata.java:90)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties (AutowiredAnnotationBeanPostProcessor.java:374)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean (AbstractAutowireCapableBeanFactory.java:1411)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean (AbstractAutowireCapableBeanFactory.java:592)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean (AbstractAutowireCapableBeanFactory.java:515)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0 (AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton (DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean (AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean (AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate (DependencyDescriptor.java:277)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency (DefaultListableBeanFactory.java:1248)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency (DefaultListableBeanFactory.java:1168)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument (ConstructorResolver.java:857)
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray (ConstructorResolver.java:760)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor (ConstructorResolver.java:218)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor (AbstractAutowireCapableBeanFactory.java:1341)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance (AbstractAutowireCapableBeanFactory.java:1187)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean (AbstractAutowireCapableBeanFactory.java:555)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean (AbstractAutowireCapableBeanFactory.java:515)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0 (AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton (DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean (AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean (AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons (DefaultListableBeanFactory.java:843)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization (AbstractApplicationContext.java:877)
    at org.springframework.context.support.AbstractApplicationContext.refresh (AbstractApplicationContext.java:549)
    at org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.refresh (ReactiveWebServerApplicationContext.java:67)
    at org.springframework.boot.SpringApplication.refresh (SpringApplication.java:775)
    at org.springframework.boot.SpringApplication.refreshContext (SpringApplication.java:397)
    at org.springframework.boot.SpringApplication.run (SpringApplication.java:316)
    at org.springframework.boot.SpringApplication.run (SpringApplication.java:1260)
    at org.springframework.boot.SpringApplication.run (SpringApplication.java:1248)
    at com.microservices.chapter5.Chapter5ApplicationKt.main (Chapter5Application.kt:12)
    at sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke (Method.java:498)
    at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run (AbstractRunMojo.java:558)
    at java.lang.Thread.run (Thread.java:748)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customerRepository' defined in file [D:\dev\projects\kotlin-micro-service\chapter5\target\classes\com\microservices\chapter5\CustomerRepository.class]: Initialization
 of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class com.microservices.chapter5.CustomerRepository: Common causes of this problem include using a final class or a non-visible class; ne
sted exception is java.lang.IllegalArgumentException: Cannot subclass final class com.microservices.chapter5.CustomerRepository
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean (AbstractAutowireCapableBeanFactory.java:601)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean (AbstractAutowireCapableBeanFactory.java:515)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0 (AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton (DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean (AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean (AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate (DependencyDescriptor.java:277)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency (DefaultListableBeanFactory.java:1248)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency (DefaultListableBeanFactory.java:1168)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject (AutowiredAnnotationBeanPostProcessor.java:593)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject (InjectionMetadata.java:90)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties (AutowiredAnnotationBeanPostProcessor.java:374)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean (AbstractAutowireCapableBeanFactory.java:1411)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean (AbstractAutowireCapableBeanFactory.java:592)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean (AbstractAutowireCapableBeanFactory.java:515)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0 (AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton (DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean (AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean (AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate (DependencyDescriptor.java:277)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency (DefaultListableBeanFactory.java:1248)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency (DefaultListableBeanFactory.java:1168)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument (ConstructorResolver.java:857)
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray (ConstructorResolver.java:760)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor (ConstructorResolver.java:218)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor (AbstractAutowireCapableBeanFactory.java:1341)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance (AbstractAutowireCapableBeanFactory.java:1187)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean (AbstractAutowireCapableBeanFactory.java:555)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean (AbstractAutowireCapableBeanFactory.java:515)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0 (AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton (DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean (AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean (AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons (DefaultListableBeanFactory.java:843)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization (AbstractApplicationContext.java:877)
    at org.springframework.context.support.AbstractApplicationContext.refresh (AbstractApplicationContext.java:549)
    at org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.refresh (ReactiveWebServerApplicationContext.java:67)
    at org.springframework.boot.SpringApplication.refresh (SpringApplication.java:775)
    at org.springframework.boot.SpringApplication.refreshContext (SpringApplication.java:397)
    at org.springframework.boot.SpringApplication.run (SpringApplication.java:316)
    at org.springframework.boot.SpringApplication.run (SpringApplication.java:1260)
    at org.springframework.boot.SpringApplication.run (SpringApplication.java:1248)
    at com.microservices.chapter5.Chapter5ApplicationKt.main (Chapter5Application.kt:12)
    at sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke (Method.java:498)
    at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run (AbstractRunMojo.java:558)
    at java.lang.Thread.run (Thread.java:748)
Caused by: org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class com.microservices.chapter5.CustomerRepository: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lan
g.IllegalArgumentException: Cannot subclass final class com.microservices.chapter5.CustomerRepository
    at org.springframework.aop.framework.CglibAopProxy.getProxy (CglibAopProxy.java:208)
    at org.springframework.aop.framework.ProxyFactory.getProxy (ProxyFactory.java:110)
    at org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor.postProcessAfterInitialization (AbstractAdvisingBeanPostProcessor.java:92)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization (AbstractAutowireCapableBeanFactory.java:429)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean (AbstractAutowireCapableBeanFactory.java:1782)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean (AbstractAutowireCapableBeanFactory.java:593)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean (AbstractAutowireCapableBeanFactory.java:515)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0 (AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton (DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean (AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean (AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate (DependencyDescriptor.java:277)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency (DefaultListableBeanFactory.java:1248)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency (DefaultListableBeanFactory.java:1168)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject (AutowiredAnnotationBeanPostProcessor.java:593)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject (InjectionMetadata.java:90)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties (AutowiredAnnotationBeanPostProcessor.java:374)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean (AbstractAutowireCapableBeanFactory.java:1411)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean (AbstractAutowireCapableBeanFactory.java:592)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean (AbstractAutowireCapableBeanFactory.java:515)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0 (AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton (DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean (AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean (AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate (DependencyDescriptor.java:277)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency (DefaultListableBeanFactory.java:1248)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency (DefaultListableBeanFactory.java:1168)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument (ConstructorResolver.java:857)
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray (ConstructorResolver.java:760)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor (ConstructorResolver.java:218)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor (AbstractAutowireCapableBeanFactory.java:1341)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance (AbstractAutowireCapableBeanFactory.java:1187)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean (AbstractAutowireCapableBeanFactory.java:555)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean (AbstractAutowireCapableBeanFactory.java:515)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0 (AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton (DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean (AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean (AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons (DefaultListableBeanFactory.java:843)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization (AbstractApplicationContext.java:877)
    at org.springframework.context.support.AbstractApplicationContext.refresh (AbstractApplicationContext.java:549)
    at org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.refresh (ReactiveWebServerApplicationContext.java:67)
    at org.springframework.boot.SpringApplication.refresh (SpringApplication.java:775)
    at org.springframework.boot.SpringApplication.refreshContext (SpringApplication.java:397)
    at org.springframework.boot.SpringApplication.run (SpringApplication.java:316)
    at org.springframework.boot.SpringApplication.run (SpringApplication.java:1260)
    at org.springframework.boot.SpringApplication.run (SpringApplication.java:1248)
    at com.microservices.chapter5.Chapter5ApplicationKt.main (Chapter5Application.kt:12)
    at sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke (Method.java:498)
    at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run (AbstractRunMojo.java:558)
    at java.lang.Thread.run (Thread.java:748)
Caused by: java.lang.IllegalArgumentException: Cannot subclass final class com.microservices.chapter5.CustomerRepository
    at org.springframework.cglib.proxy.Enhancer.generateClass (Enhancer.java:657)
    at org.springframework.cglib.transform.TransformingClassGenerator.generateClass (TransformingClassGenerator.java:33)
    at org.springframework.cglib.core.DefaultGeneratorStrategy.generate (DefaultGeneratorStrategy.java:25)
    at org.springframework.aop.framework.CglibAopProxy$ClassLoaderAwareUndeclaredThrowableStrategy.generate (CglibAopProxy.java:1007)
    at org.springframework.cglib.core.AbstractClassGenerator.generate (AbstractClassGenerator.java:358)
    at org.springframework.cglib.proxy.Enhancer.generate (Enhancer.java:582)
    at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply (AbstractClassGenerator.java:110)
    at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply (AbstractClassGenerator.java:108)
    at org.springframework.cglib.core.internal.LoadingCache$2.call (LoadingCache.java:54)
    at java.util.concurrent.FutureTask.run (FutureTask.java:266)
    at org.springframework.cglib.core.internal.LoadingCache.createEntry (LoadingCache.java:61)
    at org.springframework.cglib.core.internal.LoadingCache.get (LoadingCache.java:34)
    at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get (AbstractClassGenerator.java:134)
    at org.springframework.cglib.core.AbstractClassGenerator.create (AbstractClassGenerator.java:319)
    at org.springframework.cglib.proxy.Enhancer.createHelper (Enhancer.java:569)
    at org.springframework.cglib.proxy.Enhancer.createClass (Enhancer.java:416)
    at org.springframework.aop.framework.ObjenesisCglibAopProxy.createProxyClassAndInstance (ObjenesisCglibAopProxy.java:57)
    at org.springframework.aop.framework.CglibAopProxy.getProxy (CglibAopProxy.java:205)
    at org.springframework.aop.framework.ProxyFactory.getProxy (ProxyFactory.java:110)
    at org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor.postProcessAfterInitialization (AbstractAdvisingBeanPostProcessor.java:92)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization (AbstractAutowireCapableBeanFactory.java:429)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean (AbstractAutowireCapableBeanFactory.java:1782)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean (AbstractAutowireCapableBeanFactory.java:593)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean (AbstractAutowireCapableBeanFactory.java:515)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0 (AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton (DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean (AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean (AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate (DependencyDescriptor.java:277)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency (DefaultListableBeanFactory.java:1248)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency (DefaultListableBeanFactory.java:1168)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject (AutowiredAnnotationBeanPostProcessor.java:593)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject (InjectionMetadata.java:90)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties (AutowiredAnnotationBeanPostProcessor.java:374)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean (AbstractAutowireCapableBeanFactory.java:1411)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean (AbstractAutowireCapableBeanFactory.java:592)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean (AbstractAutowireCapableBeanFactory.java:515)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0 (AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton (DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean (AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean (AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate (DependencyDescriptor.java:277)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency (DefaultListableBeanFactory.java:1248)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency (DefaultListableBeanFactory.java:1168)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument (ConstructorResolver.java:857)
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray (ConstructorResolver.java:760)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor (ConstructorResolver.java:218)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor (AbstractAutowireCapableBeanFactory.java:1341)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance (AbstractAutowireCapableBeanFactory.java:1187)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean (AbstractAutowireCapableBeanFactory.java:555)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean (AbstractAutowireCapableBeanFactory.java:515)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0 (AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton (DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean (AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean (AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons (DefaultListableBeanFactory.java:843)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization (AbstractApplicationContext.java:877)
    at org.springframework.context.support.AbstractApplicationContext.refresh (AbstractApplicationContext.java:549)
    at org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.refresh (ReactiveWebServerApplicationContext.java:67)
    at org.springframework.boot.SpringApplication.refresh (SpringApplication.java:775)
    at org.springframework.boot.SpringApplication.refreshContext (SpringApplication.java:397)
    at org.springframework.boot.SpringApplication.run (SpringApplication.java:316)
    at org.springframework.boot.SpringApplication.run (SpringApplication.java:1260)
    at org.springframework.boot.SpringApplication.run (SpringApplication.java:1248)
    at com.microservices.chapter5.Chapter5ApplicationKt.main (Chapter5Application.kt:12)
    at sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke (Method.java:498)
    at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run (AbstractRunMojo.java:558)
    at java.lang.Thread.run (Thread.java:748)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  17.050 s
[INFO] Finished at: 2019-05-26T11:40:53+09:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:2.1.5.RELEASE:run (default-cli) on project chapter5: An exception occurred while running. null: InvocationTargetException: Error creating bean with name 'customerHandler' defined in fi
le [D:\dev\projects\kotlin-micro-service\chapter5\target\classes\com\microservices\chapter5\CustomerHandler.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyExceptio
n: Error creating bean with name 'customerServiceImpl': Unsatisfied dependency expressed through field 'customerRepository'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customerRepository' defined in f
ile [D:\dev\projects\kotlin-micro-service\chapter5\target\classes\com\microservices\chapter5\CustomerRepository.class]: Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of cla
ss com.microservices.chapter5.CustomerRepository: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class com.microservices.chapter5.CustomerRepository ->
[Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

이런다고 CustomerRepositroy 클리스에 open 을 달아주면?

@Repository
open class CustomerRepository(private val template: ReactiveMongoTemplate) {

}

런타임에 아래와 같이 NullPointException이 발생하게 된다.

java.lang.NullPointerException: null
        at com.microservices.chapter5.CustomerRepository.findCustomer(CustomerRepository.kt:48) ~[classes/:na]
        at com.microservices.chapter5.CustomerServiceImpl.searchCustomers(CustomerServiceImpl.kt:21) ~[classes/:na]
        at com.microservices.chapter5.CustomerHandler.search(CustomerHandler.kt:32) ~[classes/:na]
        at com.microservices.chapter5.CustomerRouter$customerRoutes$1$2$1.invoke(CustomerRouter.kt:17) ~[classes/:na]
        at com.microservices.chapter5.CustomerRouter$customerRoutes$1$2$1.invoke(CustomerRouter.kt:8) ~[classes/:na]
        at org.springframework.web.reactive.function.server.RouterFunctionDsl$GET$1.handle(RouterFunctionDsl.kt:158) ~[spring-webflux-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.web.reactive.function.server.support.HandlerFunctionAdapter.handle(HandlerFunctionAdapter.java:61) ~[spring-webflux-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.web.reactive.DispatcherHandler.invokeHandler(DispatcherHandler.java:166) ~[spring-webflux-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at org.springframework.web.reactive.DispatcherHandler.lambda$handle$1(DispatcherHandler.java:151) ~[spring-webflux-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:118) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:67) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:76) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.innerNext(FluxConcatMap.java:275) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:849) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:114) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:192) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:67) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onNext(FluxPeekFuseable.java:204) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2066) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.request(FluxPeekFuseable.java:138) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:1874) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:1748) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onSubscribe(FluxPeekFuseable.java:172) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:54) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.MonoPeekFuseable.subscribe(MonoPeekFuseable.java:74) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.Mono.subscribe(Mono.java:3710) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:75) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.Operators.complete(Operators.java:131) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.MonoEmpty.subscribe(MonoEmpty.java:45) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.MonoSwitchIfEmpty.subscribe(MonoSwitchIfEmpty.java:44) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.MonoPeek.subscribe(MonoPeek.java:71) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.MonoMap.subscribe(MonoMap.java:55) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.Mono.subscribe(Mono.java:3710) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:442) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onSubscribe(FluxConcatMap.java:212) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:139) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:63) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.FluxConcatMap.subscribe(FluxConcatMap.java:121) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.MonoNext.subscribe(MonoNext.java:40) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.MonoSwitchIfEmpty.subscribe(MonoSwitchIfEmpty.java:44) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.MonoFlatMap.subscribe(MonoFlatMap.java:60) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.MonoFlatMap.subscribe(MonoFlatMap.java:60) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.MonoOnErrorResume.subscribe(MonoOnErrorResume.java:44) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.MonoOnErrorResume.subscribe(MonoOnErrorResume.java:44) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.MonoPeekTerminal.subscribe(MonoPeekTerminal.java:61) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.MonoOnErrorResume.subscribe(MonoOnErrorResume.java:44) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.Mono.subscribe(Mono.java:3710) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.drain(MonoIgnoreThen.java:172) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:56) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.MonoPeekFuseable.subscribe(MonoPeekFuseable.java:70) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.core.publisher.MonoPeekTerminal.subscribe(MonoPeekTerminal.java:61) ~[reactor-core-3.2.9.RELEASE.jar:3.2.9.RELEASE]
        at reactor.netty.http.server.HttpServerHandle.onStateChange(HttpServerHandle.java:64) ~[reactor-netty-0.8.8.RELEASE.jar:0.8.8.RELEASE]
        at reactor.netty.tcp.TcpServerBind$ChildObserver.onStateChange(TcpServerBind.java:226) ~[reactor-netty-0.8.8.RELEASE.jar:0.8.8.RELEASE]
        at reactor.netty.http.server.HttpServerOperations.onInboundNext(HttpServerOperations.java:442) ~[reactor-netty-0.8.8.RELEASE.jar:0.8.8.RELEASE]
        at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:91) ~[reactor-netty-0.8.8.RELEASE.jar:0.8.8.RELEASE]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374) ~[netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360) ~[netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352) ~[netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at reactor.netty.http.server.HttpTrafficHandler.channelRead(HttpTrafficHandler.java:161) ~[reactor-netty-0.8.8.RELEASE.jar:0.8.8.RELEASE]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374) ~[netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360) ~[netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352) ~[netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:438) ~[netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:323) ~[netty-codec-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:297) ~[netty-codec-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:253) ~[netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374) ~[netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360) ~[netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352) ~[netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1408) ~[netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374) ~[netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360) ~[netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:930) ~[netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163) ~[netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:682) ~[netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:617) ~[netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:534) ~[netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:496) ~[netty-transport-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:906) ~[netty-common-4.1.36.Final.jar:4.1.36.Final]
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.36.Final.jar:4.1.36.Final]
        at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_191-1-ojdkbuild]

Solution

kotlin-mavn-pluginconfiguration 부분부터 아래와 같이 추가

            <plugin>
                <groupId>org.jetbrains.kotlin</groupId>
                <artifactId>kotlin-maven-plugin</artifactId>
                <version>${kotlin.version}</version>
                <executions>
                    ...
                </executions>
                <configuration>
                    <jvmTarget>1.8</jvmTarget>
                    <!-- https://stackoverflow.com/questions/52599769/spring-kotlin-bean-fields-are-null-at-runtime-despite-being-injected-during-in -->
                    <!-- https://dzone.com/articles/open-your-classes-and-methods-in-kotlin -->
                    <!-- https://blog.frankel.ch/open-your-classes-and-methods-in-kotlin/ -->
                    <compilerPlugins>
                        <plugin>spring</plugin>
                    </compilerPlugins>
                </configuration>
                <dependencies>
                    <!-- https://www.baeldung.com/kotlin-allopen-spring -->
                    <dependency>
                        <groupId>org.jetbrains.kotlin</groupId>
                        <artifactId>kotlin-maven-allopen</artifactId>
                        <version>${kotlin.version}</version>
                    </dependency>
                </dependencies>
            </plugin>

mvn clean spring-boot:run 으로 실행했을 때 아래와 같은 로그가 나와야 한다..

[INFO] --- kotlin-maven-plugin:1.3.31:compile (compile) @ chapter5 ---
[INFO] Applied plugin: 'spring'
[INFO] Scanning for projects...
[WARNING]
[WARNING] Some problems were encountered while building the effective model for com.microservices:chapter5:jar:0.0.1-SNAPSHOT
[WARNING] 'dependencies.dependency.(groupId:artifactId:type:classifier)' must be unique: org.jetbrains.kotlin:kotlin-stdlib-jdk8:jar -> duplicate declaration of version ${kotlin.version} @ line 58, column 21
[WARNING]
[WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.
[WARNING]
[WARNING] For this reason, future Maven versions might no longer support building such malformed projects.
[WARNING]
[INFO]
[INFO] ---------------------< com.microservices:chapter5 >---------------------
[INFO] Building chapter5 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] >>> spring-boot-maven-plugin:2.1.5.RELEASE:run (default-cli) > test-compile @ chapter5 >>>
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ chapter5 ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] Copying 0 resource
[INFO]
[INFO] --- kotlin-maven-plugin:1.3.31:compile (compile) @ chapter5 ---
[INFO] Applied plugin: 'spring'
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (compile) @ chapter5 ---
[INFO] Changes detected - recompiling the module!
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:testResources (default-testResources) @ chapter5 ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory D:\dev\projects\kotlin-micro-service\chapter5\src\test\resources
[INFO]
[INFO] --- kotlin-maven-plugin:1.3.31:test-compile (test-compile) @ chapter5 ---
[WARNING] No sources found skipping Kotlin compile
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (testCompile) @ chapter5 ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to D:\dev\projects\kotlin-micro-service\chapter5\target\test-classes
[INFO]
[INFO] <<< spring-boot-maven-plugin:2.1.5.RELEASE:run (default-cli) < test-compile @ chapter5 <<<
[INFO]
[INFO]
[INFO] --- spring-boot-maven-plugin:2.1.5.RELEASE:run (default-cli) @ chapter5 ---

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.5.RELEASE)

0000-00-00 00:00:48.664  INFO 18644 --- [           main] c.m.chapter5.Chapter5ApplicationKt       : Starting Chapter5ApplicationKt on ANTOP-GRAM with PID 18644 (D:\dev\projects\kotlin-micro-service\chapter5\target\classes started by antop in D:\dev\projects\kotlin
-micro-service\chapter5)
0000-00-00 00:00:48.669  INFO 18644 --- [           main] c.m.chapter5.Chapter5ApplicationKt       : No active profile set, falling back to default profiles: default
0000-00-00 00:00:48.863  WARN 18644 --- [kground-preinit] o.s.h.c.j.Jackson2ObjectMapperBuilder    : For Jackson Kotlin classes support please add "com.fasterxml.jackson.module:jackson-module-kotlin" to the classpath
0000-00-00 00:00:49.187  INFO 18644 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data repositories in DEFAULT mode.
0000-00-00 00:00:49.217  INFO 18644 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 25ms. Found 0 repository interfaces.
0000-00-00 00:00:49.222  INFO 18644 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data repositories in DEFAULT mode.
0000-00-00 00:00:49.225  INFO 18644 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 3ms. Found 0 repository interfaces.
0000-00-00 00:00:50.869  INFO 18644 --- [           main] org.mongodb.driver.cluster               : Cluster created with settings {hosts=[localhost:27017], mode=SINGLE, requiredClusterType=UNKNOWN, serverSelectionTimeout='30000 ms', maxWaitQueueSize=500}
0000-00-00 00:00:51.073  INFO 18644 --- [           main] org.mongodb.driver.cluster               : Cluster created with settings {hosts=[localhost:27017], mode=SINGLE, requiredClusterType=UNKNOWN, serverSelectionTimeout='30000 ms', maxWaitQueueSize=500}
0000-00-00 00:00:51.118  INFO 18644 --- [localhost:27017] org.mongodb.driver.connection            : Opened connection [connectionId{localValue:2, serverValue:1}] to localhost:27017
0000-00-00 00:00:51.124  INFO 18644 --- [localhost:27017] org.mongodb.driver.cluster               : Monitor thread successfully connected to server with description ServerDescription{address=localhost:27017, type=STANDALONE, state=CONNECTED, ok=true, version=Serve
rVersion{versionList=[4, 0, 9]}, minWireVersion=0, maxWireVersion=7, maxDocumentSize=16777216, logicalSessionTimeoutMinutes=30, roundTripTimeNanos=3995240}
0000-00-00 00:00:51.538  INFO 18644 --- [localhost:27017] org.mongodb.driver.connection            : Opened connection [connectionId{localValue:1, serverValue:2}] to localhost:27017
0000-00-00 00:00:51.545  INFO 18644 --- [localhost:27017] org.mongodb.driver.cluster               : Monitor thread successfully connected to server with description ServerDescription{address=localhost:27017, type=STANDALONE, state=CONNECTED, ok=true, version=Serve
rVersion{versionList=[4, 0, 9]}, minWireVersion=0, maxWireVersion=7, maxDocumentSize=16777216, logicalSessionTimeoutMinutes=30, roundTripTimeNanos=5762049}
0000-00-00 00:00:51.657  INFO 18644 --- [ntLoopGroup-2-4] org.mongodb.driver.connection            : Opened connection [connectionId{localValue:5, serverValue:5}] to localhost:27017
0000-00-00 00:00:51.657  INFO 18644 --- [ntLoopGroup-2-3] org.mongodb.driver.connection            : Opened connection [connectionId{localValue:4, serverValue:4}] to localhost:27017
0000-00-00 00:00:51.657  INFO 18644 --- [ntLoopGroup-2-2] org.mongodb.driver.connection            : Opened connection [connectionId{localValue:3, serverValue:3}] to localhost:27017
0000-00-00 00:00:52.261  INFO 18644 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8080
0000-00-00 00:00:52.265  INFO 18644 --- [           main] c.m.chapter5.Chapter5ApplicationKt       : Started Chapter5ApplicationKt in 4.106 seconds (JVM running for 14.602)
tag: Kotlin, Open, Spring

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

https://github.com/antop-dev/example/tree/master/mybatis-rowbounds-example


Mybatis에 RowBounds 클래스를 이용해서 페이징 처리를 할 수 있다.

package org.antop.mybatis.mapper;

import org.antop.mybatis.model.Employee;
import org.apache.ibatis.session.RowBounds;

import java.util.List;

public interface EmployeeMapper {
	List<Employee> select(RowBounds rowBounds);
}

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.antop.mybatis.mapper.EmployeeMapper">
    <resultMap id="BaseResultMap" type="org.antop.mybatis.model.Employee">
        <id property="no" column="emp_no" />
        <result property="gender" column="gender" typeHandler="org.antop.mybatis.handler.GenderTypeHandler" />
        <association property="name" javaType="org.antop.mybatis.model.Name">
            <result property="first" column="first_name" />
            <result property="last" column="last_name" />
        </association>
    </resultMap>

    <select id="select" resultMap="BaseResultMap">
        select
          *
        from
          employees
        order by
          emp_no asc
    </select>
</mapper>

위와 같이 맵퍼와 XML 이 있다.

RowBounds rowBounds = new RowBounds(5, 10);
// 15건을 가져와서 앞에 5건 건너띔
List select = mapper.select(rowBounds);

결과는 10건이 나왔지만 어떻게 동작할까?

여기저기 찾아보고 소스도 대충(?) 보면 offset + limit 만큼 가져와서 offset 만큼 건너띤다.

페이징이 뒤로 갈 수록 느려지게 된다.

RowBounds rowBounds = new RowBounds(29990, 10);
// 30000건을 가져와서 앞에 29990건 건너띔
List select = mapper.select(rowBounds);

데이터의 양이 적다면 쿼리에서 페이징을 하지 않고 RowBouns를 이용하면 빠르게 개발할 수 있을 것이다.

하긴 요즘 수천만건의 데이터를 페이징에서 마지막 페이지를 볼 일이 많을까? -_-/


XML 쿼리문에 페이징을 넣지 않고 RowBounds 클래스를 사용하면 자동으로 페이징 쿼리가 실행되게 할 수 없을까?

방법이 있다! Mybatis Intercepter를 이용하면 된다. (인터셉터의 자세한 사용법은 다루지 않겠다 ㅠㅠ)

  1. Mybatis에서 쿼리를 날리기 전에 가로챈다.
  2. RowBounds가 있으면 쿼리에 페이징 문장를 적용한다.
  3. RowBounds를 제거하여 Mybatis에서 페이징 처리를 하지 않도록 한다.

아래 소스는 쿼리에 MySQL용으로 limit 문을 붙여준다.

package org.antop.mybatis.intercepter;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.util.Properties;

@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class MysqlRowBoundsInterceptor implements Interceptor {
	private static final Logger logger = LoggerFactory.getLogger(MysqlRowBoundsInterceptor.class);

	private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
	private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
	private static final ReflectorFactory DEFAULT_REFLECTOR_FACTORY = new DefaultReflectorFactory();

	public Object intercept(Invocation invocation) throws Throwable {
		StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
		MetaObject metaStatementHandler = MetaObject.forObject(statementHandler, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, DEFAULT_REFLECTOR_FACTORY);
		String originalSql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");
		RowBounds rb = (RowBounds) metaStatementHandler.getValue("delegate.rowBounds");

		logger.debug("originalSql = {}", originalSql);
		logger.debug("RowBounds = {}", rb);

		if (rb == null || rb == RowBounds.DEFAULT) {
			// RowBounds가 없으면 그냥 실행
			return invocation.proceed();
		}

		// RowBounds가 있다!
		// 원래 쿼리에 limit 문을 붙여준다.
		StringBuffer sb = new StringBuffer();
		sb.append(originalSql);
		sb.append(" limit ");
		sb.append(rb.getOffset());
		sb.append(", ");
		sb.append(rb.getLimit());

		logger.debug("sql = {}", sb.toString());

		// RowBounds 정보 제거
		metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET);
		metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT);
		// 변경된 쿼리로 바꿔치기
		metaStatementHandler.setValue("delegate.boundSql.sql", sb.toString());

		return invocation.proceed();
	}

	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	public void setProperties(Properties properties) {

	}

}

SqlSessionFactory 설정하는 부분에 인터셉터를 적용하면 된다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd">

    <mybatis:scan base-package="org.antop.mybatis"/>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="mapperLocations" value="classpath*:mybatis/*.xml"/>
        <property name="plugins">
            <list>
                <!-- 인터셉터 설정 -->
                <bean class="org.antop.mybatis.intercepter.MysqlRowBoundsInterceptor"/>
            </list>
        </property>
    </bean>

    <bean id="dataSource" ... />
</beans>

실행시켜 보면 쿼리문에 페이징이 적용되서 쿼리가 날라가는 것을 확인할 수 있다.

select * from employees order by emp_no asc limit 29990, 10


출처

http://www.programering.com/a/MTM5gTNwATQ.html

http://hjw1456.tistory.com/10

'Framework > Mybatis' 카테고리의 다른 글

Mybatis Interceptor + RowBounds 를 이용한 페이징 처리  (0) 2017.12.12

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

LOGBack Configurator with JMX

Posted at 2014.07.20 23:11 | Posted in Java+/Example

Introduce


요즘 logback 많이 쓸꺼다... "Reasons to prefer logback over log4j"에 보면 이제는 log4j 를 버리고 logback 을 써야하는지 이유를 영어로 설명 해놓았다. ㅅㅂ -_-;;


딴건 잘 모르것고 맘에 드는걸 보자면... 

Automatic reloading of configuration files (설정 파일 자동으로 다시 로드)

- Automatic removal of old log archives (이전 로그 아카이브 자동 삭제)

- Automatic compression of archived log files (아카이브 로그 파일 자동 압축)


서버를 재시작 하지 않고 logback.xml 파일을 수정하면 바로 설정이 적용 된다고 한다. ㅎㅎ


그리고 내가 관심 있게 본 하나는 JMX 가 지원된다. JConsole 로 테스트 해보니까 로그 레벨 변경이 바로 적용 되었다.


JMX 를 통해서 할 수 있는 짓거리는 아래와 같다. 몇개 안되고 메소드명 만으로도 이해가 될꺼다.


public interface JMXConfiguratorMBean {

  

  void reloadDefaultConfiguration() throws JoranException;

  

  void reloadByFileName(String fileName) throws JoranException, FileNotFoundException;

  

  void reloadByURL(URL url) throws JoranException;

  

  void setLoggerLevel(String loggerName, String levelStr);

  

  String getLoggerLevel(String loggerName);

  

  String getLoggerEffectiveLevel(String loggerName);


  List<String> getLoggerList();

  

  List<String> getStatuses();


}


오랜만에 웹 프로젝트나 하나 만들어 볼까.. 하고 이상한거 만들어 봤다 -_-




Example


Spring Framework 4.0.5

jQuery 1.11.1

jQuery UI 1.11.0

pqGrid 1.1.3


Logback ConfiguratorLogback Configurator 화면! 매우 간단한 화면이다 ㅎㅎ.


그리드에서 로그 레벨을 클릭하면 변경 할 수 있는 콤보 박스가 나와서 수정하면 바로 반영이 된다.


그리고 아래 Test 버튼을 클릭하면 서버 에 DEBUG/INFO/WARN/ERROR 4줄의 로그를 출력하게 된다.


2014-07-20 22:50:06,375 [http-bio-8080-exec-11] DEBUG c.a.c.LogController - this is debug level log.

2014-07-20 22:50:06,375 [http-bio-8080-exec-11] INFO  c.a.c.LogController - this is info level log.

2014-07-20 22:50:06,375 [http-bio-8080-exec-11] WARN  c.a.c.LogController - this is warn level log.

2014-07-20 22:50:06,375 [http-bio-8080-exec-11] ERROR c.a.c.LogController - this is error level log.


위의 "com.antop.controller.LogController" 의 레벨을 WARN으로 바꾸고 Test 하면? 당연 WARN/ERROR 두줄만 나오게 된다.


Spring MVC 의 URL 매핑은 아래와 같아. 이걸 그림으로 표현주는 툴이 있으면 좋겠네 ㅎㅎ


스프링 MVC URL 매핑 구성도


클래스 다이어그램은 아래와 같다. 어때요? 참 쉽죠~?


Class Diagram클래스 다이어그램. 이게 전부다. -_-/


끝이다.




Download


더이상의 자세한 설명은 생략한다 -_-;; 소스 보거라.


logback.jmx.war


↑ 소스 포함 WAR 파일


logback.jmx.zip


↑ 이클립스 프로젝트 파일 (메이븐)


'Java+ > Example' 카테고리의 다른 글

LOGBack Configurator with JMX  (0) 2014.07.20
현지어로 언어명 보여주기  (0) 2014.02.09
2014년 도로명 주소 사용에 따른 우편번호 준비  (2) 2013.12.22
JSTL Custom Tag using Spring Beans  (0) 2013.12.01
Spring Message Source from Database  (1) 2013.03.03
Infinite Routing DataSource  (1) 2013.01.13
Mybatis Type Handler  (1) 2012.09.30
Using AUTO_INCREMENT keys  (0) 2011.03.01

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

현지어로 언어명 보여주기

Posted at 2014.02.09 15:34 | Posted in Java+/Example

Introduction


다국어를 지원하는 홈페이지에서 언어 선택하는 부분을 보면 대표하는 언어로 다국어명이 나오는 것을 볼 수 있다.



이거 따라해 보자 -_-;;




Locale 에서 getDisplayCountry() 뽑아낼 때 인자로 현재 Locale 을 넣어주면 자신의 Locale 언어 그대로 나온다.


for (Locale locale : Locale.getAvailableLocales()) { if (locale.getCountry().isEmpty() == false) { System.out.println("--"); System.out.println(locale.getCountry()); // 이거 System.out.println(locale.getDisplayCountry(locale)); } }


그리고 다국어 처리를 하려면 View 부분이 UTF-8 이어야 한다. 당연한건가? -_-;




Sample


locale.war


↑ 소스 포함 WAR 파일


locale.zip


↑ 메이븐 프로젝트 파일


Spring MVC 의 인터셉터 부분에서 Locale 부분을 처리하여 ModelAndView 에 넣어주는 방식으로 해봤다.



실행해 보면 아래와 같이 나온다.





'Java+ > Example' 카테고리의 다른 글

LOGBack Configurator with JMX  (0) 2014.07.20
현지어로 언어명 보여주기  (0) 2014.02.09
2014년 도로명 주소 사용에 따른 우편번호 준비  (2) 2013.12.22
JSTL Custom Tag using Spring Beans  (0) 2013.12.01
Spring Message Source from Database  (1) 2013.03.03
Infinite Routing DataSource  (1) 2013.01.13
Mybatis Type Handler  (1) 2012.09.30
Using AUTO_INCREMENT keys  (0) 2011.03.01

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

Image Pyramid

Posted at 2014.01.30 03:28 | Posted in Mini Project

Introduction


아주 오래 전 플렉스와 실버라이트가 난리를 쳤을 때 난 플렉스를 공부 했었다.


그 때 훈스닷넷에서 하는 실버라이트 세미나를 갔었는데(왜 플랙스 세미나를 안가고...) 그 때 발표자(?)가 발표를 했던게 실버라이트의 이미지 피라미드였다.


그 때.. 난 "아 참 조쿠나~" 라고 생각 해놨다가 꽤 오랜 시간이 지난 지금 생각이 났다. ㅋㅋㅋ




Fundamental


고용량의 큰 이미지가 있다. 이 이미지를 브라우저에서 본다고 하자.


그러면 실제로 화면에 보여지는 이미지의 영역은 얼마 안될 것이다.



하지만 큰 이미지를 한번에 가져오기 때문에 네트워크 낭비가 심하다.


이 문제를 해결하기 위해서 이미지를 잘게 자른 후 화면(브라우저)에 보이는 영역만 불러오는 것이다!



이것 저것 찾아보니까 이미지 피라미드에 대한 문서들이 있었다. 


Web Map Tile Services for Spatial Data.pdf


Pyramid methods in image processing.pdf



이미지 저장 시에 참고한 그림는 아래와 같다. 아래 그림에 영감을 받아서 만들어 보았다.





Project

 

데모 : http://antop.nerv.kr/image.pyramid/



데이터베이스가 서버에는 MySQL이고, 샘플은 Derby 이다.


pyramid.txp



이 ERD가 자바에서는 아래 그림과 같이 클래스 구조를 가진다. ORM!





jQuery Plugin


이미지를 저장하는 부분보다 보여주는 부분이 더 개발 비율이 많다. 화면에 보여주는데 사용한 제이쿼리 플러그인은 아래와 같다.


jQuery Form Plugin - AJAX 로 폼 전송이나 파일 업로드를 할 수 있는 플러그인


jQuery Upload File - jQuery Form Plugin 을 이용하여 파일 업로드를 하는데 상태바를 지원해준다.


jQuery Mouse Wheel Plugin - 마우스 휠이 굴러가는 이벤트를 감지할 수 있게 해준다.


jQuery Schedule - 스케쥴링을 가능하게 하는 해준다. (Quartz 같은거...)


jQuery Dragscrollable (jQuery 홈페이지 개편되면서 플러그인 링크 죽음) - 마우스를 끌면 화면을 스크롤 해줌.


perfect-scrollbar : 가상 스크롤바를 달아준다.


Element Onscreen Visibility - 선택한 태그(selector)가 화면에 보이고 있는 태그인지를 판별한다.




이제 힘들어서 못쓰겠네 후.... 알아서 ㄱㄱ 질문은 받음 -_-/


image.pyramid.src.zip


↑ 이클립스 프로젝트 소스 파일 (메이븐)


image.pyramid.war.zip.001


image.pyramid.war.zip.002


↑ WAR 파일 (7-zip 분할)


'Mini Project' 카테고리의 다른 글

Image Pyramid  (0) 2014.01.30
Proftpd Manager  (0) 2012.12.10
게시판 2.0  (5) 2011.07.27
Sudoku  (0) 2011.06.09
간단한 JAVA 실행기(?)  (1) 2011.03.21
Forecast Repository  (8) 2010.11.30
Sales History API  (2) 2010.05.27
Order Entry API  (0) 2010.05.13
Human Resources API  (0) 2010.05.10
게시판 1.0  (1) 2010.03.11
비밀번호 생성기(Generate Password)  (1) 2010.02.10

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

"트리 메뉴 구성" 의뢰

Posted at 2014.01.23 03:00 | Posted in Request Sample

Request


아래와 같은 트리 메뉴를 구성할 데이터가 들어있는 테이블이 있습니다.



path 칼럼에 상위 트리의 경로가 "/" 구분자로 들어 있습니다.


예를 들어 id가 7인 노드는 [루트 → 3 → 4 → 7] 의 경로를 가지게 됩니다.


이렇게 데이터가 들어있는 테이블을 조회해서 ul 과 li 태그의 조합으로 화면에 나타내고 싶습니다.


트리 깊이(depth)는 제한이 없습니다.


데이터베이스는 MySQL, 프레임워크는 Spring + iBatis를 사용합니다.




Response


재귀함수/재귀쿼리를 써야 한다.


MySQL 에는 기본적으로 재귀쿼리가 안된단다. -_-;


나 역시도 특정 데이터베이스에서만 있는 문법을 싫어하기 때문에 있어도 안쓸 생각이었다!


단순 select 쿼리로 아래 클래스에 만족하는 계층 클래스 구조를 만들었다.



iBatis는 MyBatis 처럼 <collection>에서 재귀 효과를 낼 수 없기 때문에 다른 곳(Service 영역)에서 이 역할을 해주었다.


아래 그림에서 PathTreeService 클래스의 findChildren() 메소드가 계속 재호출되는 부분이다.



또 이 재귀 부분이 쓰이는 부분은 화면에 <ul>과 <li>를 이용해서 출력하는 부분(JSTL Custom Tag)에서 쓰인다.


PathTreeTag 클래스의 printChildren() 메소드가 계속 재호출 되는 부분이다.



Spring + iBatis + HSQL 을 이용해 만든 샘플이다.


pathtree.war


↑ java 소스 포함 WAR 파일


pathtree.zip


↑ maven 프로젝트


아래와 같은 결과가 나오게 된다.




'Request Sample' 카테고리의 다른 글

"트리 메뉴 구성" 의뢰  (0) 2014.01.23

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

Intorduction


일반적으로 스프링과 com.fasterxml.jackson를 사용하여 컨트롤러 메소드에서 @ResponseBody 어노테이션을 이용하면 알아서 객체가 JSON 으로 변환되어 나가게 된다.


	@RequestMapping(value = "/file/list", method = RequestMethod.GET)
	@ResponseBody
	public ModelMap list() {
		ModelMap mm = new ModelMap();
		
		List<File> list = fileService.list();
		mm.put("list", list);
		
		return mm;
	}

만약 변환해야 하는 객체가 아래와 같이 순환 참조[각주:1]되는 객체일 경우 JSON 으로 변환을 하다가 에러가 나게 된다.



아래 소스 코드와 같이 간단하게 돌려보면 아래와 같은 에러를 볼 수 있다.


import com.fasterxml.jackson.databind.ObjectMapper;

public class JacksonTest {

	public static void main(String[] args) throws Exception {
		A a = new A();

		for (int i = 0; i < 10; i++) {
			B b = new B();
			b.setA(a);
			a.addB(b);
		}

		ObjectMapper om = new ObjectMapper();
		String json = om.writeValueAsString(a);

		System.out.println(json);
	}

}


에러 에세지 보기


A 객체의 bs 속성(List)을 보고 B 객체의 A 객체를 보고 다시 A 객체의 bs 속성을 보고 무한으로 반복 된다.


그러다가 최종적으로 java.lang.StackOverflowError 가 떨어지게 된다.




Solution


순환 참조를 하지 말아야 하는 속성에 @JsonBackReference 어노테이션을 달아주자.


import com.fasterxml.jackson.annotation.JsonBackReference;

public class B {

	private String name = "B";
	// 이거
	@JsonBackReference
	private A a;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public A getA() {
		return a;
	}

	public void setA(A a) {
		this.a = a;
	}

}


다시 테스트 해보면 아래와 같이 B 의 a 속성을 출력하지 않게 된다.


{
  "a": "A",
  "bs": [
    {
      "name": "B"
      // as 속성이 빠지게 된다.
      // , "as": []
    },
    {
      "name": "B"
    },
    {
      "name": "B"
    },
    {
      "name": "B"
    },
    {
      "name": "B"
    },
    {
      "name": "B"
    },
    {
      "name": "B"
    },
    {
      "name": "B"
    },
    {
      "name": "B"
    },
    {
      "name": "B"
    }
  ]
}


  1. 순환 참조(Circular reference)란, 참조하는 대상이 서로 물려 있어서 참조할 수 없게 되는 현상을 말한다. [본문으로]

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

AJAX Login with Spring Security

Posted at 2013.12.12 02:52 | Posted in Framework/Spring Security

Introduction


스프링 시큐리티를 이용하여 로그인을 처리할 때에 AJAX 방식으로 로그인 하는 방법이다.


크게 2가지로 볼 수 있겠다. ㅎㅎ




Using Handler


기본적인 10단계의 필터 체인 중에 UsernamePasswordAuthencationFilter 단계의 "authentication-success-handler-ref"와 "authentication-success-handler-ref" 를 이용하는 방법이다.


	<security:http auto-config="true" disable-url-rewriting="true" use-expressions="true">
		<security:intercept-url pattern="/login.*" access="permitAll" />		
		<security:intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
		<security:form-login login-page="/login.html"
			username-parameter="id"
			password-parameter="password"
			login-processing-url="/login.ajax" default-target-url="/index.html"
			authentication-success-handler-ref="loginSuccessHandler"
			authentication-failure-handler-ref="loginFailureHandler" />
		<security:logout invalidate-session="true"
			logout-url="/logout"
			logout-success-url="/" />
	</security:http>
	
	<!-- 로그인 성공시 핸들러 -->
	<bean id="loginSuccessHandler" class="ajax.login.security.LoginSuccessHandler" />
	<!-- 로그인 실패시 핸들러 -->
	<bean id="loginFailureHandler" class="ajax.login.security.LoginFailureHandler" />


클래스 구조를 보면 아래와 같다.




아래와 같은 필터 체인의 순서롤 작동하게 된다.




구현 방법은 각각의 핸들러에서 응답을 JSON[각주:1]이나 원하는 포멧으로 만들어서 출력하면 된다.


public class LoginSuccessHandler implements AuthenticationSuccessHandler {
	@Override
	public void onAuthenticationSuccess(HttpServletRequest request,
		HttpServletResponse response, Authentication authentication) throws IOException,
		ServletException {

		ObjectMapper om = new ObjectMapper();

		Map<String, Object> map = new HashMap<String, Object>();
		map.put("success", true);
		map.put("returnUrl", getReturnUrl(request, response));

		// {"success" : true, "returnUrl" : "..."}
		String jsonString = om.writeValueAsString(map);

		OutputStream out = response.getOutputStream();
		out.write(jsonString.getBytes());
	}

	/**
	 * 로그인 하기 전의 요청했던 URL을 알아낸다.
	 * 
	 * @param request
	 * @param response
	 * @return
	 */
	private String getReturnUrl(HttpServletRequest request, HttpServletResponse response) {
		RequestCache requestCache = new HttpSessionRequestCache();
		SavedRequest savedRequest = requestCache.getRequest(request, response);
		if (savedRequest == null) {
			return request.getSession().getServletContext().getContextPath();
		}
		return savedRequest.getRedirectUrl();
	}

}

public class LoginFailureHandler implements AuthenticationFailureHandler {

	@Override
	public void onAuthenticationFailure(HttpServletRequest request,
		HttpServletResponse response, AuthenticationException exception)
		throws IOException, ServletException {

		ObjectMapper om = new ObjectMapper();

		Map<String, Object> map = new HashMap<String, Object>();
		map.put("success", false);
		map.put("message", exception.getMessage());

		// {"success" : false, "message" : "..."}
		String jsonString = om.writeValueAsString(map);

		OutputStream out = response.getOutputStream();
		out.write(jsonString.getBytes());

	}

}


ajax.login.1.war


ajax.login.1.zip





Using @MVC


다른 방법으로 필터체인을 사용하지 않고 컨트롤러에서 직접 인증을 처리하는 방법이 있다.


개인적으로 이 두번째 방법을 선호한다. 왜냐하면 요청을 "application/json" 으로 유동적으로 한다던가 입출력관련 AOP 등등을 내 맘대로 사용할 수 있다. 그냥 컨트롤러니까~


@Controller
public class LoginController {

	@RequestMapping(value = "/login.html", method = RequestMethod.GET)
	public void loginPage(ModelAndView mav) {

	}

	@Autowired
	AuthenticationManager authenticationManager;
	@Autowired
	SecurityContextRepository repository;

	@RequestMapping(value = "/login.ajax", method = RequestMethod.POST)
	@ResponseBody
	public ModelMap login(HttpServletRequest request, HttpServletResponse response,
		@RequestParam(value = "id") String username,
		@RequestParam(value = "password") String password) {

		ModelMap map = new ModelMap();

		UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
			username, password);

		try {
			// 로그인
			Authentication auth = authenticationManager.authenticate(token);
			SecurityContextHolder.getContext().setAuthentication(auth);
			repository.saveContext(SecurityContextHolder.getContext(), request, response);

			map.put("success", true);
			map.put("returnUrl", getReturnUrl(request, response));
		} catch (BadCredentialsException e) {
			map.put("success", false);
			map.put("message", e.getMessage());
		}

		return map;
	}

	/**
	 * 로그인 하기 전의 요청했던 URL을 알아낸다.
	 * 
	 * @param request
	 * @param response
	 * @return
	 */
	private String getReturnUrl(HttpServletRequest request, HttpServletResponse response) {
		RequestCache requestCache = new HttpSessionRequestCache();
		SavedRequest savedRequest = requestCache.getRequest(request, response);
		if (savedRequest == null) {
			return request.getSession().getServletContext().getContextPath();
		}
		return savedRequest.getRedirectUrl();
	}

}


스프링 시큐리티 설정 하는 부분에서 그냥 <security:http> 를 써도 되지만 사용하지 않는 UsernamePasswordAuthenticationFilter 같은 필터 체인을 사용하지 않기 위해 수동으로 구성해 보았다. 꽤 어렵... -_-;; (자세한 내용은 context-security.xml 참조!)


ajax.login.2.war


ajax.login.2.zip





그 밖에도 요청 헤더를 까서 요청이 AJAX 인지 아닌지를 판단해서 다른 처리를 하는 것도 있고, 구현할 수 있는 방법은 무궁무진하다.


  1. JSON(제이슨, JavaScript Object Notation)은, 인터넷에서 자료를 주고받을 때 그 자료를 표현하는 방법이다. 자료의 종류에 큰 제한은 없으며, 특히 컴퓨터 프로그램의 변수값을 표현하는 데 적합하다. 그 형식은 자바스크립트의 구문 형식을 따르지만, 프로그래밍 언어나 플랫폼에 독립적이므로 C, C++, C#, 자바, 자바스크립트, 펄, 파이썬 등 많은 언어에서 이용할 수 있다. [본문으로]

'Framework > Spring Security' 카테고리의 다른 글

AJAX Login with Spring Security  (7) 2013.12.12
Spring Security Session Destroy  (3) 2013.10.27
MySql Password Encoder  (0) 2013.09.21
  1. 준영
    시큐리티 관련 찾고 있던 정보인데 감사합니당 ㅋ
  2. 비밀댓글입니다
  3. 황진식
    위의 문의를 비밀 글로 남겨 드렸었는데... 비밀번호입력시 오타로 확인 할수가 없어 다시 남겨드립니다. ㅠㅠ

    위의 첫번째 예를 다운 받아

    <security:session-management session-fixation-protection="migrateSession" session-authentication-error-url="/login.html">
    <security:concurrency-control max-sessions="1" error-if-maximum-exceeded="true" expired-url="/login.html"/>
    </security:session-management>

    추가 하여 session 테스트를 진행 하였습니다만 두번째 로그인도 정상적으로 로그인이 됩니다.
    이부분에 대해 도움을 요청드릴수 있을까요?

    믈론
    <listener>
    <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
    </listener>

    등록 한 상태 입니다.
    • 2014.05.04 18:41 신고 [Edit/Del]
      http://stackoverflow.com/questions/19319849/spring-security-3-1-session-concurrency-control-not-working-why

      동시 세션 제어를 하려면 인증의 주체가 되는 클래스를 비교 가능하게 hascode 와 equals 메소드를 구현해 주면 되네요...

      위의 ajax.login 프로젝트에서는 ajax.login.security.LoginToken 클래스죠 ^^;;

      @Override
      public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + ((id == null) ? 0 : id.hashCode());
      return result;
      }

      @Override
      public boolean equals(Object obj) {
      if (this == obj)
      return true;
      if (obj == null)
      return false;
      if (getClass() != obj.getClass())
      return false;
      LoginToken other = (LoginToken) obj;
      if (user == null) {
      if (other.user != null)
      return false;
      } else if (!user.equals(other.user))
      return false;
      return true;
      }

      물론 User 클래스도 hashcode 와 equals 가 구현 되어 있습니다.
  4. ㅎㅎ
    안녕하세요 좋은내용 잘봤습니다 ^^
    하나 궁금한게 있어서 여쭤볼려고하는데요
    먼저 필터체인을 사용하지 않고 컨트롤러에서 직접 인증을 처리하는 방법을 따라해봤는데요
    저 같은경우엔 password를 StandardPasswordEncoder 로 암호화해서 db에 저장했습니다
    아시다시피 StandardPasswordEncoder 같은경우에 복호화가 안되서요
    요런경우엔 어떤식으로 처리하면 가능할지;
    어드바이스좀 부탁드리겠습니다 감사합니다
    • 2014.06.15 01:06 신고 [Edit/Del]
      제가 만들 샘플의 MySqlPasswordEncoder 도 복호화가 안됩니다.

      로그인시 디비에 들어있는 비밀번호를 복호화해서 비교하는게 아니라, 입력받은 비밀번호를 암호화 해서 디비의 비밀번호와 비교합니다.

      {입력받은 비번} == 복호화({디비에 있는 비번}) (x)
      암호화({입력받은 비번}) == {디비에 있는 비번} (o)

      이게 원하시는 답변인지는 모르겠네요 ㅠㅠ
  5. ㅎㅎ
    답변감사합니다 ^^

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

JSTL Custom Tag using Spring Beans

Posted at 2013.12.01 17:37 | Posted in Java+/Example

Introduction


JSTL[각주:1] 태그를 만들어 사용할 때에 스프링 빈을 가져다 쓰는 방법이다.




Using Spring Beans


너무나도 간단하다. 


일반적으로 TagSupport 클래스를 상속 받아서 구현 하면 되는데, 스프링 빈을 사용하고 싶으면 RequestContextAwareTag 클래스를 상속 받아서 구현하면 된다.



사용법은 아래와 같다.


import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.tags.RequestContextAwareTag;

public class XXXTag extends RequestContextAwareTag {

	@Override
	protected int doStartTagInternal() throws Exception {
		// WebApplicationContext 를 얻는다.
		WebApplicationContext ctx = getRequestContext().getWebApplicationContext();
		// 빈을 가져와서 처리
		return SKIP_BODY;
	}

}




Example Source


WAR 파일 (소스 포함)


webapp.war


프로젝트 파일 (메이븐 구조)


webapp.zip




  1. 자바 서버 페이지 표준 태그 라이브러리(JavaServer Pages Standard Tag Library, 약칭 JSTL)은 Java EE 기반의 웹 애플리케이션 개발 플랫폼을 위한 컴포넌트 모음이다. [본문으로]

'Java+ > Example' 카테고리의 다른 글

LOGBack Configurator with JMX  (0) 2014.07.20
현지어로 언어명 보여주기  (0) 2014.02.09
2014년 도로명 주소 사용에 따른 우편번호 준비  (2) 2013.12.22
JSTL Custom Tag using Spring Beans  (0) 2013.12.01
Spring Message Source from Database  (1) 2013.03.03
Infinite Routing DataSource  (1) 2013.01.13
Mybatis Type Handler  (1) 2012.09.30
Using AUTO_INCREMENT keys  (0) 2011.03.01
tag: JSP, JSTL, Spring

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

Spring Security Session Destroy

Posted at 2013.10.27 12:39 | Posted in Framework/Spring Security

Intoduction


Spring Security 사용 중에 "사용자의 로그인 시간과 로그아웃 시간을 기록해야 한다" 라는 임무가 떨어졌다 -_-


문제는 이 로그아웃 이라는게 약간 골치 아프다.. 사용자가 직접 로그아웃 버튼을 클릭해서 로그아웃을 한다면 나이스 하지만...


대부분은 용무가 끝나면 그냥 브라우저를 끄거다 바로 다른 사이트로 넘어가게 된다. 이 경우에는 어떻게 하지?


개발자의 눈으로 보면 로그아웃 == 세션 만료로 볼 수 있다.


다른 사이트로 가거나 브라우저를 꺼버리게 되면 WAS의 기준으로 일정 시간이 지나세션이 만료되게 된다.


이 세션 만료를 캐취해서 처리를 하는 방법을 알아보자.




Using HttpSessionListener


서블릿의 세션 리스너를 이용한 방법이다.


아래와 같이 HttpSessionListener 인터페이스를 구현한 클래스를 만들고 web.xml 에 등록하면 된다.


스프링 시큐리티를 사용하지 않는다면 아래의 방법으로 처리하면 된다.


java


package session.destory.servlet;

import java.util.Date;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

import org.springframework.context.ApplicationContext;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.web.context.support.WebApplicationContextUtils;

import session.destory.entity.LoginHistory;
import session.destory.security.LoginToken;
import session.destory.service.LoginHistoryService;

public class SessionManagerListener implements HttpSessionListener {

	@Override
	public void sessionCreated(HttpSessionEvent se) {
		// nothing
	}

	@Override
	public void sessionDestroyed(HttpSessionEvent se) {
		// session
		HttpSession session = se.getSession();
		// spring web application context
		ApplicationContext context = WebApplicationContextUtils
			.getWebApplicationContext(se.getSession().getServletContext());
		// security context
		SecurityContext sc = (SecurityContext) session
			.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
		if (sc != null) {
			// authentication
			Authentication auth = sc.getAuthentication();
			// login history token
			LoginToken loginToken = (LoginToken) auth.getPrincipal();
			LoginHistory lh = loginToken.getHistory();
			lh.setLogoutDate(new Date());
			// update
			LoginHistoryService loginHistoryService = context
				.getBean(LoginHistoryService.class);
			loginHistoryService.modify(lh);
		}

	}

}


web.xml


<!-- servlet listener --> <listener> <listener-class>session.destory.servlet.SessionManagerListener</listener-class> </listener>


리스너에서 수동적으로 스프링 컨택스트와 시큐리티 컨텍스를 꺼내는게 맘에 안드는군... -_-/




Using HttpSessionEventPublisher


이번에는 스프링 시큐리티에서 지원하는 방법으로 해보겠다~!


ApplicationListener<SessionDestroyedEvent> 인터페이스를 구현한 클래스를 만들고 web.xml 에는 HttpSessionEventPublisher 클래스를 등록한다.


java


public class SessionDestoryListener implements ApplicationListener<SessionDestroyedEvent> {

	private LoginHistoryService loginHistoryService;

	@Autowired
	public void setLoginHistoryService(LoginHistoryService loginHistoryService) {
		this.loginHistoryService = loginHistoryService;
	}

	@Override
	public void onApplicationEvent(SessionDestroyedEvent event) {

		List<SecurityContext> contexts = event.getSecurityContexts();
		if (contexts.isEmpty() == false) {
			for (SecurityContext ctx : contexts) {
				Authentication atuh = ctx.getAuthentication().getPrincipal();
				// ...
			}
		}

	}

}

 

spring.xml

 

<beans ...>
	<bean id="sessionDestoryListener" class="session.destory.security.SessionDestoryListener" />
</beans>

 

web.xml


<!-- spring security event -->
<listener>
	<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>


뭔가 스프링스러워진 것 같다 -_-v


- 로그인 처리



- 로그아웃 처리






아래는 샘플 소스다. Tomcat 7, WebLogic 12c 에서 테스트 해봄.


war - 실행해 볼 수 있는 war 파일. 7-zip 으로 분할 압축함.


session.destory.zip.001


session.destory.zip.002



src.zip - Maven 구조로 만들어진 소스 압축함.


session.destory.src.zip


 

... 소스 만들고 나니까 오타가 있네.... Destory → Destroy


'Framework > Spring Security' 카테고리의 다른 글

AJAX Login with Spring Security  (7) 2013.12.12
Spring Security Session Destroy  (3) 2013.10.27
MySql Password Encoder  (0) 2013.09.21
  1. 궁금합니다!!
    안녕하세요
    세션 만료시 로그아웃 시간 입력을 위해 소스를 참고 했는데요,
    잘 안되는 부분이 있어서 질문좀 드리려고 합니다. ^^;
    spring security / spring 프레임웍을 사용중입니다.
    처음에 ApplicationListener<SessionDestroyedEvent>를 사용해서 구현했는데 SecurityContext 리스트가 계속 null이더라구요.
    결국 세션에서 얻어오는 event.getSession().getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
    방법으로 security context를 얻어왔습니다.
    궁금한게 getSecurityContexts() 로 리스트를 얻어올때는 null 이었는데 왜 세션에서 얻어오는 방법으로는 얻을수 있는지 모르겠습니다. 혹시 참고할만한 부분이나 조언을 해주시면 감사하겠습니다!
    • 2014.04.03 14:17 신고 [Edit/Del]
      1. 혹시 web.xml 에 리스너 등록 하셨죠?
      <listener>
      <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
      </listener>

      2. 잠깐 찾아보니 스프링 시큐리티 버전에 따른 문제가 있는 것 같기도 합니다. (아닐지도 모름 -_-ㄷㄷ)

      3. 저도 퇴근해서 이것저것 해보겠습니다.
  2. 궁금합니다!!
    넵 감사합니다!
    리스너는 등록한 상태구요, 사용중인 spring security 버전은 3.1.0입니다.
    getSecurityContexts()가 왜 계속 null인지.. 계속 다시 해봤는데 모르겠네요
    getSession().getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY) 과 내부적으로 같은 거라면 그냥 쓰려고 합니다.
    블로그 올려두신 내용들 많이 참고했습니다!! 감사합니다!

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기