스프링을 이해하는데는 POJO(Plain Old Java Object)를 기반으로 스프링 삼각형이라는 애칭을 가진
IoC/DI, AOP, PSA라고 하는 스프링의 3대 프로그래밍 모델에 대한 이해가 필수이다.
DI(의존성 주입)
자바에서의 의존성이란 new이다.
ex) 자동차가 내부적으로 타이어를 생산한다.
자바로 표현하자면 아래의 코드와같다.
interface Tire {
String getBrand();
}
public class KoreaTire implements Tire {
public String getBrand() {
return "코리아 타이어";
}
}
public class AmericaTire implements Tire {
public String getBrand() {
return "미국 타이어";
}
}
///////////////////////////////////////////////////////////
public class Car {
Tire tire;
public Car() {
tire = new KoreaTire(); //의존관계가 발생한다.
// tire = new AmericaTire();
}
public String getTireBrand() {
return "장착된 타이어: " + tire.getBrand();
}
}
////////////////////////////////////////////////////////////
public class Driver {
public static void main(String[] args) {
Car car = new Car();
System.out.println(car.getTireBrand());
}
}
Car클래스의 생성자 코드에서 직접 타이어를 생성해 참조변수와 연결하였다.
Car 클래스는 인터페이스 뿐만 아니라 구체화된 KoreaTire에도 의존하게 된다.
의존이 일어나고 있는 두 객체 사이의 직접적인 의존성을 해결하는 방법은 두가지이다.
1. 스프링 없이 의존성 주입하기1 - 생성자를 통한 의존성 주입
의사 코드
운전자가 타이어를 생산한다.운전자가 자동차를 생산하면서 타이어를 장착한다.
자바로 표현 - 생성자 인자 이용
Tire tire = new KoreaTire();
Car car = new Car(tire);
주입이란 말은 외부에서라는 뜻을 내포하고 있는 단어이다.
자동차 내부에서 타이어를 생성할 것이 아니라 외부에서 생산된 타이어를 자동차에 장착하는 작업이 주입니다.
앞의 의존성을 직접 해결하는 코드를 작성해보자.
//Tire인터페이스와 Tire를 구현받는 클래스들은 코드가 변경되지않으므로 생략한다.
public class Car {
Tire tire;
public Car(Tire tire) { // 생성자 주입
this.tire = tire;
}
public String getTireBrand() {
return "장착된 타이어: " + tire.getBrand();
}
}
//////////////////////////////////////////////////////////////
public class Driver {
public static void main(String[] args) {
Tire tire = new KoreaTire();
//Tire tire = new AmericaTire();
Car car = new Car(tire);
System.out.println(car.getTireBrand());
}
}
Car클래스에서 생성자부분이 달라졌다. new가 사라지고 생성자에 인자가 추가된 것이다.
new를 통해 타이어를 생산하는 부분이 Car클래스에서 Driver로 이동했다.
무엇이 개선됐을까?
기존의 직접 Car가 tire를 생산하고 연결하는 것은 tire교체 시 Car의 수정이 불가피하므로 코드의 유연성이 떨어지고 확장성도 떨어졌다. 의존성 주입을 이용함으로서 자동차가 구체적인 객체를 생성하고 연결하는 것이 아니라 운전자가 대신 원하는 의존성을 생성하고 연결해준다.
따라서, Car객체는 의존성 주입을 통해 추상적인 인터페이스에 의존하면서 실행이 가능해진다.
그리고 Car객체는 수정 없이 확장이 가능해지며 다른코드를 재컴파일 및 재배포할 필요가 없어진다.
2. 스프링 없이 의존성 주입하기 - 속성을 통한 의존성 주입
의사 코드
운전자가 타이어를 생산한다.
운전자가 자동차를 생산한다.
운전자가 자동차에 타이어를 장착한다.
자바로 표현
Tire tire = new KoreaTire();
Car car = new Car;
car.setTire(tire);
생성자 주입을 하면 더 이상 타이어를 교체할 수 없다.
사용자가 원할 때 타이어를 교체하려면 생성자 주입이 아닌 속성을 통한 의존성 주입이 필요하다. (타이어 상시 교체를 위한 속성 주입)
1/29(토)
위에까지는 스프링을 사용하지 않고 의존성을 주입하였다. 이제부터는 스프링을 이용해 의존성을 주입해보자.
1. 스프링을 통한 의존성 주입 - xml 파일사용
의사 코드
운전자가 종합 쇼핑몰에서 타이어를 구매했다.
운전자가 종합 쇼핑몰에서 자동차를 구매했다.
운전자가 자동차에 타이어를 장착했다.
자바로 표현 - 속성 메서드 사용
ApplicationContext context = new ClassPathXmlApplicationContext("expert002.xml", Driver.class);
Tire tire = (Tire)context.getBean("tire");
Car car = (Car)context.getBean("car");
car.setTire(tire);
스프링을 통한 의존성 주입은 생성자를 통한 의존성 주입과 속성을 통한 의존성 주입을 모두 지원하는데, 여기서는 속성을 통한 의존성 주입만 살펴보겠다.
위의 의사코드를 보면 생산에서 구매로 달라졌을 뿐 나머지는 바뀌지 않았다는 것을 알았을 것이다. 스프링을 도입한다고 해서 기존 방식과 달라질 것은 없다. 또한 그동안 작성해온 Tire 관련 클래스와 Car 클래스 역시 달라지는 부분이 전혀 없도록 코딩할 수 있다. 오직 Driver 클래스만 살짝 손봐주고 스프링 설정 파일 하나만 추가하면 작업이 끝난다.
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Driver {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("expert002/expert002.xml");
Car car = context.getBean("car", Car.class);
Tire tire = contet.getBean("tire", Tire.class);
car.setTire(tire);
System.out.println(car.getTireBrand());
}
}
우선 2개의 import문이 추가된 것을 알수있다.
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
종합 쇼핑몰(스프링 프레임워크)에 대한 정보를 가지고 있는 패키지라고 기억해두자. 그리고 종합 쇼핑몰에 대한 정보가 필요하다. 자바 코드는 아래와 같다.
ApplicationContext context = new ClassPathXmlApplicationContext("expert002/expert002.xml");
드디어 종합 쇼핑몰에서 상품에 해당하는 Car와 Tire를 구매하는 코드다.
Car car = context.getBean("car", Car.class);
Tire tire = context.getBean("tire", Tire.class);
한 가지 더 필요한 것이 있다. 아무리 종합 쇼핑몰이라고는 하지만 상품이 입점돼 있어야만 판매할 수 있을 것이다. 입점된 상품에 대한 정보는 어디에 숨어 있을까? 바로 위 코드에서 XML 파일이 하나 보이는데 전체 경로는 아래와 같다.
/src/main/java/expert002/expert002.xml
이 XML 파일 안에 쇼핑몰에서 구매 가능한 상품 목록이 등록돼 있어야 한다. 상품 목록이 담긴 XML 파일을 만들려면 expert002 패키지에서 마우스 오른쪽 버튼을 클릭한 후 New -> Other -> Spring -> Spring Bean Configuration File을 차례대로 선택하고 이름만 지정하면 된다.
종합 쇼핑몰 판매 목록을 등록하기 위한 XML 파일
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
이름을 expert002.xml로 지정하고 나면 위와 같은 XML 파일이 만들어진다. 이렇게 생성된 XML 파일에 쇼핑몰에서 판매하는 상품 목록을 등록하면 된다. 이제 상품을 등록해보자.
종합 쇼핑몰 판매 목록을 등록하기 위한 XML 파일
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="tire" class="expert002.KoreaTire"></bean>
<bean id="americaTire" class="expert002.AmericaTire"></bean>
<bean id="car" class="expert002.Car"></bean>
</beans>
위의 예제에서는 3개의 상품을 등록했다. 상품을 등록할 때는 bean 태그를 이용해 등록한다. 이때 각 상품을 구분하기 위한 id 속성과 그 상품을 어떤 클래스를 통해 생산(인스턴스화)해야 할지 나타내는 class 속성을 함께 지정하면 된다.
스프링을 도입해서 얻는 이득은 무엇일까? 가장 큰 이득을 꼽으라면 자동차의 타이어 브랜드를 변경할 때
그 무엇도 재컴파일/재배포하지 않아도 XML 파일만 수정하면 프로그램의 실행 결과를 바꿀 수 있다는 것이다.
2. 스프링을 통한 의존성 주입 - 스프링 설정 파일(XML)에서 속성 주입
의사 코드 - 점점 더 현실 세계를 닮아가고 있다.
운전자가 종합 쇼핑몰에서 자동차를 구매 요청한다.
종합 쇼핑몰은 자동차를 생산한다.
종합 쇼핑몰은 타이어를 생성한다.
종합 쇼핑몰은 자동차에 타이어를 장착한다.
종합 쇼핑몰은 운전자에게 자동차를 전달한다.
자바로 표현
ApplicationContext context = new ClassPathXmlApplicationContext("expert003.xml", Driver.class);
Car car = (Car)context.getBean("car");
XML로 표현
<bean id="tire" class="expert003.KoreaTire"></bean>
<bean id="Americatire" class="expert003.AmericaTire"></bean>
<bean id="car" class="expert003.Car"></bean>
<property name="tire"> ref="KoreaTire"></property>
</bean>
자바에서 접근자 및 설정자 메서드를 속성 메서드라고 하는데 영어로 속성은 Property다.
Driver.java에서 car.setTire(tire)라고 하던 부분은 XML 파일의 property 태그를 이용해 대체하는 것이다.
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Driver {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("expert003/expert003.xml");
Car car = context.getBean("car", Car.class);
System.out.println(car.getTireBrand());
}
}
이전 코드에서
Tire tire = context.getBean("tire", Tire.class);
car.setTire(tire);
두 줄이 삭제되었다.
XML 파일을 살펴보면 사라진 이유를 알 수 있다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="koreaTire" class="expert003.KoreaTire"></bean>
<bean id="americaTire" class="expert003.AmericaTire"></bean>
<bean id="car" class="expert002.Car">
<property name="tire" ref="KoreaTire"></property>
</bean>
</beans>
3. 스프링을 통한 의존성 주입 - @Autowired를 통한 속성 주입
의사 코드
운전자가 종합 쇼핑몰에서 자동차를 구매 요청한다.
종합 쇼핑몰은 자동차를 생산한다.
종합 쇼핑몰은 타이어를 생성한다.
종합 쇼핑몰은 자동차에 타이어를 장착한다.
종합 쇼핑몰은 운전자에게 자동차를 전달한다.
프로그래머의 3대 스킬이 있다.
1. Copy & Paste / 복붙
2. Divide & Conquer /분할 & 정복
3. Creative & Idleness (창조적 게으름)
가장 대표적인 창조적 게으름은 i++, for, for-each는 이미 있는 기능을 더욱 편하게 사용하려고 만든 방법들이다.
스프링의 창조적 게으름은 @Autowired이다.
Tire tire;
public void setTire(Tire tire) {
this.tire = tire;
}
반드시 설정자 메소드를 통해서 tire값을 주입해야하는 것일까?
스프링 속석주입방법 가운데 @Autowired를 이용하는 방법을 사용해보자.
이를 사용하면 스프링프레임워크가 설정파일을 통해 설정자 메소드 대신 속성을 주입해준다.
기존의 XML 설정 파일
<bean id="car" class="expert003.Car">
<property name="tire" ref="KoreaTire"></property>
</bean>
새로운 XML 설정 파일
<bean id="car" class="expert004.Car"></bean>
@Autowired를 통해 car의 property를 자동으로 엮어줄 수 있으므로(자동 의존성 주입) 생략 가능해진 것이다.
스프링의 @Autowired는 id와 type 중 type 구현에 우선순위가 있다.
4. 스프링을 통한 의존성 주입 - @Resource를 통한 속성 주입
@Resource는 @Autowired와 같이 속성을 자동으로 엮어준다.
하지만 @Autowired는 스프링의 어노테이션이고 @Resource는 자바 표준 어노테이션이다.
또한 둘은 검색방식에서 차이를 보인다. 리소스는 id먼저 type 후순위이고 Autowired는 반대이다.
참고
스프링 입문을 위한 자바 객체지향의 원리
'스프링' 카테고리의 다른 글
Spring Bean (0) | 2022.02.20 |
---|---|
[SpringBoot] PasswordEncoder 적용하기 (0) | 2022.02.18 |
스프링 삼각형과 설정 정보 - AOP / PSA (0) | 2022.02.09 |
스프링 (0) | 2022.02.09 |
1장 오브젝트와 의존관계 (0) | 2021.12.01 |