Spring DI 활용하기 - 실습
ApplicationContext ac = new GenericXmlApplicationContext("config.xml");
Car car = (Car)ac.getBean("car"); //byName. 아래와 같은 문장
// Car car = ac.getBean("car", Car.class);
Car car2 = (Car)ac.getBean(Car.class); // byType
Engine engine = (Engine)ac.getBean("engine"); // byName
// Engine engine = (Engine)ac.getBean(Engine.class); // byType - 같은 타입이 3개라서 에러
Door door = (Door)ac.getBean("door");
car.setColor("red");
car.setOil(100);
car.setEngine(engine);
car.setDoors(new Door[]{ ac.getBean("door", Door.class), ac.getBean("door", Door.class)});
1)
config.xml 파일에 bean을 생성하고
ApplicationContext를 이용해서 직접 객체를 생성한 후
값을 넣어주는 방법. (하드코딩)
<?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="car2" class="com.fastcampus.firstSpring.Car2"/>
<bean id="engine" class="com.fastcampus.firstSpring.Engine"/>
<bean id="door" class="com.fastcampus.firstSpring.Door" scope="prototype"/>
</beans>
xml 파일에서 bean을 다음과 같이 생성해준다.
2)
beans 태그 안에 <property> 태그를 삽입하여 iv값을 넣어줄 수 있다(클래스 내부에 setter 메소드가 정의되어 있어야 property 태그를 사용 가능)
<?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="car2" class="com.fastcampus.firstSpring.Car2">
<property name="color" value="red"/>
<property name="oil" value="100"/>
<property name="engine" ref="engine"/>
<property name="doors">
<array value-type="com.fastcampus.firstSpring.Door">
<ref bean="door"/>
<ref bean="door"/>
</array>
</property>
</bean>
<bean id="engine" class="com.fastcampus.firstSpring.Engine"/>
<bean id="door" class="com.fastcampus.firstSpring.Door" scope="prototype"/>
</beans>
또는 <constructor-arg> 태그(생성자)를 이용해서 iv값을 초기화 할 수 있다(클래스 내부에 생성자 코드가 있어야 가능)
<?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="car2" class="com.fastcampus.firstSpring.Car2">
<constructor-arg name="color" value="red"/>
<constructor-arg name="oil" value="100"/>
<constructor-arg name="engine" ref="engine"/>
<constructor-arg name="doors">
<array value-type="com.fastcampus.firstSpring.Door">
<ref bean="door"/>
<ref bean="door"/>
</array>
</constructor-arg>
</bean>
<bean id="engine" class="com.fastcampus.firstSpring.Engine"/>
<bean id="door" class="com.fastcampus.firstSpring.Door" scope="prototype"/>
</beans>
ex) @Component와 @Autowired, @Resource 의 활용
package com.fastcampus.ch3;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Arrays;
//@Component("engine")
class Engine {} // <bean id="engine" class="com.fastcampus.ch3.Engine"/>
@Component class SuperEngine extends Engine{}
@Component class TurboEngine extends Engine{}
@Component class Door {}
@Component
class Car {
@Value("red") String color;
@Value("100") int oil;
// @Autowired // byType
// @Qualifier("superEngine")
@Resource(name="superEngine") // byName
Engine engine; // byType - 타입으로 먼저 검색, 여러개면 이름으로 검색. - engine, superEngine, turboEngine (Autowired의 특징임)
@Autowired Door[] doors;
public Car() {} // 기본 생성자를 꼭 만들어줘야 한다.
public Car(String color, int oil, Engine engine, Door[] doors) {
this.color = color;
this.oil = oil;
this.engine = engine;
this.doors = doors;
}
public void setColor(String color) {
this.color = color;
}
public void setOil(int oil) {
this.oil = oil;
}
public void setEngine(Engine engine) {
this.engine = engine;
}
public void setDoors(Door[] doors) {
this.doors = doors;
}
@Override
public String toString() {
return "Car{" +
"color='" + color + '\'' +
", oil=" + oil +
", engine=" + engine +
", doors=" + Arrays.toString(doors) +
'}';
}
}
public class SpringDITest {
public static void main(String[] args) {
ApplicationContext ac = new GenericXmlApplicationContext("config.xml");
// Car car = (Car)ac.getBean("car"); //byName. 아래와 같은 문장
Car car = ac.getBean("car", Car.class);
// Car car2 = (Car)ac.getBean(Car.class); // byType
//
// Engine engine = (Engine)ac.getBean("engine"); // byName
// Engine engine = (Engine)ac.getBean(Engine.class); // byType - 같은 타입이 3개라서 에러
// Door door = (Door)ac.getBean("door");
// car.setColor("red");
// car.setOil(100);
// car.setEngine(engine);
// car.setDoors(new Door[]{ ac.getBean("door", Door.class), ac.getBean("door", Door.class)});
//
System.out.println("car = " + car);
}
}
3)
먼저 config.xml 파일에
<context:component-scan base-package="com.fastcampus.ch3">
<context:exclude-filter type="regex" expression="com.fastcampus.ch3.diCopy*.*"/>
</context:component-scan>
<context:annotation-config/>
다음과 같은 코드들이 추가되어야 함.
component-scan 태그 : com.fastcampus.ch3 패키지에 있는 모든 클래스중에서
component 애너테이션이 있는 클래스들의 객체를 bean으로 등록해줌(객체 생성)
(2번째 줄 - 클래스가 중복되지 않게 다른 패키지에 있는 클래스들을 제외시켰음)
(4번째 줄 - @Autowired 애너테이션을 사용하려면 해당 코드를 꼭 추가해줘야 함)
이제 @Component 애너테이션을 붙여서 객체를 생성하고
Car 클래스 안의 iv에 @Autowired, @Resource를 붙여서, 생성된 객체들을 넣어주었음.
@Autowired는 Type으로 찾아서 객체를 넣어주고
@Resource는 Name으로 찾아서 객체를 넣어주는데
일반적으로 @Autowired를 많이 사용한다.
클래스의 Type은 잘 변하지 않고, 상대적으로 Name은 바뀌기 쉬우며 오타 실수가 생기기 쉬움.
Spring DI 활용하기 - 이론
1. 빈(bean)이란?
JavaBeans - 재사용 가능한 컴포넌트, 상태(iv), getter&setter, no-args constructor
Servlet & JSP bean - MVC의 Model, EL, scope, JSP container가 관리
EJB(Enterpris Java Beans) - 복잡한 규칙, EJB container가 관리(빈 생성, 소멸)
Spring Bean - POJO(Plain Old Java Object). 단순, 독립적, Spring container가 관리
2. BeanFactory와 ApplicationContext
Bean - Spring Container가 관리하는 객체
Spring Container - Bean 저장소, Bean을 저장, 관리(생성, 소멸, 연결)
1) BeanFactory - Bean을 생성, 연결 등의 기본 기능을 정의
2) ApplicationContext - BeanFactory를 확장해서 여러 기능을 추가 정의
(Spring Container와 ApplicationContext는 거의 같은 개념)
3. ApplicationContext의 종류
다양한 종류의 ApplicationContext구현체를 제공
AC의 종류 | XML | Java Config |
non-Web | GenericXmlApplicationContext | AnnotationConfigApplicationContext |
Web | XmlWebApplicationContext | AnnotationConfigWebApplicationContext |
4. Root AC와 Servlet AC
부모 XmlWebApplicationContext와 자식 XmlWebApplicationContext가 있음.
ApplicationContext라는 기본객체에 attributes(속성)으로 저장된 부모 XmlWebApplicationContext.
서블릿을 Map 형태로 관리하는 children에 저장된 DispatcherServlet 안에 저장된 자식 XmlWebApplicationContext.
자식 XmlWebApplicationContext은 부모를 참조하고 있음.
자식 XmlWebApplicationContext에서 Beans를 먼저 찾고, 없으면 부모 XmlWebApplicationContext에서 Beans를 찾게됨.
5. ApplicationContext의 주요 메소드
(getBean : 빈 얻기, getBeanDefinitionCount : 정의된 빈의 갯수)
6. IoC와 DI
Inversion of flow Control
실행 흐름이 역전되는 것
- 분리
1) 관심사의 분리
2) 변하는 것과 변하지 않는 것
3) 중복 코드
7. 스프링 애너테이션 - @Autowired
(byType)
인스턴스 변수(iv), setter, 참조형 매개변수를 가진 생성자, 메소드에 적용 가능
@Autowired // 생성자의 @Autowired는 생략이 가능함. 단, 클래스의 생성자가 하나일 경우만 가능
public Car(@Value("red") String color, @Value("100") int oil, Engine engine, Door[] doors) {
this.color = color;
this.oil = oil;
this.engine = engine;
this.doors = doors;
}
생성자의 각 매개변수들에 자동으로 객체를 넣어줌
(만약 @Value가 없으면 넣어줄 String 타입이 없으므로 에러가 발생함)
(생성자를 이용해서 객체를 주입해주는것이 좋음)
@Autowired
public void setEngineAndDoor(Engine engine, Door[] doors) {
this.engine = engine;
this.doors = doors;
}
메소드의 각 매개변수에 자동으로 객체를 넣어줌
class Car {
@Autowired Engine engine;
@Autowired Engine[] engines;
@Autowired(required = false)
SuperEngine superEngine;
...
}
인스턴스 변수에 자동으로 객체를 넣어줌
(required가 false면 객체가 없어도 에러가 발생하지 않음. null이 대신 들어감)
Spring Container에서 타입으로 빈을 검색해서 참조 변수에 자동 주입(DI)
검색된 빈이 n개이면, 그 중에 참조변수와 이름이 일치하는 것을 주입.
주입 대상이 변수일 때, 검색된 빈이 1개 아니면 예외 발생
주입 대상이 배열일 때, 검색된 빈이 n개라도 예외 발생X, 배열에 다 집어 넣어버림
@Autowired(required=false)일 때, 주입할 빈을 못 찾아도 에외 발생X. 대신 null이 들어가게 됨.
예시)
Engine 타입이 여러개인 경우, Bean의 id와 변수의 이름이 일치하는 객체를 주입하게 된다.
(engine과 SuperEngine의 Bean id가 engine으로 동일하므로 SuperEngine 객체가 주입됨)
Bean id를 따로 주지 않으면 클래스의 첫 글자를 소문자로 해서 Bean의 id를 갖게 된다( ex : turboEngine )
배열의 경우에는 Engine 타입이 여러개여도 배열이라 모두 담을 수 있기 때문에 에러가 발생하지 않는다.
스프링 애너테이션 - @Resource
(byName)
Spring Container에서 이름으로 빈을 검색해서 참조 변수에 자동 주입(DI)
일치하는 이름의 빈이 없으면, 예외 발생
class Car {
// @Resource(name="engine") 아래 두 줄과 같음.
@Resource
Engine engine;
...
}
name을 따로 주지 않으면 참조변수의 이름을 name으로 사용함.
스프링 애너테이션 - @Component
<component-scan>으로 @Component가 클래스를 자동 검색해서 빈으로 등록함.
@Component는 @Controller, @Service, @Repository, @ControllerAdvice의 메타 애너테이션이다.
<context:component-scan base-package="com.fastcampus.ch3"/> 로 서브 패키지까지 컴포넌트를 찾는다.
스프링 애너테이션 - @Value와 @PropertySource
@Value 애너테이션으로 값을 직접 지정해 줄 수 있고
환경변수의 값을 불러와서 사용할 수도 있다.
@PropertySource와 같이 활용하여 properties 파일에서 값을 불러올 수도 있음.
8. 스프링 애너테이션 vs. 표준 애너테이션(JSR-330)
이름이 같은 경우가 있기 때문에 import할 때 주의.
9. 빈의 초기화 - <property>와 setter
<property>를 이용한 빈 초기화. setter를 이용
코드에서 직접 값을 세팅하는 대신에 xml 파일에서 <property> 태그를 이용해서 초기화 해준다.
setter 메소드가 정의되어 있어야 가능함.
<constructor-arg>를 이용한 빈 초기화. 생성자가 정의되어 있어야 함.
코드에서 직접 값을 세팅하는 대신에 xml 파일에서 <constructor-arg> 태그를 이용해서 초기화 해준다.
'Spring & SpringBoot > Spring' 카테고리의 다른 글
220504 Spring - Chapter 3. Spring DI와 AOP (Part. 4) (0) | 2022.05.05 |
---|---|
220503 Spring - Chapter 3. Spring DI와 AOP (Part. 3) (0) | 2022.05.03 |
220430 Spring - Chapter 3. Spring DI와 AOP (0) | 2022.04.30 |
220428 Spring - Chapter 2. Spring MVC (Part 7) (0) | 2022.04.29 |
220427 Spring - Chapter 2. Spring MVC (Part 6) (0) | 2022.04.27 |