Spring에서의 의존성 주입이란 다음과 같다.
어떠한 객체에 스프링 컨테이너가 또 다른 객체와 의존성을 맺어주는 행위
Spring프레임워크의 3가지 핵심 프로그래밍 모델 중 하나로,
외부에서 두 객체간의 관계를 결정해주는 디자인 패턴으로 인터페이스를 사이에 두고 클래스 레벨에서는 의존관계가 고정되지 않도록 하고 런타임 시에 관계를 동적으로 주입하여 결합도를 낮출 수 있게 하는 기법이다.
DI (Dependency Injection)
의존성 주입은 IoC(Invesoin of Control, 의존성 역전) 원칙하에 객체간의 결합을 약하게 해 주고 유지보수가 좋은 코드를 만들어준다.
즉, 외부에서 생성된 객체를 이용하는 것이다.
한 객체가 어떤 객체에 의존할것인지는 별도의 관심사이다. DI컨테이너를 통해 서로 강하게 결합되어있는 두 클래스를 분리하고,
두 객체간 관계를 결정해줌으로써 결합도를 낮추고 유연성을 확보하고자 한다.
(이때 다른 빈을 주입받으려면 자기 자신도 반드시 컨테이너의 빈이여야 한다.)
예를 들어, 배터리(의존 객체)를 사용하는 장난감(어떤 객체)에게 배터리를 넣어주는 것을 의존성 주입이라고 생각하면 좋다.
1. Spring을 사용하지 않은 DI
1.1 - 기본적인 DI(의존 주입)
// 1. 스프링 없이 주입(생성자 주입)
class Apple {
void f1() {
System.out.println("test");
}
}
class Orange {
Apple apple;
Orange (Apple apple) {
this.apple = apple;
}
void f2() {
apple.f1();
}
}
public class Hello {
public static void main(String[] args) {
Orange orange = new Orange(new Apple());
orange.f2();
}
}
1.2 - 스프링 없이 주입(수정자 주입)
//2. 스프링 없이 주입(수정자 주입)
class Apple {
void f1() {
System.out.println("test");
}
}
class Orange {
Apple apple;
void setApple (Apple apple) {
this.apple = apple;
}
void f2() {
apple.f1();
}
}
public class Hello {
public static void main(String[] args) {
Orange orange = new Orange();
orange.setApple(new Apple());
orange.f2();
}
}
2. Spring을 사용한 DI
- Spring 제작사에서 사용하지 않기를 권장함
- 하지만 실전에서 사용을 많이 한다.
Setter 주입 - 주입을 시킬 객체가 많이 있다.
생성자 주입 - 한 두개 정도 있다.
@Autowired 어노테이션 사용의 이유 - 객체를 생성시키는 코드가 많이 줄기 때문.
2.1 - @Autowired 없이 스프링으로 주입 (생성자 주입)
//3. @Autowired 없이 스프링으로 주입(생성자 주입)
class Apple {
void f1() {
System.out.println("test");
}
}
class Orange {
Apple apple;
Orange(Apple apple) {
this.apple = apple;
}
void f2() {
apple.f1();
}
}
@Configuration
class AppConfig{
@Bean
Apple apple() {
return new Apple();
}
@Bean
Orange orange() {
return new Orange(apple());
}
}
public class Hello {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
Orange orange = ctx.getBean(Orange.class);
orange.f2();
ctx.close();
}
}
2.2 - @Autowired 없이 스프링으로 주입 (세터(수정자) 주입)
// 4. @Autowired 없이 스프링으로 주입(세터(수정자 주입))
class Apple {
void f1() {
System.out.println("test");
}
}
class Orange {
Apple apple;
void setApple(Apple apple) {
this.apple = apple;
}
void f2() {
apple.f1();
}
}
@Configuration
class AppConfig{
@Bean
Apple apple() {
return new Apple();
}
@Bean
Orange orange() {
Orange orange = new Orange();
orange.setApple(apple());
return orange;
}
}
public class Hello {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
Orange orange = ctx.getBean(Orange.class);
orange.f2();
ctx.close();
}
}
2.3 - @Autowired를 사용하여 스프링으로 주입 (필드 주입)
//5. @Autowired를 사용하여 스프링으로 주입(필드 주입)
class Apple {
void f1() {
System.out.println("test");
}
}
class Orange {
@Autowired
Apple apple;
void f2() {
apple.f1();
}
}
@Configuration
class AppConfig{
@Bean
Apple apple() {
return new Apple();
}
@Bean
Orange orange() {
return new Orange();
}
}
public class Hello {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
Orange orange = ctx.getBean(Orange.class);
orange.f2();
ctx.close();
}
}
2.4 - @Autowired를 사용하여 스프링으로 주입 (세터 주입)
//6. @Autowired를 사용하여 스프링으로 주입(세터 주입)
class Apple {
void f1() {
System.out.println("test");
}
}
class Orange {
Apple apple;
@Autowired
void setApple(Apple apple) {
this.apple = apple;
}
void f2() {
apple.f1();
}
}
@Configuration
class AppConfig{
@Bean
Apple apple() {
return new Apple();
}
@Bean
Orange orange() {
return new Orange();
}
}
public class Hello {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
Orange orange = ctx.getBean(Orange.class);
orange.f2();
ctx.close();
}
}
DI - 비행기 연료 예제
기본적인 DI(의존 주입)
class Airplane{
Water water;
Airplane(Water water){
this.water = water;
}
void fly() {
this.water.use();
System.out.println("FLY");
}
}
class Water{
void use() {
System.out.println("물 사용");
}
}
@Configuration
class AppConfig {
}
public class Hello {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext(AppConfig.class);
Airplane airplane = new Airplane(new Water());
airplane.fly();
ctx.close();
}
}
연료만 Bean 객체
class Airplane{
Water water;
Airplane(Water water){
this.water = water;
}
void fly() {
this.water.use();
System.out.println("FLY");
}
}
class Water{
void use() {
System.out.println("물 사용");
}
}
@Configuration
class AppConfig {
@Bean
Water water() {
return new Water();
}
}
public class Hello {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext(AppConfig.class);
Water water = ctx.getBean(Water.class);
Airplane airplane = new Airplane(water);
airplane.fly();
ctx.close();
}
}
연료 비행기 Bean 객체
class Airplane{
Water water;
Airplane(Water water){
this.water = water;
}
void fly() {
this.water.use();
System.out.println("FLY");
}
}
class Water{
void use() {
System.out.println("물 사용");
}
}
@Configuration
class AppConfig {
@Bean
Water water() {
return new Water();
}
@Bean
Airplane airplane() {
// 생성자 주입 (물 주입)
return new Airplane(water());
}
}
public class Hello {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext(AppConfig.class);
Airplane airplane = ctx.getBean(Airplane.class);
airplane.fly();
ctx.close();
}
}
오토와이어 사용(생성자 주입)
class Airplane{
@Autowired
Water water;
void fly() {
this.water.use();
System.out.println("FLY");
}
}
class Water{
void use() {
System.out.println("물 사용");
}
}
@Configuration
class AppConfig {
@Bean
Water water() {
return new Water();
}
@Bean
Airplane airplane() {
return new Airplane();
}
}
public class Hello {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext(AppConfig.class);
Airplane airplane = ctx.getBean(Airplane.class);
airplane.fly();
ctx.close();
}
}
오토와이어 사용(세터 사용)
class Airplane{
Water water;
@Autowired
void setWater(Water water) {
this.water = water;
}
void fly() {
this.water.use();
System.out.println("FLY");
}
}
class Water{
void use() {
System.out.println("물 사용");
}
}
@Configuration
class AppConfig {
@Bean
Water water() {
return new Water();
}
@Bean
Airplane airplane() {
return new Airplane();
}
}
public class Hello {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext(AppConfig.class);
Airplane airplane = ctx.getBean(Airplane.class);
airplane.fly();
ctx.close();
}
}
- set방식 3가지 -
void setApple(Apple apple)
void setApple(@Nullable Apple apple)
void setApple(Optional<Apple> apple)
@Nullable - 어노테이션
클래스의 디폴트 기능이 있는데
사용자가 만약에 추가적인 객체를 만들어서 주입을 해준다면
그 기능까지 해주기 위해 @Nullable을 사용하여
디폴트에는 쓰이지 않는 추가적인 기능을 제공할 수 있다.
class Apple {
void f1() {
System.out.println("---헤드라인 뉴스---");
}
}
class Orange {
Apple apple;
// @Nullable : 객체가 생성이 안될때 제어프로그램을 돌리기 위해
// 어노테이션을 사용하여 프로그램이 터지더라도 실행을 시킨다.
@Autowired
void setApple(@Nullable Apple apple) {
System.out.println("호랑이");
if (apple != null) {
this.apple = apple;
}
this.apple = apple;
}
void f2() {
// 방어적 프로그램
if (apple != null) {
apple.f1();
}
System.out.println("내일은 대통령 선거일 입니다.");
}
}
@Configuration
class AppConfig {
// apple 객체가 없을때
/*@Bean
Apple apple() {
return new Apple();
}*/
@Bean
Orange orange() {
return new Orange();
}
}
public class Hello {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
Orange orange = ctx.getBean(Orange.class);
orange.f2();
ctx.close();
}
}
@Qualifier : 한정자
사용범위를 조건이나 제한을 걸때에 사용되는 예약어
class Apple {
void f1() {
System.out.println("Apple call");
}
}
class Orange extends Apple{
void f1() {
System.out.println("Orange call");
}
}
// @Qualifier : 한정자
// 사용범위를 조건이나 제한을 걸때에 사용되는 예약어
class Kiwi{
// 업캐스팅이 가능하기 때문에
// Apple 이나 Orange를 받아도 성립되는 문법
Apple a1 = new Apple();
Apple a2 = new Orange();
@Autowired
@Qualifier("apple")
//@Qualifier("orange")
Apple apple;
void f2() {
apple.f1();
}
}
@Configuration
class AppConfig {
@Bean
Apple apple() {
return new Apple();
}
@Bean
Orange orange() {
return new Orange();
}
@Bean
Kiwi kiwi() {
return new Kiwi();
}
}
public class Hello {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
Kiwi kiwi = ctx.getBean(Kiwi.class);
kiwi.f2();
ctx.close();
}
}
결과 : Apple call
@ComponentScan
- 빈으로 등록 될 준비를 마친 클래스들을 스캔하여, 빈으로 등록해주는 것이다.
빈으로 등록 될 준비를 하는 것이 무엇일까?
우리가 @Controller, @Service, @Component, @Repository 등 어노테이션을 붙인
클래스들이 빈으로 등록 될 준비를 한 것이다. - 클래스의 이름을 객체의 이름으로 한다. (ex ) class Airplane -> airplane)
// 클래스 자체가 객체가 생성되고 있는 중
// @Configuration를 쓰지 못하는 상황에서 사용
@Component
class Airplane{
Water water;
@Autowired
void setWater(Water water) {
this.water = water;
}
void fly() {
this.water.use();
System.out.println("FLY");
}
}
class Water{
void use() {
System.out.println("물 사용");
}
}
@Configuration
// @Component가 달려있는 모든 클래스를 찾아서 모두 객체를 생성 시켜준다.
@ComponentScan
class AppConfig {
@Bean
Water water() {
return new Water();
}
}
public class Hello {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext(AppConfig.class);
Airplane airplane = ctx.getBean(Airplane.class);
airplane.fly();
ctx.close();
}
}
'IT EDU > SPRING' 카테고리의 다른 글
[Spring] 스프링 부트 배포하기 (War 파일로 빌드) (1) | 2022.03.18 |
---|---|
[Spring] 스프링 부트 - Spring Boot 설치 및 기본 세팅 (3) | 2022.03.18 |
[Spring] MVC 데이터 전송 예제 (2) | 2022.03.10 |
[Spring] MVC를 위한 Maven 프로젝트 생성 (1) | 2022.03.10 |
댓글