본문 바로가기

프로그래밍/Java

Lombok 파해쳐보기 - 1(미완)

반응형

포스트 작성 목적

자바 어노테이션을 공부하면서 어떻게 선언해야하는지 알게되었다. 그러나, 그뿐 실제로 어노테이션을 선언하고 사용하지는 못했다. 기껏해야 리플렉션을 이용해, 어노테이션이 붙은 메소드들에 들어간 값을 확인하는 수준에서 그쳤다. 어노테이션을 직접 선언하고 사용할 일이 많지 않겠지만, 실제 어떻게 동작하는지 원리를 파악하고, 이를 기반으로 Transactional 어노테이션을 만들어보고자 한다.

그러기 위해 어노테이션을 쓰기위해 많이쓰이는 Lombok라이브러리는 어떻게 어노테이션을 선언하고 구현했는지 파해쳐보겠다.

Lombok 어노테이션 디컴파일

Lombok 어노테이션이 붙은 소스파일은 클래스파일로 컴파일 됬을때 어떻게 변하고, 또 어노테이션 선언은 어떻게 될까???

@Embeddable
@Getter //Setter는 아예 제공 안하는게 좋다! enumable 하게!!
public class Address {
	private String city;
	private String street;
	private String zipcode;
	
	//JPA스펙상 만들어 둔것, 의미없다.
	protected Address() {
	}
	
	public Address(String city, String street, String zipcode) {
		this.city = city;
		this.street = street;
		this.zipcode = zipcode;
	}
	   
}
$ javap Address.class
Compiled from "Address.java"
public class jpabook.jpashop.domain.Address {
  protected jpabook.jpashop.domain.Address();
  public jpabook.jpashop.domain.Address(java.lang.String, java.lang.String, java.lang.String);
  public java.lang.String getCity();
  public java.lang.String getStreet();
  public java.lang.String getZipcode();
}

@Getter어노테이션을 붙였을 뿐인데, Address클래스의 맴버변수들에 대한 get()메소드가 생성된 것을 볼수 있다. 그럼 Lombok의 @Getter어노테이션은 어떻게 선언되어있길래, 저렇게 코드가 바뀐걸까?

package lombok;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Getter {
	/**
	 * If you want your getter to be non-public, you can specify an alternate access level here.
	 * 
	 * @return The getter method will be generated with this access modifier.
	 */
	lombok.AccessLevel value() default lombok.AccessLevel.PUBLIC;
	
	/**
	 * Any annotations listed here are put on the generated method.
	 * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br>
	 * up to JDK7:<br>
	 *  {@code @Getter(onMethod=@__({@AnnotationsGoHere}))}<br>
	 * from JDK8:<br>
	 *  {@code @Getter(onMethod_={@AnnotationsGohere})} // note the underscore after {@code onMethod}.
	 *  
	 * @return List of annotations to apply to the generated getter method.
	 */
	AnyAnnotation[] onMethod() default {};
	
	boolean lazy() default false;
	
	/**
	 * Placeholder annotation to enable the placement of annotations on the generated code.
	 * @deprecated Don't use this annotation, ever - Read the documentation.
	 */
	@Deprecated
	@Retention(RetentionPolicy.SOURCE)
	@Target({})
	@interface AnyAnnotation {}
}

@Target어노테이션을 통해 필드와 클래스(인터페이스)에 선언할 수 있다는 것과,

@Retention어노테이션을 통해 이 어노테이션은 Source파일에만 존재하고, 컴파일 후에는 사라지는 것을 알수있다. 그러나 어떻게 각 맴버변수들에 대한 get()메소드를 생성했는지는, 어노테이션의 몸체에 선언된 코드만으로는 이해가 도통 되지를 않는다.

Lombok의 구동원리

Lombok은 컴파일시점에

어노테이션 프로세서

를 사용해 소스코드의 AST(abstract syntax tree)를 조작한다.

어노테이션 프로세서란?

어노테이션 프로세서는

java 컴파일러

의 플러그인의 일종이다.

내가 만든 어노테이션에

구체적인 동작 행위

를 하기위해서 자바에서 제공하는 api 이다.

자바 컴파일러의 컴파일 단계에서, 유저가 정의한 어노테이션의 소스코드를 분석,처리하기위해 사용되는 훅이다. 컴파일 에러나 컴파일 경고를 만들어 내거나, 소스코드(.java)와 바이트코드(.class)를 내보내기도 한다.

어노테이션 프로세선의 장점

  • 런타임 비용이 없다.(컴파일 타임에 조작이 완료된 상황이기 때문에)

커스텀 Getter메소드 예제

예제코드들과 Lombok의 코드를 보았으나, Lombok코드는 해석이 힘들었다.

.class파일이 아닌 다른 파일들도 있었는데, 뭔가 암호화가 된것같다. 이런 암호화된 파일들이 어노테이션의 구현체가 아닐까 생각해본다.

코드들을 이해해보려 했지만, 대략적인 흐름만 이해할 수 있었고, 나머지는 이해를 잘 하지 못하겠다.

중요한건 processing()메소드에서 어노테이션에 대한 실질적인 처리를 해준다는 것이다.

Lombok의 Transactional 코드

* Copyright 2002-2020 the original author or authors.

package org.springframework.transaction.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.core.annotation.AliasFor;
import org.springframework.transaction.TransactionDefinition;


@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {

	@AliasFor("transactionManager")
	String value() default "";


	@AliasFor("value")
	String transactionManager() default "";

	String[] label() default {};


	Propagation propagation() default Propagation.REQUIRED;


	Isolation isolation() default Isolation.DEFAULT;


	int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;


	String timeoutString() default "";


	boolean readOnly() default false;


	Class<? extends Throwable>[] rollbackFor() default {};


	String[] rollbackForClassName() default {};


	Class<? extends Throwable>[] noRollbackFor() default {};


	String[] noRollbackForClassName() default {};

}

역시 Lombok의 선언코드만으로는 어떻게 Transactional이 작동하는지 모르겠다.

Transactional 어노테이션 선언해보자

추후 작성 예정

포스트 작성으로 알게된 점

어노테이션을 직접작성하는것이 너무나 어렵다는것을 알게되었다. 자바의신이나 다른 블로그의 예제들은 어노테이션을 직접 사용한다기 보다는, 선언하고 어노테이션에 적용된 값을 알아오는 테스트 코드일뿐, 실제 어노테이션을 통해 개발을 진행할수는 없다고 보여진다. 실제 어노테이션에서는 인터페이스 처럼 선언만 이뤄지고,

실제 구현은 어노테이션 프로세서에서 이뤄진다.

내부 구현로직까지는 암호화되어 알아볼수 없었다.

Reference

Lombok @Getter,@Setter 직접 만들어 보자

[Android] Annotation Processor 만들기

[Java] 어노테이션 프로세싱 - 어노테이션은 어떻게 동작하나?

12주차 과제 : 애노테이션

반응형

'프로그래밍 > Java' 카테고리의 다른 글

LRU 캐시  (0) 2021.05.26
Proxy-@Transactional  (0) 2021.05.25
ArrayList VS LinkedList  (0) 2021.05.21
java.lang -2 System클래스와 로깅  (0) 2021.05.20
상속과 컴포지션  (0) 2021.05.20