SpringBoot App에 외부 libs 추가

JAVA/Spring|2023. 11. 19. 22:57

SpringBoot는 기본적으로 jar내에 BOOT-INF에 classes, libs를 클래스패스로 물고 올라간다.

build.gradle혹은 pom.xml에 설정하여 빌드 타이밍에 같이 묶긴하면 BOOT-INF에 포함되어 사용하는데 문제 없지만

별도 jar로 제공 받아 활용을 해야한다면.

추가적으로 외부의 Libs를 ClassPath를 설정하기 위해서는 PropertiesLauncher 를 사용하고

-Dloader.path 를 설정하여 외부 jar도 활용 가능하다.

(특정 Bean을 external.jar로 만들어 추가한다거나..)

 

ex)

java -cp bootApp.jar -Dloader.path=external-plain.jar org.springframework.boot.loader.PropertiesLauncher

java -cp bootApp.jar -Dloader.path=plugins/ org.springframework.boot.loader.PropertiesLauncher

https://www.masterspringboot.com/configuration/web-server/how-to-use-an-external-jar-in-a-spring-boot-application/

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

[Spring] SessionStatus는 어떻게 동작할까?  (5) 2020.09.08
[JPA] LockType  (0) 2020.07.23
[Spring] LifeCycle, SmartLifeCycle  (0) 2020.06.23
[MSA] sidecar 패턴  (0) 2020.01.13
[SpringConfig] Properties 암호화  (0) 2019.12.24

댓글()

구분자와 문자열 추가 StringJoiner

JAVA|2021. 8. 26. 21:47

Effective Java에서 바로  StringBuffer, StringBuilder는 관련해서 나오는 이야기는 두가지 정도로 기억한다.

 

첫번째는 "text" + "text" 연산보다 StringBuffer, StringBuilder 가 좋은 이유는 +연산마다 새로운 객체를 생성되기 때문에 많은 문자열 더하기를 할때는 계속 불필요한 객체생성되는 + 연산보다는 StringBuffer, StringBuilder 를 사용하라

 

두번째는 StringBuffer는 append()시에 락(synchronized)을 잡기 때문에 여러 스레드에서 같은 StringBuffer 객체를 공유해서 써야하는경우(이런 경우를 지금까지는 보지 못했다..)아니고서는 락을 잡는것은 낭비이기 때문에 락(synchronized)을 잡지 않는 StringBuilder를 사용하라

 

 

StringBuffer.append()
StringBuilder.append()

 

보통 로깅을 한다거나, 문자열 붙이기를 할때

문자열 사이에 라인변경(\n)을 추가하거나, 콤마(,) 를 자주 추가하곤했는데

간단한 코드지만 중간에 구분자를 넣는작업은 귀찮긴 했습니다.

 

그래서 guava의 Joiner를 많이 사용했었는데

JDK8에 StringJoiner로 대체할수 있습니다.

 

StringBuilder로 추가 문자열마다 라인변경을 한다고 했을때

(첫번째 추가할때는 구분자를 넣지 않고, 첫번째가 아니라면 구분자 추가)

ArrayList<String> strings = Lists.newArrayList("name1", "name2", "name3");
        final StringBuilder stb = new StringBuilder();
        strings.forEach(string -> {
            stb.append(string);
            if (stb.length() != 0) {
                stb.append("\n");
            }
        });

 

StringJoiner로 추가 문자열마다 라인 변경을 한다고 했을때 생성자에 구분자("\n")넣고 add로 문자열을 붙인다

StringJoiner는 생성자가 두종류, (구분자) , (구분자, Prefix, Suffix)

ArrayList<String> strings = Lists.newArrayList("name1", "name2", "name3");
        final StringJoiner joiner = new StringJoiner("\n");
        strings.forEach(string -> {
            joiner.add(string);
        });

StringJoiner.add(), 여기서 elts는 String[]

동작이 조금 다른건 전달받은 String을 StringBuilder는 byte[] 로 관리, StringJoiner는 String[]로 관리

 

StringJoiner의 경우 추가한 문자열에대해서만 String[]에 추가하고, del의 경우는 추가로 저장하고 있는것은 아니기 때문에

