※ Annotation이란?
소스코드에 메타 코드를 주는 것.
컴파일 또는 런타임 시에 해석
자바 1.5버전부터 지원되는 기능
클래스, 메소드, 변수 등에 붙여서 사용
▶ Annotation 선언
interface에 @를 붙여서 선언.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Annotation {}
1. @Target
어노테이션이 적용되는 대상(위치)을 결정.
import java.lang.annotation.Target;
ElementType 요소 중에서 선택해서 지정
import java.lang.annotation.ElementType;
[어노테이션 요소 목록]
TYPE | 클래스 및 인터페이스 |
FIELD | 클래스의 멤버변수 |
METHOD | 메서드 |
PARAMETER | 파라미터 |
CONSTRUCTOR | 생성자 |
LOCAL_VARIABLE | 지역변수 |
ANNOTATION_TYPE | 어노테이션 타입 |
PACKAGE | 패키지 |
TYPE_PARAMETER | 타입 파라미터 |
TYPE_USE | 타입 사용 |
2. @Retention
어노테이션이 적용될 범위를 결정 (어떤 시점까지 영향을 미치는지)
import java.lang.annotation.Retention;
- Class : 어노테이션 작성 시 기본값으로 클래스 파일에 포함되지만 JVM이 로드하지 않는다
- Runtime : 클래스 파일에 포함되고, JVM에 의해서 참조 가능
- Source : 컴파일때만 사용되고 사라진다. 클래스 파일에 포함 X (소스 상에서만 확인 가능 -> 주석)
≫ Annoation 생성 예
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String strValue();
int intValue();
}
public class MyService {
@MyAnnotation(strValue = "hello", intValue = 1111)
public void printSomething() {
System.out.println("test my annotation");
}
}
//MyAnnotation 값 확인
public class AnnotationApp {
public static void main(String ar[]) throws Exception {
//MyService 클래스에서 메소드 가져옴
Method[] methods = Class.forName(MyService.class.getName()).getMethods();
for(int i = 0; o < methods.length; i++) {
//MyAnnotation이 존재하는 지 확인
if(methods[i].isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation an = methods[i].getAnnotation(MyAnnotation.class);
System.out.println("str:" + an.strValue());
System.out.println("int:" + an.intValue());
}
}
}
}
// 출력
str : hello
int : 1111
▶ 스프링 부트 어노테이션
부트에서 내부적으로 사용되는 어노테이션
1. ImportSelector
어노테이션의 값에 따라서 설정 클래스의 로딩 여부가 결정.
msg필드를 가지고 있는 MyBean 클래스를 사용하는 UseMyBean 클래스
public class UseMyBean {
@Autowired
private MyBean myBean;
public void printMsg() {
System.out.println(myBean.getMsg());
}
}
myBean을 Autowired로 의존성 주입 해주기 위해 myBean을 bean으로 등록해주어야 함.
MyBean 클래스를 빈으로 등록해주는 설정 클래스 2가지
@Configuration
public class AConfig {
@Bean
MyBean myBean() {
return new MyBean("from Aconfig");
}
}
@Configuration
public class BConfig {
@Bean
MyBean myBean() {
return new MyBean("from Bconfig");
}
}
어노테이션 값에 따라서 AConfig/BConfig 선택하는 MyImportSelector
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
AnnotationAttributes attr = AnnotationAttributes.fromMap(
importingClassMetadata.getAnnotationAttributes(EnableAutoMyModule.class.getName(), false));
String value = attr.getString("value");
//EnableAutoMyModule 어노테이션 값이 a면 AConfig / 아니면 BConfig
if("a".equals(value))
return new String[]{AConfig.class.getName()};
else
return new String[]{BConfig.class.getName()};
}
}
ImportSelect 인터페이스를 상속받아 selectImports 메서드를 구현한 클래스
@EnableAutoMyModule 어노테이션 값에 따라 클래스 이름 리턴.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MyImportSelector.class)
public @interface EnableAutoMyModule {
String value() default "";
}
@Configuration
@EnableAutoMyModule("a")
public class MainConfig {
@Bean
public UseMyBean useMyBean() {
return new UseMyBean();
}
}
@EnableAutoMyModule 어노테이션에 a를 넣어줘서 AConfig가 로드되도록!
//확인
public class ImportSelectApp {
public static void main(String ar[]) {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
UseMyBean bean = context.getBean(UseMyBean.class);
bean.printMsg();
}
}
// 출력
from AConfig
2. @Conditional
조건에 따라 자바 설정 클래스를 선택할 수 있게 해주는 어노테이션.
개발 환경, 운영 환경에 따라서 다르게 해줘야 하는 부분에 많이 사용.
public interface MsgBean {
default void printMsg() {
System.out.println("My Bean default is running");
}
}
자바 8부터 인터페이스에 default 키워드를 이용해 몸체가 있는 메서드 작성 가능.
환경 변수값(env)으로 입력된 값이 sitea / siteb 와 같은지 체크하는 클래스 2개
public class SiteAConfigCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return "sitea".equals(context.getEnvironment().getProperty("env", "sitea"));
}
}
public class SiteBConfigCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return "siteb".equals(context.getEnvironment().getProperty("env", "siteb"));
}
}
sitea, siteb 조건들을 @Conditional 어노테이션 값으로 사용하는 SiteABean / SiteBBean
//SiteAConfigCondition의 matches 메소드 반환값이 true면 동작
@Component
@Conditional(SiteAConfigCondition.class)
public class SiteABean implements MsgBean {
@Override
public void printMsg() {
System.out.printf("site a is working);
}
}
//SiteBConfigCondition의 matches 메소드 반환값이 true면 동작
@Component
@Conditional(SiteBConfigCondition.class)
public class SiteBBean implements MsgBean {
@Override
public void printMsg() {
System.out.printf("site b is working);
}
}
테스트
@Configuration
public class ConditionalMainConfig {
@Autowired
MsgBean msgBean;
}
public class ConditionApp {
public static void main(String ar[]) {
Package pack = ConditionApp.class.getPackage();
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext();
context.scan(pack.getName());
context.refresh();
MsgBean bean = context.getBean(MsgBean.class);
bean.printMsg();
}
}
같은 타입을 가지고 있는 클래스들이 스프링이 관리해야 할 대상이 되어야 하므로 스캔을 하고 MsgBean을 context에 등록한다.
실행 전에 env에서 값을 sitea(혹은 siteb) 로 전달한 후 실행하면
출력
site a is working //sitea로 설정했을 경우
3. @AutoConfigurationPackage
스프링 부트로 만든 애플리케이션 내에서 @AutoConfigurationPackage 어노테이션이 선언된 패키지 경로를 스프링 콘텍스트가 스캔 가능하도록!
@EnableAutoConfiguration 어노테이션이 @AutoConfigurationPackage를 포함한다.
ConfigurableApplicationContext c = app.run(args);
AutoConfigurationPackages.get(c);
위 소스로 스프링 부트 실행 시에 인식된 패키지 정보를 알 수 있다.
4. @EnableConfigurationProperties
프로퍼티들을 그룹화해서 사용하는 것.
//application.properties
spring.main.web-environment=false
myapp.server-ip=192.168.34.56
myapp.name=My Config App
myapp.description=This is an example
myapp로 시작되는 프로퍼티들을 사용하려면, prefix를 사용해서 참조
@ConfigurationProperties(prefix="myapp")
public class MyProperties {
private String name;
private String description;
private String serverIp;
--- getter, setter ---
}
@SpringBootApplication
@EnableConfigurationProperties({MyProperties.class})
public class PropApp {
public static void main(String ar[]) {
SpringApplication.run(PropApp.class, ar);
}
@Autowired
MyProperties prop;
@Bean
CommandLineRunner values() {
return ar -> {
System.out.println("server ip:" + prop.getServerIp());
};
}
}
'CS > Web' 카테고리의 다른 글
스프링부트의 구성요소와 커스텀 스프링 부트 스타터 (0) | 2020.09.05 |
---|---|
HTTP 요청 흐름 이해하기 (0) | 2020.08.29 |
스프링 프레임워크란? ( IoC, 스프링 mvc ) (2) | 2020.08.15 |
REST와 REST API (6) | 2020.08.10 |
댓글