λ°±κΈ°μ λμ λ μλ°, Java 8 , λ μλ°, μ½λλ₯Ό μ‘°μνλ λ€μν λ°©λ² κ°μλ₯Ό μμ½ν λ΄μ©μ
λλ€.
Java 8
Java 8
LTS(Long-Term-Support) λ²μ
μ£Όμ κΈ°λ₯
λλ€ ννμ, λ©μλ λ νΌλ°μ€, μ€νΈλ¦Ό API, Optional ...
Open JDK: Oracle, AdoptOpenJDK, Amazon Corretto, Azul Zulu
.
LTS(Long-Term-Support)
λΉ-LTS
μ§μ κΈ°κ°: λ°°ν¬ μ΄ν 6κ°μ
(λ€μ λ²μ μ΄ λμ€λ©΄ μ§μ μ’
λ£)
LTS
λ°°ν¬ μ£ΌκΈ°: 3λ
(맀 6λ²μ§Έ λ°°ν¬νμ΄ LTS)
μ§μ κΈ°κ°: 5λ
μ΄μ
(JDK μ 곡 λ°΄λμ μ΄μ©νλ μλΉμ€μ λ°λΌ μ°¨μ΄)
μ€μ μλΉμ€ μ΄μ νκ²½μμλ LTS λ²μ κΆμ₯
맀λ
3μκ³Ό 9μμ μ λ²μ λ°°ν¬
Functional Interface & Lambda
ν¨μν μΈν°νμ΄μ€(Functional Interface)
Copy @FunctionalInterface
public interface RunSomething {
void doIt();
}
μΆμ λ©μλλ₯Ό λ± νλλ§ κ°μ§κ³ μλ μΈν°νμ΄μ€
SAM(Single Abstract Method) μΈν°νμ΄μ€
@FuncationInterface μ λ
Έν
μ΄μ
μ κ°μ§κ³ μλ μΈν°νμ΄μ€
.
λλ€ ννμ(Lambda Expressions)
Copy 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 νμ
μ 리ν΄νλ ν¨μ μΈν°νμ΄μ€
Copy 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 νμ
μ 리ν΄νλ ν¨μ μΈν°νμ΄μ€
Copy @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 νμ
μ λ°μμ μ무κ°λ 리ν΄νμ§ μλ ν¨μ μΈν°νμ΄μ€
Copy @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 νμ
μ κ°μ μ 곡νλ ν¨μ μΈν°νμ΄μ€
Copy @Test
void get() throws Exception {
/**
* T get()
*/
Supplier<Integer> get10 = () -> 10;
Assertions.assertEquals(10, get10.get());
}
Predicate<T>
T νμ
μ λ°μμ booleanμ 리ν΄νλ ν¨μ μΈν°νμ΄μ€
ν¨μ μ‘°ν©μ© λ©μλ
Copy 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 μμ)
μ
λ ₯κ° νλλ₯Ό λ°μμ λμΌν νμ
μ 리ν΄νλ ν¨μ μΈν°νμ΄μ€
Copy 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>μ νΉμν νν
λμΌν νμ
μ μ
λ ΅κ° λ κ°λ₯Ό λ°μ 리ν΄νλ ν¨μ μΈν°νμ΄μ€
Copy @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
Lambda Expressions
(μΈμ 리μ€νΈ) -> {λ°λ}
.
μΈμ 리μ€νΈ
μΈμκ° ν κ°: (one) λλ one
μΈμκ° μ¬λ¬ κ°: (one, two)
μΈμμ νμ
μ μλ΅ κ°λ₯(μ»΄νμΌλ¬κ° μΆλ‘ νμ§λ§ λͺ
μλ κ°λ₯)
.
λ°λ
νμ΄ν μ€λ₯Έμͺ½μ ν¨μ λ³Έλ¬Έ μ μ
μ¬λ¬ μ€μΈ κ²½μ° {} μ¬μ©
ν μ€μΈ κ²½μ° λ°λ, return μλ΅ κ°λ₯
.
λ³μ μΊ‘μ²(Variable Capture)
λ‘컬 λ³μ μΊ‘μ²
final, effective final μΈ κ²½μ°μλ§ μ°Έμ‘° κ°λ₯
κ·Έλ μ§ μμ κ²½μ°, concurrency λ¬Έμ κ° λ°μν μ μμ΄μ μ»΄νμΌλ¬κ° λ°©μ§
effective final
μλ° 8λΆν° μ§μνλ κΈ°λ₯
final ν€μλλ₯Ό μ¬μ©νμ§ μμ§λ§, λ³κ²½μ΄ μλ λ³μλ₯Ό μ΅λͺ
ν΄λμ€ κ΅¬ν체, λλ€μμ μ°Έμ‘° κ°λ₯
λλ€λ μ΅λͺ
ν΄λμ€ κ΅¬ν체μ λ¬λ¦¬ Shadowing νμ§ μμ
μ΅λͺ
ν΄λμ€λ μλ‘μ΄ μ€μ½₯μ λ§λ€μ§λ§, λλ€λ λλ€λ₯Ό κ°μΈκ³ μλ μ€μ½₯κ³Ό κ°μ
λλ€λ₯Ό κ°μΌ μ€μ½₯μ μλ λμΌν μ΄λ¦μ λ³μ μ μ λΆκ°
Method Reference
Method References λ₯Ό μ¬μ©ν΄μ λ©μλ, μμ±μ νΈμΆλ₯Ό λ§€μ° κ°κ²°νκ² νν κ°λ₯
Copy @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
Default Methods
Collection interface
Copy /**
* 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
Iterable κΈ°λ³Έ λ©μλ
Copy 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
}
Collection κΈ°λ³Έ λ©μλ
parallelStream(), spliterator()
Copy 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());
}
Comparator κΈ°λ³Έ λ©μλ λ° μ€νν± λ©μλ
static reverseOrder() / naturalOrder()
static nullsFirst() / nullsLast()
Copy 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());
}
Spliterator κΈ°λ³Έ λ©μλ
forEachRemaining(Consumer)
Stream
Package java.util.stream
Stream
λ°μ΄ν°λ₯Ό λ΄κ³ μλ μ μ₯μ(컬λ μ
)κ° μλλΌ, μ΄λ ν μ°μλ λ°μ΄ν°λ₯Ό μ²λ¦¬νλ μ€νΌλ μ΄μ
λ€μ λͺ¨μ
μ€νΈλ¦Ό μ²λ¦¬ μ λ°μ΄ν° μλ³Έμ λ³κ²½νμ§ μμ
μ€νΈλ¦ΌμΌλ‘ μ²λ¦¬νλ λ°μ΄ν°λ μ€μ§ ν λ²λ§ μ²λ¦¬
μ€μκ°μΌλ‘ μ€νΈλ¦Ό λ°μ΄ν°κ° λ€μ΄μ¬ κ²½μ° λ¬΄ν μ²λ¦¬(Short Circuit λ©μλλ₯Ό μ¬μ©ν΄μ μ ν κ°λ₯)
μ€κ° μ€νΌλ μ΄μ
μ κ·Όλ³Έμ μΌλ‘ lazy νΉμ±μ κ°μ§
λ°μ΄ν°κ° λ°©λν κ²½μ° parallelStream() μΌλ‘ μμ½κ² λ³λ ¬ μ²λ¦¬ κ°λ₯
μ€λ λ μμ±, λ³λ ¬μ²λ¦¬ ν μμ§, μ€λ λ κ° μ»¨ν
μ€νΈ μ€μμΉ λ±μ λΉμ©μΌλ‘ 무쑰건 λΉ¨λΌμ§λ 건 μλ
μ€νΈλ¦Ό νμ΄νλΌμΈ
0 λλ λ€μμ μ€κ° μ€νΌλ μ΄μ
κ³Ό ν κ°μ μ’
λ£ μ€νΌλ μ΄μ
μΌλ‘ ꡬμ±
μ€νΈλ¦Όμ λ°μ΄ν° μμ€λ μ€μ§ ν°λ―Έλ μ€νΌλ€μ΄μ
μ μ€νν λμλ§ μ²λ¦¬
μ€κ° μ€νΌλ μ΄μ
(intermediate operation)
Stateless / Stateful μ€νΌλ μ΄μ
μΌλ‘ λ μμΈνκ² κ΅¬λΆ κ°λ₯
λλΆλΆ Stateless operation
μ΄μ μμ€ λ°μ΄ν°λ₯Ό μ°Έμ‘°ν΄μΌ νλ μ€νΌλ μ΄μ
(ex. distinct, sorted)μ Stateful μ€νΌλ μ΄μ
filter, map, limit, skip, sorted ...
μ’
λ£ μ€νΌλ μ΄μ
(terminal operation)
collect, allMatch, count, forEach, min, max ...
.
Stream API
StreamTest
νν°λ§
Copy @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());
}
μ€νΈλ¦Ό λ³κ²½
Copy @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());
}
μ€νΈλ¦Ό μμ±κ³Ό μ ν
Copy @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);
}
μ€νΈλ¦Όμ μλ λ°μ΄ν°κ° νΉμ 쑰건μ λ§μ‘±νλμ§ νμΈ
Copy @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
Class Optional
Copy 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 λ‘ λ λ² κ°μΈμ§ μκΈ°
Tired of Null Pointer Exceptions? Consider Using Java SE 8's "Optional"!
.
Optional API
OptionalTest
Optional μμ±
Copy Optional.of()
Optional.ofNullable()
Optional.empty()
Optional κ° ν¬ν¨ μ¬λΆ νμΈ
Copy optional.isPresent()
optional.isEmpty() // Java 11 μ΄ν
Optional κ° κ°μ Έμ€κΈ°
Copy optional.get(); // λΉμ΄μμ κ²½μ° NoSuchElementException μμΈ λ°μ
Optional μ κ°μ΄ μ‘΄μ¬ν κ²½μ° λμ μν
Copy optional.ifPresent(oc -> System.out.println(oc.getTitle()));
Optional μ κ°μ΄ μμ κ²½μ° κΊΌλ΄κ³ , 무쑰건 μλ‘μ΄ ν΄λμ€ μμ±
Copy optional.orElseGet(OptionalTest::createNewClass);
Optional μ κ°μ΄ μμ κ²½μ° κΊΌλ΄κ³ , μμΌλ©΄ μλ‘μ΄ ν΄λμ€ μ 곡
Copy result.orElseGet(OptionalTest::createNewClass);
Optional μ κ°μ΄ μμ κ²½μ° κΊΌλ΄κ³ , μμΌλ©΄ μμΈ
Copy assertThrows(NoSuchElementException.class, () -> {
result.orElseThrow();
});
assertThrows(IllegalStateException.class, () -> {
result.orElseThrow(IllegalStateException::new);
});
Optional κ°μ νν°λ§
Copy Optional<OnlineClass> jpaClass = result.filter(Predicate.not(OnlineClass::isClosed));
Optional κ°μ 맀ν(λ³ν)
Copy 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λΆν° μμνλ λ±..)
λ μ§, μκ° μ²λ¦¬κ° 볡μ‘ν μ ν리μΌμ΄μ
μμλ λ³΄ν΅ Joda Time μ¬μ©
java 8 μμ μ 곡νλ Date-Time API
Design Principles
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 λ₯Ό μ¬μ©ν΄μ μΌμλ₯Ό νΉμ ν λ¬Έμμ΄λ‘ ν¬λ§€ν
κ°λ₯
μ°Έκ³
.
DateTest.java
κΈ°κ³μ© μκ°
(machine time) νν
UTC(Universal Time Coordinated) == GMT(Greenwich Mean Time)
λ³΄ν΅ μκ°μ μ¬λ κ²½μ° μ¬μ©
Copy 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 μ νΉμ μΌμ
Copy 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]
λ μ§ μ°μ°
Copy LocalDateTime now = LocalDateTime.now();
LocalDateTime plusDay = now.plus(10, ChronoUnit.DAYS);
LocalDateTime plusMonth = now.plus(2, ChronoUnit.MONTHS);
κΈ°κ° νν
Copy // 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
Copy // 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 λ‘ μνΈ λ³ν κ°λ₯
Copy 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
Java Concurrency
Concurrent Software
λμμ μ¬λ¬ μμ
μ ν μ μλ μννΈμ¨μ΄
Java Concurrency Programming
λ©ν°νλ‘μΈμ±(ProcessBuilder)
Java multi-thread Programming
μ°λ λ μ£Όμ κΈ°λ₯(example )
sleep : νμ¬ μ°λ λ λ©μΆκΈ°
λ€λ₯Έ μ°λ λκ° μ²λ¦¬ν μ μλλ‘ κΈ°ν μ 곡(λ½μ λμ§ μμ, λ°λλ½ λ°μ κ°λ₯)
interrupt : λ€λ₯Έ μ°λ λ κΉ¨μ°κΈ°
λ€λ₯Έ μ°λ λλ₯Ό κΉ¨μμ interruptedExeption λ°μ
join : λ€λ₯Έ μ°λ λ λκΈ°
λ€λ₯Έ μ°λ λκ° λλ λκΉμ§ λκΈ°
λ€μμ μ€λ λλ₯Ό μ½λ©μΌλ‘ κ΄λ¦¬νκΈ° μ΄λ €μ. Execute μμ±.
Executors
High-Level Concurrency Programming
μ°λ λλ₯Ό μμ±νκ³ κ΄λ¦¬νλ μμ
μ μ ν리μΌμ΄μ
μμ λΆλ¦¬νκ³ Executors μκ² μμ
Executors μ νλ μΌ
μ°λ λ μμ±: μ ν리μΌμ΄μ
μ΄ μ¬μ©ν μ°λ λ νμ λ§λ€μ΄ κ΄λ¦¬
μ°λ λ κ΄λ¦¬: μ°λ λ μλͺ
μ£ΌκΈ°λ₯Ό κ΄λ¦¬
μμ
μ²λ¦¬ λ° μ€ν: μ°λ λλ‘ μ€νν μμ
μ μ 곡ν μ μλ API μ 곡
μ£Όμ μΈν°νμ΄μ€
Executor
: execute(Runnable)
ExecutorService
: Executor λ₯Ό μμ λ°μ μΈν°νμ΄μ€
Callable, Runnable μ€ν, Executor μ’
λ£
μ¬λ¬ Callable λμ μ€ν λ±μ κΈ°λ₯ μ 곡
ScheduledExecutorService
: ExecutorService λ₯Ό μμ λ°μ μΈν°νμ΄μ€
νΉμ μκ° μ΄ν λλ μ£ΌκΈ°μ μΌλ‘ μμ
μ€ν
example
Copy /**
* 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 κ³Ό μ μ¬νμ§λ§ μμ
μ κ²°κ³Όλ₯Ό 리ν΄
Future
λΉλκΈ°μ μΈ μμ
μ νμ¬ μνλ₯Ό μ‘°ννκ±°λ κ²°κ³Ό 리ν΄
CallableAndFuture.java
Copy /**
* 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
.
λΉλκΈ°λ‘ μμ
μ€ννκΈ°
Copy /**
* 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());
.
μ½λ°± μ 곡νκΈ°
μ½λ°± μ체λ₯Ό λ λ€λ₯Έ μ°λ λμμ μ€ν κ°λ₯
Copy /**
* 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();
.
μ‘°ν©νκΈ°
Copy /**
* 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();
}
.
μμΈμ²λ¦¬
Copy /**
* 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 ν¬ν¨ λͺ¨λ νμ
μ μΈλΆμ μ¬μ© κ°λ₯
Copy static class XXX<@Chicken T> {
/**
* <C> : type parameter
* C : type
*/
public static <@Chicken C> void print(C c){
}
}
μ λ
Έν
μ΄μ
μ€λ³΅ μ¬μ© κ°λ₯
Copy // μ€λ³΅ μ¬μ©ν μ λ
Έν
μ΄μ
@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 νλ μμν¬λ₯Ό μ¬μ©ν΄μ λ°°μ΄μ λ³λ ¬λ‘ μ λ ¬
νλ κΈ°λ₯ μ 곡
λ³λ ¬ μ λ ¬ μκ³ λ¦¬λ¬
λ°°μ΄μ λλ‘ κ³μ μͺΌκ° ν ν©μΉλ©΄μ μ λ ¬
Copy 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
μμ)
κΈ°λ³Έκ°μΌλ‘ μ νλ ν¬κΈ°
λ₯Ό κ°μ§κ³ μμ
Metaspace
ν΄λμ€ λ©νλ°μ΄ν°λ₯Ό λ΄λ μ μ₯μ(Heap μμμ΄ μλλΌ, Native Memory
μμ)
κΈ°λ³Έκ°μΌλ‘ μ νλ ν¬κΈ°λ₯Ό κ°μ§κ³ μμ§ μμ(νμν λ§νΌ κ³μ μ¦κ°
)
java 8 λΆν°λ PermGen κ΄λ ¨ μ΅μ
μ 무μ
Copy -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)λ‘ μ»΄νμΌ κ°λ₯
μλ° μ λ£ν ? μ€λΌν΄μμ λ§λ Oracle JDK 11 λ²μ λΆν° μμ©μΌλ‘ μ¬μ© μμλ§ μ λ£
JVM μΈμ΄
JVM κΈ°λ°μΌλ‘ λμνλ νλ‘κ·Έλλ° μΈμ΄
Clojure, Groovy, JRuby, Jython, Kotlin, Scala, ...
μ°Έκ³
.
JVM ꡬ쑰
Class Loader System
.class μμ λ°μ΄νΈμ½λλ₯Ό μ½κ³ λ©λͺ¨λ¦¬μ μ μ₯
loading: ν΄λμ€ μ½μ΄μ€λ κ³Όμ
linking: λ νΌλ°μ€λ₯Ό μ°κ²°νλ κ³Όμ
initialization: static κ°λ€μ μ΄κΈ°ν λ° λ³μμ ν λΉ
Memory
Java JVM Run-time Data Areas
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++λ‘ μμ±λ λΌμ΄λΈλ¬λ¦¬
μ°Έκ³
.
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 λ₯Ό μ¬μ©ν΄μ ν΄λμ€μ μλ λ©ν μ 보 μ‘°ν
μ°Έκ³
.
λ°μ΄νΈμ½λ μ‘°μ λΌμ΄λΈλ¬λ¦¬
ByteBuddy
.class νμΌ μ체λ₯Ό λ³κ²½μν€λ λ°©λ²
κΆμ₯νλ λΌμ΄λΈλ¬λ¦¬
Copy new ByteBuddy().redefine(Moja.class)
.method(named("pullOut")).intercept(FixedValue.value("Rabbit!"))
.make().saveIn(new File("../target/classes/"))
Javassist
ν΄λμ€ λ‘λκ° ν΄λμ€λ₯Ό μ½μ΄μ¬ λ javaagent λ₯Ό κ±°μ³μ λ³κ²½λ λ°μ΄νΈμ½λλ₯Ό μ½μ΄μ΄
premain: μμ μ λΆμ΄λ λ°©μ
agentmain: λ°νμ μ€ λμ μΌλ‘ λΆμ΄λ λ°©μ
Copy 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);
}
Reflection
리νλ μ
μ μμμ Class<T>
Class<T> μ κ·Ό λ°©λ²
λͺ¨λ ν΄λμ€λ₯Ό λ‘λ© ν λ€μ Class<T> μΈμ€ν΄μ€ μμ±
νμ
.class
λ‘ μ κ·Ό κ°λ₯
λͺ¨λ μΈμ€ν΄μ€λ getClass() λ©μλ 보μ
μΈμ€ν΄μ€.getClass()
λ‘ μ κ·Ό κ°λ₯
ν΄λμ€λ₯Ό λ¬Έμμ΄λ‘ μ½μ΄μ€λ λ°©λ²
ν΄λμ€ν¨μ€μ ν΄λΉ ν΄λμ€κ° μλ€λ©΄ ClassNotFoundException λ°μ
Copy 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: ν΄λΉ μ λ
Έν
μ΄μ
μ νμ ν΄λμ€κΉμ§ μ λ¬ν κ²μΈκ°.
Copy @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Inherited
public @interface MyAnnotation {
String name() default "aaron";
int number();
}
Reflection
getAnnotations(): μμλ°μ(@Inherit) μ λ
Έν
μ΄μ
κΉμ§ μ‘°ν
getDeclaredAnnotations(): μκΈ° μμ μλ§ λΆμ΄μλ μ λ
Έν
μ΄μ
μ‘°ν
Copy // μμλ°μ(@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
BallTest
Copy /**
* 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 νλ μμν¬ λ§λ€μ΄λ³΄κΈ°
@Inject μ μΈμΌλ‘ νλ μ£Όμ
μ ν΄μ£Όλ 컨ν
μ΄λ μλΉμ€
ContainerService.getObject
classType μ ν΄λΉνλ νμ
μ κ°μ²΄ μμ±
ν΄λΉ κ°μ²΄μ νλ μ€μ @Inject κ° μλ€λ©΄ ν΄λΉ νλλ κ°μ΄ λ§λ€μ΄ μ 곡
Copy 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
λ°νμμ μΈν°νμ΄μ€/ν΄λμ€μ νλ‘μ μΈμ€ν΄μ€/ν΄λμ€λ₯Ό λ§λ€μ΄ μ¬μ©νλ νλ‘κ·Έλλ° κΈ°λ²
Spring Data JPA λ μ΄λ»κ² λμν κΉ?
μΈν°νμ΄μ€ νμ
μ μΈμ€ν΄μ€λ λκ° λ§λ€μ΄ μ€κΉ?
JpaRepository μΈν°νμ΄μ€λ₯Ό μμλ°μΌλ©΄ κ°μ²΄λ μμ±λκ³ , λΉμΌλ₯΄λ λ±λ‘
Spring AOP κΈ°λ°μΌλ‘ λμνλ©° RepositoryFactorySupport μμ νλ‘μ κ°μ²΄ μμ±
μμ±λ νλ‘μ κ°μ²΄κ° λΉμΌλ‘ λ±λ‘λκ³ μ£Όμ
Dynamic Proxy μ¬μ© μ
Hibernate lazy initialzation ...
.
Proxy Pattern
νλ‘μμ λ¦¬μΌ μλΈμ νΈκ° 곡μ νλ μΈν°νμ΄μ€κ° μκ³ , ν΄λΌμ΄μΈνΈλ ν΄λΉ μΈν°νμ΄μ€ νμ
μΌλ‘ νλ‘μ μ¬μ©
ν΄λΌμ΄μΈνΈλ νλ‘μλ₯Ό κ±°μ³μ λ¦¬μΌ μλΈμ νΈλ₯Ό μ¬μ©
νλ‘μλ λ¦¬μΌ μλΈμ νΈμ λν μ κ·Ό κ΄λ¦¬, λΆκ° κΈ°λ₯ μ 곡, 리ν΄κ° λ³κ²½ κ°λ₯
λ¦¬μΌ μλΈμ νΈλ μμ μ΄ ν΄μΌ ν μΌλ§ νλ©΄μ(SRP ) νλ‘μ μ¬μ©
λΆκ°μ μΈ κΈ°λ₯(μ κ·Ό μ ν, λ‘κΉ
, νΈλμμ
λ±) μ 곡 μ νλ‘μ ν¨ν΄ μ μ£Όλ‘ μ¬μ©
λ¨μ
νλ‘μ ν¨ν΄μΌλ‘ ꡬννλ κ²μ λ²κ±°λ‘μ΄ μΌ
λΆκ°μ μΈ κΈ°λ₯μ μΆκ°ν λλ§λ€ λ³λ νλ‘μ μμ± νμ
νλ‘μλ‘ νλ‘μλ₯Ό κ°μΈμΌ νλ μΌλ λ°μ
λͺ¨λ ꡬν체μμ μλ νκ²μΌλ‘ μμνλ©΄μ μ€λ³΅ μ½λ λ°μ
Proxy Pattern example
νλ‘μ ν¨ν΄μ λ¬Έμ λ₯Ό ν΄κ²°νκΈ° μν΄ λμ μΌλ‘ λ°νμμ νλ‘μλ₯Ό μμ±ν΄λ΄λ λ€μ΄λλ―Ή νλ‘μ
λ±μ₯
.
Dynamic Proxy
λ°νμμ νΉμ μΈν°νμ΄μ€λ€μ ꡬννλ ν΄λμ€ λλ μΈμ€ν΄μ€λ₯Ό λ§λλ κΈ°μ
Copy 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
μΈν°νμ΄μ€ μμ΄ νλ‘μ λ§λ€κΈ°
CGlib
Spring, Hibernate μμλ μ¬μ©νλ λΌμ΄λΈλ¬λ¦¬
λ²μ νΈνμ±μ΄ μ’μ§ μμμ μλ‘ λ€λ₯Έ λΌμ΄λΈλ¬λ¦¬ λ΄λΆμ λ΄μ₯λ ννλ‘ μ 곡λκΈ°λ ν¨
Copy 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);
ByteBuddy
λ°μ΄νΈ μ½λ μ‘°μ λΏ μλλΌ λ°νμ(λ€μ΄λλ―Ή) νλ‘μ μμ± μμλ μ¬μ©
Copy 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 λ₯Ό μ¬μ©νμ¬ μνΌ ν΄λμ€μ ν΄λΉνλ λ©μλμ μΌμΉνλ νλͺ©μ΄ μλ€λ©΄ μ»΄νμΌ μ€λ₯ λ°μ
Dagger
μ»΄νμΌ νμ DI μ 곡
μ₯μ
λ°νμ λΉμ©μ΄ μ λ‘
λ¨μ
κΈ°μ‘΄ ν΄λμ€ μ½λλ₯Ό λ³κ²½ν λλ μ½κ°μ hack νμ
.
Lombok
@Getter, @Setter λ±μ μ λ
Έν
μ΄μ
κ³Ό μ λ
Έν
μ΄μ
νλ‘μΈμλ₯Ό μ 곡νμ¬ νμ€ μμ± μ½λλ₯Ό κ°λ°μ λμ μμ±ν΄μ£Όλ λΌμ΄λΈλ¬λ¦¬
둬볡μ λμ μ리
둬볡μ λ
Όλ 거리
곡κ°λ API κ° μλ μ»΄νμΌλ¬ λ΄λΆ ν΄λμ€λ₯Ό μ¬μ©νμ¬ κΈ°μ‘΄ μμ€ μ½λλ₯Ό μ‘°μ
νΉν μ΄ν΄λ¦½μ€μ κ²½μ°μ java agent λ₯Ό μ¬μ©νμ¬ μ»΄νμΌλ¬ ν΄λμ€κΉμ§ μ‘°μνμ¬ μ¬μ©
ν΄λΉ ν΄λμ€λ€ μμ 곡κ°λ API κ° μλλ€λ³΄λ λ²μ νΈνμ±μ λ¬Έμ κ° μκΈΈ μ μμ
κ·ΈλΌμλ μμ²λ νΈλ¦¬ν¨μΌλ‘ λ리 μ°μ΄κ³ , λμμ΄ λͺκ°μ§ μμ§λ§ 둬볡μ λͺ¨λ κΈ°λ₯κ³Ό νΈμμ±μ λ체 λΆκ°
.
Annotation processor
Processor Interface
μ¬λ¬ λΌμ΄λ(rounds)μ κ±°μ³ μμ€ λ° μ»΄νμΌ λ μ½λλ₯Ό μ²λ¦¬
AutoService
μ»΄νμΌ μμ μ μ λ
Έν
μ΄μ
νλ‘μΈμλ₯Ό μ¬μ©νμ¬ META-INF/services/javax.annotation.processor.Processor
νμΌ μλ μμ±
javapoet
μμ€ μ½λ μμ± μ νΈλ¦¬ν°
Interface Filer
μμ€ μ½λ, ν΄λμ€ μ½λ λ° λ¦¬μμ€λ₯Ό μμ±ν μ μλ μΈν°νμ΄μ€
Copy @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;
}
}
μ°Έκ³ .