[SpringBatch] @ConfigurationProperties 이용한 데이터소스 주입 관련

JAVA/SpringBatch|2020. 4. 28. 21:20

설정이 다 jar 내에 있으면 좋겠지만

외부에서 설정 받는 경우를 고려해야하고 엔진에서 어떤 데이터소스를 사용하게 될지 명시를 해줘야하기 때문에

기존 설정인 "spring.datasource.hikari.xxx"설정은 감추고 "engine.ds.xxx" 형식으로 지원을 처리하고 있었습니다.

 

그리고 설정이 되어있지 않다면 H2를 메모리 방식으로 동작하는것을 의도해서

코드를 아래와같이 했었습니다.

 

	
    @Bean("xxxDS")
    @Primary
    @ConfigurationProperties(prefix = "engine.ds")
    public DataSource xxxDS() {
        HikariDataSource ds = DataSourceBuilder.create().type(HikariDataSource.class).build();
        if(!ds.isRunning()){
            ds = memDataSource();
        }

        return ds;
    }

그런데 아래와 같이 에러가 떨어집니다..

 

일부..

Caused by: java.lang.IllegalStateException: Unable to set value for property jdbc-url
at org.springframework.boot.context.properties.bind.JavaBeanBinder$BeanProperty.setValue(JavaBeanBinder.java:349)
at org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:96)
at org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:79)
at org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:56)
at org.springframework.boot.context.properties.bind.Binder.lambda$bindDataObject$5(Binder.java:452)
at org.springframework.boot.context.properties.bind.Binder$Context.withIncreasedDepth(Binder.java:572)
at org.springframework.boot.context.properties.bind.Binder$Context.withDataObject(Binder.java:558)
at org.springframework.boot.context.properties.bind.Binder$Context.access$400(Binder.java:513)
at org.springframework.boot.context.properties.bind.Binder.bindDataObject(Binder.java:450)
at org.springframework.boot.context.properties.bind.Binder.bindObject(Binder.java:391)
at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:320)
... 123 more
Caused by: java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.boot.context.properties.bind.JavaBeanBinder$BeanProperty.setValue(JavaBeanBinder.java:346)
... 133 more
Caused by: java.lang.IllegalStateException: The configuration of the pool is sealed once started. Use HikariConfigMXBean for runtime changes.
at com.zaxxer.hikari.HikariConfig.checkIfSealed(HikariConfig.java:1015)
at com.zaxxer.hikari.HikariConfig.setJdbcUrl(HikariConfig.java:504)
... 138 more

HikariDataSource를 보니 HikariConfig를 생성자로 받아 생성된경우거나 getConnection()이 한번 불려서 seal처리가 되어있어서 setter를 부를수 없어 발생하는 에러였습니다

 

아마도 이 경우에는 HikariConfig 를 arg로 받는 생성자가 불려 seal 처리 된거겠지요

그런데.. 저는setter를 불러준적이 없습니다..

물론 그렇다면 DataSourceBuilder쪽에서 build 할때 발생하는 거겠지요

 

하지만.. 실제로는 DataSourceBuilder쪽에서 build할때 리플렉션으로 setter를 부르는게 아니라

@ConfigurationProperties에 의해 이미 Bean이 된 engineDS에 setter를 부르고 있었습니다..

 

정리하면!

HikariDataSource ds = DataSourceBuilder.create().type(HikariDataSource.class).build();

에 생성된 ds는 빈껍데기고

체크하는 로직에 당연히 들어오니 memDataSource가 만들어진 후 memDataSource가 engineDS로 등록되었고

그후 @ConfigurationProperties에 의해 setter가 불리는 상황..

 

그래서 어떻게할까 하다. memDataSource를 만들때 HikariConfig를 생성자로 넘기지 않고

HikariDataSource 에 setter들을 불러 생성하면 사용하기 전까지는 seal처리가 되지 않기에 이 방법으로 우회했습니다.

어차피 기본값도 정의를 할 부분이였기 때문에..

(fastPool을 안쓰는거지요, getConnection을 보니 한번은 락을 잡지만 pool 생성후에는 잡지 않으니 문제는 없을것 같습니다.)

(추후 다른 방법을 찾으면 변경하는걸로..)

 

역시 어노테이션을 통한 리플렉션이 편리한건 맞지만..

동작을 모르면 예상치 못한 상황이 나온다는 교훈을... 또 얻어 갑니다.

댓글()