πŸ“–
Aaron's TECH BOOK
  • Intro
    • About me
  • Lecture
    • Kubernetes
      • Begin Kubernetes
    • Kafka
      • Begin Kafka
    • Kotlin
      • TDD, Clean Code Preview
      • woowa Kotlin
    • Java
      • Multithread Concurrency
      • The Java
    • Toby
      • Toby Spring 6
      • Toby Spring Boot
    • MSA
      • 01.Micro Service
      • 02.DDD 섀계
      • 03.DDD κ΅¬ν˜„
      • 04.EDA κ΅¬ν˜„
    • Spring Boot
    • Spring Batch
    • Spring Core Advanced
    • Spring DB Part II
    • Spring DB Part I
    • JPA API and Performance Optimization
    • JPA Web Application
    • JPA Programming Basic
    • Spring MVC Part 2
      • 01.Thymeleaf
      • 02.ETC
      • 03.Validation
      • 04.Login
      • 05.Exception
    • Spring MVC Part 1
      • 01.Servlet
      • 02.MVC
    • Http
      • 01.Basic
      • 02.Method
      • 03.Header
    • Spring Core
    • Study
      • Concurrency issues
      • First Come First Served
      • Performance Test
      • TDD
      • IntelliJ
  • Book
    • Kafka Streams in Action
      • 01.μΉ΄ν”„μΉ΄ 슀트림즈
      • 02.μΉ΄ν”„μΉ΄ 슀트림즈 개발
      • 03.μΉ΄ν”„μΉ΄ 슀트림즈 관리
    • Effective Kotlin
      • 01.쒋은 μ½”λ“œ
      • 02.μ½”λ“œ 섀계
      • 03.νš¨μœ¨μ„±
    • 이벀트 μ†Œμ‹±κ³Ό MSA
      • 01.도메인 주도 섀계
      • 02.객체지ν–₯ 섀계 원칙
      • 03-04.이벀트 μ†Œμ‹±
      • 05.λ§ˆμ΄ν¬λ‘œμ„œλΉ„μŠ€ ν˜‘μ—…
      • 06.결과적 일관성
      • 07.CQRS
      • 08.UI
      • 09.ν΄λΌμš°λ“œ ν™˜κ²½
    • λͺ½κ³ DB μ™„λ²½ κ°€μ΄λ“œ
      • I. λͺ½κ³ DB μ‹œμž‘
      • II. λͺ½κ³ DB 개발
    • Kotlin Cookbook
      • μ½”ν‹€λ¦° 기초
      • μ½”ν‹€λ¦° κΈ°λŠ₯
      • ETC
    • Kotlin in Action
      • ν•¨μˆ˜/클래슀/객체/μΈν„°νŽ˜μ΄μŠ€
      • λžŒλ‹€μ™€ νƒ€μž…
      • μ˜€λ²„λ‘œλ”©κ³Ό κ³ μ°¨ ν•¨μˆ˜
      • μ œλ„€λ¦­μŠ€, μ• λ…Έν…Œμ΄μ…˜, λ¦¬ν”Œλ ‰μ…˜
    • Kent Beck Tidy First?
    • λŒ€κ·œλͺ¨ μ‹œμŠ€ν…œ 섀계 기초
      • 01.μ‚¬μš©μž μˆ˜μ— λ”°λ₯Έ 규λͺ¨ ν™•μž₯μ„±
      • 02.개랡적인 규λͺ¨ μΆ”μ •
      • 03.μ‹œμŠ€ν…œ 섀계 κ³΅λž΅λ²•
      • 04.처리율 μ œν•œ μž₯치 섀계
      • 05.μ•ˆμ • ν•΄μ‹œ 섀계
      • 06.ν‚€-κ°’ μ €μž₯μ†Œ 섀계
      • 07.유일 ID 생성기 섀계
      • 08.URL 단좕기 섀계
      • 09.μ›Ή 크둀러 섀계
      • 10.μ•Œλ¦Ό μ‹œμŠ€ν…œ 섀계
      • 11.λ‰΄μŠ€ ν”Όλ“œ μ‹œμŠ€ν…œ 섀계
      • 12.μ±„νŒ… μ‹œμŠ€ν…œ 섀계
      • 13.검색어 μžλ™μ™„μ„± μ‹œμŠ€ν…œ
      • 14.유튜브 섀계
      • 15.ꡬ글 λ“œλΌμ΄λΈŒ 섀계
      • 16.배움은 κ³„μ†λœλ‹€
    • μ‹€μš©μ£Όμ˜ ν”„λ‘œκ·Έλž˜λ¨ΈπŸ“–
    • GoF Design Patterns
    • 도메인 주도 개발 μ‹œμž‘ν•˜κΈ°
      • 01.도메인 λͺ¨λΈ μ‹œμž‘ν•˜κΈ°
      • 02.μ•„ν‚€ν…μ²˜ κ°œμš”
      • 03.μ• κ·Έλ¦¬κ±°νŠΈ
      • 04.리포지터리와 λͺ¨λΈ κ΅¬ν˜„
      • 05.Spring Data JPAλ₯Ό μ΄μš©ν•œ 쑰회 κΈ°λŠ₯
      • 06.μ‘μš© μ„œλΉ„μŠ€μ™€ ν‘œν˜„ μ˜μ—­
      • 07.도메인 μ„œλΉ„μŠ€
      • 08.μ• κ·Έλ¦¬κ±°νŠΈ νŠΈλžœμž­μ…˜ 관리
      • 09.도메인 λͺ¨λΈκ³Ό λ°”μš΄λ””λ“œ μ»¨ν…μŠ€νŠΈ
      • 10.이벀트
      • 11.CQRS
    • Effective Java 3/E
      • 객체, 곡톡 λ©”μ„œλ“œ
      • 클래슀, μΈν„°νŽ˜μ΄μŠ€, μ œλ„€λ¦­
    • μ†Œν”„νŠΈμ›¨μ–΄ μž₯인
    • ν•¨κ»˜ 자라기
    • Modern Java In Action
      • 01.기초
      • 02.ν•¨μˆ˜ν˜• 데이터 처리
      • 03.슀트림과 λžŒλ‹€λ₯Ό μ΄μš©ν•œ 효과적 ν”„λ‘œκ·Έλž˜λ°
      • 04.맀일 μžλ°”μ™€ ν•¨κ»˜
    • Refactoring
      • 01.λ¦¬νŽ™ν„°λ§ 첫 번째 μ˜ˆμ‹œ
      • 02.λ¦¬νŽ™ν„°λ§ 원칙
      • 03.μ½”λ“œμ—μ„œ λ‚˜λŠ” μ•…μ·¨
      • 06.기본적인 λ¦¬νŽ™ν„°λ§
      • 07.μΊ‘μŠν™”
      • 08.κΈ°λŠ₯ 이동
      • 09.데이터 쑰직화
      • 10.쑰건뢀 둜직 κ°„μ†Œν™”
      • 11.API λ¦¬νŒ©ν„°λ§
      • 12.상속 닀루기
    • 객체지ν–₯의 사싀과 μ˜€ν•΄
      • 01.ν˜‘λ ₯ν•˜λŠ” κ°μ²΄λ“€μ˜ 곡동체
      • 02.μ΄μƒν•œ λ‚˜λΌμ˜ 객체
      • 03.νƒ€μž…κ³Ό 좔상화
      • 04.μ—­ν• , μ±…μž„, ν˜‘λ ₯
      • 05.μ±…μž„κ³Ό λ©”μ‹œμ§€
      • 06.객체 지도
      • 07.ν•¨κ»˜ λͺ¨μœΌκΈ°
      • 뢀둝.좔상화 기법
    • Clean Code
    • μžλ°” ORM ν‘œμ€€ JPA ν”„λ‘œκ·Έλž˜λ°
Powered by GitBook
On this page
  • Java 8
  • Functional Interface & Lambda
  • Functional Interface
  • Lambda
  • Method Reference
  • Interface
  • Stream
  • Optional
  • Date & Time API
  • CompletableFuture
  • Java Concurrency
  • Executors
  • Callable & Future
  • CompletableFuture
  • Etc..
  • JVM
  • Bytecode Operation
  • Reflection
  • Dynamic Proxy
  • Annotation processor
  1. Lecture
  2. Java

The Java

Last updated 1 year ago

λ°±κΈ°μ„ λ‹˜μ˜ , κ°•μ˜λ₯Ό μš”μ•½ν•œ λ‚΄μš©μž…λ‹ˆλ‹€.

Java 8

Java 8

  • LTS(Long-Term-Support) 버전

  • μΆœμ‹œμΌ: 2014λ…„ 3μ›”

  • μ£Όμš” κΈ°λŠ₯

    • λžŒλ‹€ ν‘œν˜„μ‹, λ©”μ†Œλ“œ 레퍼런슀, 슀트림 API, Optional ...

  • Open JDK: Oracle, AdoptOpenJDK, Amazon Corretto, Azul Zulu

  • μ°Έκ³ .

.

LTS(Long-Term-Support)

  • λΉ„-LTS

    • 배포 μ£ΌκΈ°: 6κ°œμ›”

    • 지원 κΈ°κ°„: 배포 이후 6κ°œμ›”(λ‹€μŒ 버전이 λ‚˜μ˜€λ©΄ 지원 μ’…λ£Œ)

  • LTS

    • 배포 μ£ΌκΈ°: 3λ…„(λ§€ 6번째 배포판이 LTS)

    • 지원 κΈ°κ°„: 5년이상(JDK 제곡 밴더와 μ΄μš©ν•˜λŠ” μ„œλΉ„μŠ€μ— 따라 차이)

    • μ‹€μ œ μ„œλΉ„μŠ€ 운영 ν™˜κ²½μ—μ„œλŠ” LTS 버전 ꢌμž₯

  • λ§€λ…„ 3μ›”κ³Ό 9월에 μƒˆ 버전 배포

Functional Interface & Lambda

ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€(Functional Interface)

@FunctionalInterface
public interface RunSomething {
    void doIt();
}
  • 좔상 λ©”μ†Œλ“œλ₯Ό λ”± ν•˜λ‚˜λ§Œ κ°€μ§€κ³  μžˆλŠ” μΈν„°νŽ˜μ΄μŠ€

  • SAM(Single Abstract Method) μΈν„°νŽ˜μ΄μŠ€

  • @FuncationInterface μ• λ…Έν…Œμ΄μ…˜μ„ κ°€μ§€κ³  μžˆλŠ” μΈν„°νŽ˜μ΄μŠ€

.

λžŒλ‹€ ν‘œν˜„μ‹(Lambda Expressions)

RunSomething runSomething = () -> System.out.println("Hello");
RunSomething2 runSomething2 = number -> number + 10;

runSomething.doIt();
runSomething2.doIt();
  • κ°„κ²°ν•œ μ½”λ“œ

  • ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€μ˜ μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“œλŠ” λ°©λ²•μœΌλ‘œ μ‚¬μš© κ°€λŠ₯

  • λ©”μ†Œλ“œ λ§€κ°œλ³€μˆ˜, 리턴 νƒ€μž…, λ³€μˆ˜λ‘œ λ§Œλ“€μ–΄ μ‚¬μš© κ°€λŠ₯

.

μžλ°”μ—μ„œ ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°

  • ν•¨μˆ˜λ₯Ό First class object둜 μ‚¬μš© κ°€λŠ₯

  • 순수 ν•¨μˆ˜(Pure function)

    • μ‚¬μ΄λ“œ 이팩트 μ—†μŒ(ν•¨μˆ˜ 밖에 μžˆλŠ” 값을 λ³€κ²½ν•˜μ§€ μ•ŠμŒ)

    • μƒνƒœκ°€ μ—†μŒ(ν•¨μˆ˜ 밖에 μžˆλŠ” 값을 μ‚¬μš©ν•˜μ§€ μ•ŠμŒ)

  • κ³ μ°¨ ν•¨μˆ˜(Higher-Order Function)

    • ν•¨μˆ˜κ°€ ν•¨μˆ˜λ₯Ό λ§€κ°œλ³€μˆ˜λ‘œ 받을 수 있고, ν•¨μˆ˜ 리턴 κ°€λŠ₯

  • λΆˆλ³€μ„±

Functional Interface

Java κΈ°λ³Έ 제곡 ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€

Function<T, R>

  • T νƒ€μž…μ„ λ°›μ•„μ„œ R νƒ€μž…μ„ λ¦¬ν„΄ν•˜λŠ” ν•¨μˆ˜ μΈν„°νŽ˜μ΄μŠ€

private final Function<Integer, Integer> plus10 = (num) -> num + 10;
  private final Function<Integer, Integer> multiply2 = (num) -> num * 2;

  @Test
  void apply() throws Exception {
      /**
       * R apply(T t)
       */
      Assertions.assertEquals(11, plus10.apply(1));
  }

  @Test
  void compose() throws Exception {
      /**
       * Function<V, R> compose(Function<? super V, ? extends T> before)
       * multiply2 μ‹€ν–‰ 이후 plus10 μ‹€ν–‰
       */
      Function<Integer, Integer> multiply2AndPlus10 = plus10.compose(multiply2);
      Assertions.assertEquals(14, multiply2AndPlus10.apply(2)); // (num * 2) + 10
  }

  @Test
  void andThen() throws Exception {
      /**
       * Function<T, V> andThen(Function<? super R, ? extends V> after)
       * plus10 μ‹€ν–‰ 이후 multiply2 μ‹€ν–‰
       */
      Function<Integer, Integer> plus10AndMultiply2 = plus10.andThen(multiply2);
      Assertions.assertEquals(24, plus10AndMultiply2.apply(2)); // (num + 10) * 2
  }

BiFunction<T, U, R>

  • 두 개의 κ°’(T, U)λ₯Ό λ°›μ•„μ„œ R νƒ€μž…μ„ λ¦¬ν„΄ν•˜λŠ” ν•¨μˆ˜ μΈν„°νŽ˜μ΄μŠ€

  • R apply(T t, U u)