메모리를 적게 먹을것이고, 

 

StringBuilder의 경우 del이나 Prefix, Suffix 구분이 딱히 없어서 일반 추가한 문자열 취급을 하기 때문에

bytes에 추가될것임

 

문자를 붙일때마다, 구분자가 필요하거나 Prefix, Suffix가 필요한경우

StringJoiner를 사용하는것이 여러모로 유리하다고 생각합니다.

'JAVA' 카테고리의 다른 글

ActiveMQ JDK Version  (0) 2020.09.09
DCL(Double Checked Locking)  (0) 2020.09.08
Java Stream  (0) 2020.07.14
Java Time  (0) 2020.07.06
SortedQueue...  (0) 2020.07.02

댓글()

ActiveMQ JDK Version

JAVA|2020. 9. 9. 12:51

ActiveMQ는 "Classic" 버전과 "Artemis" 버전이 있습니다.

각 버전의 차이점은 여러가지가 있겠지만

그중 Classic은 JMS1.1을 지원 Artemis는 JMS1.1과 2.0를 지원 합니다.

 

체감한 1.1과 2.0의 차이는

1.1에서는 ConnectionFactory를 통해 Connection을 생성하고 Session을 생성해야하는것을

2.0에서는 Connection과 Session을 모두 포함한 JMSContext를 제공하여

2.0에서는 ConnectionFactory로 JMSContext를 생성하여 관리 할 수 있습니다.

 

현재 활용하는 WAS는 JMS2.0을 지원하기 때문에 그에 맞는 테스트를 위해

"Classic" 과 "Artemis" 중 "Artemis"를 선택했고

 

개발 최소 환경이 JDK7이 될수 있기 때문에 그에 맞는 버전을 찾아야 했습니다.

 

현재 "Classic" 최신버전은 5.16이며 JDK8로 빌드가 되어있고

JDK7으로 빌드된 버전을 디컴파일러로 찾아보니 5.11~5.14는 JDK7으로 빌드되어있습니다.

 

그리고 "Artemis"는 현재 최신버전은 2.15이며 2.x에는 모두 JDK8로 빌드가 되어있고

쭉 찾아보니 1.4.0버전은 JDK7으로 빌드되어있어서 1.4.0 기준으로 테스트 예정입니다.

 

JDK 버전 제약이 있으면 최신 오픈소스들을 활용하는데에 쉽지 않네요..;;;

 

 

'JAVA' 카테고리의 다른 글

구분자와 문자열 추가 StringJoiner  (0) 2021.08.26
DCL(Double Checked Locking)  (0) 2020.09.08
Java Stream  (0) 2020.07.14
Java Time  (0) 2020.07.06
SortedQueue...  (0) 2020.07.02

댓글()

DCL(Double Checked Locking)

JAVA|2020. 9. 8. 19:48
public class DCLInstance {
    private static volatile DCLInstance instance;

    public static DCLInstance getInstance() {
        if (instance == null) {
            synchronized (DCLInstance.class) {
                if (instance == null) {
                    instance = new DCLInstance();
                }
            }
        }
        return instance;
    }
}

 

DCL..

Double Checked Locking..

 

이름을 모르고 락을 잡아 세팅 후 그 이후는 락을 안잡기 위해서 어떻게 해야할까를 고민하다

활용하고 있는 방식인데.. 그게 이름이 있었네요..

그 이름이 DCL 이였습니다

(자바 병렬 프로그래밍에 이 이름이 있었는지는 나중에 다시 읽어봐야겠습니다... 나왔을지도..)

 

뭔가 외우는건 잘 못하지만.. 하나하나 아는것이 실력이니

부족함을 알고 접할때마다 꼼꼼히 이름들을 알고 쓰는걸로..!!

'JAVA' 카테고리의 다른 글

구분자와 문자열 추가 StringJoiner  (0) 2021.08.26
ActiveMQ JDK Version  (0) 2020.09.09
Java Stream  (0) 2020.07.14
Java Time  (0) 2020.07.06
SortedQueue...  (0) 2020.07.02

