[IOC 세가지 방식]
- 스프링에서 제공하는 IOC의 세 가지 방식: DL, DI, 자동객체생성
* DL: 사용할 변수에 알맞은 객체를 검색하는 기능
* DI: 해당 변수에 검색된 알맞은 객체를 대입하는 기능
* 자동객체생성: 컨테이너가 설정파일에 등록되거나 어노테이션이 달린 클래스의 객체를 생성하는 기능
[DI의 세 가지 방식]
1. 생성자 함수를 이용한 의존성 주입: bean 엘리먼트 사이에 constructor-arg 엘리먼트로 생성자에 전달할 파라미터를 지정할 수 있다. value속성으로 일반 리터럴을 넣을 수도 있고 아니면 ref 속성으로 bean 객체를 참조하여 넣을 수도 있다.
<bean id="hCar" class=".....HyundaiCar">
// ref는 메소드임
<constructor-arg ref="carAudio">
// value는 변수임
<constructor-arg value="red">
</bean>
<bean id="carAudio" class=".....CarAudio">
</bean>
[예시]
[CarAudio 인터페이스 생성]
public interface CarAudio {
public void volumeUp();
public void volumeDown();
}
[HyundaiCar 클래스 수정]
- CarAudio의 객체가 계속 늘어날수록(SonyCarAudio, AppleCarAudio 등..) 결합도가 높아져 독립성이 낮아진다.
이를 위해 CarAudio의 인터페이스를 생성해서 객체를 생성하고, root-context.xml에서 ref만 수정해서 변경할 수 있다.
package com.bit.springboard.coupling;
public class HyundaiCar implements Car {
private CarAudio carAudio;
// private SonyCarAudio sonyCarAudio;
// private AppleCarAudio appleCarAudio;
// private SamsungCarAudio samsungCarAudio;
private String color;
public HyundaiCar() {
System.out.println("HyundaiCar 생성자1 호출");
}
public HyundaiCar(CarAudio carAudio) {
System.out.println("HyundaiCar 생성자2 호출");
this.carAudio = carAudio;
}
public HyundaiCar(CarAudio carAudio, String color) {
System.out.println("HyundaiCar 생성자3 호출");
this.carAudio = carAudio;
this.color = color;
}
// public HyundaiCar(SonyCarAudio sonyCarAudio, AppleCarAudio appleCarAudio, String color) {
// System.out.println("HyundaiCar 생성자3 호출");
// this.sonyCarAudio = sonyCarAudio;
// this.appleCarAudio = appleCarAudio;
// this.color = color;
// }
public void engineOn() {
System.out.println("HyundaiCar 시동을 건다.");
}
public void engineOff() {
System.out.println("HyundaiCar 시동을 끈다.");
}
public void speedUp() {
System.out.println("HyundaiCar 속도를 올린다.");
}
public void speedDown() {
System.out.println("HyundaiCar 속도를 낮춘다.");
}
public void initMethod() {
System.out.println("HyundaiCar 객체 생성");
}
public void destroyMethod() {
System.out.println("HyundaiCar 객체 삭제");
}
public void volumeUp() {
// carAudio = new CarAudio();
carAudio.volumeUp();
}
public void volumeDown() {
// carAudio = new CarAudio();
carAudio.volumeDown();
}
// getColor 생성
public String getColor() {
return color;
}
// setColor 생성
public void setColor(String color) {
this.color = color;
}
}
[appleCarAudio 생성]
package com.bit.springboard.coupling;
public class AppleCarAudio implements CarAudio{
public AppleCarAudio() {
System.out.println("AppleCarAudio 객체 생성");
}
public void volumeUp() {
System.out.println("AppleCarAudio 소리 증가");
}
public void volumeDown() {
System.out.println("AppleCarAudio 소리 감소");
}
}
[root-context.xml 수정]
- ref 부분만 바꿔서 appleCarAudio, SonyCarAudio 등 수정한다.
<bean id="hyundaiCar" class="com.bit.springboard.coupling.HyundaiCar"
init-method="initMethod" destroy-method="destroyMethod"
lazy-init="true">
<!--constructor-arg: 생성자의 매개변수를 전달하는 엘리먼트
일반적인 값을 매개변수로 전달할 때는 value 속성으로 지정
bean 객체 타입의 값을 매개변수로 전달할 때는 ref 속성으로 지정-->
<!--constructor-arg 지정 개수 만큼 매개변수를 가지고 있는 생성자를 자동으로 호출한다.-->
<!--index 속성: 전달할 매개변수의 순서를 지정-->
<constructor-arg index="1" value="red"></constructor-arg>
<constructor-arg index="0" ref="appleCarAudio"></constructor-arg>
</bean>
<bean id="sonyCarAudio" class="com.bit.springboard.coupling.SonyCarAudio"></bean>
<bean id="appleCarAudio" class="com.bit.springboard.coupling.AppleCarAudio"></bean>
[CarOwner_Solve_Coupling 실행]
package com.bit.springboard.coupling;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class CarOwner_Solve_Coupling {
public static void main(String[] args) {
// 1. 스프링 컨테이너 구동
// 스프링 컨테이너 객체 생성.
// bean 엘리먼트로 등록되어 있는 클래스들의 객체 자동으로 생성
AbstractApplicationContext factory =
// 어떠한 설정파일로 스프링 컨테이너를 구동할지 지정
new GenericXmlApplicationContext("root-context.xml");
// 2. 의존성 검색(DL: Dependency Lookup): bean 객체로 생성된 객체를 찾는 기능
// 의존성 주입(DI: Dependency Injection): 의존성 검색으로 찾은 객체를 변수에 담아주는 기능
Car car = (Car)factory.getBean("kiaCar");
// Car car = factory.getBean("kiaCar", Car.class);
car.engineOn();
car.speedUp();
car.speedDown();
car.engineOff();
// scope: "prototype" 객체를 요청할 때마다 새로운 객체 생성
car = (Car)factory.getBean("kiaCar");
// lazy-init: "true" 스프링컨테이너 구동시 객체를 생성하지 않고 요청시에 객체를 생성한다.
HyundaiCar hCar = factory.getBean("hyundaiCar", HyundaiCar.class);
hCar.engineOn();
hCar.speedUp();
hCar.speedDown();
hCar.engineOff();
hCar.volumeUp();
hCar.volumeDown();
System.out.println(hCar.getColor());
// 3. 스프링 컨테이너 구동 종료
// 스프링 컨테이너가 종료되면서 생성되어 있던 bean객체들을 모두 삭제한다.
factory.close();
}
}
[결과]
2. setter 메소드를 이용한 의존성 주입
- 의존성에 해당하는 setter 메소드가 미리 클래스안에 정의되어 있어야 한다.
- bean 엘리먼트 안에 property 엘리먼트를 정의하여 세터함수를 호출
<bean id="hCar" class=".....HyundaiCar">
<property name="필드 변수명(carAudio)" ref or value="참조할 bean 객체의 id or 값">
</bean>
<bean id="sonyCarAudio" class=".....SonyCarAudio"/>
[예시]
[root-context.xml 수정]
<bean id="kiaCar" class="com.bit.springboard.coupling.KiaCar"
init-method="initMethod" destroy-method="destroyMethod"
scope="prototype"
>
<!--property: name 속성값으로 지정된 멤버변수의 setter 메소드를 통해 의존성 주입하는 엘리먼트-->
<!--bean 객체 생성 직후에 setter 메소드를 호출해서 의존성을 주입한다.-->
<property name="color" value="blue"></property>
<property name="carAudio" ref="sonyCarAudio"></property>
</bean>
<!--p namespace: property 엘리먼트를 간편하게 설정할 수 있는 네임스페이스.
bean 엘리먼트의 속성으로 property 엘리먼트를 지정할 수 있다.
p:변수명: 리터럴값으로 setter 메소드 실행
p:변수명-ref: bean 객체를 참조하여 setter 메소드 실행-->
<bean id="hyundaiCar" class="com.bit.springboard.coupling.HyundaiCar"
p:carAudio-ref="sonyCarAudio" p:color="blue"/>
3. 필드 주입을 이용한 의존성
- @Autowired라는 어노테이션의 기능을 이용해서 생성된 bean 객체를 멤버 변수에 직접 주입하는 것.
//CarAudio의 형태의 생성된 객체가 factory에 있는지 검사하여 찾으면 주입 없으면 에러 발생
@Autowired
CarAudio carAudio;
[어노테이션을 이용한 객체 생성]
어노테이션이란 사전적의미로는 주석이라는 뜻. 기능을 가직도 있는 주석. 코드 사이사이에 사용하며 특별한 의미나 특별한 기능을 수행해주는 주석.
프로그램에게 추가적 정보를 제공해주는 메타 데이터. 메타 데이터는 데이터들을 처리하기 위한 정보.
namespace 탭에서 context 추가. context에는 component-scan이라는 기능이 존재. 이 기능을 이용하면 특정 패키지에 있는 클래스 안에서 @Component 어노테이션을 찾아서 객체를 자동 생성.
@Component 어노테이션은 최상의 어노테이션이고 상속받은 @Controller(Servlet 기능을 추가한 어노테이션), @Service(업무로직 관련 기능 추가한 어노테이션), @Repository(DB연동 관련 기능 추가한 어노테이션) 등이 존재한다.
위 세개의 어노테이션도 @Component를 상속받았기 때문에 component-scan에 의해 자동 객체 생성이 일어난다.
<context:component-scan base-package="컴포넌트 스캔할 패키지명">
[어노테이션을 이용한 DL, DI]
어노테이션을 통해서 변수에 알맞은 객체를 검색하고 주입하는 기능을 한 번에 처리할 수 있다.
@Autowired: 선언된 멤버 변수의 형태가 같은 객체를 찾아서(DL) 주입함(DI).
@Autowired // CarAudio 형태의 객체가 컨테이너에 생성되어 있는지 검색하여 주입
CarAudio carAudio;
동일한 형태의 객체가 두 개이상 컨테이너에 생성되어 있으면 에러가 발생
@Qualifier("id"): 지정된 아이디로 객체를 찾아서 주입. 항상 @Autowired와 함께 사용
@Autowired
@Qualifier("sonyCarAudio")
CarAudio carAudio;
@Resource(name="id"): @Autowired + @Qulifier의 기능을 가지는 어노테이션. 같은 형태이면서 지정된 id로 된 객체를 찾아서 주입.
@Resource(name = "sonyCarAudio")
CarAudio carAudio;
[예시]
[root-context.xml 수정]
<!--context:component-scan: 지정된 패키지의 class중 @Component, @Controller, @Service, @Repository 어노테이션이
달려있는 클래스들을 자동으로 객체를 생성하여 bean 객체로 등록하는 엘리먼트
-->
<context:component-scan base-package="com.bit.springboard.coupling"/>
[sonyCarAudio.java 수정]
package com.bit.springboard.coupling;
import org.springframework.stereotype.Component;
// 스프링 설정파일의 context:component-scan 엘리먼트에 의해서 자동으로 객체 생성된다.
@Component
public class SonyCarAudio implements CarAudio {
public SonyCarAudio() {
System.out.println("SonyCarAudio 객체 생성");
}
public void volumeUp() {
System.out.println("SonyCarAudio 소리 증가");
}
public void volumeDown() {
System.out.println("SonyCarAudio 소리 감소");
}
}
[HyundaiCar.java 수정]
- CarAudio를 상속받은 SonyCarAudio에 @Component 등록을 하면서 HyundaiCar에서 CarAudio객체가 @Autowired될 때에는 SonyCarAudio만 해당이 된다.
하지만 만약 CarAudio를 상속받은 AppleCarAudio도 @Component 등록을 하게 되면 @AutoWired시 어떤 CarAudio를 가져와야 할지 알지 못한다.
이 때 @Qualifier를 사용하여 하나의 객체만 특정하도록 할 수 있다.
그리고 @Autowired와 @Qualifier를 합친것이 @Resource를 뜻한다.
package com.bit.springboard.coupling;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
public class HyundaiCar implements Car {
// 1. 필드주입
// @Autowire: 선언된 변수의 형태의 객체가 bean에 등록되어 있는지 검사하고 등록되어 있으면 그 객체를 의존성 주입해주는 어노테이션
// @Autowired
// @Qualifier: bean 객체가 의존성 주입할 때 같은 타입의 객체가 두 개 이상일 때 객체의 아이디로
// 하나의 객체만 특정할 수 있는 어노테이션. @Autowired와 항상 같이 사용해야 한다.
// @Component, @Contorller, @Service, @Repository 어노테이션에서 ()로 생성할 객체의 아이디를 지정할 수 있다.
// @Component("sonyCarAudio")
// 만약 id를 지정하지 않으면 기본적으로 클래스명에서 첫글자만 소문자로 바꾼 이름이 id로 지정되게 된다.
// @Qualifier("sonyCarAudio")
// @Resource: @Autowired + @Qualifier
// @Resource(name = "sonyCarAudio")
private CarAudio carAudio;
// private SonyCarAudio sonyCarAudio;
// private AppleCarAudio appleCarAudio;
// private SamsungCarAudio samsungCarAudio;
private String color;
// public HyundaiCar() {
// System.out.println("HyundaiCar 생성자1 호출");
// }
// 2. 생성자 주입
// @Autowired
// public HyundaiCar(@Qualifier("sonyCarAudio") CarAudio carAudio) {
// System.out.println("HyundaiCar 생성자2 호출");
// this.carAudio = carAudio;
// }
// public HyundaiCar(CarAudio carAudio, String color) {
// System.out.println("HyundaiCar 생성자3 호출");
// this.carAudio = carAudio;
// this.color = color;
// }
// public HyundaiCar(SonyCarAudio sonyCarAudio, AppleCarAudio appleCarAudio, String color) {
// System.out.println("HyundaiCar 생성자3 호출");
// this.sonyCarAudio = sonyCarAudio;
// this.appleCarAudio = appleCarAudio;
// this.color = color;
// }
public void engineOn() {
System.out.println("HyundaiCar 시동을 건다.");
}
public void engineOff() {
System.out.println("HyundaiCar 시동을 끈다.");
}
public void speedUp() {
System.out.println("HyundaiCar 속도를 올린다.");
}
public void speedDown() {
System.out.println("HyundaiCar 속도를 낮춘다.");
}
public void initMethod() {
System.out.println("HyundaiCar 객체 생성");
}
public void destroyMethod() {
System.out.println("HyundaiCar 객체 삭제");
}
public void volumeUp() {
// carAudio = new CarAudio();
carAudio.volumeUp();
}
public void volumeDown() {
// carAudio = new CarAudio();
carAudio.volumeDown();
}
public String getColor() {
return color;
}
public void setColor(String color) {
System.out.println("HyundaiCar setColor 호출");
this.color = color;
}
public CarAudio getCarAudio() {
return carAudio;
}
@Autowired
public void setCarAudio(@Qualifier("sonyCarAudio") CarAudio carAudio) {
System.out.println("HyundaiCar setCarAudio 호출");
this.carAudio = carAudio;
}
}
[CarOwnerAnnotation.java 생성]
package com.bit.springboard.coupling;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class CarOwnerAnnotation {
public static void main(String[] args) {
AbstractApplicationContext factory =
new GenericXmlApplicationContext("root-context.xml");
HyundaiCar hCar = (HyundaiCar) factory.getBean("hyundaiCar");
hCar.volumeUp();
hCar.volumeDown();
factory.close();
}
}
[결과]
[XML과 어노테이션을 함께 사용하여 DL, DI 구현]
- 사용자 정의 클래스들은 대부분 어노테이션으로 등록하여 객체 자동 생성 및 의존성 주입을 하게되고 라이브러리에 포함된 클래스들은 개발자가 어노테이션을 추가할 수 없어서 bean 객체로 등록하여 사용한다.
[예시]
[pom.xml 의존성 주입]
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.12.0</version>
</dependency>
[root-context.xml 수정]
<!--라이브러리(jar 파일)의 클래스에는 어노테이션을 달 수 없기때문에 bean 엘리먼트를 통해서
bean 객체를 생성하고 등록한다.-->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/studydb?serverTimezone=UTC"/>
<property name="username" value="study"/>
<property name="password" value="비밀"/>
</bean>
'백엔드 > Spring Framework' 카테고리의 다른 글
웹 개발의 역사, WEB/WAS, SpringFramework (0) | 2024.07.29 |
---|---|
게시판 구현 / 8-2. '상세 글' 파일 업로드(미리보기, 수정하기, 삭제) (1) | 2024.07.23 |
게시판 구현 / 8-1. '글 등록' 파일 업로드(미리보기, 수정하기, 삭제) (2) | 2024.07.23 |
게시판 구현 / 7. 파일 업로드(notice) (0) | 2024.07.22 |
게시판 구현 / 7. 파일 업로드(boardFile) (0) | 2024.07.22 |