@Test
void apply() throws Exception {
    /**
     * R apply(T t, U u);
     */
    BiFunction<Integer, Integer, Integer> add = (num1, num2) ->  num1 + num2;
    BiFunction<Integer, Integer, Integer> minus = (num1, num2) -> num1 - num2;
    BiFunction<Integer, Integer, Integer> multiple = (num1, num2) -> num1 * num2;

    Assertions.assertEquals(15, add.apply(10, 5));
    Assertions.assertEquals(5, minus.apply(10, 5));
    Assertions.assertEquals(50, multiple.apply(10, 5));
}

Consumer<T>

  • T νƒ€μž…μ„ λ°›μ•„μ„œ 아무값도 λ¦¬ν„΄ν•˜μ§€ μ•ŠλŠ” ν•¨μˆ˜ μΈν„°νŽ˜μ΄μŠ€

@Test
void accept() throws Exception {
    /**
     * void accept(T t);
     */
    Consumer<Integer> printT = System.out::println;
    printT.accept(10); // 10
}

@Test
void andThen() throws Exception {
    /**
     * Consumer<T> andThen(Consumer<? super T> after)
     */
    Consumer<String> printJava = s -> System.out.println(s + "Java ");
    Consumer<String> printWorld = s -> System.out.println(s + "World ");;
    printJava.andThen(printWorld).accept("Hello"); // HelloJava -> HelloWorld
}

Supplier<T>

  • T νƒ€μž…μ˜ 값을 μ œκ³΅ν•˜λŠ” ν•¨μˆ˜ μΈν„°νŽ˜μ΄μŠ€

@Test
void get() throws Exception {
    /**
     * T get()
     */
    Supplier<Integer> get10 = () -> 10;
    Assertions.assertEquals(10, get10.get());
}

Predicate<T>

  • T νƒ€μž…μ„ λ°›μ•„μ„œ boolean을 λ¦¬ν„΄ν•˜λŠ” ν•¨μˆ˜ μΈν„°νŽ˜μ΄μŠ€

  • ν•¨μˆ˜ μ‘°ν•©μš© λ©”μ†Œλ“œ

private final Predicate<Integer> isEven = i -> i % 2 == 0;
private final Predicate<Integer> under10 = i -> i < 10;

@Test
void test() throws Exception {
    /**
     * boolean test(T t);
     */
    Predicate<String> startsWithHello = s -> s.startsWith("hello");

    Assertions.assertTrue(startsWithHello.test("hello Aaron"));
    Assertions.assertTrue(isEven.test(8));
}

@Test
void and() throws Exception {
    /**
     * Predicate<T> and(Predicate<? super T> other)
     */
    Assertions.assertTrue(isEven.and(under10).test(4));
    Assertions.assertFalse(isEven.and(under10).test(12));
}

@Test
void or() throws Exception {
    /**
     * Predicate<T> or(Predicate<? super T> other)
     */
    Assertions.assertTrue(isEven.or(under10).test(4));
    Assertions.assertTrue(isEven.or(under10).test(12));
    Assertions.assertTrue(isEven.or(under10).test(7));
}

@Test
void negate() throws Exception {
    /**
     * Predicate<T> negate()
     */
    Assertions.assertTrue(isEven.negate().test(5));
    Assertions.assertTrue(under10.negate().test(17));
    Assertions.assertFalse(isEven.negate().test(4));
    Assertions.assertFalse(under10.negate().test(5));
}

UnaryOperator<T>

  • Function<T, R>의 νŠΉμˆ˜ν•œ ν˜•νƒœ(Function 상속)

  • μž…λ ₯κ°’ ν•˜λ‚˜λ₯Ό λ°›μ•„μ„œ λ™μΌν•œ νƒ€μž…μ„ λ¦¬ν„΄ν•˜λŠ” ν•¨μˆ˜ μΈν„°νŽ˜μ΄μŠ€

private final UnaryOperator<Integer> plus10 = (num) -> num + 10;
private final UnaryOperator<Integer> multiply2 = (num) -> num * 2;

@Test
void test() throws Exception {
    Assertions.assertEquals(11, plus10.apply(1));
    Assertions.assertEquals(14, plus10.compose(multiply2).apply(2)); // (num * 2) + 10
    Assertions.assertEquals(24, plus10.andThen(multiply2).apply(2)); // (num + 10) * 2
}

BinaryOperator<T>

  • BiFunction<T, U, R>의 νŠΉμˆ˜ν•œ ν˜•νƒœ

  • λ™μΌν•œ νƒ€μž…μ˜ μž…λ ΅κ°’ 두 개λ₯Ό λ°›μ•„ λ¦¬ν„΄ν•˜λŠ” ν•¨μˆ˜ μΈν„°νŽ˜μ΄μŠ€

@Test
void apply() throws Exception {
    /**
     * R apply(T t, U u);
     */
    BinaryOperator<Integer> add = (num1, num2) ->  num1 + num2;
    BinaryOperator<Integer> minus = (num1, num2) -> num1 - num2;
    BinaryOperator<Integer> multiple = (num1, num2) -> num1 * num2;

    Assertions.assertEquals(15, add.apply(10, 5));
    Assertions.assertEquals(5, minus.apply(10, 5));
    Assertions.assertEquals(50, multiple.apply(10, 5));
}

Lambda

(인자 리슀트) -> {λ°”λ””}

.

인자 리슀트

  • 인자 μ—†μŒ: ()

  • μΈμžκ°€ ν•œ 개: (one) λ˜λŠ” one

  • μΈμžκ°€ μ—¬λŸ¬ 개: (one, two)

  • 인자의 νƒ€μž…μ€ μƒλž΅ κ°€λŠ₯(μ»΄νŒŒμΌλŸ¬κ°€ μΆ”λ‘ ν•˜μ§€λ§Œ λͺ…μ‹œλ„ κ°€λŠ₯)

.

λ°”λ””

  • ν™”μ‚΄ν‘œ 였λ₯Έμͺ½μ— ν•¨μˆ˜ λ³Έλ¬Έ μ •μ˜

  • μ—¬λŸ¬ 쀄인 경우 {} μ‚¬μš©

  • ν•œ 쀄인 경우 λ°”λ””, return μƒλž΅ κ°€λŠ₯

.

λ³€μˆ˜ 캑처(Variable Capture)

  • 둜컬 λ³€μˆ˜ 캑처

    • final, effective final 인 κ²½μš°μ—λ§Œ μ°Έμ‘° κ°€λŠ₯

    • κ·Έλ ‡μ§€ μ•Šμ„ 경우, concurrency λ¬Έμ œκ°€ λ°œμƒν•  수 μžˆμ–΄μ„œ μ»΄νŒŒμΌλŸ¬κ°€ λ°©μ§€

  • effective final

    • μžλ°” 8λΆ€ν„° μ§€μ›ν•˜λŠ” κΈ°λŠ₯

    • final ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šμ§€λ§Œ, 변경이 μ—†λŠ” λ³€μˆ˜λ₯Ό 읡λͺ… 클래슀 κ΅¬ν˜„μ²΄, λžŒλ‹€μ—μ„œ μ°Έμ‘° κ°€λŠ₯

    • 읡λͺ… ν΄λž˜μŠ€λŠ” μƒˆλ‘œμš΄ 슀μ½₯을 λ§Œλ“€μ§€λ§Œ, λžŒλ‹€λŠ” λžŒλ‹€λ₯Ό 감싸고 μžˆλŠ” 슀μ½₯κ³Ό κ°™μŒ

    • λžŒλ‹€λ₯Ό 감싼 슀μ½₯에 μžˆλŠ” λ™μΌν•œ μ΄λ¦„μ˜ λ³€μˆ˜ μ •μ˜ λΆˆκ°€

Method Reference

@Test
void static_method_reference() throws Exception {
    // static method reference(Type::static-method)
    UnaryOperator<String> hi = Greeting::hi;
    assertEquals("hi aaron", hi.apply("aaron"));
}

@Test
void random_object_instance_method_reference() throws Exception {
    String[] names = {"ccc", "aaa", "bbb"};
    // random object instance method reference(Type::instance-method)
    Arrays.sort(names, String::compareToIgnoreCase);
    assertEquals("[aaa, bbb, ccc]", Arrays.toString(names));
}

@Test
void no_arg_constructor_reference() throws Exception {
    // no arg constructor reference(Type::new)
    Supplier<Greeting> greetingSupplier = Greeting::new;
    Greeting greeting = greetingSupplier.get();
    // specific object instance method reference(Object-reference::instance-method)
    UnaryOperator<String> hello = greeting::hello;

    assertEquals("Hello Aaron", hello.apply("Aaron"));
}

@Test
void AllArgsConstructor() throws Exception {
    // arg constructor reference(Type::new)
    Function<String, Greeting> greetingFunction = Greeting::new;
    Greeting greeting = greetingFunction.apply("aaron");
    assertEquals("aaron", greeting.getName());
}

@Getter
@NoArgsConstructor
@AllArgsConstructor
private class Greeting {
    private String name;

    public String hello(String name) {
        return "Hello " + name;
    }

    public static String hi(String name) {
        return "hi " + name;
    }
}

Interface

Collection interface

/**
 * Removes all of the elements of this collection that satisfy the given
 * predicate...
 *
 * @implSpec
 * The default implementation traverses all elements of the collection using
 * its {@link #iterator}.  Each matching element is removed using
 * {@link Iterator#remove()}.  If the collection's iterator does not
 * support removal then an {@code UnsupportedOperationException} will be
 * thrown on the first matching element.
 *
 * ...
 */
default boolean removeIf(Predicate<? super E> filter) {
    Objects.requireNonNull(filter);
    boolean removed = false;
    final Iterator<E> each = iterator();
    while (each.hasNext()) {
        if (filter.test(each.next())) {
            each.remove();
            removed = true;
        }
    }
    return removed;
}
  • μΈν„°νŽ˜μ΄μŠ€μ— λ©”μ†Œλ“œ 선언이 μ•„λ‹ˆλΌ κ΅¬ν˜„μ²΄λ₯Ό μ œκ³΅ν•˜λŠ” 방법

  • κ΅¬ν˜„ 클래슀λ₯Ό κΉ¨λœ¨λ¦¬μ§€ μ•Šκ³  μƒˆ κΈ°λŠ₯ μΆ”κ°€ κ°€λŠ₯

  • Default Methods λŠ” κ΅¬ν˜„μ²΄ λͺ¨λ₯΄κ²Œ μΆ”κ°€λœ κΈ°λŠ₯으둜 리슀크 쑴재

    • 컴파일 μ—λŸ¬λŠ” μ•„λ‹ˆμ§€λ§Œ κ΅¬ν˜„μ²΄μ— 따라 λŸ°νƒ€μž„ μ—λŸ¬(ex. NPE) λ°œμƒ κ°€λŠ₯

    • λ°˜λ“œμ‹œ λ¬Έμ„œν™” ν•„μš”(@implSpec μ‚¬μš©)

    • ν•„μš” μ‹œ κ΅¬ν˜„μ²΄κ°€ μž¬μ •μ˜

  • Object κ°€ μ œκ³΅ν•˜λŠ” κΈ°λŠ₯(equals, hasCode)은 κΈ°λ³Έ λ©”μ†Œλ“œλ‘œ 제곡 λΆˆκ°€

    • κ΅¬ν˜„μ²΄κ°€ μž¬μ •μ˜

  • 본인이 μˆ˜μ •ν•  수 μžˆλŠ” μΈν„°νŽ˜μ΄μŠ€μ—λ§Œ κΈ°λ³Έ λ©”μ†Œλ“œ 제곡 κ°€λŠ₯

  • μΈν„°νŽ˜μ΄μŠ€λ₯Ό μƒμ†λ°›λŠ” μΈν„°νŽ˜μ΄μŠ€μ—μ„œ κΈ°λ³Έ λ©”μ†Œλ“œλ₯Ό λ‹€μ‹œ 좔상 λ©”μ†Œλ“œλ‘œ λ³€κ²½ κ°€λŠ₯

  • κΈ°λ³Έ λ©”μ†Œλ“œκ°€ μΆ©λ™ν•˜λŠ” 경우 직접 μ˜€λ²„λΌμ΄λ”© ν•„μš”

.

Static Method

  • ν•΄λ‹Ή νƒ€μž… κ΄€λ ¨ 헬퍼, μœ ν‹Έλ¦¬ν‹° λ©”μ†Œλ“œ 제곡 μ‹œ 유용

.

Java 8 Default Methods

private static final List<String> name = List.of("park", "aaron", "keesun", "whiteship");

@Test
void forEach() throws Exception {
    /**
     * default void forEach(Consumer<? super T> action)
     * - λͺ¨λ“  μš”μ†Œκ°€ μ²˜λ¦¬λ˜κ±°λ‚˜ μ˜ˆμ™Έκ°€ λ°œμƒν•  λ•ŒκΉŒμ§€ Iterable 각 μš”μ†Œμ— λŒ€ν•΄ μ§€μ •λœ μž‘μ—… μˆ˜ν–‰
     */
    name.forEach(System.out::println);
}

@Test
void spliterator() throws Exception {
    /**
     * default Spliterator<E> spliterator()
     * - Creates a Spliterator over the elements described by this Iterable.
     */
    Spliterator<String> spliterator1 = name.spliterator();
    Spliterator<String> spliterator2 = spliterator1.trySplit();
    while(spliterator1.tryAdvance(System.out::println)); // keesun, whiteship
    while(spliterator2.tryAdvance(System.out::println)); // park, aaron
}
  • parallelStream(), spliterator()

private List<String> name = new ArrayList<>();

@BeforeEach
void beforeEach() {
    name.add("park");
    name.add("aaron");
    name.add("keesun");
    name.add("whiteship");
}

@Test
void stream() throws Exception {
    /**
     * default Stream<E> stream()
     */
    long count = name.stream()
            .map(String::toUpperCase)
            .filter(s -> s.startsWith("A"))
            .count();

    Assertions.assertEquals(1, count);
}

@Test
void removeIf() throws Exception {
    /**
     * default Stream<E> stream()
     */
    name.removeIf(s -> s.startsWith("w"));
    Assertions.assertEquals(3, name.size());
}
  • thenComparing()

  • static reverseOrder() / naturalOrder()

  • static nullsFirst() / nullsLast()

  • static comparing()