댓글()

[Spring] SessionStatus는 어떻게 동작할까?

JAVA/Spring|2020. 9. 8. 17:02

Spring MVC 에서 SessionStatus 라는것이 존재합니다.

(현재는 대부분의 서비스회사들이 Session을 사용하지 않기 때문에 사용하지 않는 회사들은 무의미 할수 있겠네요)

 

SessionStatus는 @SessionAttributes를 활용해 Session에 남긴 데이터를 정리하는데 활용을 하는 인터페이스 입니다.

(Controller에 args로 선언해두면 injection이 되며 정리는 setComplete() 를 통해 정리 flag를 세팅합니다.)

 

SessionStatus 에 setComplete()를 호출하게 되면 내부적으로 complete boolean을 세팅하게 되고

RequestMappingHandlerAdapter에서 modelFactory의 updateModel() 내부에서 대상 attribute들을 정리하게 됩니다.

 

여기서 @SessionAttributes이 등장하는데

 

@SessionAttributes는 names를 args로 받게 되고

지정된 names들에 대해서 model에 접근하면 기존 model 저장소가 아닌 Session 영역까지 확장되도록 도움을 줍니다.

 

 

model.addAttribute("dutch", "hello");

 

일반적으로 위 코드 처럼 "dutch" 라는 키로 model에 세팅하면

응답 이후 값이 삭제 됩니다.

 

하지만 컨트롤러 클래스 상단에  @SessionAttribues 를 세팅 후 사용한다면 동작이 변경되게 됩니다.

@RequestMapping("dutch")
@Controller
@SessionAttributes("dutch")
public class DutchController

@SessionAttributes선언 후 model.addAttribute 를 호출한다면 해당 key,value가 Session에 저장 되고,

다음 요청이 동일 세션으로 왔을때 session의 Key를 "dutch"로 조회하면 addAttribute값이 존재하는것을 볼 수 있습니다. 그리고 model을 통해 "dutch"를 조회했을때도 값이 존재하는것을 확인 할 수 있습니다.

 

주의해야할 사항은 해당 컨트롤러가 아닌, 다른 컨트롤러에서 모델에서 "dutch"으로 값을 얻으려 시도하면

값이 들어 있지 않습니다.

위의 컨트롤러의 경우에는 model에 "dutch" 에 해당하는 값을 줘! 라고 하면 @SessionAttributes가 설정되어있는 Key는 Session에서 값을 찾지만

@SessionAttributes("dutch") 가 달려있지 않은 컨트롤러에서 model에 ""dutch" 에 해당하는 값을 줘!" 라고 한다면

Session에서 찾는게 아니라 정말 model에서 찾기 때문에 값이 존재 하지 않습니다.

 

 

 

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

SpringBoot App에 외부 libs 추가  (0) 2023.11.19
[JPA] LockType  (0) 2020.07.23
[Spring] LifeCycle, SmartLifeCycle  (0) 2020.06.23
[MSA] sidecar 패턴  (0) 2020.01.13
[SpringConfig] Properties 암호화  (0) 2019.12.24

댓글()

[JPA] LockType

JAVA/Spring|2020. 7. 23. 19:16

주로 업무상 디비 락이 필요한경우 Select for update를 주로 활용했는데
optimistic lock을 JPA에서 편리하게 사용할수 있게 지원하고 있어
테스트를 하며 정리해봤습니다.
(@Version이 있고 없고, 변경사항이 있을때 어떤 쿼리가 발생하는지 등)

작성한 github 위치 : https://github.com/Meteorkor/JPA_Study/edit/master/docs/LockType.md

테스트 코드는 추후 정리해서 업로드 예정입니다.

