[Spring] 기본편 07. 의존관계 자동 주입1
이 글은 스프링 [핵심원리 - 기본편]을 듣고 정리한 내용입니다
📌 다양한 의존관계 주입 방법
- 의존관계 주입 방법
- 생성자 주입
- 수정자 주입(setter주입)
- 필드 주입
- 일반 메서드 주입
🌱 생성자 주입
- 생성자를 통해 의존관계를 주입받는 방법
- 생성자 호출시점에 딱 1번만 호출되는 것이 보장된다
-> 불편, 필수 의존관계에 사용
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
- *중요: 생성자가 딱 1개있으면
@Autowired
생략해도 자동 주입된다.
(당연히 스프링 빈 일때만 해당)
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
// @Autowired - 생성자 딱 1개니까 생략해도 자동주입 됨.
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
🌱 수정자 주입(setter 주입)
setter
라 불리는 수정자 메서드를 통해서 의존관계를 주입하는 방법이다.- 선택, 변경의 가능성이 있는 의존관계에 사용
@Component
public class OrderServiceImpl implements OrderService {
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired //수정자 주입
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Autowired //수정자 주입
public void setDiscountPolicy(DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
}
- *참고 :
@Autowired
의 기본동작은 주입할 대상이 없으면 오류남. @Autowired(required = false)
로 지정하면 주입할 대상없어도 동작한다.
자바빈 프로퍼티 규약이란?
-> 자바에서는 과거부터 필드의 값을 직접 변경하지 않고 (Data.aa=aa), setXxx, getXxx 메서드를 통해서 값을 읽거나 수정하는 규칙을 만들었다.
- 자바빈 프로퍼티 규약 예시
class Data {
private int age;
public void setAge(int age){
this.age=age;
}
public int getAge(){
return age;
}
}
🌱 필드 주입
- 필드에 바로 주입하는 방식이다.
- 코드가 간결하지만, 외부에서 변경이 불가능하므로 테스트 하기에 힘들다 (치명적인 단점)
- DI 프레임워크가 없으면 아무것도 할 수 없다.
- 테스트코드나
@Configurtaion
(설정 관련) 같은곳에서만 특별한 용도로 사용할 수 있다. - 사용하지 말자!!!
@Component
public class OrderServiceImpl implements OrderService {
@Autowired
private MemberRepository memberRepository;
@Autowired
private DiscountPolicy discountPolicy;
}
* 참고
- 순수한 자바코드에서는 당연히
@Autowired
가 동작하지 않는다.@SpringBootTest
처럼 스프링 컨테이너를 테스트에 통합한 경우에만 가능!- 예를 들어, 스프링 빈이 아닌(
@Component
가 붙지 않은 혹은@bean
수동등록도 되어있지 않은)Member
같은 클래스에서@Autowired
를 적용해도 아무런 기능도 동작하지 않는다.
🌱 일반 메서드 주입
- 일반 메서드를 통해서도 주입 가능하다
- 한번에 여러 필드를 주입 받을 수 있다.
- 잘 사용하지 않는다.
@Component
public class OrderServiceImpl implements OrderService {
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
//일반 메서드를 통해 주입받는 예시
@Autowired
public void init(MemberRepository memberRepository, DiscountPolicy discountPolicy){
this.memberRepository=memberRepository;
this.discountPolicy=discountPolicy;
}
}
📌 옵션 처리
- 주입할 빈이 없어도 동작해야 할 때가 있다.
@Autowired
는required
옵션의 기본값이true
이므로, 자동 주입 대상이 없으면 오류가 발생한다.- 자동 주입 대상을 옵션으로 처리하는 방법은 아래와같이 세가지가 있다.
package hello.core.autowired;
public class AutowiredTest {
@Test
void AutowiredOption() {
ApplicationContext ac = new AnnotationConfigApplicationContext(TestBean.class);
}
static class TestBean {
//호출 자체가 안됨.
//Member는 스프링 빈이 아니다.
@Autowired(required=false)
public void setNoBean1(Member noBean1){
System.out.println("noBean1 = " + noBean1);
}
//null 호출
@Autowired
public void setNoBean2(@Nullable Member noBean2){
System.out.println("noBean2 = " + noBean2);
}
//Optional.empty 호출
@Autowired
public void setNoBean3(Option al<Member> noBean3){
System.out.println("noBean3 = " + noBean3);
}
}
}
@Autowired(required=false)
: 자동 주입할 대상이 없으면 메서드 자체가 호출이 안된다.org.springframework.lang.@Nullable
: 자동 주입할 대상이 없으면Null
이 입력된다.Optional<>
: 자동 주입할 대상이 없으면Optional.empty
가 입력된다.- 출력 결과
- 참고
@Nullable, Optional은 스프링 전반에 걸쳐서 지원된다.
예를들어, 생성자 자동 주입에서 특정 필드에만 사용해도 된다.
📌 생성자 주입을 선택하라!
- 다양한 의존관계 주입 방법중에 생성자 주입이 권장된다.
생성자 주입이 권장되는 이유
- 불변
- 의존 관계 주입이 되면, 대부분 변경할 일이 없다. 오히려 대부분의 의존관계는 애플리케이션 종료 전까지 변하면 안된다. (불변해야함)
- 수정자 주입을 사용하면 setXX를 public으로 열어두어야 하는데 누군가 실수로 변경할수도 있다.
- 생성자 주입은 객체 생성시에 1번만 호출되므로, 더이상 호출될일이 없어서 불변에 기여한다.
- 누락
- 생성자 주입을 사용하면 주입 데이터를 누락 했을 때 컴파일 오류가 발생한다.
@Test //컴파일 오류 발생
void createOrder() { //memberRepository, discountPolicy가 누락됨.
OrderServiceImpl orderService = new OrderServiceImple();
orderService.createOrder(1L, "itemA", 10000);
}
- final 키워드
- 생성자 주입을 사용하면 필드에 final 키워드를 사용할 수 있어서, 생성자에서 값이 설정되지 않는 오류를 컴파일 시점에 막아준다.
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired// 생성자 딱 1개니까 생략해도 자동주입 됨.
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
//discountPolicy값이 설정되지 않음
//this.discountPolicy = discountPolicy;
}
}
- 필수 필드인
discountPolicy
값을 설정하는 부분이 누락 되어서 다음 오류가 발생한다. java: variable discountPolicy might not have been initialized
- 컴파일 오류는 세상에서 가장 빠르고, 좋은 오류다!!!
- 컴파일 타임이란?
- 개발자에 의해 개발 언어로 소스코드가 작성 된 뒤, 컴파일 과정을 통해 컴퓨터가 인식할 수 있는 기계어 코드로 변환되어 실행 가능한 프로그램이 되는 과정
- 컴파일 에러란?
- 소스코드가 컴파일 되는 과정 중에 발생하는 Syntax error, 파일 참조 오류 등과 같은 문제들로 인해 컴파일이 방해되어 발생하는 오류.
- 컴파일 에러 발생 시, 문제가 되는 소스코드를 알려준다. (그래서 좋음)
- 런타임이란?
- 컴파일 과정을 마친 응용프로그램이 사용자에 의해서 실행되어지는 때를 의미한다.
- 런타임 에러란?
- 이미 컴파일이 완료되어 프로그램일 실행중임에도 불구하고, 의도치 않은 예외 상황으로 인해 프로그램 실행 중에 발생하는 오류.
정리
- 생성자 주입 방식은 프레임워크에 의존하지 않고, 순수 자버 언어의 특징을 잘 살리는 방법이기도 하다.
- 항상 생성자 주입을 선택하자! 가끔 옵션을 필요하면 수정자 주입을 선택하자. (필드 주입은 사용하지 않는게 좋다.)
📌 롬복과 최신 트랜드
-
개발을 하다보면 대부분이 불변으로 해야되어서 final 키워드를 사용한다.
-
그러나 final방식은 생성자도 만들어야하고, 주입 해주는 코드도 만들어야 하고 귀찮다.
-
final 방식의 기본 코드
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy
discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
- 생성자 1개일때
@Autowired
생략 가능
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
//@Autowired 생략
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy
discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
-
롬복 적용
-
@RequiredArgConstructor
어노테이션을 사용하면final
이 붙은 필드를 모아서 생성자를 자동으로 만들어준다. -
생성자가 만들어졌는지는
command+f12
를 통해 보면 확인 가능하다. -
롬복 사용한 코드
-
롬복이 자바의 어노테이션 프로세서 라는 기능을 이용해서 컴파일 시점에 생성자 코드를 자동으로 생성해주는 것이다.
@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
}
최신 트랜드
- 생성자를 딱 1개두고
@Autowired
를 생략하거나 - 여기에,
Lombok
라이브러리의@RequiredArgsConstructor
를 사용한다.
롬복 라이브러리 적용 방법
- build.gradle에 라이브러리 및 환경 설정 코드 추가
plugins {
id 'org.springframework.boot' version '2.6.5'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'hello'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
//lombok 설정 추가 시작
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
//lombok 설정 추가
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
//lombok 라이브러리 추가 시작
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
//lombok 라이브러리 추가 끝
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
tasks.named('test') {
useJUnitPlatform()
}
- 코드를 작성하고나서 아래 과정 해줘야한다.
- Preferences -> plugin -> lombok 검색해서 설치 + 재시작
- Preferences -> Annotaion Processor 검색 -> Enable annotation processing 체크 + 재시작
- 임의의 클래스 만들고 ,@Getter, @Setter 확인
package hello.core;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
@Getter
@Setter
@ToString
public class HelloLombok {
private String name;
private int age;
public static void main(String[] args) {
HelloLombok helloLombok = new HelloLombok();
helloLombok.setName("aaaaaa");
//@ToString 어노테이션을 통해 스트링으로 바로 출력할 수 있다.
System.out.println("helloLombok = " + helloLombok);
}
}
Author And Source
이 문제에 관하여([Spring] 기본편 07. 의존관계 자동 주입1), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@somyeong0623/Spring-기본편-07.-의존관계-자동-주입저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)