private List<String> name = new ArrayList<>();

@BeforeEach
void beforeEach() {
    name.add("park");
    name.add("aaron");
    name.add("keesun");
    name.add("whiteship");
}

@Test
void sort() throws Exception {
    /**
     * default void sort(Comparator<? super E> c)
     */
    // μˆœμ°¨μ •λ ¬
    name.sort(String::compareToIgnoreCase);

    // μ—­μˆœμ •λ ¬
    Comparator<String> compareToIgnoreCase = String::compareToIgnoreCase;
    name.sort(compareToIgnoreCase.reversed());
}
  • forEachRemaining(Consumer)

  • getExactSizeIfKnown()

  • hasCharacteristics()

  • getComparator()

Stream

  • 데이터λ₯Ό λ‹΄κ³  μžˆλŠ” μ €μž₯μ†Œ(μ»¬λ ‰μ…˜)κ°€ μ•„λ‹ˆλΌ, μ–΄λ– ν•œ μ—°μ†λœ 데이터λ₯Ό μ²˜λ¦¬ν•˜λŠ” μ˜€νΌλ ˆμ΄μ…˜λ“€μ˜ λͺ¨μŒ

  • 슀트림 처리 μ‹œ 데이터 원본은 λ³€κ²½ν•˜μ§€ μ•ŠμŒ

  • 슀트림으둜 μ²˜λ¦¬ν•˜λŠ” λ°μ΄ν„°λŠ” 였직 ν•œ 번만 처리

  • μ‹€μ‹œκ°„μœΌλ‘œ 슀트림 데이터가 λ“€μ–΄μ˜¬ 경우 λ¬΄ν•œ 처리(Short Circuit λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•΄μ„œ μ œν•œ κ°€λŠ₯)

  • μ€‘κ°œ μ˜€νΌλ ˆμ΄μ…˜μ€ 근본적으둜 lazy νŠΉμ„±μ„ 가짐

  • 데이터가 λ°©λŒ€ν•œ 경우 parallelStream() 으둜 μ†μ‰½κ²Œ 병렬 처리 κ°€λŠ₯

    • μŠ€λ ˆλ“œ 생성, λ³‘λ ¬μ²˜λ¦¬ ν›„ μˆ˜μ§‘, μŠ€λ ˆλ“œ κ°„ μ»¨ν…μŠ€νŠΈ μŠ€μœ„μΉ­ λ“±μ˜ λΉ„μš©μœΌλ‘œ 무쑰건 λΉ¨λΌμ§€λŠ” 건 μ•„λ‹˜

슀트림 νŒŒμ΄ν”„λΌμΈ

  • 0 λ˜λŠ” λ‹€μˆ˜μ˜ μ€‘κ°œ μ˜€νΌλ ˆμ΄μ…˜κ³Ό ν•œ 개의 μ’…λ£Œ μ˜€νΌλ ˆμ΄μ…˜μœΌλ‘œ ꡬ성

  • 슀트림의 데이터 μ†ŒμŠ€λŠ” 였직 터미널 μ˜€νΌλ„€μ΄μ…˜μ„ μ‹€ν–‰ν•  λ•Œμ—λ§Œ 처리

μ€‘κ°œ μ˜€νΌλ ˆμ΄μ…˜(intermediate operation)

  • Stream 리턴

  • Stateless / Stateful μ˜€νΌλ ˆμ΄μ…˜μœΌλ‘œ 더 μƒμ„Έν•˜κ²Œ ꡬ뢄 κ°€λŠ₯

    • λŒ€λΆ€λΆ„ Stateless operation

    • 이전 μ†ŒμŠ€ 데이터λ₯Ό μ°Έμ‘°ν•΄μ•Ό ν•˜λŠ” μ˜€νΌλ ˆμ΄μ…˜(ex. distinct, sorted)은 Stateful μ˜€νΌλ ˆμ΄μ…˜

  • filter, map, limit, skip, sorted ...

μ’…λ£Œ μ˜€νΌλ ˆμ΄μ…˜(terminal operation)

  • Stream 리턴 X

  • collect, allMatch, count, forEach, min, max ...

.

Stream API

필터링

@Test
@DisplayName("spring 으둜 μ‹œμž‘ν•˜λŠ” μˆ˜μ—…")
void test01() {
    /**
     * Stream<T> filter(Predicate<? super T> predicate);
     */
    List<OnlineClass> springClass = springClasses.stream()
            .filter(oc -> oc.getTitle().startsWith("spring"))
            .collect(Collectors.toList());
    Assertions.assertEquals(5, springClass.size());
}

슀트림 λ³€κ²½

@Test
@DisplayName("μˆ˜μ—… μ΄λ¦„λ§Œ λͺ¨μ•„μ„œ 슀트림 λ§Œλ“€κΈ°")
void test03() {
    /**
     * <R> Stream<R> map(Function<? super T, ? extends R> mapper);
     */
    springClasses.stream()
            .map(OnlineClass::getTitle)
            .forEach(System.out::println);
}

...

@Test
@DisplayName("두 μˆ˜μ—… λͺ©λ‘μ— λ“€μ–΄ μžˆλŠ” λͺ¨λ“  μˆ˜μ—… 아이디")
void test04() {
    /**
     * <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
     */
    List<OnlineClass> allClasses = aaronEvents.stream()
            .flatMap(Collection::stream)
            .collect(Collectors.toList());

    Assertions.assertEquals(8, allClasses.size());
}

슀트림 생성과 μ œν•œ

@Test
@DisplayName("10λΆ€ν„° 1μ”© μ¦κ°€ν•˜λŠ” λ¬΄μ œν•œ 슀트림 μ€‘μ—μ„œ μ•žμ— 10개 λΉΌκ³  μ΅œλŒ€ 10개 κΉŒμ§€λ§Œ")
void test05() {
    /**
     * public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
     * Stream<T> skip(long n);
     * Stream<T> limit(long maxSize);
     * long count();
     */
    long count = Stream.iterate(10, i -> i + 1)
            .skip(10)
            .limit(10)
            .count();
    Assertions.assertEquals(10, count);
}

μŠ€νŠΈλ¦Όμ— μžˆλŠ” 데이터가 νŠΉμ • 쑰건을 λ§Œμ‘±ν•˜λŠ”μ§€ 확인

@Test
@DisplayName("μžλ°” μˆ˜μ—… 쀑 Testκ°€ λ“€μ–΄ μžˆλŠ” μˆ˜μ—…μ΄ μžˆλŠ”μ§€ 확인")
void test06() {
    /**
     * boolean anyMatch(Predicate<? super T> predicate);
     * boolean allMatch(Predicate<? super T> predicate);
     * boolean noneMatch(Predicate<? super T> predicate);
     */
    boolean result = javaClasses.stream()
            .anyMatch(oc -> oc.getTitle().contains("Test"));
    Assertions.assertTrue(result);
}

μŠ€νŠΈλ¦Όμ„ 데이터 ν•˜λ‚˜λ‘œ λ­‰μΉ˜κΈ°

  • reduce(identity, BiFunction), collect(), sum(), max()

Optional

OptionalInt.of(10);
Optional.empty();
Optional.ofNullable(progress);

NullPointerException 을 λ§Œλ‚˜λŠ” 이유

  • null 을 λ¦¬ν„΄ν•˜κ³ , null 체크λ₯Ό λ†“μΉ˜κΈ° λ–„λ¬Έ

λ©”μ†Œλ“œμ—μ„œ μž‘μ—… 쀑 νŠΉλ³„ν•œ μƒν™©μ—μ„œ 값을 μ œλŒ€λ‘œ 리턴할 수 μ—†λŠ” 경우 선택할 수 μžˆλŠ” 방법

  • μ˜ˆμ™Έ λ˜μ§„κΈ° (μŠ€νƒνŠΈλ ˆμ΄μŠ€λ₯Ό μ°μ–΄λ‹€λ³΄λ‹ˆ λΉ„μ‹Ό λΉ„μš© λ°œμƒ)

  • null 리턴 (ν΄λΌμ΄μ–ΈνŠΈμͺ½μ—μ„œ null 처리 ν•„μš”)

  • Optional 리턴 (ν΄λΌμ΄μ–ΈνŠΈμ—κ²Œ λͺ…μ‹œμ μœΌλ‘œ 빈 값일 μˆ˜λ„ μžˆλ‹€λŠ” 것을 μ „λ‹¬ν•˜κ³ , 빈 값인 κ²½μš°μ— λŒ€ν•œ 처리λ₯Ό κ°•μ œ)

Optional

  • ν•œ 개의 값이 λ“€μ–΄μžˆμ„ μˆ˜λ„ 없을 μˆ˜λ„ μžˆλŠ” μ»¨λ„€μ΄λ„ˆ

주의점

  • λ¦¬ν„΄κ°’μœΌλ‘œλ§Œ μ‚¬μš© ꢌμž₯

    • λ©”μ†Œλ“œ λ§€κ°œλ³€μˆ˜ νƒ€μž…μœΌλ‘œ μ‚¬μš© μ‹œ, 번거둭게 null + optional 체크 ν•„μš”

    • 맡의 ν‚€ νƒ€μž…μœΌλ‘œ μ‚¬μš© μ‹œ, 맡의 ν‚€κ°€ 없을 μˆ˜λ„ μžˆλ‹€λŠ” μœ„ν—˜ 제곡

    • μΈμŠ€ν„΄μŠ€ ν•„λ“œ νƒ€μž…μœΌλ‘œ μ‚¬μš© μ‹œ, ν•„λ“œκ°€ 없을 μˆ˜λ„ μžˆλ‹€λŠ” μœ„ν—˜ 제곡

  • null λŒ€μ‹  Optional.empty() 리턴 ꢌμž₯

  • Primitive Type Optional 제곡

    • λ°•μ‹±, μ–Έλ°•μ‹± λ°œμƒμ„ λ°©μ§€ν•˜κ³ , μ„±λŠ₯ ν–₯상을 μœ„ν•΄ μ‚¬μš© ꢌμž₯

    • OptionalInt, OptionalLong ...

  • Collection, Map, Stream Array, Optional은 Opiontal 둜 두 번 감싸지 μ•ŠκΈ°

.

Optional API

Optional 생성

Optional.of()
Optional.ofNullable()
Optional.empty()

Optional κ°’ 포함 μ—¬λΆ€ 확인

optional.isPresent()
optional.isEmpty() // Java 11 이후

Optional κ°’ κ°€μ Έμ˜€κΈ°

optional.get(); // λΉ„μ–΄μžˆμ„ 경우 NoSuchElementException μ˜ˆμ™Έ λ°œμƒ

Optional 에 값이 μ‘΄μž¬ν•  경우 λ™μž‘ μˆ˜ν–‰

optional.ifPresent(oc -> System.out.println(oc.getTitle()));

Optional 에 값이 μžˆμ„ 경우 κΊΌλ‚΄κ³ , 무쑰건 μƒˆλ‘œμš΄ 클래슀 생성

optional.orElseGet(OptionalTest::createNewClass);

Optional 에 값이 μžˆμ„ 경우 κΊΌλ‚΄κ³ , μ—†μœΌλ©΄ μƒˆλ‘œμš΄ 클래슀 제곡

result.orElseGet(OptionalTest::createNewClass);

Optional 에 값이 μžˆμ„ 경우 κΊΌλ‚΄κ³ , μ—†μœΌλ©΄ μ˜ˆμ™Έ

assertThrows(NoSuchElementException.class, () -> {
    result.orElseThrow();
});

assertThrows(IllegalStateException.class, () -> {
    result.orElseThrow(IllegalStateException::new);
});

Optional 값을 필터링

Optional<OnlineClass> jpaClass = result.filter(Predicate.not(OnlineClass::isClosed));

Optional 값을 λ§€ν•‘(λ³€ν™˜)

Optional<Integer> jpaClassId = result.map(OnlineClass::getId);
  • flatMap(Function): Optional μ•ˆμ— λ“€μ–΄μžˆλŠ” μΈμŠ€ν„΄μŠ€κ°€ Optional 인 경우 편리

Date & Time API

java 8 에 μƒˆλ‘œμš΄ λ‚ μ§œ/μ‹œκ°„ API κ°€ 생긴 이유

  • κ·Έ μ „κΉŒμ§€ μ‚¬μš©ν•˜λ˜ java.util.Date ν΄λž˜μŠ€λŠ” mutable ν•˜κΈ° λ•Œλ¬Έμ— thead safe ν•˜μ§€ μ•ŠμŒ

  • 클래슀 이름이 λͺ…ν™•ν•˜μ§€ μ•ŠμŒ(Date 인데 μ‹œκ°„κΉŒμ§€ λ‹€λ£¨λŠ” λ“±..)

  • 버그가 λ°œμƒν•  μ—¬μ§€κ°€ 많음(νƒ€μž… μ•ˆμ •μ„±μ΄ μ—†κ³ , 월이 0λΆ€ν„° μ‹œμž‘ν•˜λŠ” λ“±..)

java 8 μ—μ„œ μ œκ³΅ν•˜λŠ” Date-Time API

    • Clear: λ™μž‘μ΄ λͺ…ν™•ν•˜κ³  μ˜ˆμƒ κ°€λŠ₯

    • Fluent: μœ μ—°ν•œ μΈν„°νŽ˜μ΄μŠ€ 제곡. λ©”μ†Œλ“œ ν˜ΈμΆœμ„ μ—°κ²°ν•˜μ—¬ 간결함 제곡

    • Immutable: λΆˆλ³€ 객체 생성, thead safe

    • Extensible: ν™•μž₯ κ°€λŠ₯