LockModeType

  • OPTIMISTIC

    • 트랜잭션 중 변경사항이 있을때
      • SELECT : 이때 버전을 같이 가져옴(해당 필드에 @Version, org.springframework.data.annotation.Version 가 아니라 javax.persistence.Version)
      • UPDATE : 이때 앞에서 Select한 version을 where에, set절에는 version=version+1 으로 version 업데이트
        • UPDATE 된 값이 없으면 중간에 버전이 변경된것으로 파악하여 에러 발생
    • 트랜잭션 중 변경사항이 없을때
      • SELECT : 이때 버전을 같이 가져옴(해당 필드에 @Version, org.springframework.data.annotation.Version 가 아니라 javax.persistence.Version)
      • SELECT : 이때 앞에서 Select한 version을 where에, 조회된 결과가 없으면 에러 발생
    • 수정을 하게 되면 version은 한개씩 올라감, 수정을 하지 않으면 version은 올라가지 않음
  • OPTIMISTIC_FORCE_INCREMENT

    • 트랜잭션 중 변경사항이 있을때
      • SELECT : 이때 버전을 같이 가져옴(해당 필드에 @Version, org.springframework.data.annotation.Version 가 아니라 javax.persistence.Version)
      • UPDATE : 이때 앞에서 Select한 version을 where에, set절에는 version=version+1 으로 version 업데이트
        • UPDATE 된 값이 없으면 중간에 버전이 변경된것으로 파악하여 에러 발생
      • UPDATE : Version을 한번 더 업데이트
    • 트랜잭션 중 변경사항이 없을때
      • SELECT : 이때 버전을 같이 가져옴(해당 필드에 @Version, org.springframework.data.annotation.Version 가 아니라 javax.persistence.Version)
      • UPDATE : Version을 업데이트
    • 수정을 하게 되면 version은 두개씩 올라감
    • 수정을 하지 않으면 version은 한개씩 올라감
  • READ(OPTIMISETIC과 동작 같음)

  • WRITE(OPTIMISTIC_FORCE_INCREMENT 과 동작 같음)

  • PESSIMISTIC_READ

    • @Version이 없는 경우
      • 트랜잭션 중 변경사항이 없을때
        • SELECT FOR UPDATE
      • 트랜잭션 중 변경사항이 있을때
        • SELECT FOR UPDATE - UPDATE
    • @Version이 있는 경우
      • 트랜잭션 중 변경사항이 없을때
        • SELECT FOR UPDATE
      • 트랜잭션 중 변경사항이 있을때
        • SELECT FOR UPDATE - UPDATE(version도 함께 올라감)
  • PESSIMISTIC_WRITE

    • @Version이 없는 경우
      • 트랜잭션 중 변경사항이 없을때
        • SELECT FOR UPDATE
      • 트랜잭션 중 변경사항이 있을때
        • SELECT FOR UPDATE - UPDATE
    • @Version이 있는 경우
      • 트랜잭션 중 변경사항이 없을때
        • SELECT FOR UPDATE
      • 트랜잭션 중 변경사항이 있을때
        • SELECT FOR UPDATE - UPDATE(version도 함께 올라감)
  • PESSIMISTIC_FORCE_INCREMENT

    • @Version이 없는 경우
      • 에러 발생
    • @Version이 있는 경우
      • 트랜잭션 중 변경사항이 없을때
        • SELECT FOR UPDATE - update version
      • 트랜잭션 중 변경사항이 있을때
        • SELECT FOR UPDATE - update version - updateUPDATE(version도 함께 올라감)
  • NONE

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

SpringBoot App에 외부 libs 추가  (0) 2023.11.19
[Spring] SessionStatus는 어떻게 동작할까?  (5) 2020.09.08
[Spring] LifeCycle, SmartLifeCycle  (0) 2020.06.23
[MSA] sidecar 패턴  (0) 2020.01.13
[SpringConfig] Properties 암호화  (0) 2019.12.24

댓글()

Java Stream

JAVA|2020. 7. 14. 19:34

20.07.16

 

글 작성 방법을 조금 변경하려고 합니다.

업무중 소스를 까보며 혹은 책이나 로컬로 테스트를 하며 하나씩 정리해서 글을 작성하곤 했는데

아무래도 글을 작성하기 위해 쓴 코드들은 대부분 버려지다 보니

아쉬움이 있어서 github에 repo를 하나씩 파서 샘플들을 작성하며 README와 글을 작성하려고 합니다

