Skip to content
정명주(myeongju.jung) edited this page Jun 28, 2019 · 1 revision

AsyncConfig

@Configuration
@EnableAsync(proxyTargetClass = true)   // 비동기 활성화
@EnableConfigurationProperties(AsyncProperties.class)
public class AsyncConfig implements AsyncConfigurer {
    public static final String STORAGE_EXECUTOR = "storage_executor";
    private final AsyncProperties asyncProperties;

    public AsyncConfig(AsyncProperties asyncProperties) {
        this.asyncProperties = asyncProperties;
    }

    @Override
    public Executor getAsyncExecutor() {
        return asyncProperties.getBasic().newThreadPoolExecutor(null);
    }

    @Bean(STORAGE_EXECUTOR)
    public ThreadPoolTaskExecutor storageExecutor() {
        return asyncProperties.getStorage().newThreadPoolExecutor(null);
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new DefaultAsyncUncaughtExceptionHandler();
    }
}

@Slf4j
class DefaultAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
    @Override
    public void handleUncaughtException(@NonNull Throwable cause, @NonNull Method method, @NonNull Object... params) {
        log.error("{}[ASYNC] {}\n{}\n{}", PREFIX_FOR_ALERT, cause.toString(), method, params, cause);
    }
}
@ConfigurationProperties(prefix = "async")
@Data
@Validated
public class AsyncProperties {
    /**
     * 비동기를 위한 기본 threadPoolExecutor 구성 for {@link org.springframework.scheduling.annotation.Async}
     */
    @NotNull
    private AsyncThreadPoolProperties basic;
    /**
     * 스토리지 인프라를 위한 threadPoolExecutor 구성 for {@link org.springframework.scheduling.annotation.Async}
     */
    @NotNull
    private AsyncThreadPoolProperties storage;

    /**
     * @see java.util.concurrent.ThreadPoolExecutor#setRejectedExecutionHandler(RejectedExecutionHandler)
     */
    public enum RejectedExecutionType {
        CALLER_RUNS() {
            @Override
            protected RejectedExecutionHandler rejectedExecutionHandler() {
                return new ThreadPoolExecutor.CallerRunsPolicy();
            }
        },
        ABORT() {
            @Override
            protected RejectedExecutionHandler rejectedExecutionHandler() {
                return new ThreadPoolExecutor.AbortPolicy();
            }
        },
        DISCARD() {
            @Override
            protected RejectedExecutionHandler rejectedExecutionHandler() {
                return new ThreadPoolExecutor.DiscardPolicy();
            }
        },
        DISCARD_OLDEST() {
            @Override
            protected RejectedExecutionHandler rejectedExecutionHandler() {
                return new ThreadPoolExecutor.DiscardOldestPolicy();
            }
        };

        protected abstract RejectedExecutionHandler rejectedExecutionHandler();
    }

    @Data
    public static class AsyncThreadPoolProperties {
        /**
         * 기본 threadPool 사이즈
         */
        private int corePoolSize = 32;
        /**
         * 최대 threadPool 사이즈
         */
        private int maxPoolSize = 64;
        /**
         * 기본 블록킹큐 사이즈
         */
        private int queueCapacity = 128;
        /**
         * 해당 threadPool의 스레드명 접두어
         */
        private String threadNamePrefix;
        /**
         * 비동기 실행 시 Rejected 상황에서의 전략
         */
        private RejectedExecutionType rejectedExecutionType;

        public ThreadPoolTaskExecutor newThreadPoolExecutor(String beanName) {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(corePoolSize);
            executor.setMaxPoolSize(maxPoolSize);
            executor.setQueueCapacity(queueCapacity);
            if (threadNamePrefix != null) {
                executor.setThreadNamePrefix(threadNamePrefix);
            }
            executor.setWaitForTasksToCompleteOnShutdown(true);
            if (rejectedExecutionType != null) {
                executor.setRejectedExecutionHandler(rejectedExecutionType.rejectedExecutionHandler());
            }
            if (beanName != null) {
                executor.setBeanName(beanName);
            }
            executor.initialize();
            return executor;
        }
    }
}

JacksonConfig

@JsonComponent
public class JacksonConfig {

    JacksonConfig() {
        // config class
    }

    @SuppressWarnings("unused")
    public static class ZonedDateTimeSerializer extends StdSerializer<ZonedDateTime> {
        private static final long serialVersionUID = -771199448470001583L;

        public ZonedDateTimeSerializer() {
            super(ZonedDateTime.class);
        }

        @Override
        public void serialize(ZonedDateTime zonedDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
                throws IOException {
            jsonGenerator.writeString(zonedDateTime.toOffsetDateTime().toString());
        }
    }

    @SuppressWarnings("unused")
    public static class ZonedDateTimeDeserializer extends StdDeserializer<ZonedDateTime> {
        private static final long serialVersionUID = 7252395096303780674L;

        public ZonedDateTimeDeserializer() {
            super(ZonedDateTime.class);
        }