μ£Όμš” API

  • κΈ°κ³„μš© μ‹œκ°„(machine time)κ³Ό 인λ₯˜μš© μ‹œκ°„(human time)으둜 ꡬ뢄

  • κΈ°κ³„μš© μ‹œκ°„

    • EPOCK(1970λ…„ 1μ›” 1일 0μ‹œ 0λΆ„ 0초)λΆ€ν„° ν˜„μž¬κΉŒμ§€μ˜ νƒ€μž„μŠ€νƒ¬ν”„λ₯Ό ν‘œν˜„

    • νƒ€μž„μŠ€νƒ¬ν”„λŠ” Instant μ‚¬μš©

  • 인λ₯˜μš© μ‹œκ°„

    • μš°λ¦¬κ°€ ν”νžˆ μ‚¬μš©ν•˜λŠ” μ—°,μ›”,일,μ‹œ,λΆ„,초 등을 ν‘œν˜„

    • νŠΉμ • λ‚ μ§œ(LocalDate), μ‹œκ°„(LocalTime), μΌμ‹œ(LocalDateTime) μ‚¬μš© κ°€λŠ₯

    • 기간을 ν‘œν˜„ν•  λ•ŒλŠ” Duration(μ‹œκ°„ 기반)κ³Ό Period(λ‚ μ§œ 기반) μ‚¬μš© κ°€λŠ₯

    • DateTimeFormatter λ₯Ό μ‚¬μš©ν•΄μ„œ μΌμ‹œλ₯Ό νŠΉμ •ν•œ λ¬Έμžμ—΄λ‘œ ν¬λ§€νŒ… κ°€λŠ₯

μ°Έκ³ 

.

κΈ°κ³„μš© μ‹œκ°„(machine time) ν‘œν˜„

  • UTC(Universal Time Coordinated) == GMT(Greenwich Mean Time)

  • 보톡 μ‹œκ°„μ„ μž¬λŠ” 경우 μ‚¬μš©

Instant instant = Instant.now();
System.out.println(instant); // 2023-09-30T12:44:46.452980Z
System.out.println(instant.atZone(ZoneId.of("UTC"))); // 2023-09-30T12:44:46.452980Z[UTC]
System.out.println(instant.atZone(ZoneId.of("GMT"))); // 2023-09-30T12:45:17.336132Z[GMT]

ZoneId zone = ZoneId.systemDefault();
ZonedDateTime zonedDateTime = instant.atZone(zone);
System.out.println(zone); // Asia/Seoul
System.out.println(zonedDateTime); // 2023-09-30T21:44:46.452980+09:00[Asia/Seoul]

인λ₯˜μš© μ‹œκ°„(human time) ν‘œν˜„

  • LocalDateTime.of(int, Month, int, int, int, int): 둜컬 νŠΉμ • μΌμ‹œ

  • ZonedDateTime.of(int, Month, int, int, int, int, ZoneId): νŠΉμ • Zone 의 νŠΉμ • μΌμ‹œ

LocalDateTime now = LocalDateTime.now(); // ν˜„μž¬ μ‹œμŠ€ν…œ Zone μΌμ‹œ
System.out.println(now); // 2023-09-30T21:57:26.029797

LocalDateTime today = LocalDateTime.of(20023, Month.SEPTEMBER, 30, 0, 0, 0, 0);
System.out.println(today); // +20023-09-30T00:00

ZonedDateTime nowInLosAngeles = ZonedDateTime.now(ZoneId.of("America/Los_Angeles"));
System.out.println(nowInLosAngeles); // 2023-09-30T05:57:26.033318-07:00[America/Los_Angeles]

Instant instant = Instant.now();
ZonedDateTime zonedDateTime = instant.atZone(ZoneId.of("America/Los_Angeles"));
System.out.println(zonedDateTime); // 2023-09-30T05:57:26.034100-07:00[America/Los_Angeles]

λ‚ μ§œ μ—°μ‚°

LocalDateTime now = LocalDateTime.now();
LocalDateTime plusDay = now.plus(10, ChronoUnit.DAYS);
LocalDateTime plusMonth = now.plus(2, ChronoUnit.MONTHS);

κΈ°κ°„ ν‘œν˜„

// Machine Time Duration
Instant now = Instant.now();
Instant plus = now.plus(10, ChronoUnit.SECONDS);
Duration between = Duration.between(now, plus);
System.out.println(between.getSeconds()); // 10

// Human Time Period
LocalDate today = LocalDate.now();
LocalDate christmas = LocalDate.of(2023, Month.DECEMBER, 25);

Period period = Period.between(today, christmas);
System.out.println(period.getMonths()); // 2

Period until = today.until(christmas);
System.out.println(until.getDays()); // 25

Pasing/Formatting

// formatting
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
System.out.println(now.format(formatter)); // 2023/09/30

DateTimeFormatter isoLocalDate = DateTimeFormatter.ISO_LOCAL_DATE;
System.out.println(now.format(isoLocalDate)); // 2023-09-30

// parsing
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
LocalDate parse = LocalDate.parse("2023/09/30", formatter);
System.out.println(parse); // 2023-09-30

λ ˆκ±°μ‹œ API 지원

  • GregorianCalendar, Date νƒ€μž…μ˜ μΈμŠ€ν„΄μŠ€λ₯Ό Instant/ZonedDateTime 으둜 λ³€ν™˜ κ°€λŠ₯

  • java.util.TimeZone μ—μ„œ java.time.ZoneId 둜 μƒν˜Έ λ³€ν™˜ κ°€λŠ₯

Date date = new Date(); // Sat Sep 30 22:24:04 KST 2023
Instant instant = date.toInstant(); // 2023-09-30T13:24:04.618Z
Date dateFromInstant = Date.from(instant);

GregorianCalendar gregorianCalendar = new GregorianCalendar(); // java.util.GregorianCalendar[time=1696080458867,areFieldsSet=true,areAl...
ZonedDateTime zonedDateTime = gregorianCalendar.toInstant().atZone(ZoneId.systemDefault()); // 2023-09-30T22:27:38.867+09:00[Asia/Seoul]
GregorianCalendar gregorianCalendarFromZonedDateTime = GregorianCalendar.from(zonedDateTime);

ZoneId zoneId = TimeZone.getTimeZone("PST").toZoneId(); // America/Los_Angeles
TimeZone timeZone = TimeZone.getTimeZone(zoneId); // sun.util.calendar.ZoneInfo[id="America/Los_Angeles",of
ZoneId timeZoneFromZonId = timeZone.toZoneId();

CompletableFuture

Java Concurrency

Concurrent Software

  • λ™μ‹œμ— μ—¬λŸ¬ μž‘μ—…μ„ ν•  수 μžˆλŠ” μ†Œν”„νŠΈμ›¨μ–΄

Java Concurrency Programming

  • λ©€ν‹°ν”„λ‘œμ„Έμ‹±(ProcessBuilder)

  • λ©€ν‹°μ“°λ ˆλ“œ

Java multi-thread Programming

  • Thread / Runnable

    • λ‹€λ₯Έ μ“°λ ˆλ“œκ°€ μ²˜λ¦¬ν•  수 μžˆλ„λ‘ 기회 제곡(락을 놓진 μ•ŠμŒ, λ°λ“œλ½ λ°œμƒ κ°€λŠ₯)

    • λ‹€λ₯Έ μ“°λ ˆλ“œλ₯Ό κΉ¨μ›Œμ„œ interruptedExeption λ°œμƒ

    • λ‹€λ₯Έ μ“°λ ˆλ“œκ°€ 끝날 λ•ŒκΉŒμ§€ λŒ€κΈ°

λ‹€μˆ˜μ˜ μŠ€λ ˆλ“œλ₯Ό μ½”λ”©μœΌλ‘œ κ΄€λ¦¬ν•˜κΈ° 어렀움. Execute 생성.

Executors

High-Level Concurrency Programming

  • μ“°λ ˆλ“œλ₯Ό μƒμ„±ν•˜κ³  κ΄€λ¦¬ν•˜λŠ” μž‘μ—…μ„ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ λΆ„λ¦¬ν•˜κ³  Executors μ—κ²Œ μœ„μž„

  • μ“°λ ˆλ“œ 생성: μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ μ‚¬μš©ν•  μ“°λ ˆλ“œ 풀을 λ§Œλ“€μ–΄ 관리

  • μ“°λ ˆλ“œ 관리: μ“°λ ˆλ“œ 생λͺ… μ£ΌκΈ°λ₯Ό 관리

  • μž‘μ—… 처리 및 μ‹€ν–‰: μ“°λ ˆλ“œλ‘œ μ‹€ν–‰ν•  μž‘μ—…μ„ μ œκ³΅ν•  수 μžˆλŠ” API 제곡

μ£Όμš” μΈν„°νŽ˜μ΄μŠ€

  • Executor: execute(Runnable)

  • ExecutorService: Executor λ₯Ό 상속 받은 μΈν„°νŽ˜μ΄μŠ€

    • Callable, Runnable μ‹€ν–‰, Executor μ’…λ£Œ

    • μ—¬λŸ¬ Callable λ™μ‹œ μ‹€ν–‰ λ“±μ˜ κΈ°λŠ₯ 제곡

  • ScheduledExecutorService: ExecutorService λ₯Ό 상속 받은 μΈν„°νŽ˜μ΄μŠ€

    • νŠΉμ • μ‹œκ°„ 이후 λ˜λŠ” 주기적으둜 μž‘μ—… μ‹€ν–‰

/**
 * ExecutorService
 * 
 * void shutdown(): 이전에 제좜된 μž‘μ—…μ΄ μ‹€ν–‰λ˜μ§€λ§Œ μƒˆ μž‘μ—…μ€ ν—ˆμš©λ˜μ§€ μ•ŠλŠ” 순차적 μ’…λ£Œ(Graceful Shutdown)
 * List<Runnable> shutdownNow(): ν˜„μž¬ μ‹€ν–‰ 쀑인 λͺ¨λ“  μž‘μ—…μ„ μ€‘μ§€ν•˜λ €κ³  μ‹œλ„ν•˜κ³ , λŒ€κΈ° 쀑인 μž‘μ—…μ˜ 처리λ₯Ό μ€‘μ§€ν•˜κ³ , μ‹€ν–‰ λŒ€κΈ° 쀑인 μž‘μ—… λͺ©λ‘μ„ λ°˜ν™˜
 */
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(() -> System.out.println("Thread " + Thread.currentThread().getName()));

executorService.shutdown();

/**
 * ScheduledExecutorService.schedule
 */
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
executorService.schedule(() ->
                System.out.println("Thread " + Thread.currentThread().getName()),
        5, TimeUnit.SECONDS);

executorService.shutdown();

...

/**
 * ScheduledExecutorService.scheduleAtFixedRate
 */
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
executorService.scheduleAtFixedRate(() ->
                System.out.println("Thread " + Thread.currentThread().getName()),
        1, 2, TimeUnit.SECONDS);

Fork/Join ν”„λ ˆμž„μ›Œν¬

  • ExecutorService κ΅¬ν˜„μ²΄λ‘œ μ‰¬μš΄ λ©€ν‹° ν”„λ‘œμ„Έμ„œ ν™œμš© 지원

Callable & Future

Callable

  • Runnable κ³Ό μœ μ‚¬ν•˜μ§€λ§Œ μž‘μ—…μ˜ κ²°κ³Όλ₯Ό 리턴

  • 비동기적인 μž‘μ—…μ˜ ν˜„μž¬ μƒνƒœλ₯Ό μ‘°νšŒν•˜κ±°λ‚˜ κ²°κ³Ό 리턴

/**
 * V get(): κ²°κ³Ό κ°€μ Έμ˜€κΈ°
 *
 * - Blocking Call: 값을 κ°€μ Έμ˜¬ λ•ŒκΉŒμ§€ λŒ€κΈ°
 * - timeout(μ΅œλŒ€ λŒ€κΈ° μ‹œκ°„) μ„€μ • κ°€λŠ₯
 */
future.get();

/**
 * boolean isDone(): μž‘μ—… μƒνƒœ 확인
 */
boolean isDone = future.isDone());

/**
 * boolean cancel(boolean mayInterruptIfRunning): 진행쀑인 μž‘μ—…μ„ interrupt μš”μ²­μœΌλ‘œ μ’…λ£Œ
 * - parameter
 *   - true: ν˜„μž¬ 진행쀑인 μ“°λ ˆλ“œλ₯Ό interrupt
 *   - false: ν˜„μž¬ 진행쀑인 μž‘μ—…μ΄ λλ‚ λ•ŒκΉŒμ§€ λŒ€κΈ°
 * - μ·¨μ†Œ ν–ˆμœΌλ©΄ true λͺ» ν–ˆμœΌλ©΄ false 리턴
 * - μ·¨μ†Œ 이후에 get() μš”μ²­ μ‹œ CancellationException λ°œμƒ
 */
boolean cancel = future.cancel(true);

/**
 * <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
 * - λ™μ‹œμ— μ‹€ν–‰(κ°€μž₯ 였래 κ±Έλ¦¬λŠ” μž‘μ—… 만큼 μ‹œκ°„ μ†Œμš”)
 */
List<Future<String>> futures = executorService.invokeAll(Arrays.asList(hello, the, java));

/**
 * <T> T invokeAny(Collection<? extends Callable<T>> tasks)
 * - Blocking Call
 * - λ™μ‹œ μ‹€ν–‰ μž‘μ—… 쀑 κ°€μž₯ 짧게 μ†Œμš”λ˜λŠ” μž‘μ—… 만큼 μ‹œκ°„ μ†Œμš”
 */
String result = executorService.invokeAny(Arrays.asList(hello, the, java));

CompletableFuture

μžλ°”μ—μ„œ 비동기(Asynchronous) ν”„λ‘œκ·Έλž˜λ°μ΄ κ°€λŠ₯ν•˜λ„λ‘ μ§€μ›ν•˜λŠ” μΈν„°νŽ˜μ΄μŠ€

  • Future λ‘œλ„ 비동기 μ²˜λ¦¬κ°€ μ–΄λŠμ •λ„ κ°€λŠ₯ν•˜μ§€λ§Œ, μ–΄λ €μš΄ μž‘μ—…λ“€μ΄ λ‹€μˆ˜ 쑴재

    • Future λ₯Ό μ™ΈλΆ€μ—μ„œ μ™„λ£Œ 처리 λΆˆκ°€

      • cancel(), get() νƒ€μž„μ•„μ›ƒ 섀정은 κ°€λŠ₯

    • λΈ”λ‘œν‚Ή μ½”λ“œ(ex. get())λ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šκ³ μ„œλŠ” μž‘μ—…μ΄ 끝났을 λ•Œ 콜백 μ‹€ν–‰ λΆˆκ°€

    • μ—¬λŸ¬ Future μ‘°ν•© λΆˆκ°€

      • ex. 행사 정보 쑰회 ν›„ 행사 참석 νšŒμ› λͺ©λ‘ μ‘°νšŒν•˜κΈ°

    • μ˜ˆμ™Έ 처리용 API 제곡 X