(README와 글이 동일 할 수도 있고, 조금 더 풀어서 작성할 수 있을수도 있을것 같습니다)

 

해당 글의 Repo는

https://github.com/Meteorkor/Stream-Study

 

Meteorkor/Stream-Study

Contribute to Meteorkor/Stream-Study development by creating an account on GitHub.

github.com

스트림, 데이터의 흐름

  • 배열 또는 컬렉션, 또는 여러개의 조합으로 가공 및 필터링을 수행할 수 있도록 기능 제공

  • for, foreach에 비해 코드 복잡도를 줄일 수 있음

  • 람다를 활용하여 코드의 양을 줄이고 간결하게 표현 가능

  • 간단하게 병렬처리 가능(parallel())

  • primitive의 경우 IntStream이나 LongStream을 사용하지 않는다면, Boxing으로 인해 성능 저하가 발생할 수 있으니 주의 필요

Stream 생성

  • Array, Collection
  • Stream
    • Stream.builder()
    • Stream.of()
    • Stream.empty()
    • Stream.generate()
    • Stream.iterate()
      • (final T seed, final UnaryOperator f)
      • (T seed, Predicate<? super T> hasNext, UnaryOperator next)
        • JDK9
    • Stream.concat()
  • 기본타입(IntStream, LongStream), String, 파일 스트림(BytesReader)
  • 병렬스트림
  • StreamSupport(추후)

가공(transformer)

  • Filtering
  • Mapping
  • Sorting
  • Iterating

결과(terminal)

  • Calculating
  • Reduction
  • Collecting
  • Matching
  • Iterating

주의 사항

  • primitive

    • primitive 타입을 boxed 된 Stream을 사용하게 된다면 boxing unboxing이 반복되어 나타날수 있음
    • 필요에 따라 IntStream, LongStream, DoubleStream 을 잘 구분하여 사용할것
      • mapToInt, mapToLong, mapToDouble 도 마찬가지
  • 병렬 스트림

    • 메인 스레드 혹은 일반 스레드에서 parallel()의 경우 ForkJoinPool.common을 사용
      • ForkJoinPool.commonPool()은 보통 Runtime.getRuntime().availableProcessors() 만큼의 스레드를 가지고 있음
      • 다른 각각 스레드에서 parallel()을 사용하는 경우에도 같은 ForkJoinPool.commonPool() 공유해서 사용하기 때문에 Blocking 작업으로 인해 전체적으로 성능이 떨어질수 있음
        • 방지하기 위해서는 ForkJoinPool을 신규로 만들고 신규로 만든 ForkJoinPool에 submit을 하면 ForkJoinPool.commonPool() 이 아닌 신규로 만든 Pool에서 병렬처리로 동작
  • 파이프라이닝

    • Stream은 하나의 데이터를 파이프라이닝 방식으로 처리
      • {A,B,C}.map(변환1).map(변환2)... 를 수행할때, A,B,C 변환1 을 수행하는것이 아니라 A에 대해 변환1, 변환2, B에 대해 변환1, 변환2 방식으로 동작
        • 물론 병렬의 경우 이 작업들을 서로 다른 스레드에서 처리 할 수 있음
    • 흐름 정의에 따라 처리내용이 수행되지 않을 수 있음
      • {A,B,C}.peek().map().count() 의 경우 peek와 map이 count()에 영향을 주지 않기 때문에 동작하지 않을 수 있음
        • unit-test에서는 동작하지 않았으나 travis-ci에서는 동작하던...
      • {A,B,C}.peek().map() , terminal(결과처리들, calc, reduce, collect 등) 호출이 없을 경우 결국 peek나 map나 의미가 없기 때문에 수행되지 않음

'JAVA' 카테고리의 다른 글

ActiveMQ JDK Version  (0) 2020.09.09
DCL(Double Checked Locking)  (0) 2020.09.08
Java Time  (0) 2020.07.06
SortedQueue...  (0) 2020.07.02
java.util.function 인터페이스  (0) 2019.12.30

댓글()

Java Time

JAVA|2020. 7. 6. 20:13

