TIL - 0615
@Annotation
어노테이션
- 자바 1.5부터 등장한 API
- 코드에 메타 데이터를 설정 : 자신에 대한 부가 정보를 담고 있음
@Transactional(rollbackFor = CannotDeleteException.class)
public void deleteQuestion(User loginUser, Long id) throws CannotDeleteException {
deleteHistoryService.saveAll(findQuestionById(id).delete(loginUser));
}
이렇게 사용된다
- 컴파일러에 정보 제공 : @FunctionalInterface, @SuppressWarnings
이렇게 사용된다
- 코드 생성 : 롬복 @Setter, @Getter 등
- 런타임 프로세싱(조작) : Spring의 @Autowired(DI), JUnit의 @Test(테스트 할 메소드), Jackson의 @JsonIgnore(json 생성 시 참고) 등
종류
- 빌트인 어노테이션 : 제공되는 어노테이션
@Override
: 상위 클래스의 메소드를 오버라이드 했음을 컴파일러에게 알려줌, 제대로 오버라이드 하지 않았을 때 컴파일러가 컴파일 fail@Deprecated
: 해당 코드는 오래된 방법으로 곧 삭제될 코드 임을 표시하는 메타데이터, 컴파일러가 알리도록 설정함@SuppressWarnings
: 개발자 의도에 의해 실행되는 흐름이지만 컴파일러가 경고를 띄울 때가 있는데, 띄우지 않도록 알려줌
- 메타 어노테이션 : 어노테이션을 만들 때 필요한 메타데이터를 설정하는 어노테이션
@Target
: 어노테이션이 적용될 코드@Retention
: 어노테이션의 스코프(메타데이터의 생명주기)@Inherited
: 해당 어노테이션이 적용된 클래스의 하위 클래스일 경우 똑같이 어노테이션이 적용됨
- 커스텀 어노테이션 : 만든 어노테이션, 보통 어노테이션을 만들 일은 거의 없고 프레임워크 개발자들이 만들면 우리는 쓰면 됨
@Transactional
@Autowired
@JsonIgnore
@Column
정의하면서 기본 구성보기
/* 어노테이션 정의 */
@Target(ElementType.TYPE) // @Target({METHOD, FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Author {
String company = "codesquad";
String name() default "colin";
}
/* 어노테이션 사용 */
@Author(name = "jinbro")
public class Book {
}
/* 어노테이션 설정된 값 가져오기(리플렉션 API) */
public class BookTest {
@Test
public void read() throws Exception {
/* Annotation[] annotations = Book.class.getAnnotations(); */
Class<?> clazz = Book.class;
assertTrue(clazz.isAnnotationPresent(Author.class));
Annotation annotation = clazz.getAnnotation(Author.class);
System.out.println(annotation);
System.out.println(Author.company);
}
}
@interface
: 어노테이션 정의임을 알리는 선언, 특별한 인터페이스(메소드 선언만 해두고 구현은 하지않음)- 선언한 메소드의 리턴값 : 기본값을 설정해두거나 기본값을 설정하지않을 경우 어노테이션 사용하는 쪽에서 어노테이션 옆 (name = 값) 형태로 지정해줘야함
@Target
: 어노테이션이 적용될 수 있는 대상, 타겟은 복수개 설정 가능(중괄호 내에 콤마로 구분해서 타입을 여러개 설정할 수 있음 {TYPE1, TYPE2} )- RetentionPolicy.TYPE : 타입 선언 시
- RetentionPolicy.METHOD : 메소드
- RetentionPolicy.CONSTRUCTOR : 생성자 외 몇가지 더 있음
@Retention
: 어노테이션이 언제까지 유효한지 설정(하나만 설정가능)- ElementType.SOURCE : 컴파일 시에 사용되고, 바이트 코드로 변환되지않고 코드 상에서 삭제됨
- ElementType.CLASS : 바이트 코드 변환은 되지만 런타임에는 사용할 수 없음(있는지만 표시)
- ElementType.RUNTIME : 실행 시 어노테이션 정보가 jvm에 의해 사용 - 리플렉션 API
String company
: 메타데이터의 고정 설정값, 사용하는 쪽 모두가 공통적으로 가지는 값String name() default "colin"
: 메타데이터 설정값의 변수 역할, 사용하는 쪽에서 유동적으로 설정할 수 있음, 기본값 설정 가능
어노테이션 in 스프링(스프링빈 등록), 어노테이션 이외 방법
어노테이션만으로 스프링빈 등록, 관리하기
@Controller
public class UserController {
@Autowired
private UserService userService;
}
- 대략적인 흐름
- 스프링 실행 시 빈 스캔을 하고 시작함 : 스프링빈으로 메타데이터(stereotype 어노테이션) 설정된 클래스의 인스턴스 생성 후 컨테이너에서 관리
- 단점 : 스캔 범위가 넓어지면 오래걸림(기본적으로 메인 클래스의 하위 패키지 전체 스캔 - 메인클래스 @SpringBootApplication 설정값으로 스캔 패키지 커스텀)
.xml 사용
<bean id="userController" class="com.example.web.UserController">
<property name="userService" ref="userService" />
</bean>
<bean id="userService" class="com.example.serivce.UserService">
<property name="name" value="xmlBean" />
</bean>
- 어노테이션은 스캐닝 해서 찾아야하지만, xml은 직접적으로 어디에 메타정보를 주입해줘야할지 설정이 되어있기 때문에 바로 찾기 가능
- 단점이라고 생각되는 부분 : xml의 크기가 커짐(유지보수가 어려울 것이라 생각됨 - 자바 코드 변경되면 xml도 변경), 빈의 특성 구분없이 bean 하나로 퉁침
java config 파일 + 어노테이션 사용
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages");
messageSource.setDefaultEncoding("UTF-8");
messageSource.setCacheSeconds(30);
return messageSource;
}
}
- 직접 인스턴스를 생성해서 컨테이너에 등록해야만 할 때 사용 : 주로 외부 라이브러리 수동으로 함(스프링빈 설정이 되어있지않음)
- 단점이라고 생각되는 부분 : bean 태그를 @Bean로 만든 것 - 인스턴스 생성을 수동으로 해줘야함
어노테이션 in Lombok
어노테이션을 활용한 코드 생성 자동화
- 컴파일 타임에 어노테이션에 따라 코드를 생성함 : 컴파일러가 해당 자바 파일을 .class로 변환할 때 어노테이션 프로세서가 처리함
사용 설정하기
- 인텔리j 사용할 경우 : Lombok 플러그인 설치
- 컴파일러가 해당 어노테이션을 만났을 때 어노테이션 프로세서을 동작시킬 수 있도록 허용해줘야함 : Compiler > Annotation Processors
- 롬복 디펜던시 설정 : 컴파일 타임에만 필요하므로 디펜던시 스코프를 complieOnly로 설정(gradle 기준)
dependencies {
compileOnly('org.projectlombok:lombok')
}
소스 파일(.java)과 컴파일 파일(.class) 비교해보기
/* .java */
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
@ToString
public class User {
String name;
Question question;
}
/* .class */
public class User {
Question question;
String name;
public User() {
}
public User(Question question, String name) {
this.question = question;
this.name = name;
}
public void setQuestion(Question question) {
this.question = question;
}
public void setName(String name) {
this.name = name;
}
public Question getQuestion() {
return this.question;
}
public String getName() {
return this.name;
}
public String toString() {
return "User(question=" + this.getQuestion() + ", name=" + this.getName() + ")";
}
}
사용할 때 생각해야할 점
- 사용 환경이 갖춰져야 사용할 수 있음 : 같은 코드지만, 사용 환경이 갖춰지지 않은 곳에서는 컴파일 실행 시킬 수 없음(에러 발생)
- @toString 사용할 때 : 해당 클래스의 모든 속성을 가지고 작성 - 서로 연관된 객체라면(속성에 서로가 서로를 가진다면)?
- 객체 호출하면 객체의 toString() 호출 -> 연관 객체의 toString() 실행 시 또다시 연관된 toString() 호출 : 스택오버플로우 발생