[java] 어노테이션 정리2 (의존성 주입 관련 및 기타)
Bean 객체 등록
- 빈(Bean)
- 스프링 컨테이너에 의해 자바 객체가 만들어지게 되면 이 객체를 스프링은 스프링빈이라 부름
- 스프링 빈과 자바 일반 객체와 차이는 없고 스프링 컨테이너에 의해 만들어진 객체를 스프링 빈이라고만 부를뿐
- 스프링 컨테이너에 의해 자바 객체가 만들어지게 되면 이 객체를 스프링은 스프링빈이라 부름
- 스프링 빈의 어노테이션
- @Component, @Service, @contorller @Repository @Bean, @Configuration 등으로 필요한 빈들을 등록하고 필요한 곳에서 @Autowired를 통해 주입받아 사용하는 것이 일반적
- Bean 을 주입하는 방식 3가지
- @Autowired
- setter injection
- 생성자(@AllArgsConstructor 사용)
- @Component , @Autowired
- 스프링빈 으로 등록하기 가장 쉬운 방법은 클래스 선언부 위에 Component 어노테이션을 사용하는것
- component-scan을 선언에 의해 특정 패키지 안의 클래스들을 스캔
- ex
public interface AdminService { void activeService(); }
@Component public class AdminServiceImpl implements AdminService { @Override public void activeService(String void){ } }
public class AdminController { @Autowired private AdminService adminService; @PostMapping(value = "/active") public Response activeService(@RequestHeader(value = "test", required = false) String test, @RequestBody(required = true) @Valid RequestSetData requestSetData){ adminService.activeService(transactionId); ... } }
- @Primary, @Qualifier
- @Primary : @Bean 혹은 @Component와 함께 사용하며 객체 생성의 우선권을 부여
- @Quallifier : @Autowired와 함께 사용하며 Bean 의 이름이 같은 객체를 찾음
- 같은 타입의 빈이 2개 이상 존재할 경우 특정 의존성을 주입할수있도록 하기위해 사용
- @Bean(“name”) 으로 주고 @Autowired(“name”)로 사용.
- @Bean 과 @Component의 차이
- @Bean은 메소드 레벨에서 선언하며, 반환되는 객체(인스턴스)를 개발자가 수동으로 빈으로 등록하는 애노테이션이다.
- (예를 들면 Redis와 연동하기위해 RedisConfig 클래스에 @Component를 선언할수는 없으니 RedisTemplate의 인스턴스를 생성하는 redisTemplate메소드를 만들고 해당 메소드에 @Bean을 선언하여 Bean으로 등록한다.)
- @Component는 클래스 레벨에서 선언함으로써 스프링이 런타임시에 컴포넌트스캔을 하여 자동으로 빈을 찾고(detect) 등록하는 애노테이션이다.
- @Bean은 메소드 레벨에서 선언하며, 반환되는 객체(인스턴스)를 개발자가 수동으로 빈으로 등록하는 애노테이션이다.
스프링 프레임 워크에서 의존성 주입 방법
- 등록된 빈을 사용하기 위한 스프링 프레임워크의 DI(Dependency Injection) 방법은 3가지
- 생성자 주입(Constructor Injection)
- 필드 주입(Field Injection)
- 수정자 주입(Setter Injection)
생성자 주입
@Component
public class TestCode {
private final Test test;
public TestCode(Test test){
this.test = test;
}
}
- 단일 생성자의 경우 @Autowired 어노테이션 붙이지 않아도 되지만, 생성자가 2개이상이면 생성자에 붙여줘야함
- 롬복을 쓰면 쉽게 처리된다
@Component @RequiredArgsConstructor public class TestCode { private final Test test; }
- 만약 같은 타입의 빈이 있을 경우는 @Qualifer 를 통해 구현체의 클래스명이나 지정한 빈의 이름으로 가져올수 있다.
@Component @RequiredArgsConstructor public class TestCode { @Qualifier("test") private final Test test; }
필드 주입
- 필드에 @Autoried 어노테이션을 붙이면 된다.
- 생성자 주입과 달리 필드를 final로 정의하지 못한다
- 같은 타입의 빈이 있을 경우 @Qaualifer이나 @Resource 를 통해 빈의 이름으로 가져와서 주입할수 있다.
@Component
public class TestCode {
@Autowired
private Test test;
}
수정자 주입
- 이 방식도 final 선언이 안된다.
- 꼭 setter 메서드일 필요없이 메서드 이름이 수정자 네이밍 패텅이 아니어도 동일 기능을 하면 된다.
@Component
public class TestCode {
private Test test;
@Autowired
public void setTest(Test test){
this.test = test;
}
}
어떤 방식이 좋을까?
- 아래의 이유로 생성자 주입을 권장한다
- 순환참조 방지
- 생성자 주입 방식은 필드 주입이나 수정자 주입과는 빈을 주입하는 순서가 다르다
- 필드 주입, 수정자 주입은 먼저 빈을 생성한 뒤 주입하려는 빈을 찾아 주입한다
- 생성자 주입은 먼저 생성자안의 안자에서 사용되는 빈을 찾거나 빈 팩토리에서 만든다.
- 먼저 빈을 생성하지 않고 주입하려는 빈을 먼저 찾는다.
- 생성자 주입 방식은 필드 주입이나 수정자 주입과는 빈을 주입하는 순서가 다르다
- final 선언이 가능
- 필드 주입과 수정자 주입은 필드를 final로 선언할수없다 즉, 나중에 변경될수 있다.
- 럴타임에 객체 불변성을 보장하려면 생성자 주입으로 하자
- 테스트 코드 작성 용이
- 순환참조 방지
기타
- @RequestParam
- @PathVariable
- @RequestBody
- @ModelAttribute
- 아래 샘플을 보면 그냥 이해가 될듯하여 설명은 패스.
@PutMapping(value = "/keys/{key}/nodes/{node}")
public ResponseCommon updateNode(@RequestHeader(value = "TRANSACTION_ID", required = false) String transactionId,
@RequestHeader(value = "SECRET_KEY", required = false, defaultValue = "") String secretKey,
@PathVariable String key,
@PathVariable String node,
@RequestBody(required = true) @Valid RequestUpdateNode requestUpdateNode){
//...
}
기타?
- @PostConstruct, @PreDestroy
- https://docs.oracle.com/javaee/7/api/javax/annotation/PostConstruct.html
- PostConstruct 는 의존성 주입이 이루어진 후 초기화를 수행
- PreDestroy 는 그 반대겠지?
- @RestController
- @Controller와 @ResponseBody의 조합
- 다음 두 코드는 Spring MVC에서 동일한 동작
@Controller @ResponseBody public class MVCController{ logic... } @RestController public class ReftFulController{ logic... }
- json 으로 응답보내기 위한 어노테이션
- 하위의 메소드들은 전부 @ResponseBody 가 적용됨
- @ResponseBody
- @Controller 어노테이션을 가지고 있지만 텍스트를 반환하고 싶을때 사용.
- 아래 내용 추가 기입하기.
- ConditionalOnBean
- DependsOn