.

λΉ„λ™κΈ°λ‘œ μž‘μ—… μ‹€ν–‰ν•˜κΈ°

/**
 * CompletableFuture
 * - μ™ΈλΆ€μ—μ„œ Complete 울 λͺ…μ‹œμ μœΌλ‘œ μ‹œν‚¬ 수 있음
 * - Executor λ₯Ό λ§Œλ“€μ–΄μ„œ μ‚¬μš©ν•  ν•„μš”κ°€ μ—†μŒ
 */
CompletableFuture<String> future = new CompletableFuture<>();
future.complete("aaron"); // νŠΉμ • μ‹œκ°„ 이내에 응닡이 μ—†μœΌλ©΄ κΈ°λ³Έ κ°’μœΌλ‘œ λ¦¬ν„΄ν•˜λ„λ‘ μ„€μ • κ°€λŠ₯

/**
 * public T get() throws InterruptedException, ExecutionException: κ²°κ³Ό λ°˜ν™˜
 * public T join(): get() κ³Ό λ™μΌν•˜μ§€λ§Œ Unchecked Exception
 */
System.out.println(future.get());

...

/**
 * runAsync(): 리턴값이 μ—†λŠ” 비동기 μž‘μ—…
 */
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> System.out.println("Hello " + Thread.currentThread().getName()));

future.get();

...

/**
 * supplyAsync(): 리턴값이 μžˆλŠ” 비동기 μž‘μ—…
 */
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    System.out.println("Hello " + Thread.currentThread().getName());
    return "Hello";
});

System.out.println(future.get());

.

콜백 μ œκ³΅ν•˜κΈ°

  • 콜백 자체λ₯Ό 또 λ‹€λ₯Έ μ“°λ ˆλ“œμ—μ„œ μ‹€ν–‰ κ°€λŠ₯

/**
 * public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
 * - 리턴값을 λ°›μ•„μ„œ λ‹€λ₯Έ κ°’μœΌλ‘œ λ°”κΎΈκ³  λ¦¬ν„΄ν•˜λŠ” 콜백
 */
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    System.out.println("Hello " + Thread.currentThread().getName());
    return "Hello";
}).thenApply(s -> s.toUpperCase());

System.out.println(future.get()); // HELLO

...

/**
 * public CompletableFuture<Void> thenAccept(Consumer<? super T> action)
 * - λ¦¬ν„΄κ°’μœΌλ‘œ 또 λ‹€λ₯Έ μž‘μ—…μ„ μ²˜λ¦¬ν•˜κ³  리턴이 μ—†λŠ” 콜백
 */
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
    System.out.println("Hello " + Thread.currentThread().getName());
    return "Hello";
}).thenAccept(s -> {
    System.out.println(s.toUpperCase());
});

future.get(); // HELLO

...

/**
 * public CompletableFuture<Void> thenRun(Runnable action)
 * - 리턴값을 λ°›μ§€ μ•Šκ³  λ‹€λ₯Έ μž‘μ—…μ„ μ²˜λ¦¬ν•˜λŠ” 콜백
 */
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
    System.out.println("Hello " + Thread.currentThread().getName());
}).thenRun(() -> {
    System.out.println(Thread.currentThread().getName());
});

future.get();

...

/**
 * μ›ν•˜λŠ” Executor(thread-pool)λ₯Ό μ‚¬μš©ν•΄μ„œ μ‹€ν–‰ κ°€λŠ₯
 * - default: ForkJoinPool.commonPool()
 */
ExecutorService executorService = Executors.newFixedThreadPool(4);
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
    System.out.println("Hello " + Thread.currentThread().getName());
}, executorService).thenRunAsync(() -> {
    System.out.println(Thread.currentThread().getName());
}, executorService);

future.get(); // pool-1-thread-2

executorService.shutdown();

.

μ‘°ν•©ν•˜κΈ°

/**
 * public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn)
 * - 연관이 μžˆλŠ” 두 μž‘μ—…μ΄ μ„œλ‘œ μ΄μ–΄μ„œ μ‹€ν–‰ν•˜λ„λ‘ μ‘°ν•©
 */
CompletableFuture<String> hello = CompletableFuture.supplyAsync(() -> {
    System.out.println("Hello " + Thread.currentThread().getName());
    return "Hello";
});

CompletableFuture<String> future = hello.thenCompose(CombinationTestApp::getWorld);
System.out.println(future.get());



private static CompletableFuture<String> getWorld(String message) {
    return CompletableFuture.supplyAsync(() -> {
        System.out.println("World " + Thread.currentThread().getName());
        return message + " World";
    });
}

...

/**
 * public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T, ? super U, ? extends V> fn)
 * - 연관이 μ—†λŠ” 두 μž‘μ—…μ„ λ…λ¦½μ μœΌλ‘œ μ‹€ν–‰ν•˜κ³  두 μž‘μ—…μ΄ λͺ¨λ‘ μ’…λ£Œλ˜μ—ˆμ„ λ•Œ 콜백 μ‹€ν–‰
 */
CompletableFuture<String> future = hello.thenCombine(world, (h, w) -> h + " " + w);

...

/**
 * public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
 * - μ—¬λŸ¬ μž‘μ—…μ„ λͺ¨λ‘ μ‹€ν–‰ν•˜κ³  λͺ¨λ“  μž‘μ—… 결과에 콜백 μ‹€ν–‰
 */
CompletableFuture[] futures = {hello, world};
CompletableFuture<List<Object>> results = CompletableFuture.allOf(futures)
        .thenApply(v -> Arrays.stream(futures)
                .map(CompletableFuture::join)
                .collect(Collectors.toList()));

results.get().forEach(System.out::println);

...

/**
 *  public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
 *  - μ—¬λŸ¬ μž‘μ—… 쀑에 κ°€μž₯ 빨리 μ’…λ£Œλœ ν•˜λ‚˜μ˜ 결과에 콜백 μ‹€ν–‰
 */
CompletableFuture<Void> future = CompletableFuture.anyOf(hello, world).thenAccept(System.out::println);
future.get();
}

.

μ˜ˆμ™Έμ²˜λ¦¬

/**
 * public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn)
 * - μ˜ˆμ™Έ 처리
 */
boolean throwError = true;
CompletableFuture<String> hello = CompletableFuture.supplyAsync(() -> {
    if (throwError) {
        throw new IllegalArgumentException();
    }

    System.out.println("Hello " + Thread.currentThread().getName());
    return "Hello";
}).exceptionally(ex -> {
    System.out.println(ex);
    return "Error!";
});

System.out.println(hello.get());

...

/**
 * public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn)
 * - 성곡 μΌ€μ΄μŠ€μ™€ μ˜ˆμ™Έ μΌ€μ΄μŠ€ λͺ¨λ‘ 처리
 */
boolean throwError = true;
CompletableFuture<String> hello = CompletableFuture.supplyAsync(() -> {
    if (throwError) {
        throw new IllegalArgumentException();
    }

    System.out.println("Hello " + Thread.currentThread().getName());
    return "Hello";
}).handle((result, ex) -> {
    if (ex != null) {
        System.out.println(ex);
        return "Error!";
    }
    return result;
});

System.out.println(hello.get());

Etc..

μ• λ…Έν…Œμ΄μ…˜μ˜ λ³€ν™”

java 8 μ• λ…Έν…Œμ΄μ…˜ κ΄€λ ¨ 두 κ°€μ§€ 큰 λ³€ν™”

  • μ• λ…Έν…Œμ΄μ…˜μ„ νƒ€μž… μ„ μ–ΈλΆ€(μ œλ„€λ¦­ νƒ€μž…, λ³€μˆ˜ νƒ€μž…, λ§€κ°œλ³€μˆ˜ νƒ€μž…, μ˜ˆμ™Έ νƒ€μž…...)에도 μ‚¬μš© κ°€λŠ₯

    • TYPE_PARAMETER: νƒ€μž… λ³€μˆ˜μ—λ§Œ μ‚¬μš© κ°€λŠ₯

    • TYPE_USE: TYPE_PARAMETER 포함 λͺ¨λ“  νƒ€μž… 선언뢀에 μ‚¬μš© κ°€λŠ₯

    static class XXX<@Chicken T> {
      /**
       * <C> : type parameter
        * C : type
        */
      public static <@Chicken C> void print(C c){
    
      }
    }
  • μ• λ…Έν…Œμ΄μ…˜ 쀑볡 μ‚¬μš© κ°€λŠ₯

    // 쀑볡 μ‚¬μš©ν•  μ• λ…Έν…Œμ΄μ…˜
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE_USE)
    @Repeatable(ChickenContainer.class)
    public @interface Chicken {
        String value();
    }
    
    ...
    
    // 쀑볡 μ• λ…Έν…Œμ΄μ…˜ μ»¨ν…Œμ΄λ„ˆ
    // 쀑볡 μ• λ…Έν…Œμ΄μ…˜κ³Ό @Retention, @Target 이 κ°™κ±°λ‚˜ 더 λ„“μ–΄μ•Ό 함
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE_USE)
    public @interface ChickenContainer {
        Chicken[] value();
    }
    
    ...
    
    @Chicken("양념")
    @Chicken("λ§ˆλŠ˜κ°„μž₯")
    public class App {
        public static void main(String[] args) {
            ChickenContainer chickenContainer = App.class.getAnnotation(ChickenContainer.class);
            Arrays.stream(chickenContainer.value()).forEach(c -> {
                System.out.println(c.value());
            });
        }
    }

.

Array Parallel Sorting

Arrays.parallelSort()

  • Fork/Join ν”„λ ˆμž„μ›Œν¬λ₯Ό μ‚¬μš©ν•΄μ„œ 배열을 λ³‘λ ¬λ‘œ μ •λ ¬ν•˜λŠ” κΈ°λŠ₯ 제곡

  • 병렬 μ •λ ¬ μ•Œκ³ λ¦¬λ“¬

    • 배열을 λ‘˜λ‘œ 계속 μͺΌκ°  ν›„ ν•©μΉ˜λ©΄μ„œ μ •λ ¬

int size = 1500;
int[] numbers = new int[size];
Random random = new Random();

/**
 * Dual-Pivot Quicksort.
 * μ•Œκ³ λ¦¬λ“¬ νš¨μœ¨μ„±μ€ 동일. μ‹œκ°„ O(n log(n)) 곡간 O(n)
 */
IntStream.range(0, size).forEach(i -> numbers[i] = random.nextInt());
long start = System.nanoTime();
Arrays.sort(numbers);
System.out.println("serial sorting took " + (System.nanoTime() - start)); // 629500

IntStream.range(0, size).forEach(i -> numbers[i] = random.nextInt());
start = System.nanoTime();
Arrays.parallelSort(numbers);
System.out.println("parallel sorting took " + (System.nanoTime() - start)); // 400375

.

GC Metaspace

JVM의 μ—¬λŸ¬ λ©”λͺ¨λ¦¬ μ˜μ—­ 쀑에 PermGen λ©”λͺ¨λ¦¬ μ˜μ—­μ΄ μ—†μ–΄μ§€κ³  Metaspace μ˜μ—­μ΄ λ“±μž₯

PermGen(permanent generation)

  • 클래슀 메타데이터λ₯Ό λ‹΄λŠ” μ €μž₯μ†Œ(Heap μ˜μ—­)

  • κΈ°λ³Έκ°’μœΌλ‘œ μ œν•œλœ 크기λ₯Ό κ°€μ§€κ³  있음

  • -XX:PermSize=N // PermGen 초기 μ‚¬μ΄μ¦ˆ μ„€μ •
    -XX:MaxPermSize=N // PermGen μ΅œλŒ€ μ‚¬μ΄μ¦ˆ μ„€μ •

Metaspace

  • 클래슀 메타데이터λ₯Ό λ‹΄λŠ” μ €μž₯μ†Œ(Heap μ˜μ—­μ΄ μ•„λ‹ˆλΌ, Native Memory μ˜μ—­)

  • κΈ°λ³Έκ°’μœΌλ‘œ μ œν•œλœ 크기λ₯Ό κ°€μ§€κ³  μžˆμ§€ μ•ŠμŒ(ν•„μš”ν•œ 만큼 계속 증가)

  • java 8 λΆ€ν„°λŠ” PermGen κ΄€λ ¨ μ˜΅μ…˜μ€ λ¬΄μ‹œ

    -XX:MetaspaceSize=N // Metaspace 초기 μ‚¬μ΄μ¦ˆ μ„€μ •
    -XX:MaxMetaspaceSize=N // Metaspace μ΅œλŒ€ μ‚¬μ΄μ¦ˆ μ„€μ •

μ°Έκ³ 

JVM

JAVA, JVM, JDK, JRE

JVM(Java Virtual Machine)

  • μžλ°” λ°”μ΄νŠΈ μ½”λ“œ(.class)λ₯Ό OS νŠΉν™”λœ μ½”λ“œλ‘œ λ³€ν™˜(인터프리터와 JIT 컴파일러)ν•˜μ—¬ μ‹€ν–‰

    • νŠΉμ • ν”Œλž«νΌμ— 쒅속적

  • λ°”μ΄νŠΈ μ½”λ“œλ₯Ό μ‹€ν–‰ν•˜λŠ” ν‘œμ€€(JVM μžμ²΄λŠ” ν‘œμ€€)이자 κ΅¬ν˜„μ²΄(νŠΉμ • 밴더가 κ΅¬ν˜„ν•œ JVM)

    • JVM 밴더: Oracle, Amazon, Azul, ...