제품이 JDK6 그리고 JDK7을 주로 사용하다보니 Calendar와 Date 사용에 익숙했었지만,

요즘 JDK8을 사용하며 다시 Time관련쪽을 살펴볼일이 있었습니다.

(이전에는 JodaTime도 써보고 했지만.. 하위버전에 Date와 Calendar가 익숙하다보니..)

 

때문에 JDK8에 신규로 등장한 LocalDateTime과 LocalDate, LocalTime에 대해 살펴보고

왜 변경되었는지 살펴볼 예정입니다.

 

Calendar, Date 문제점 및 불편하던점

1. 불변 객체가 아님

 - 객체를 외부에서 조작해서 문제가 생길수도 있음

 - 외부로 노출될때는 카피를 해서 노출하도록 해야함

 

2. int 상수 필드 남용

  - Calendar에서 set 하는 경우 Calendar의 상수를 사용하는데, 예를 들면 'Calendar.DAY_OF_MONTH', 상수로는 파악하기 어려움

 

3. 월 지정

  - 월지정에 1월을 0으로 표현, 1일은 1로 표현, 12를 넣어도(13월이 되는) 에러발생하지 않음

 

4. 일관성 없는 요일 상수

  - Calendar에서 일요일은 1, Date에서 일요일은 0

 

5. 불필요한 클래스 분리

  - 기존은 Date만 존재, 국제화 사용, 날짜 단위 계산 등으로 인해 Calendar 추가

  - 최종 결과물이 Date인 경우, Calendar를 통해 Date를 재생성,

  - Calendar의 생성비용은 비싼편

  - 날짜 시간을 모두 저장하는 클래스이름이 Date라는 부분

6. 오류 발생시키지 않는 시간대 지정 

  - TimeZone 지정에 오류가 있더라도 오류를 발생시키지 않는

 

7. java.util.Date 하위 클래스 문제

  - jdbc 활용시 사용되기도 하는 java.sql.Date는 상위 클래스와 이름이 같다

  - TimeStamp 클래스는 Date에 나노초를 더한 클래스, equals의 대칭성 문제가 발생할 수 있음

 

 

참고 및 출처 : https://d2.naver.com/helloworld/645609

 

8. SimpleDateFormat의 Thread-Safe 하지 않음

  - Date를 포맷팅 하기 위해 주로 사용되는 SimpleDateFormat는 ThreadSafe하지 않아 매번 생성하곤 합니다(혹은 sync를 잡아서..)

  - 신규로 추가된 DateTimeFormatter는 Thread-safe 합니다, 즉 한번 생성하고 재활용해서 여러 스레드에서 쓸수 있습니다.

 

JDK8 에서 추가된 LocalDateTime, LocalDate, LocalTime

  - LocalDateTime : 시간과 날짜 표현

  - LocalDate : 날짜만 표현

  - LocalTime : 시간만 표현

 

추가된 Time 클래스 특성

위에 특성들이 개선된 내용들이겠지만 몇가지만 보면

  - 불변클래스로 디자인

    - 생성자가 아닌 static 메소드를 통해 객체를 생성하도록 하며, 다양한 메소드로 다양한 요건들 충족할 수 있도록 지원

    - 3개 클래스 모두 상속을 막은 final class

    - 3개 클래스 모두 Temporal, TemporalAdjuster 인터페이스 구현

      - Temporal(시간) : TemporalAccessor를 상속 받은 인터페이스, plus 및 minus 메소드 지원

        - TemporalAccessor : get 및 query , getLong

      - TemporalAdjuster(시간조정자) : adjusInto 메소드 지원, DateTime에 adjusInto(LocalDate)를 하면 Time은 그대로 Date만 변경

 

 

Date & Calendar 사용법과 많이 달라 메소드를 다양하게 사용해봐야 할것

'JAVA' 카테고리의 다른 글

DCL(Double Checked Locking)  (0) 2020.09.08
Java Stream  (0) 2020.07.14
SortedQueue...  (0) 2020.07.02
java.util.function 인터페이스  (0) 2019.12.30
JEP 218: Generics over Primitive Types  (0) 2019.12.10

댓글()