Spring + @Lazy

Posted at 2019. 8. 5. 00:46 | Posted in Framework/Spring
반응형

https://github.com/antop-dev/spring-lazy

요즘 MSA를 공부하면서 아래와 같은 문구를 보았다.

마이크로서비스에서는 완전 자동화를 달성하기 위해 초소한의 시동/종료 시간을 갖도록 애플리케이션의 크기를 가능한 한 작게 유지하는 것이 극단적으로 아주 중요하다.
이를 위해 마이크로서비스에서는 객체와 데이터의 지연 로딩lazy loading에 대해서도 고려해봐야 한다.

이 때 떠오른 단어는 Spring@Lazy이다.

스프링 빈 설정시 @Lazy 애노테이션만 달아주면 이 빈을 가져오는 시점에 생성하기 때문에 모든 빈을 처음 초기화시에 만들지 않는다.

그런데 약간의 의문사항이 있어서 테스트 해봤다... 구글링을 대충 해도 나오는 자료이지만 직접 해봤다.

등장 클래스

아래와 같이 3개의 클래스를 빈으로 등록했다. 패턴은 다 같고 First, Second, Third 만 바뀐다.

@Component
public class First {
    @Autowired
    private Second second;

    public First() {
        System.out.println("first class constructor");
    }

    public void go() {
        System.out.println("Hello first!");
        second.go();
    }
}

테스트 코드는 아래와 같다.

public class LazyApplicationTests {
    @Test
    public void lazy() {
        ApplicationContext context = new AnnotationConfigApplicationContext(LazyApplication.class);
        System.out.println("call first.go()");
        First first = context.getBean(First.class);
        first.go();
    }
}

일반적인 빈 설정

먼저 위와 같이 @Lazy를 사용하지 않는 일반 적인 설정이다. 후에 스프링 컨텍스트에서 First 인스턴스를 꺼내서 go() 메서드를 수행할 것이다.

예상한 대로 빈이 전부 생성 된 후(①) FIrst.go() 이후의 작업이 수행된다(②).

first class constructor
second class constructor
third class constructor
Spring context loaded call
first.go()
Hello first!
Hello second
Hello third!

하지만 좀 이상한 점이 있는데 예상은 ThridSecondFirst 순으로 생성될 줄 알았지만 반대로 생성된다. 생성자를 사용하지 않고 @Autowired나 세터setter를 사용하면 스프링이 이 클래스를 까서(?) 적용을 하게 된다.

@Autowired를 사용하지 않고 생성자constructor를 사용하면 예상한 순서대로 빈이 만들어진다.

@Component
public class First {
    private final Second second;

    public First(Second second) {
        this.second = second;
        System.out.println("first class constructor");
    }

    public void go() {
        System.out.println("Hello first!");
        second.go();
    }
}
third class constructor second class constructor first class constructor Spring context loaded call first.go() Hello first! Hello second Hello third!

@Lazy 적용

첨부파일
다운로드

Thrid, Second, Fourth 는 스프링 초기화시 생성되고 First는 지연 로딩이 적용된다.

third class constructor
second class constructor
fourth class constructor
Spring context loaded
call first.go()
first class constructor
Hello first!
Hello second
Hello third!

동시성 concurrency

First 빈이 생성 되는데 3초가 걸린다고 가정해보자.

@Component
@Lazy
public class First {
    private final Second second;

    public First(Second second) {
        this.second = second;
        // 인스턴스를 생성하는데 3초가 걸린다.
        System.out.println("It takes 3 seconds to create an instance");
        try {
            for (int i = 0; i < 3; i++) {
                System.out.println((3 - i) + "..");
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            System.out.println(e.getMessage());
        }
        System.out.println("first class constructor");
    }

    public void go() {
        System.out.println("Hello first!");
        second.go();
    }
}

First 인스턴스를 사용하는 다른 인스턴스에서 동시에(거의 동시에?) 요청이 들어온다면?

테스트 코드는 아래와 같다.

@Test
    public void concurrency() throws Exception {
        ApplicationContext context = new AnnotationConfigApplicationContext(LazyApplication.class);
        System.out.println("Spring context loaded");

        for (int i = 0; i < 3; i++) {
            Thread thread = new Thread(() -> {
                System.out.println("call first.go()");
                First first = context.getBean(First.class);
                first.go();
            });

            thread.start();
            System.out.println(thread.getName() + " started.");
            Thread.sleep(100);
        }

        // 결과를 보기 위해 5초 대기
        Thread.sleep(5000);
    }

걱정과는 다르게 매우 잘 작동한다! 스프링 구욷!

third class constructor
second class constructor
fourth class constructor
Spring context loaded
Thread-2 started.
Thread-2 call first.go()
It takes 3 seconds to create an instance
3..
Thread-3 started.
Thread-3 call first.go()
Thread-4 started.
Thread-4 call first.go()
2..
1..
first class constructor
Thread-2 Hello first!
Thread-4 Hello first!
Thread-2 Hello second
Thread-4 Hello second
Thread-4 Hello third!
Thread-3 Hello first!
Thread-3 Hello second
Thread-3 Hello third!
Thread-2 Hello third!
반응형

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

Mockito.thenThrow() 주의사항!  (0) 2021.09.25
Twelve Best Practices For Spring XML Configurations  (0) 2010.06.23
//

[Kotlin + Spring] Maven configuration

Posted at 2019. 5. 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)
반응형
//
반응형

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

반응형
//

LOGBack Configurator with JMX

Posted at 2014. 7. 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' 카테고리의 다른 글

현지어로 언어명 보여주기  (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
//

현지어로 언어명 보여주기

Posted at 2014. 2. 9. 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
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
//

Image Pyramid

Posted at 2014. 1. 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


01


이 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' 카테고리의 다른 글

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
//

"트리 메뉴 구성" 의뢰

Posted at 2014. 1. 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 프로젝트


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




반응형
//
반응형

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)란, 참조하는 대상이 서로 물려 있어서 참조할 수 없게 되는 현상을 말한다. [본문으로]
반응형
//