Spring Framework 6.0, Spring Boot 3.0 에서 Java 17 버전을 선택하여 9 버전 이후부터 업데이트 된 내용을 확인하고자 한다.
[Preparing for Spring Boot 3.0](https://spring.io/blog/2022/05/24/preparing-for-spring-boot-3-0)
Java 11(LTS) 까지의 업데이트
- Java 9
- Jigsaw module system 참고:Baeldung
- 라이브러리와 규모가 큰 시스템을 모듈화하고 강력한 접근 제어를 통해 느슨한 결합, 모듈 간 결합 방지, 보안성 향상 등에 이점을 갖는다
- JPMS(Java Platform Module System)의 일환으로 JRE를 작은 단위 모듈로 쪼개고 사용하는 모듈만 패키징하여 배포 가능(module-info.java)
- JShell(The Java Shell (Read-Eval-Print Loop)) Tool
- script 형태로 Main Method 없이 즉석에서 실행 가능한 도구
- jlink(The Java Linker)
- 최근에는 JDK만 제공하고 JRE는 제공하고 있지 않기 떄문에 필요시 jlink 기능을 사용하여 추출 가능
- Immutable Set(of)
Set<String> keySet = Set.of("key1", "key2", "key3") - Optional.stream()
List<String> filteredList = listOfOptionals.stream() .flatMap(Optional::stream) .collect(Collectors.toList()); - Interface private method 도입
@FunctionalInterface public interface PrivateMethodInterface { private static String staticPrivate() { return "Static Private"; } private String instancePrivate() { return "Instance Private"; } void printPrivateMethod(); default void check() { String result = staticPrivate(); PrivateMethodInterface privateMethodInterface = () -> { System.out.println(this.instancePrivate()); }; privateMethodInterface.printPrivateMethod(); result = privateMethodInterface.instancePrivate(); } } - New HTTP Client
- java.net.http 패키지하에 있는 신규 API로 HTTP/2 프로토콜, 웹소켓을 지원한다 ```java HttpRequest request = HttpRequest.newBuilder() .uri(new URI(“http://localhost:8080/members”)) .GET() .build();
HttpResponse
response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString()); ```
- Jigsaw module system 참고:Baeldung
- Java 10
- Optional.orElseThrow
- var 변수 선언 가능(타입 추론) 참고:Java 10 LocalVariable
- 주의사항
var variable1; // compile error without initializer var variable2 = null; // compile error is null public var variable3 = "Hello Java10"; // compile error non-local variable var lambda = (String s) -> s.length > 10; // lambda expression needs an explicit target-type //Predicate<String> filter = s -> s.length() > 10; var arr = {1, 2, 3}; // compile error, array initializer needs an explicit target-type - 사용 방법
int[] arr = {1, 2, 3}; for (var n : arr) { System.out.println(n); } var map = new HashMap<Integer, String>();
- 주의사항
- 불변 컬렉션 생성 메소드 추가(copyOf, toUnmodifiableSet, …)
- Parallel Full GC for G1
- G1GC에서 Full GC 발생 시 병렬 처리하여 성능 향상
- Java 11
- HTTP Client (http://openjdk.org/jeps/321)
- non-blocking backpressure를 이용한 비동기 스트림 처리 지원 API 추가
- HTTP2 를 구현하는 신규 클라이언트 API 제공, 기존 HttpURLConnection API 대체
- ZGC
- Z GarbageCollector로 stop-the-world 성능 저하 개선을 위한 목적으로 개발
- 처리시간이 10ms 를 초과하지 않아 짧은 지연시간 보장
- Z GarbageCollector로 stop-the-world 성능 저하 개선을 위한 목적으로 개발
- Lambda 내에서도 var 변수 사용 가능
Stream.of(1, 2, 3, 4) .map((var i) -> i * 2); .collect(Collectors.toList());- var 를 붙이는 이유는, var 앞에 @NonNull과 같은 애노테이션을 붙일 수 있고, 컴파일러가 타입추론을 해준다.
- Collection to Array
List<String> list = Arrays.asList("Hello", "Java", "11"); String[] array = list.toArray(String[]::new); - String 신규 method 추가
- isBlank, lines, strip, stripLeading, stripTrailing, repeat 등 신규 String Method 추가
- HTTP Client (http://openjdk.org/jeps/321)
Java 17(LTS) 까지의 업데이트
- Java 12, 13, 14
- switch 문 개선 및 표준화(12~14)
boolean isTodayHoliday = switch (day) { case "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY" -> false; case "SATURDAY", "SUNDAY" -> true; default -> throw new IllegalArgumentException("What's a " + day); }; - Collectors.teeing 신규 메소드(12)
- downstream collector에 대한 copmosite 제공
Collector<T, ?. R> teeing(Collector<? super T, ?, R1> downstream1, Collector<? super T, ?, R2> downstream2, BiFunction<? super R1, ? super R2, R> merger)double mean = Stream.of(1, 2, 3, 4) .collect(Collectors.teeing(Collectors.summingDouble(i -> i), Collectors.counting() , (sum, count) -> sum / count));
- downstream collector에 대한 copmosite 제공
- Pattern matching for instanceof
Object s = "Hello World"; // AS-IS if(s instanceof String) { String str1 = (String) s; System.out.println(str1); } // TO-BE if(s instanceof String str2) { System.out.println(str2); } - multi line string(13)
- 텍스트 블록 기능 추가
// AS-IS String JSON_STRING = "{\r\n" + "\"name\" : \"Baeldung\",\r\n" + "\"website\" : \"https://www.%s.com/\"\r\n" + "}"; // TO-BE String TEXT_BLOCK_JSON = """ { "name" : "Baeldung", "website" : "https://www.%s.com/" } """;
- 텍스트 블록 기능 추가
- NumberFormat 신규 method 추가(12, 14)
- NumberFormat fmt = NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
- record 키워드
- immutable 객체를 생성하는 방법 중 하나로 toString, equals, hashCode 등에 구현 자동 제공
public record User(int id, String password) { public User { if(password.isBlank() || password.length() < 10) { throw new IllegalArgumentException("Password length"); } } }; User user = new User(1, "password1234"); user.id(); user.password();
- immutable 객체를 생성하는 방법 중 하나로 toString, equals, hashCode 등에 구현 자동 제공
- switch 문 개선 및 표준화(12~14)
- Java 15
- CharSequence 클래스에 메소드 추가
- isEmpty
- TreeMap 클래스에 메소드 추가
- putIfAbsend, computeIfAbsent, ComputeIfPresent, compute, merge
- Sealed Class
- 상속관계에서 부모 클래스에서 자식 클래스의 집합을 제한하여 모델링을 간소화할 수 있도록 한다
public abstract sealed class Person permits Employee, Manager { //... }
- 상속관계에서 부모 클래스에서 자식 클래스의 집합을 제한하여 모델링을 간소화할 수 있도록 한다
- Hidden Class
- Runtime에서 클래스를 생성하고 reflection을 통해 간접적으로 사용하는 프레임워크에서 사용하기 위한 것
- Hidden Class에서는 접근 제어를 member로 정의하며 다른 class와 독립적으로 unload할 수 있다
- ZGC 운영 환경 사용할 수 있는 준비 완료
- CharSequence 클래스에 메소드 추가
- Java 16
- InvokeHandler 인터페이스에 invokeDefault default method 정의 (Proxy Instances)
interface HelloWorld { default String hello() { return "world"; } }Object proxy = Proxy.newProxyInstance(getSystemClassLoader(), new Class<?>[] { HelloWorld.class }, (prox, method, args) -> { if (method.isDefault()) { return InvocationHandler.invokeDefault(prox, method, args); } // ... } ); Method method = proxy.getClass().getMethod("hello"); assertThat(method.invoke(proxy)).isEqualTo("world"); - NIO SocketChannel, ServerSocketChannel에 AF_UNIX(Unix domain sockets) 추가
- Stream.toList() 메소드 추가
List<String> integersAsString = Arrays.asList("1", "2", "3"); List<Integer> ints = integersAsString.stream().map(Integer::parseInt).collect(Collectors.toList()); List<Integer> intsEquivalent = integersAsString.stream().map(Integer::parseInt).toList();
- InvokeHandler 인터페이스에 invokeDefault default method 정의 (Proxy Instances)
- Java 17
- Sealed Class 기능 완료
- java.lang.Class에는 아래와 같이, 2개의 새로운 메서드가 추가 되어, 어떤 클래스가 sealed되어 있는지 확인하고, 허가된 서브클래스의 리스트를 가져오는 역할을 합니다
- Class<?>[] getPermittedSubclasses(); boolean isSealed();
- switch 문 패턴 매칭
- DatagramSocket이 Multicast Group에 바로 join되어 사용 가능하도록 수정
- Foreign Function & Memory API
- JNI 대체를 위한 것으로 모든 네이티브 라이브러리를 쉽게 사용할 수 있도록 하는 것이 목표. 자바 프로그램이 네이티브 라이브러리와 상호 작용할 수 있는 기반 제공
- Sealed Class 기능 완료
Java 21 까지의 변화
- Java 18: 비교적 작지만, 개발 환경을 쾌적하게 만들어주는 실용적인 기능이 추가되었다.
- UTF-8 기본 인코딩 적용(JEP400): 한글 깨짐 현상등이 발생하였는데 file.encoding의 기본값이 모든 플랫폼에서 UTF-8로 통일되어 이식성 향상
- 간단한 웹서버(Simple Web Server JEP408): 정적 파일을 손쉽게 제공할 수 있는 커맨드라인 툴 추가
- 별도 톰캣 설정 없이 터미널에서 jwebserver 명령어만 입력하면 로컬 웹 서버가 실행되어 테스트나 로컬 개발 시 유용하다.
- Java 19 & 20: 프로젝트 Loom 의 결과물들이 프리뷰로 등장
- Virtual Thread 등장
- 19에서 첫 프리뷰로 등장하였고 20에서 피드백을 받아 개선 21에 정식 기능
- 다양한 패턴 매칭의 진화
- switch문 패턴 매칭과 record 패턴 매칭이 계속해서 프리뷰로 제공되며 문법이 다듬어졌다.
- Virtual Thread 등장
- Java 21
- Virtual Thread(JEP444)
- 기존의 OS 스레드(Platform Thread)와 달리 JVM내에 관리되는 매우 가벼운 스레드
- 장점: 기존에 스레드를 생성하는 데 많은 리소스가 필요해 스레드 풍르 사용해야 했지만, 가상 스레드는 수백만개를 생성해 메모리나 컨텍스트 스위칭 비용이 매우 적다
- 효과: Webflux와 같은 복잡성 없이, 기존의 동기식 코드를 그대로 작성하며 높은 처리량을 낼 수 있다.
- Sequenced Collections(순서가 있는 컬렉션 - JEP431)
- 컬렉션 프레임워크에 처음과 마지막 요소에 접근하는 표준화된 인터페이스가 추가되었다.
- SequencedCollection, SequencedSet, SequencedMap
- 제공되는 메소드: addFirst(), addLast(), getFirst(), getLast(), removeFirst(), removeLast(), reversed()
- SequencedSet / SequencedMap특징
- HashSet은 순서를 전혀 보장하지 않기 때문에 SequencedSet에 포함되지 않는다. 순서를 가지는 Set들만 이 새로운 인터페이스를 구현하도록 개편
- LinkedHashSet/LinkedHashMap(입력 순서 보장): add 된 순서를 보장 - 시간복잡도 O(1)
SequencedSet<String> set1 = new LinkedHashSet<>(); set1.add("C"); set1.add("B"); set1.add("A"); System.out.println(set1.getFirst(); // C System.out.println(set1.getLast()); // A - TreeSet/TreeMap(값 기준 정렬 보장): 데이터의 값을 기준으로 정렬된 순서 보장 - 시간복잡도 O(logN)
- 구조적으로 TreeSet이 구현하는 SortedSet 인터페이스가 이제 SequencedSet을 상속받도록 변경 됨
- 따라서 정렬된 상태를 기준으로 getFirst()(가장 작은 값), getLast() (가장 큰값)
SequencedSet<String> set2 = new TreeSet<>(); set2.add("C"); set2.add("B"); set2.add("A"); System.out.println(set2.getFirst(); // A System.out.println(set2.getLast()); // C
- Siwtch 패턴 매칭(JEP441)
- if-else instanceof 체인을 깔끔한 switch 문으로 대체 가능, null처리도 switch 내부에서 가능
switch(obj) { case Integer i -> System.out.println("Integer: " + i); case String s -> System.out.println("String: " + s); case null -> System.out.println("NULL!!"); default -> obj.toString();] }
- if-else instanceof 체인을 깔끔한 switch 문으로 대체 가능, null처리도 switch 내부에서 가능
- Record 패턴(JEP440)
- record객체의 구성 요소를 분해아혀 바로 변수로 추출할 수 있는 기능이 도입되어 switch 패턴 매칭과 결합했을 때 좋은 시너지 발생
- Generational ZGC (세대별 ZGC - JEP439)
- Young Generation과 Old Generation을 분리하여 관리
- Virtual Thread(JEP444)
Java 25까지의 정리: 개발자 생산성 향상, 성능 최적화에 초점
- Java 22
- Unnamed Variables & Patterns(_): 가독성을 위해 사용하지 않는 변수를 하이브리드 언어처럼 언더스코어(_) 표시(catch(Exception _)
- [Preview] super() 호출 전, 유효성 검사와 같은 로직을 실행할 수 있게 하여 객체 초기화를 더 유연해졌다.
- [Preview] Stream Gatherers: stream 에서 windowFixed, scan같은 복잡한 중간 연산을 커스텀 가능
- Java 23
- Markdown Documentation Comments: javadoc에서 HTMl 대신 MD를 사용할 수 있도록 했다.
- [Preview] Primitive Types in patterns: instanceof, switch문에서 Primitive 타입이 사용할 수 있게 되었다
- [Preview] Module Import Declarations: import module java.base; 한줄로 해당 모듈 내의 모든 패키지를 가저올 수 있게 되었다.
- ZGC - Generational Mode Default: ZGC가 세대별 모드를 기본값으로 사용하여 성능 향상
- Java 24
- Stream Gatherers 정식화
- Class-File API: Java 클래스 파일을 읽고 수정하는 API가 표준화 되었다. (바이트 버디같은 외부라이브러리 의존도를 낮출 수 있게 되었다)
- [Experimental] Compact Object Headers: 64비트 아키텍트에서 객체 헤더 크기를 줄여 메모리 사용량을 절감하는 실험적 기능 도입, 클라우드 환경에서 매우 유용하다.
- 왜 클라우드 환경에서 유용한가? 메모리 밀집도의 향상
- 클라우드 환경에서는 CPU, RAM 사용량만큼 비용지불이 되기 때문
- Heap 메모리 사용절감, CPU 캐시 효율 및 성능 향상, GC 부하 감소등으로 비용최적화 가능
- Java 25
- 언어 및 문법
- Flexible Constructor Bodies: super() 호출 전 유효성 검사나 필드 초기화 로직을 넣을 수 있게 되었다.
- Module Import 정식
- Scoped Values 정식: ThreadLocal의 현대적 대안. 불변성을 보장하며 가상스레드 환경에서 훨씬 안전하고 효율적으로 데이터를 공유한다.
- ThreadLocal의 한계 극복
- 가변성: 누구나 set 메서드로 값을 바꿀 수 있기 때문에 추적 어려움
- 무한 생명주기: remove()를 명시적으로 호출하지 않으면 메모리 누수 가능
- 성능 저하: 가상 스레드 환경에서는 수만개의 스레드가 복사본을 갖게되어 메모리 부하가 커진다.
- ThreadLocal은 각 스레드 내부에 ThreadLocalMap이라는 저장소를 갖고 새로운 스레드가 생성될 때 부모의 데이터를 자신의 저장소로 전부 복사한다.
- ScopedValue의 핵심 특징
- 특정 범위 내에서만 유효한 값을 의미한다
- 불변성: 한번 설정된 값은 해당 범위 내에서 불변성을 갖기 때문에 멀티스레드 환경에서도 데이터 신뢰성 보장 가능
- 자동 생명주기 관리: 별도 삭제코드를 작성할 필요 없이 블록이 끝나면 값은 자동으로 사라진다.
- 가상스레드 최적화: 데이터를 스레드마다 복사하는 대신, 호출 스택을 통해 참조를 공유한다. 수백만개의 가상 스레드가 있어도 메모리 오버헤드가 거의 없다.
- ScopedValue는 부모의 데이터를 전부 복사하는 것이 아닌 호출 스택의 프레임과 연결된 불변의 데이터 구조를 활용하여 값 자체는 힙에 하나만 존재하고, 이 값을 참조하라는 포인터(링크)가 추가된다.
- ThreadLocal의 한계 극복
- 성능 및 런타임 최적화(인프라 효율)
- Compact Object Headers 정식
- AOT Method Profiling: 이전 실행의 프로파일링 정보를 저장했다가 재시작 시 활용, 이를 통해 애플리케이션의 웜업 시간이 드라마틱하게 단축된다.
- 모니터링 및 진단 (운영 안전성)
- JFR Method Timing & Tracing
- 언어 및 문법
출처
- 출처: JDK Release Notes
- 출처: Baeldung: NewFeatures
- 출처: 여기어때 기술 블로그