- Coding, Computer이라는 두개의 클래스가 있다고 생각하자.
- Coding은 Computer에 의존한다
- 필드주입은 final 키워드를 사용할 수 없다.
Computer
- computer라는 클래스 생성 후 어노테이션을 통해 해당 객체를 스프링에서 관리하도록 지정
- Data 어노테이션을 등록한다.( 롬복에서 제공해주는 몇몇 메서드를 자동으로 생성해준다.)
package com.example.ex00.dependency;
import org.springframework.stereotype.Component;
import lombok.Data;
@Component
@Data
public class Computer {
}
Coding
- 똑같이 스프링에서 관리하도록 Component어노테이션을 등록해주고
- Data어노테이션을 만들어준다. 이 어노테이션을 통해서 원래 필드주입에서는 final키워드를 사용하지 못하지만 생성자가 만들어 지면서 지금당장은 사용할 수 있게 된다.(생성자를 통해서 값을 받아서 그 값이 초기화에 들어갔기 때문에 오류가 안나는것)
- 아무튼 Coding 클래스 안에 의존하는 객체를 만들어주는데, 의존은 너무나 단단해서 Computer밖에 못쓰는 걸 방지하고자 의존성 주입을 하게 된다. 그래서 new 키워드로 직접 주입하는 것이 아니라 @Autowired 어노테이션을 사용한다.
- WebApplicationContext객체가 서버가 돌아가 실행되면서 이런 어노테이션이 붙어져있는 애들을 처리하고,
- Coding을 사용하기 위해서 Coding의 필드를 메모리에 올리는 순간 computer도 스프링에서 자동으로 주입된다.
⚠️ 정석으로는 필드주입에서는 final키워드를 사용할 수 없어 다른곳에서 변형이 가능하다.
package com.example.ex00.dependency;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import lombok.Data;
@Component
@Data
public class Coding {
@Autowired
private final Computer computer;
}
단위테스트를 해보자.
- 단위테스는 WAS서버랑 다르게 JUit이라는 프로그램으로 돌린다.
- 그래서 RunWith를 통해 어떤 프로그램인지 , xml파일 경로를 설정해야한다.
- Log4j는 출력하는 용도의 어노테이션이다.
- root-context에 관리하는 파일들의 패키지를 작성해줘야 @Component 어노테이션이 붙어있는 클래스들을 찾을 수 있다.
- 고로, Namespace에 context를 클릭해주고, <context:component-scan base-package=””/>에 패키지를 입력해줘야 한다.
package com.example.ex00.dependency;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import lombok.extern.log4j.Log4j;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j
public class DependencyTests {
@Autowired
private Coding coding;
@Test
public void checkDependencyInjection() {
log.info("-----------------");
log.info("coding :"+coding);
log.info("computer :"+coding.getComputer());
log.info("-----------------");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<context:component-scan base-package="com.example.ex00.dependency" />
</beans>
정리
- 어떤 클래스를 만들었을 때, 외부에서 이 클래스의 필드에 접근하기 위해서 객체화 한다.
- 유연한 개발을 위해서 의존성 주입을 Autowired를 통해서 하는데, 그렇기 위해서는 스프링이 존재에 대해서 알아야하고(@Component), root-context에 그 존재의 경로를 적어줘야한다. 그러면WebApplicationContext객체가 root-context에 들어가서 componentent들을 찾는다.
💡 final 사용하면 안되지만 **필드주입에서 final 키워드를 써도 오류가 안났던 이유**
- final은 상수 이기 때문에, 선언과 동시에 값을 지정해줘야한다.
- 필드 주입을 하게 되면 클래스를 메모리에 올리고 난 후에 주입을 하는 거라서 값이 없으니 오류가 난다.(필드주입으로 만 사용했을 경우에는 final 키워드를 사용할 수 없다. )
- 하지만, @Data를 사용하면 초기화 생성자 까지도 자동으로 만들어진다.(ex) Coding(computer))
- 생성자를 통해서 값을 받아서 그 값이 상수의 초기화때 들어가서 사용할 수 있었다.