JRE(Java Runtime Environment): JVM + Library

  • μžλ°” μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ μ‹€ν–‰ν•  수 μžˆλ„λ‘ κ΅¬μ„±λœ 배포판

  • JVM κ³Ό 핡심 라이브러리 및 μžλ°” λŸ°νƒ€μž„ ν™˜κ²½μ—μ„œ μ‚¬μš©ν•˜λŠ” ν”„λ‘œνΌν‹° μ„ΈνŒ…μ΄λ‚˜ λ¦¬μ†ŒμŠ€ 파일 보유

  • 개발 κ΄€λ ¨ λ„κ΅¬λŠ” 미포함(JDKμ—μ„œ 제곡)

JDK(Java Development Kit): JRE + Development Tool

  • μ†ŒμŠ€ μ½”λ“œλ₯Ό μž‘μ„±ν•  λ•Œ μ‚¬μš©ν•˜λŠ” μžλ°” μ–Έμ–΄λŠ” ν”Œλž«νΌμ— 독립적

  • μ˜€λΌν΄μ€ μžλ°” 11λΆ€ν„°λŠ” JDK 만 μ œκ³΅ν•˜λ©° JRE 미제곡

  • Write Once Run Anywhere(WORA, ν•œ 번만 μž‘μ„±ν•˜λ©΄ μ–΄λ””μ—μ„œλ“  μ‹€ν–‰ κ°€λŠ₯)

JAVA

  • ν”„λ‘œκ·Έλž˜λ° μ–Έμ–΄

  • JDK 에 λ“€μ–΄μžˆλŠ” μžλ°” 컴파일러(javac)λ₯Ό μ‚¬μš©ν•˜μ—¬ λ°”μ΄νŠΈμ½”λ“œ(.class)둜 컴파일 κ°€λŠ₯

JVM μ–Έμ–΄

  • JVM 기반으둜 λ™μž‘ν•˜λŠ” ν”„λ‘œκ·Έλž˜λ° μ–Έμ–΄

  • Clojure, Groovy, JRuby, Jython, Kotlin, Scala, ...

μ°Έκ³ 

.

JVM ꡬ쑰

Class Loader System

  • .class μ—μ„œ λ°”μ΄νŠΈμ½”λ“œλ₯Ό 읽고 λ©”λͺ¨λ¦¬μ— μ €μž₯

  • loading: 클래슀 μ½μ–΄μ˜€λŠ” κ³Όμ •

  • linking: 레퍼런슀λ₯Ό μ—°κ²°ν•˜λŠ” κ³Όμ •

  • initialization: static 값듀을 μ΄ˆκΈ°ν™” 및 λ³€μˆ˜μ— ν• λ‹Ή

Memory

  • Stack Area

    • μ“°λ ˆλ“œλ§ˆλ‹€ λŸ°νƒ€μž„ μŠ€νƒμ„ λ§Œλ“€κ³ , κ·Έ μ•ˆμ— λ©”μ†Œλ“œ ν˜ΈμΆœμ„ μŠ€νƒ ν”„λ ˆμž„μ΄λΌ λΆ€λ₯΄λŠ” λΈ”λŸ­μœΌλ‘œ μŒ“μŒ

    • μ“°λ ˆλ“œλ₯Ό μ’…λ£Œν•˜λ©΄ λŸ°νƒ€μž„ μŠ€νƒλ„ μ†Œλ©Έ(μ“°λ ˆλ“œμ—μ„œλ§Œ 곡유)

  • PC(Program Counter) registers Area

    • μ“°λ ˆλ“œλ§ˆλ‹€ μ“°λ ˆλ“œ λ‚΄ ν˜„μž¬ μ‹€ν–‰ν•  instruction μœ„μΉ˜λ₯Ό κ°€λ¦¬ν‚€λŠ” 포인터 생성(μ“°λ ˆλ“œμ—μ„œλ§Œ 곡유)

  • native method stack Area

    • μ“°λ ˆλ“œλ§ˆλ‹€ μƒμ„±λ˜κ³ , native method 호좜 μ‹œ μ‚¬μš©ν•˜λŠ” λ³„λ„μ˜ method stack(μ“°λ ˆλ“œμ—μ„œλ§Œ 곡유)

  • heap Area

    • 객체 μ €μž₯(곡유 μžμ›)

  • method Area

    • 클래슀 μˆ˜μ€€μ˜ 정보(클래슀 이름, νŒ¨ν‚€μ§€ 경둜, λΆ€λͺ¨ 클래슀 이름, λ©”μ†Œλ“œ, λ³€μˆ˜)μ €μž₯(곡유 μžμ›)

Execution Engine

  • 인터프리터

    • λ°”μ΄νŠΈ μ½”λ“œλ₯Ό ν•œμ€„μ”© μ‹€ν–‰

  • JIT(Just-In-Time) 컴파일러

    • 인터프리터 νš¨μœ¨μ„ 높이기 μœ„ν•΄ 인터프리터가 λ°˜λ³΅λ˜λŠ” μ½”λ“œλ₯Ό λ°œκ²¬ν•˜λ©΄ JIT 컴파일러둜 λ°˜λ³΅λ˜λŠ” μ½”λ“œλ₯Ό λ„€μ΄ν‹°λΈŒ μ½”λ“œλ‘œ λ³€κ²½

    • κ·Έ λ‹€μŒλΆ€ν„° μΈν„°ν”„λ¦¬ν„°λŠ” λ„€μ΄ν‹°λΈŒ μ½”λ“œλ‘œ 컴파일된 μ½”λ“œλ₯Ό λ°”λ‘œ μ‚¬μš©

  • GC(Garbage Collector)

    • 더이상 μ°Έμ‘°λ˜μ§€ μ•ŠλŠ” 객체λ₯Ό λͺ¨μ•„μ„œ 정리

JNI(Java Native Interface)

  • μžλ°” μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ C, C++, μ–΄μ…ˆλΈ”λ¦¬λ‘œ μž‘μ„±λœ ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•  수 μžˆλŠ” 방법 제곡

  • Native ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•œ λ©”μ†Œλ“œ 호좜

Native Method Library

  • C, C++둜 μž‘μ„±λœ 라이브러리

  • JNI λ₯Ό 톡해 μ‚¬μš©

μ°Έκ³ 

.

Class Loader System

λ‘œλ”©, 링크, μ΄ˆκΈ°ν™” 순으둜 μ§„ν–‰

Loading

  • 클래슀 λ‘œλ”κ°€ .class νŒŒμΌμ„ 읽고 κ·Έ λ‚΄μš©μ— 따라 μ μ ˆν•œ λ°”μ΄λ„ˆλ¦¬ 데이터λ₯Ό λ§Œλ“€κ³  Method Memory μ˜μ—­μ— μ €μž₯

  • Method Memory μ˜μ—­μ— μ €μž₯ν•˜λŠ” 데이터

    • FQCN(Full Qualified Class Name)

    • 클래슀/μΈν„°νŽ˜μ΄μŠ€/μ΄λŠ„

    • λ©”μ†Œλ“œμ™€ λ³€μˆ˜

  • λ‘œλ”©μ΄ λλ‚˜λ©΄ ν•΄λ‹Ή 클래슀 νƒ€μž…μ˜ Class 객체λ₯Ό μƒμ„±ν•˜μ—¬ Heap μ˜μ—­μ— μ €μž₯

클래슀 λ‘œλ”λŠ” 계측 ꡬ쑰둜 이뀄져 있으면 기본적으둜 μ„Έκ°€μ§€ 클래슀 λ‘œλ”κ°€ 제곡

  • BootstrapClassLoader

    • μ΅œμƒμœ„ μš°μ„ μˆœμœ„λ₯Ό κ°€μ§„ 클래슀 λ‘œλ”

    • JAVA_HOME\lib 에 μžˆλŠ” μ½”μ–΄ μžλ°” API 제곡

  • PlatformClassLoader

    • JAVA_HOME\lib\ext 폴더 λ˜λŠ” java.ext.dirs μ‹œμŠ€ν…œ λ³€μˆ˜μ— ν•΄λ‹Ήν•˜λŠ” 클래슀λ₯Ό 읽음

  • AppClassLoader

    • μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 클래슀 νŒ¨μŠ€μ—μ„œ 클래슀λ₯Ό 읽음

    • 클래슀 패슀: μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ‹€ν–‰ μ‹œ -classpath μ˜΅μ…˜ λ˜λŠ” java.class.path ν™˜κ²½ λ³€μˆ˜ 값에 ν•΄λ‹Ήν•˜λŠ” μœ„μΉ˜

μ΅œμƒμœ„ 클래슀 λ‘œλ”λΆ€ν„° 클래슀λ₯Ό μ°Έμƒ‰ν•˜λŠ”λ° λͺ¨λ“  클래슀 λ‘œλ”κ°€ 클래슀λ₯Ό μ°Ύμ§€ λͺ» ν•œλ‹€λ©΄ ClassNotFoundException λ°œμƒ

Linking

  • Verify: .class 파일 ν˜•μ‹μ΄ μœ νš¨ν•œμ§€ 체크

  • Preparation: 클래슀 λ³€μˆ˜(static λ³€μˆ˜)와 기본값에 ν•„μš”ν•œ λ©”λͺ¨λ¦¬ μ€€λΉ„

  • Resolve(optional): 심볼릭 λ©”λͺ¨λ¦¬ 레퍼런슀λ₯Ό λ©”μ†Œλ“œ μ˜μ—­μ— μžˆλŠ” μ‹€μ œ 레퍼런슀둜 ꡐ체

Initialization

  • Static λ³€μˆ˜μ˜ 값을 ν• λ‹Ή(static λΈ”λŸ­μ΄ μžˆλ‹€λ©΄ μ΄λ•Œ μ‹€ν–‰)

Bytecode Operation

μ½”λ“œ 컀버리지 μΈ‘μ •

  • ν…ŒμŠ€νŠΈ μ½”λ“œκ°€ ν™•μΈν•œ μ†ŒμŠ€ μ½”λ“œ λΉ„μœ¨

.

클래슀 λ‘œλ”© μ „ λ°”μ΄νŠΈμ½”λ“œ μ‘°μž‘

ν”„λ‘œκ·Έλž¨ 뢄석

  • μ½”λ“œμ—μ„œ 버그λ₯Ό μ°ΎλŠ” 툴

  • μ½”λ“œ λ³΅μž‘λ„ 계산

클래슀 파일 생성

  • ν”„λ‘μ‹œ

  • νŠΉμ • API 호좜 μ ‘κ·Ό μ œν•œ

  • 슀칼라 같은 μ–Έμ–΄μ˜ 컴파일러

그밖에도 μžλ°” μ†ŒμŠ€μ½”λ“œλ₯Ό κ±΄λ“œλ¦¬μ§€ μ•Šκ³  μ½”λ“œ 변경이 ν•„μš”ν•œ μ—¬λŸ¬ κ²½μš°μ— μ‚¬μš© κ°€λŠ₯

  • ν”„λ‘œνŒŒμΌλŸ¬: CPU μ‚¬μš©λ₯  및 λ©”λͺ¨λ¦¬ μ‚¬μš©λŸ‰, Thread 정보 ..

  • μ΅œμ ν™”

  • λ‘œκΉ…

  • ...

μŠ€ν”„λ§μ˜ λ°”μ΄νŠΈμ½”λ“œ μ‘°μž‘ 툴 μ‚¬μš©: μŠ€ν”„λ§ μ»΄ν¬λ„ŒνŠΈ μŠ€μΊ”

  • 빈으둜 등둝할 후보 클래슀 정보λ₯Ό μ°ΎλŠ”λ° ASM μ‚¬μš©

  • ClassPathScanningCandidateComponentProvider -> SimpleMetadataReader

  • ClassReader, Visitor λ₯Ό μ‚¬μš©ν•΄μ„œ ν΄λž˜μŠ€μ— μžˆλŠ” 메타 정보 쑰회

μ°Έκ³ 

.

λ°”μ΄νŠΈμ½”λ“œ μ‘°μž‘ 라이브러리

    • .class 파일 자체λ₯Ό λ³€κ²½μ‹œν‚€λŠ” 방법

    • ꢌμž₯ν•˜λŠ” 라이브러리

    new ByteBuddy().redefine(Moja.class)
            .method(named("pullOut")).intercept(FixedValue.value("Rabbit!"))
            .make().saveIn(new File("../target/classes/"))
    • 클래슀 λ‘œλ”κ°€ 클래슀λ₯Ό μ½μ–΄μ˜¬ λ•Œ javaagent λ₯Ό κ±°μ³μ„œ λ³€κ²½λœ λ°”μ΄νŠΈμ½”λ“œλ₯Ό μ½μ–΄μ˜΄

      • premain: μ‹œμž‘ μ‹œ λΆ™μ΄λŠ” 방식

      • agentmain: λŸ°νƒ€μž„ 쀑 λ™μ μœΌλ‘œ λΆ™μ΄λŠ” 방식

    public static void premain(String agentArgs, Instrumentation inst) {
        new AgentBuilder.Default()
              .type(ElementMatchers.any())
              .transform((builder, typeDescription, classLoader, javaModule) ->
              builder.method(named("pullOut")).intercept(FixedValue.value("Rabbit!"))).installOn(inst);
              }
  • CGlib

Reflection

Class<T> μ ‘κ·Ό 방법

  • λͺ¨λ“  클래슀λ₯Ό λ‘œλ”© ν•œ λ‹€μŒ Class<T> μΈμŠ€ν„΄μŠ€ 생성

    • νƒ€μž….class 둜 μ ‘κ·Ό κ°€λŠ₯

  • λͺ¨λ“  μΈμŠ€ν„΄μŠ€λŠ” getClass() λ©”μ†Œλ“œ 보유

    • μΈμŠ€ν„΄μŠ€.getClass() 둜 μ ‘κ·Ό κ°€λŠ₯

  • 클래슀λ₯Ό λ¬Έμžμ—΄λ‘œ μ½μ–΄μ˜€λŠ” 방법

    • Class.forName("FQCN")

    • ν΄λž˜μŠ€νŒ¨μŠ€μ— ν•΄λ‹Ή ν΄λž˜μŠ€κ°€ μ—†λ‹€λ©΄ ClassNotFoundException λ°œμƒ