        @Override
        public ZonedDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
                throws IOException {

            return Optional.ofNullable(jsonParser.getValueAsString())
                           .map(DateTimeFormatter.ISO_OFFSET_DATE_TIME::parse)
                           .map(Instant::from)
                           .map(it -> ZonedDateTime.ofInstant(it, ZoneId.systemDefault()))
                           .orElse(null);

        }
    }

    @SuppressWarnings("unused")
    public static class LocalDateTimeSerializer extends StdSerializer<LocalDateTime> {
        private static final long serialVersionUID = -8319362646256071330L;

        public LocalDateTimeSerializer() {
            super(LocalDateTime.class);
        }

        @Override
        public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeString(localDateTime.toString());
        }
    }

    @SuppressWarnings("unused")
    public static class LocalDateTimeDeserializer extends StdDeserializer<LocalDateTime> {
        private static final long serialVersionUID = -3825800866013191618L;

        public LocalDateTimeDeserializer() {
            super(LocalDateTime.class);
        }

        @Override
        public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
                throws IOException {

            return Optional.ofNullable(jsonParser.getValueAsString())
                           .map(DateTimeFormatter.ISO_DATE_TIME::parse)
                           .map(LocalDateTime::from)
                           .orElse(null);

        }
    }

    @SuppressWarnings("unused")
    public static class LongSerializer extends StdSerializer<Long> {
        private static final long serialVersionUID = -7524016618355224119L;

        public LongSerializer() {
            super(Long.class);
        }

        @Override
        public void serialize(Long aLong, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeString(aLong.toString());
        }
    }

    @SuppressWarnings("unused")
    public static class ValueObjectSerializer extends StdSerializer<ValueObject> {
        private static final long serialVersionUID = 465497269532473353L;

        public ValueObjectSerializer() {
            super(PageCommentId.class);
        }

        @Override
        public void serialize(ValueObject value, JsonGenerator gen, SerializerProvider provider) throws IOException {
            gen.writeString(value.getId().toString());
        }
    }

    @SuppressWarnings("unused")
    public static class ValueObjectDeserializer extends StdDeserializer<ValueObject> {

        private static final long serialVersionUID = -1393674262465789088L;

        public ValueObjectDeserializer() {
            super(ValueObject.class);
        }

        @Override
        public ValueObject deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
            return Optional.ofNullable(p.getValueAsString())
                           .map(Long::valueOf)
                           .map(ValueObject::new)
                           .orElse(null);
        }
    }
}

JpaConfig

@Configuration
@EnableJpaAuditing  // For @CreatedAt, @LastModifiedOn
@SuppressWarnings("squid:S1118")
public class JpaConfig {
    public static final String TINYINT = "tinyint";
    public static final String LONGVARCHAR = "mediumtext";
}

MessageConfig

  • xml 지원
@Configuration
public class MessageConfig {

    /**
     * {@link org.springframework.context.MessageSource}
     */
    @Bean
    public ReloadableResourceBundleMessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasenames("classpath:messages/error", "classpath:messages/message");
        messageSource.setCacheSeconds(-1);
        return messageSource;
    }
}

# SpringConfig

```java
@Configuration
@EnableDiscoveryClient                  // Service Discovery(Registry) 클라이언트 활성화
@EnableHystrix                          // Circuit Breaker by Netflix Hystrix 활성화
@EnableConfigurationProperties(MyServiceProperties.class)
public class SpringCloudConfig {
    public SpringCloudConfig() {

    }

    /**
     * netflix openfeign 구성
     */
    @Configuration
    @EnableFeignClients(basePackageClasses = MyAppApplication.class)
    public static class FeignConfig {

        @Component
        @Slf4j
        public static class DefaultRequestInterceptor implements RequestInterceptor {
            public DefaultRequestInterceptor(GlobalProperties globalProperties) {
                this.globalProperties = globalProperties;
            }

            @Override
            public void apply(RequestTemplate input) {
                input.header(MyAppHeaders.APP_KEY, globalProperties.getAppKey());
                input.header(MyAppHeaders.CALLER, MyAppHeaders.CALLER_APP);
                String requestId = AppContextHolder.getRequestId();
                if (requestId != null) {
                    input.header(MyAppHeaders.REQUEST_ID, requestId);
                }
            }
        }
    }
}

WebMvcConfig

@Configuration
@ConditionalOnWebApplication
@EnableSpringDataWebSupport
@EnableConfigurationProperties(MultipartProperties.class)
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(slf4jMdcLoggingInterceptor());
        registry.addInterceptor(myAppContextInterceptor());
    }

    @Bean
    Slf4jMdcLoggingInterceptor slf4jMdcLoggingInterceptor() {
        return new Slf4jMdcLoggingInterceptor();
    }

    @Bean
    DoorayContextInterceptor myAppContextInterceptor() {
        return new MyAppContextInterceptor();
    }
}
Clone this wiki locally