아무런 생각없이...

엔티티 객체를 모델매퍼ModelMapper(http://modelmapper.org/user-manual/property-mapping/) 를 이용해서 매핑처리를 했는데...

디버깅을 하다보니 계층구조가 복잡한 엔티티 객체를 매핑할 때면 속도가 느려지는 현상(아마도 엔티티 객체의 지연로딩LazyLoading)이 나타나는 것을 발견했다. 귀찮기는 하지만... 복잡한 객체에 대해서는 모델매퍼를 이용해서 매핑하는 것은 자제해야할 듯 하다.

그래도... 필드가 많은 객체는 매퍼를 사용하는게 속도저하 현상이 있더라도 피할수 없는 유혹이긴 하다....!!

아니면, 매핑에 사용하는 목적지 클래스를 간단한 형태로 정의하고 사용하는 방법도 있겠지.

스프링부트 기본배너

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::   v1.3.0.BUILD-SNAPSHOT

스프링애플리케이션에서 배너와 관련된 설정

Class 정의

public static void main(String[] args) {
    SpringApplication app = new SpringApplication(MySpringConfiguration.class);
    app.setShowBanner(false);
    app.run(args);
}

외부설정 정의

spring.main.show_banner=false

배너 변경

SpringBoot가 실행될때 배너를 보여주게 되는데, 이를 변경하고 싶을 때는

  • banner.txt 생성
    • banner.txt 파일을 root classpath(src/main/resource)에 위치
    • 별도의 위치에 두고 외부설정(properties or yaml)으로 banner.location 을 선언하고 위치를 정의

배너변경 결과

11:26:18,781 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@1c4e4471 - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
11:26:18,783 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@1c4e4471 - Naming appender as [STDOUT]
11:26:18,921 |-WARN in ch.qos.logback.core.ConsoleAppender[STDOUT] - This appender no longer admits a layout as a sub-component, set an encoder instead.
11:26:18,921 |-WARN in ch.qos.logback.core.ConsoleAppender[STDOUT] - To ensure compatibility, wrapping your layout in LayoutWrappingEncoder.
11:26:18,921 |-WARN in ch.qos.logback.core.ConsoleAppender[STDOUT] - See also http://logback.qos.ch/codes.html#layoutInsteadOfEncoder for details
11:26:18,924 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@1c4e4471 - Setting level of logger [org.thymeleaf] to INFO
11:26:18,931 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@1c4e4471 - Setting level of logger [org.springframework.web] to DEBUG
11:26:18,931 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@1c4e4471 - Setting level of logger [org.springframework] to INFO
11:26:18,931 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@1c4e4471 - Setting level of logger [org.hibernate] to INFO
11:26:18,931 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@1c4e4471 - Setting level of logger [org.eclipse.jetty] to INFO
11:26:18,931 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@1c4e4471 - Setting level of logger [jndi] to INFO
11:26:18,934 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@1c4e4471 - Setting level of logger [ROOT] to DEBUG
11:26:18,937 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@1c4e4471 - Attaching appender named [STDOUT] to Logger[ROOT]

  _   _     U  ___ u  _   _   U _____ u __   __  __  __    U  ___ u  _   _     
 |'| |'|     \/"_ \/ | \ |"|  \| ___"|/ \ \ / /U|' \/ '|u   \/"_ \/ | \ |"|    
/| |_| |\    | | | |<|  \| |>  |  _|"    \ V / \| |\/| |/   | | | |<|  \| |>   
U|  _  |u.-,_| |_| |U| |\  |u  | |___   U_|"|_u | |  | |.-,_| |_| |U| |\  |u   
 |_| |_|  \_)-\___/  |_| \_|   |_____|    |_|   |_|  |_| \_)-\___/  |_| \_|    
 //   \\       \\    ||   \\,-.<<   >>.-,//|(_ <<,-,,-.       \\    ||   \\,-. 