Class<Book> bookClass = Book.class;

Book book = new Book();
Class<? extends Book> aClass = book.getClass();

Class<?> aClass1 = Class.forName("com.example.java. reflection.Book");

Class<T> λ₯Ό 톡해 ν•  수 μžˆλŠ” 것

  • ν•„λ“œ(λͺ©λ‘) κ°€μ Έμ˜€κΈ°

  • λ©”μ†Œλ“œ(λͺ©λ‘) κ°€μ Έμ˜€κΈ°

  • μƒμœ„ 클래슀 κ°€μ Έμ˜€κΈ°

  • μΈν„°νŽ˜μ΄μŠ€(λͺ©λ‘) κ°€μ Έμ˜€κΈ°

  • μ• λ…Έν…Œμ΄μ…˜ κ°€μ Έμ˜€κΈ°

  • μƒμ„±μž κ°€μ Έμ˜€κΈ° ...

.

Annotation & Reflection

Annotaion

  • @Retention: ν•΄λ‹Ή μ• λ…Έν…Œμ΄μ…˜μ„ μ–Έμ œκΉŒμ§€ μœ μ§€ν•  것인가. (SOURCE, CLASS, RUNTIME)

  • @Target: 어디에 μ‚¬μš©ν•  수 μžˆλŠ”κ°€. (TYPE, FIELD, METHOD, PARAMETER ..)

  • @Inherit: ν•΄λ‹Ή μ• λ…Έν…Œμ΄μ…˜μ„ ν•˜μœ„ ν΄λž˜μŠ€κΉŒμ§€ 전달할 것인가.

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Inherited
public @interface MyAnnotation {
    String name() default "aaron";
    int number();
}

Reflection

  • getAnnotations(): 상속받은(@Inherit) μ• λ…Έν…Œμ΄μ…˜κΉŒμ§€ 쑰회

  • getDeclaredAnnotations(): 자기 μžμ‹ μ—λ§Œ λΆ™μ–΄μžˆλŠ” μ• λ…Έν…Œμ΄μ…˜ 쑰회

// 상속받은(@Inherit) μ• λ…Έν…Œμ΄μ…˜κΉŒμ§€ 쑰회
Arrays.stream(Book.class.getAnnotations()).forEach(System.out::println);

// 자기 μžμ‹ μ—λ§Œ λΆ™μ–΄μžˆλŠ” μ• λ…Έν…Œμ΄μ…˜ 쑰회
Arrays.stream(MyBook.class.getDeclaredAnnotations()).forEach(System.out::println);

// μ• λ…Έν…Œμ΄μ…˜ 정보 쑰회
@Test
void getAnnotatedFieldValue() throws Exception {
    Arrays.stream(Book.class.getDeclaredFields()).forEach(f -> {
        Arrays.stream(f.getAnnotations()).forEach(a -> {
            MyAnotherAnnotation myAnotherAnnotation = (MyAnotherAnnotation) a;
            System.out.println(f);
            System.out.println(myAnotherAnnotation.value());
        });
    });
}

.

Edit class information or Run

/**
 * Class μΈμŠ€ν„΄μŠ€ μƒμ„±ν•˜κΈ°
 */
Class<Ball> ballClass = Ball.class;
// ballClass.newInstance(); -> deprecated
Constructor<Ball> constructor = ballClass.getConstructor(String.class);
// μƒμ„±μžλ‘œ μΈμŠ€ν„΄μŠ€ 생성
Ball ball = constructor.newInstance("myBall");
System.out.println(ball);

/**
 * Class Field μˆ˜μ •ν•˜κΈ°
 */
Field field = Ball.class.getDeclaredField("A");
// public static 은 νŠΉμ • μΈμŠ€ν„΄μŠ€μ— ν•΄λ‹Ήν•˜λŠ” 값이 μ•„λ‹ˆλΌμ„œ 클래슀 값에 ν•΄λ‹Ήν•˜λ―€λ‘œ μΈμŠ€ν„΄μŠ€λ₯Ό 전달할 ν•„μš”κ°€ μ—†μŒ
System.out.println(field.get(null));
field.set(null, "newA");
System.out.println(field.get(null));

/**
 * Instance Field μˆ˜μ •ν•˜κΈ°
 */
Field field = Ball.class.getDeclaredField("b");
field.setAccessible(true); // private ν•„λ“œ 접근을 μœ„ν•œ μ„€μ •
// νŠΉμ • μΈμŠ€ν„΄μŠ€κ°€ κ°€μ§€κ³  μžˆλŠ” 값을 가져와야 ν•˜λ―€λ‘œ μΈμŠ€ν„΄μŠ€ ν•„μš”
System.out.println(field.get(ball));
field.set(ball, "newB");
System.out.println(field.get(ball));

/**
 * Private Method μ‹€ν–‰
 */
Method method = Ball.class.getDeclaredMethod("c");
method.setAccessible(true);
method.invoke(ball);

/**
 * Public Method μ‹€ν–‰
 */
Method method = Ball.class.getDeclaredMethod("sum", int.class, int.class);
int invoke = (int) method.invoke(ball, 1, 2);
System.out.println(invoke);

.

Reflection 을 ν™œμš©ν•˜μ—¬ κ°„λ‹¨ν•œ DI ν”„λ ˆμž„μ›Œν¬ λ§Œλ“€μ–΄λ³΄κΈ°

ContainerService.getObject

  • classType 에 ν•΄λ‹Ήν•˜λŠ” νƒ€μž…μ˜ 객체 생성

  • ν•΄λ‹Ή 객체의 ν•„λ“œ 쀑에 @Inject κ°€ μžˆλ‹€λ©΄ ν•΄λ‹Ή ν•„λ“œλ„ 같이 λ§Œλ“€μ–΄ 제곡

public static <T> T getObject(Class<T> classType) {
    T instance = createInstance(classType);
    Arrays.stream(classType.getDeclaredFields()).forEach(f -> {
        // @Inject μ„ μ–Έ ν•„λ“œ 탐색
        if (f.getAnnotation(Inject.class) != null) {
            Object fieldInstance = createInstance(f.getType());
            f.setAccessible(true);
            try {
                //  @Inject μ„ μ–Έ ν•„λ“œμ— μΈμŠ€ν„΄μŠ€ μ£Όμž…
                f.set(instance, fieldInstance);
            } catch (IllegalAccessException e) {
                throw new RuntimeException();
            }
        }
    });

    return instance;
}

.

Reflection 정리

λ¦¬ν”Œλ ‰μ…˜ μ‚¬μš© μ‹œ 주의

  • μ§€λ‚˜μΉœ μ‚¬μš©(λ¬΄λΆ„λ³„ν•œ μΈμŠ€ν„΄μŠ€ μƒμ„±μœΌλ‘œ)은 μ„±λŠ₯ 이슈λ₯Ό μ•ΌκΈ°ν•  수 μžˆμœΌλ―€λ‘œ λ°˜λ“œμ‹œ ν•„μš”ν•œ κ²½μš°μ—λ§Œ μ‚¬μš© ꢌμž₯

  • 컴파일 νƒ€μž„μ— ν™•μΈλ˜μ§€ μ•Šκ³  λŸ°νƒ€μž„ μ‹œμ—λ§Œ λ°œμƒν•˜λŠ” 문제λ₯Ό λ§Œλ“€ κ°€λŠ₯μ„± 쑴재

  • μ ‘κ·Ό μ§€μ‹œμž λ¬΄μ‹œ

λ¦¬ν”Œλ ‰μ…˜ μ‚¬μš© 사둀

  • Spring.

    • μ˜μ‘΄μ„± μ£Όμž…

    • MVC View μ—μ„œ λ„˜μ–΄μ˜¨ 데이터λ₯Ό 객체에 바인딩 ν•  λ•Œ

  • Hibernate.

    • @Entity ν΄λž˜μŠ€μ— Setter κ°€ μ—†λ‹€λ©΄ λ¦¬ν”Œλ ‰μ…˜ μ‚¬μš©

Reference.

Dynamic Proxy

λŸ°νƒ€μž„μ— μΈν„°νŽ˜μ΄μŠ€/클래슀의 ν”„λ‘μ‹œ μΈμŠ€ν„΄μŠ€/클래슀λ₯Ό λ§Œλ“€μ–΄ μ‚¬μš©ν•˜λŠ” ν”„λ‘œκ·Έλž˜λ° 기법

  • java.lang.reflect.proxy

Spring Data JPA λŠ” μ–΄λ–»κ²Œ λ™μž‘ν• κΉŒ?

  • μΈν„°νŽ˜μ΄μŠ€ νƒ€μž…μ˜ μΈμŠ€ν„΄μŠ€λŠ” λˆ„κ°€ λ§Œλ“€μ–΄ μ€„κΉŒ?

    • JpaRepository μΈν„°νŽ˜μ΄μŠ€λ₯Ό μƒμ†λ°›μœΌλ©΄ 객체도 μƒμ„±λ˜κ³ , 빈으λ₯΄λ„ 등둝

  • Spring AOP 기반으둜 λ™μž‘ν•˜λ©° RepositoryFactorySupport μ—μ„œ ν”„λ‘μ‹œ 객체 생성

    • μƒμ„±λœ ν”„λ‘μ‹œ 객체가 빈으둜 λ“±λ‘λ˜κ³  μ£Όμž…

Dynamic Proxy μ‚¬μš© 예

  • Spring Data JPA

  • Spring AOP

  • Mockito

  • Hibernate lazy initialzation ...

.

Proxy Pattern

  • ν”„λ‘μ‹œμ™€ 리얼 μ„œλΈŒμ νŠΈκ°€ κ³΅μœ ν•˜λŠ” μΈν„°νŽ˜μ΄μŠ€κ°€ 있고, ν΄λΌμ΄μ–ΈνŠΈλŠ” ν•΄λ‹Ή μΈν„°νŽ˜μ΄μŠ€ νƒ€μž…μœΌλ‘œ ν”„λ‘μ‹œ μ‚¬μš©

  • ν΄λΌμ΄μ–ΈνŠΈλŠ” ν”„λ‘μ‹œλ₯Ό κ±°μ³μ„œ 리얼 μ„œλΈŒμ νŠΈλ₯Ό μ‚¬μš©

    • ν”„λ‘μ‹œλŠ” 리얼 μ„œλΈŒμ νŠΈμ— λŒ€ν•œ μ ‘κ·Ό 관리, λΆ€κ°€ κΈ°λŠ₯ 제곡, 리턴값 λ³€κ²½ κ°€λŠ₯

단점

  • ν”„λ‘μ‹œ νŒ¨ν„΄μœΌλ‘œ κ΅¬ν˜„ν•˜λŠ” 것은 번거둜운 일

  • 뢀가적인 κΈ°λŠ₯을 μΆ”κ°€ν•  λ•Œλ§ˆλ‹€ 별도 ν”„λ‘μ‹œ 생성 ν•„μš”

  • ν”„λ‘μ‹œλ‘œ ν”„λ‘μ‹œλ₯Ό 감싸야 ν•˜λŠ” 일도 λ°œμƒ

  • λͺ¨λ“  κ΅¬ν˜„μ²΄μ—μ„œ μ›λž˜ νƒ€κ²ŸμœΌλ‘œ μœ„μž„ν•˜λ©΄μ„œ 쀑볡 μ½”λ“œ λ°œμƒ

ν”„λ‘μ‹œ νŒ¨ν„΄μ˜ 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ λ™μ μœΌλ‘œ λŸ°νƒ€μž„μ— ν”„λ‘μ‹œλ₯Ό μƒμ„±ν•΄λ‚΄λŠ” λ‹€μ΄λ‚˜λ―Ή ν”„λ‘μ‹œ λ“±μž₯

.

Dynamic Proxy

λŸ°νƒ€μž„μ— νŠΉμ • μΈν„°νŽ˜μ΄μŠ€λ“€μ„ κ΅¬ν˜„ν•˜λŠ” 클래슀 λ˜λŠ” μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“œλŠ” 기술

BookService defaultBookService = (BookService) Proxy.newProxyInstance(
        BookService.class.getClassLoader(),
        new Class[]{BookService.class}, // μ–΄λ–€ μΈν…‰νŽ˜μ΄μŠ€ νƒ€μž…μ˜ κ΅¬ν˜„μ²΄μΈμ§€
        new InvocationHandler() { // ν”„λ‘μ‹œμ— μ–΄λ–€ λ©”μ†Œλ“œκ°€ 호좜이 될 λ•Œ κ·Έ λ©”μ†Œλ“œ ν˜ΈμΆœμ„ μ–΄λ–»κ²Œ μ²˜λ¦¬ν• μ§€μ— λŒ€ν•œ μ„€λͺ…
            BookService bookService = new DefaultBookService();

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (method.getName().equals("rent")) {
                    System.out.println("qqqqq");
                    Object invoke = method.invoke(bookService, args);
                    System.out.println("zzzzz");
                    return invoke;
                }
                return method.invoke(bookService, args);
            }
        }
);

단점.

  • 클래슀 기반의 ν”„λ‘μ‹œ 생성 λΆˆκ°€ -> μΈν„°νŽ˜μ΄μŠ€κ°€ 없을 경우 λ‹€μ΄λ‚˜λ―Ή ν”„λ‘μ‹œ 적용 λΆˆκ°€

  • λΆ€κ°€κΈ°λŠ₯이 λ§Žμ•„μ§ˆμˆ˜λ‘ μ½”λ“œκ°€ μ»€μ§€λŠ” μœ μ—°ν•˜μ§€ μ•Šμ€ ꡬ쑰

ν”„λ‘μ‹œ 기반 AOP 인 μŠ€ν”„λ§ AOP λ“±μž₯

.

Class Proxy

μΈν„°νŽ˜μ΄μŠ€ 없이 ν”„λ‘μ‹œ λ§Œλ“€κΈ°

  • Spring, Hibernate μ—μ„œλ„ μ‚¬μš©ν•˜λŠ” 라이브러리

  • 버전 ν˜Έν™˜μ„±μ΄ μ’‹μ§€ μ•Šμ•„μ„œ μ„œλ‘œ λ‹€λ₯Έ 라이브러리 내뢀에 λ‚΄μž₯된 ν˜•νƒœλ‘œ μ œκ³΅λ˜κΈ°λ„ 함

MethodInterceptor handler = new MethodInterceptor() {
    BallService bookService = new BallService();
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        if (method.getName().equals("rent")) {
            System.out.println("bbbbb");
            Object invoke = method.invoke(bookService, args);
            System.out.println("ccccc");
            return invoke;
        }
        return method.invoke(bookService, args);
    }
};
BallService ballService = (BallService) Enhancer.create(BallService.class, handler);

Book book = new Book();
book.setTitle("spring");
ballService.rent(book);
  • λ°”μ΄νŠΈ μ½”λ“œ μ‘°μž‘ 뿐 μ•„λ‹ˆλΌ λŸ°νƒ€μž„(λ‹€μ΄λ‚˜λ―Ή) ν”„λ‘μ‹œ 생성 μ‹œμ—λ„ μ‚¬μš©

Class<? extends BallService> proxyClass = new ByteBuddy().subclass(BallService.class)
        .method(named("rent")).intercept(InvocationHandlerAdapter.of(new InvocationHandler() {
            BallService bookService = new BallService();

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("qqqq");
                Object invoke = method.invoke(bookService, args);
                System.out.println("eeee");
                return invoke;
            }
        }))
        .make().load(BallService.class.getClassLoader()).getLoaded();
BallService ballService = proxyClass.getConstructor(null).newInstance();

Book book = new Book();
book.setTitle("spring");
ballService.rent(book);

μΈν„°νŽ˜μ΄μŠ€ 없이 ν”„λ‘μ‹œλ₯Ό 생성할 경우 단점

  • 상속을 μ‚¬μš©ν•˜μ§€ λͺ»ν•˜λŠ” 경우 ν”„λ‘μ‹œ 생성 λΆˆκ°€

    • Final 클래슀인 경우

    • Private μƒμ„±μžλ§Œ μžˆλŠ” 경우

가급적 ν”„λ‘μ‹œ 적용 μ‹œ μΈν„°νŽ˜μ΄μŠ€λ₯Ό λ§Œλ“€μ–΄μ„œ μΈν„°νŽ˜μ΄μŠ€ ν”„λ‘μ‹œλ₯Ό μ‚¬μš©ν•˜λŠ” 것을 ꢌμž₯

Annotation processor

μ• λ…Έν…Œμ΄μ…˜ ν”„λ‘œμ„Έμ„œ μ‚¬μš© 예

  • Lombok

    • ν‘œμ€€μœΌλ‘œ μž‘μ„±ν•΄μ•Ό ν•˜λŠ” μ½”λ“œλ₯Ό 개발자 λŒ€μ‹  생성해 μ£ΌλŠ” 라이브러리

  • AutoService

    • java.util.ServiceLoader 용 파일 생성 μœ ν‹Έλ¦¬ν‹°

  • @Override

    • Reflection API λ₯Ό μ‚¬μš©ν•˜μ—¬ 슈퍼 ν΄λž˜μŠ€μ— ν•΄λ‹Ήν•˜λŠ” λ©”μ„œλ“œμ™€ μΌμΉ˜ν•˜λŠ” ν•­λͺ©μ΄ μ—†λ‹€λ©΄ 컴파일 였λ₯˜ λ°œμƒ

    • 컴파일 νƒ€μž„ DI 제곡

μž₯점

  • λŸ°νƒ€μž„ λΉ„μš©μ΄ 제둜

단점

  • κΈ°μ‘΄ 클래슀 μ½”λ“œλ₯Ό λ³€κ²½ν•  λ•ŒλŠ” μ•½κ°„μ˜ hack ν•„μš”

.

Lombok

  • @Getter, @Setter λ“±μ˜ μ• λ…Έν…Œμ΄μ…˜κ³Ό μ• λ…Έν…Œμ΄μ…˜ ν”„λ‘œμ„Έμ„œλ₯Ό μ œκ³΅ν•˜μ—¬ ν‘œμ€€ μž‘μ„± μ½”λ“œλ₯Ό 개발자 λŒ€μ‹  μƒμ„±ν•΄μ£ΌλŠ” 라이브러리

둬볡의 λ™μž‘ 원리

둬볡의 λ…Όλž€ 거리

  • 곡개된 API κ°€ μ•„λ‹Œ 컴파일러 λ‚΄λΆ€ 클래슀λ₯Ό μ‚¬μš©ν•˜μ—¬ κΈ°μ‘΄ μ†ŒμŠ€ μ½”λ“œλ₯Ό μ‘°μž‘

  • 특히 이클립슀의 κ²½μš°μ—” java agent λ₯Ό μ‚¬μš©ν•˜μ—¬ 컴파일러 ν΄λž˜μŠ€κΉŒμ§€ μ‘°μž‘ν•˜μ—¬ μ‚¬μš©

  • ν•΄λ‹Ή ν΄λž˜μŠ€λ“€ μ—­μ‹œ 곡개된 API κ°€ μ•„λ‹ˆλ‹€λ³΄λ‹ˆ 버전 ν˜Έν™˜μ„±μ— λ¬Έμ œκ°€ 생길 수 있음

  • κ·ΈλŸΌμ—λ„ μ—„μ²­λ‚œ νŽΈλ¦¬ν•¨μœΌλ‘œ 널리 쓰이고, λŒ€μ•ˆμ΄ λͺ‡κ°€μ§€ μžˆμ§€λ§Œ 둬볡의 λͺ¨λ“  κΈ°λŠ₯κ³Ό νŽΈμ˜μ„±μ„ λŒ€μ²΄ λΆˆκ°€

.

Annotation processor

  • μ—¬λŸ¬ λΌμš΄λ“œ(rounds)에 거쳐 μ†ŒμŠ€ 및 컴파일 된 μ½”λ“œλ₯Ό 처리

AutoService

  • 컴파일 μ‹œμ μ— μ• λ…Έν…Œμ΄μ…˜ ν”„λ‘œμ„Έμ„œλ₯Ό μ‚¬μš©ν•˜μ—¬ META-INF/services/javax.annotation.processor.Processor 파일 μžλ™ 생성

javapoet

  • μ†ŒμŠ€ μ½”λ“œ 생성 μœ ν‹Έλ¦¬ν‹°

Interface Filer

  • μ†ŒμŠ€ μ½”λ“œ, 클래슀 μ½”λ“œ 및 λ¦¬μ†ŒμŠ€λ₯Ό 생성할 수 μžˆλŠ” μΈν„°νŽ˜μ΄μŠ€

@AutoService(Processor.class)
public class MagicMojaProcessor extends AbstractProcessor {
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return Set.of(Magic.class.getName());
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Magic.class);
        for (Element element : elements) {
            Name elementName = element.getSimpleName();
            // Magic annotation 이 Interface κ°€ μ•„λ‹Œ λ‹€λ₯Έ 곳에 μ„ μ–Έλ˜μ–΄ μžˆμ„ 경우.
            if (element.getKind() != ElementKind.INTERFACE) {
                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Magic annotation can not be used on" + elementName);
            } else {
                processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Processing " + elementName);
            }

            TypeElement typeElement = (TypeElement) element;
            ClassName className = ClassName.get(typeElement);

            // λ©”μ†Œλ“œ 생성
            MethodSpec pullOut = MethodSpec.methodBuilder("pullOut")
                    .addModifiers(Modifier.PUBLIC)
                    .returns(String.class)
                    .addStatement("return $S", "Rabbit!")
                    .build();

            // 클래슀 생성
            TypeSpec magicMoja = TypeSpec.classBuilder("MagicMoja")
                    .addModifiers(Modifier.PUBLIC)
                    .addSuperinterface(className)
                    .addMethod(pullOut)
                    .build();
            Filer filer = processingEnv.getFiler();
            try {
                JavaFile.builder(className.packageName(), magicMoja)
                        .build()
                        .writeTo(filer);
            } catch (IOException e) {
                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "FATALERROR:" + e);
            }
        }
        return true;
    }
}

μ°Έκ³ .

λžŒλ‹€λŠ” 읡λͺ… 클래슀 κ΅¬ν˜„μ²΄μ™€ 달리 ν•˜μ§€ μ•ŠμŒ

λ₯Ό μ‚¬μš©ν•΄μ„œ λ©”μ†Œλ“œ, μƒμ„±μž 호좜λ₯Ό 맀우 κ°„κ²°ν•˜κ²Œ ν‘œν˜„ κ°€λŠ₯

κΈ°λ³Έ λ©”μ†Œλ“œ

κΈ°λ³Έ λ©”μ†Œλ“œ

κΈ°λ³Έ λ©”μ†Œλ“œ 및 μŠ€νƒœν‹± λ©”μ†Œλ“œ

κΈ°λ³Έ λ©”μ†Œλ“œ

λ‚ μ§œ, μ‹œκ°„ μ²˜λ¦¬κ°€ λ³΅μž‘ν•œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œλŠ” 보톡 μ‚¬μš©

κ΅¬ν˜„μ²΄ 제곡

μ“°λ ˆλ“œ μ£Όμš” κΈ°λŠ₯()

: ν˜„μž¬ μ“°λ ˆλ“œ λ©ˆμΆ”κΈ°

: λ‹€λ₯Έ μ“°λ ˆλ“œ 깨우기

: λ‹€λ₯Έ μ“°λ ˆλ“œ λŒ€κΈ°

의 ν•˜λŠ” 일

Implements Future,

Result

? μ˜€λΌν΄μ—μ„œ λ§Œλ“  Oracle JDK 11 버전뢀터 μƒμš©μœΌλ‘œ μ‚¬μš© μ‹œμ—λ§Œ 유료

Result

Result

μ‚¬μš©

λ¦¬ν”Œλ ‰μ…˜μ˜ μ‹œμž‘μ€

Result

리얼 μ„œλΈŒμ  νŠΈλŠ” μžμ‹ μ΄ ν•΄μ•Ό ν•  일만 ν•˜λ©΄μ„œ() ν”„λ‘μ‹œ μ‚¬μš©

뢀가적인 κΈ°λŠ₯(μ ‘κ·Ό μ œν•œ, λ‘œκΉ…, νŠΈλžœμž­μ…˜ λ“±) 제곡 μ‹œ 을 주둜 μ‚¬μš©

컴파일 μ‹œμ μ— λ₯Ό μ‚¬μš©ν•˜μ—¬ μ†ŒμŠ€μ½”λ“œμ˜ (abstract syntax tree) μ‘°μž‘

λ ˆμ§€μŠ€νŠΈλ¦¬ 생성기

더 μžλ°”, Java 8
더 μžλ°”, μ½”λ“œλ₯Ό μ‘°μž‘ν•˜λŠ” λ‹€μ–‘ν•œ 방법
Oracle JDK
Java Development Kit 8 Update Release Notes
Which versions of Java do you regularly use?
Oracle Java SE Support Roadmap
Java version history
What Does Long-Term Support Mean for OpenJDK?
java.lang.funcation package
Lambda Expressions
Shadowing
Method References
Default Methods
Evolving Interfaces
Iterable
Collection
Comparator
Spliterator
Package java.util.stream
Stream
StreamTest
Class Optional
Tired of Null Pointer Exceptions? Consider Using Java SE 8's "Optional"!
OptionalTest
Joda Time
JSR-310 슀팩
Design Principles
ALL ABOUT JAVA.UTIL.DATE
Date-Time
Standard Calendar
DateTest.java
DateTimeFormatter
Java Concurrency
example
sleep
interrupt
join
Executors
example
Future
CallableAndFuture.java
CompletableFuture
CompletionStage
CompletableFutureTestApp.java
ForkJoinPool
CompletableFutureCallbackTestApp.java
CombinationTestApp.java
CompletableFutureExceptionTestApp.java
PermGen Elimination project is promoting
Java 8: From PermGen to Metaspace
JVM λ©”λͺ¨λ¦¬ 이해와 μΌ€μ΄μŠ€
λ©”λͺ¨λ¦¬ λͺ¨λ‹ˆν„°λ§κ³Ό 원인뢄석
JVM 슀팩
μžλ°” μœ λ£Œν™”
Understanding JIT compiler
Difference between JDK, JRE and JVM in Java
List of JVM languages
Java JVM Run-time Data Areas
A Simple Java Native Interface(JNI) example in Java and Scala
How JVM Works – JVM Architecture?
The JVM Architecture Explained
JVM Internals
JaCoCo documentation
Branch Coverage for Arbitrary Languages Made Easy
Living in the Matrix with Bytecode Manipulation(New Relic)
ByteBuddy
Javassist
java.lang.instrument
ASM
Class<T>
ReflectionTest
ReflectionAnnotationTest
BallTest
@Inject μ„ μ–ΈμœΌλ‘œ ν•„λ“œ μ£Όμž…μ„ ν•΄μ£ΌλŠ” μ»¨ν…Œμ΄λ„ˆ μ„œλΉ„μŠ€
JUnit ReflectionUtils
The Reflection API
Java Reflection - Dynamic Proxies
SRP
ν”„λ‘μ‹œ νŒ¨ν„΄
Proxy Pattern example
Dynamic Proxy Classes
ν”„λ‘μ‹œ μΈμŠ€ν„΄μŠ€ 생성
CGlib
ByteBuddy
How do annotations like @Override work internally in Java?
Dagger
Project Lombok
Lombok Execution Path
μ• λ…Έν…Œμ΄μ…˜ ν”„λ‘œμ„Έμ„œ
AST
AutoValue
Immutables
Processor Interface
google/auto/service
ServiceProvider
square/javapoet
Filer
Annotation Processing 101
Project Lombok: Creating Custom Transformations
Annotation Processing in Java
Annotation Processing : Don’t Repeat Yourself, Generate Your Code.
Java programming language compiler - ANNOTATION PROCESSING