(_") ("_)     (__)   (_")  (_/(__) (__)\_) (__) (./  \.)     (__)   (_")  (_/  


11:26:19 DEBUG o.s.w.c.s.StandardServletEnvironment - Adding [servletConfigInitParams] PropertySource with lowest search precedence
11:26:19 DEBUG o.s.w.c.s.StandardServletEnvironment - Adding [servletContextInitParams] PropertySource with lowest search precedence
11:26:19 DEBUG o.s.w.c.s.StandardServletEnvironment - Adding [jndiProperties] PropertySource with lowest search precedence
11:26:19 DEBUG o.s.w.c.s.StandardServletEnvironment - Adding [systemProperties] PropertySource with lowest search precedence
11:26:19 DEBUG o.s.w.c.s.StandardServletEnvironment - Adding [systemEnvironment] PropertySource with lowest search precedence

위와 같은 결과를 볼 수 있다.

참고사항

SpringBoot

이미지 to ASCII CODE image


http://www.springcamp.io/2015/


3번째 준비하는 스프링캠프.

이번에는 영상촬영을 담당하는 것을 목표로...?

얼마 남지 않았음.


import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;

import org.junit.Test;
import org.quartz.CronExpression;
import org.quartz.CronScheduleBuilder;

/**
 * Quartz CronExpression 정규식 처리를 이해하기 위한 테스트 코드
 * 
 * @author ihoneymon
 * @see http://quartz-scheduler.org/api/2.2.0/org/quartz/CronExpression.html
 */
public class QuartzCronExpressionTest {

    /**
     * Null은 예외발생
     */
    @Test(expected = IllegalArgumentException.class)
    public void testNullCronExpressionException() {
        assertThat(CronExpression.isValidExpression(null), is(false));
        fail();
    }

    /**
     * 빈값이나 cron 정규식 표현이 아니라면 실패
     */
    @Test
    public void testEmptyCronExpressionException() {
        assertThat(CronExpression.isValidExpression(""), is(false));
        assertThat(CronExpression.isValidExpression("1 1+2 1*2"), is(false));
        assertThat(CronExpression.isValidExpression("testWrongChronExpression"), is(false));
    }

    /**
     * Quartz CronExpression 중에 DayOfMonth와 DayOfWeek에 대한 확인 DayOfMonth와
     * DayOfWeek의 표현식에 대해서는 신경을 써야한다.
     * 
     * Second Minute Hour DayOfMonth Month DayOfWeek (optional)Year
     */
    @Test
    public void testDayOfMonthAndDayOfWeekCronExpression() {
        /**
         * same DayOfMonth & DayOfMonth is valid fail DayOfMonth & DayOfMonth is
         * all: false DayOfMonth & DayOfMonth is none: valid fail
         */
        assertThat(CronExpression.isValidExpression("* * * * * *"), is(false));
        assertThat(CronExpression.isValidExpression("* * * ? * ?"), is(false));

        /**
         * DayOfMonth is all and DayOfMonth is none: valid success DayOfMonth is
         * all and DayOfMonth is define: valid fail
         */
        assertThat(CronExpression.isValidExpression("* * * * * ?"), is(true));
        assertThat(CronExpression.isValidExpression("* * * * * MON"), is(false));
        assertThat(CronExpression.isValidExpression("* * * * * 2"), is(false));
        assertThat(CronExpression.isValidExpression("* * * * * 2,5"), is(false));

        /**
         * DayOfMonth is none and DayOfMonth is all: valid success DayOfMonth
         * required none DayOfMonth DayOfMonth가 none(?)인 경우에는 DayOfMonth에 허용되는
         * 정규식은 대부분 통과
         */
        assertThat(CronExpression.isValidExpression("* * * ? * *"), is(true));
        assertThat(CronExpression.isValidExpression("* * * ? * MON"), is(true));
        assertThat(CronExpression.isValidExpression("* * * ? * MON-FRI"), is(true));
        assertThat(CronExpression.isValidExpression("* * * ? * MON-FRI,SUN"), is(true));
        assertThat(CronExpression.isValidExpression("* * * ? * MON#2"), is(true));
        assertThat(CronExpression.isValidExpression("* * * ? * 2#2"), is(true));
        assertThat(CronExpression.isValidExpression("* * * ? * 2#2,1#2"), is(false));

        /**
         * DayOfMonth에
         */
        assertThat(CronExpression.isValidExpression("* * * 1 * ?"), is(true));
        assertThat(CronExpression.isValidExpression("* * * 1-3 * ?"), is(true));
        assertThat(CronExpression.isValidExpression("* * * 1,6,9 * ?"), is(true));
        assertThat(CronExpression.isValidExpression("* * * 1 * *"), is(false));
        assertThat(CronExpression.isValidExpression("* * * 1-3 * *"), is(false));
        assertThat(CronExpression.isValidExpression("* * * 1,6,9 * FRI"), is(false));
        /**
         * DayOfMonth use L(last) special character
         */
        assertThat(CronExpression.isValidExpression("* * * L * ?"), is(true));
        assertThat(CronExpression.isValidExpression("* * * L * *"), is(false));
        assertThat(CronExpression.isValidExpression("* * * L * MON"), is(false));
        /**
         * DayOfMonth에 L은 단독으로 사용되거나 '-nth day'만 허용
         */
        assertThat(CronExpression.isValidExpression("* * * 15L * *"), is(false));
        assertThat(CronExpression.isValidExpression("* * * L15 * *"), is(false));
        assertThat(CronExpression.isValidExpression("* * * 15L * MON"), is(false));
        assertThat(CronExpression.isValidExpression("* * * 15L * ?"), is(false));
        assertThat(CronExpression.isValidExpression("* * * L-3 * ?"), is(true));
        assertThat(CronExpression.isValidExpression("* * * L-3 * *"), is(false));
        assertThat(CronExpression.isValidExpression("* * * L-3 * MON"), is(false));

        /**
         * DayOfMonth use W(week: mon-fri) special character
         */
        assertThat(CronExpression.isValidExpression("* * * W * *"), is(false));
        assertThat(CronExpression.isValidExpression("* * * 15W * *"), is(false));
        assertThat(CronExpression.isValidExpression("* * * 15W * ?"), is(true));
        assertThat(CronExpression.isValidExpression("* * * 15W,16W * ?"), is(true));
        assertThat(CronExpression.isValidExpression("* * * 15W-17W * ?"), is(true));
        assertThat(CronExpression.isValidExpression("* * * 15W/2W * ?"), is(true));

        /**
         * DayOfMonth use LW(last-week: mon-fri) special character
         */
        assertThat(CronExpression.isValidExpression("* * * LW * *"), is(false));
        assertThat(CronExpression.isValidExpression("* * * LW * ?"), is(true));
        assertThat(CronExpression.isValidExpression("* * * 1LW * ?"), is(false));
        assertThat(CronExpression.isValidExpression("* * * LW2 * ?"), is(false));
        assertThat(CronExpression.isValidExpression("* * * LW * MON"), is(false));
        assertThat(CronExpression.isValidExpression("* * * LW * 2"), is(false));
    }

    @Test(expected = RuntimeException.class)
    public void testCronBuilder() {
        assertThat(CronExpression.isValidExpression("* * * LW * *"), is(false));
        CronScheduleBuilder.cronSchedule("* * * LW * *").build();
    }
}

CronExpression.isValidExpression() 에서 false가 발생한 정규식을 CronScheduleBuilder.cronSchedule()를 사용하여 스케줄을 생성하려하면 RuntimeException이 발생한다.

필드명 Field Name 허용값Allowed Value 허용특수문자 Allowed Special Chracters
Seconds 0-59 , - * /
Minutes 0-59 , - * /
Hours 0-23 , - * /
Day-of-month 1-31 , - * ? / L W
Month 1-12 or JAN-DEC , - * /
Day-of-Week 1-7 or SUN-SAT , - * ? / L #
Year (Optional) empty, 1970-2199 , - * /

허용특수문자

  • *: 정의된 모든 값을 사용, 분에 사용하면 매 분단위로.
  • ?: 지정된 값 없이 무작위로 진행되며, Day-of-month와 Day-of-Week에서만 사용하며, Day-of-month가 지정되었을 때 Day-of-Week의 적용을 배제하거나 반대의 경우 사용할 수 있겠다.
  • -: 범위를 지정할 때 사용, 10-12는 10, 11 그리고 12
  • ,: 특정값을 여러개 사용할 때 사용, MON,WED,FRI 는 월요일, 수요일, 금요일
  • /: 시작값/증가값의 형태로 사용하며 0/15인 경우 0, 15, 30, 45 실행되며, 5/15인 경우 5, 20, 35, 50실행됨
  • L: Day-of-month와 Day-of-Week에서만 사용하는 특수문자로, 마지막 값을 반환하는 용도로 사용된다.
    • Day-of-Week에서 사용하면 토요일(7=Saturday)이다.
    • Day-of-month에서 사용시에 ‘6L’인 경우에는 이번달 마지막주 금요일을 지정하는 것과 같다. ‘L-3’인 경우에는 마지막날에서 3일전을 의미한다.
  • W: 주간을 지정하며 15W로 정의하면 이번달 15일 실행되는지 여부를 확인하여 15일이 토요일인 경우에는 14일 금요일 실행되고, 15일이 일요일인 경우에는 16일 월요일 실행된다.
  • LW: ‘L’과 ‘W’를 결합해서 사용가능한데, Day-of-month 필드에 ‘LW’를 정의하면 매달 주간 마지막날에 실행이되는데, 마지막 날이 토요일인지 일요일인지에 따라서 ‘W’의 처리방법을 적용하게 된다.
  • #: Day-of-Week 에서만 허용되는 특수문자로 6#3의 경우는 3번째 금요일(“6”=Friday and “#3”은 3rd one in month)에 실행된다는 의미를 가진다.

정해진 문자와 월, 요일에 대해서는 대소문자를 가리지 않는다.

참고

+ Recent posts