강좌 ch 3. 4강 요약
이전에 사용했던 config.txt 파일을 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>
복사 붙여넣기를 해주었다.
여기에 우리가 사용할 클래스들을 bean태그로 추가해준다.
<bean id="car" class="com.fastcampus.ch3.Car"/>
<bean id="engine" class="com.fastcampus.ch3.Engine"/>
<bean id="door" class="com.fastcampus.ch3.Door"/>
(말 그대로 콩이라고 생각하니까 조금 귀엽다)
Application Context를 만들어준다. SpringFramework의 ApplicationContext를 import해서 사용한다.
ApplicationContext ac = new GenericXmlApplicationContext("config.xml");
* SpringDiTest.java 안에 Car, Engine, Door를 생성하면 안되고 밖에 놔야 한다. 바보같이 안에 생성해서 xml이 위치를 못잡았다.
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); // 타입을 옵션으로 주면 형변환을 따로 해줄 필요 x
Car car2 = (Car)ac.getBean(Car.class); // byType
Engine engine = (Engine)ac.getBean("engine");
Engine engine2 = (Engine)ac.getBean(Engine.class);
Door door = (Door)ac.getBean("door");
Door door2 = (Door)ac.getBean(Door.class);
System.out.println("car = " + car);
System.out.println("car2 = " + car2);
System.out.println("engine = " + engine);
System.out.println("door = " + door);
}
실행해보면, car과 car2의 메모리 번지가 같은 것으로 나온다.
기본적으로 싱글톤이라서 같은 객체를 반환하기 때문이다. 같은 기능을 하는 객체를 여러 개 만들 필요가 없어서 대부분의 서버 프로그램은 싱글톤이지만, 매번 다른 객체일 필요가 있을 때는 config.xml에 "prototype"으로 scope 옵션을 추가해주면 된다.
생략하면 "singleton"으로 기본 옵션이 들어간다.
싱글톤 SINGLETON:
클래스의 객체를 하나만 생성
<bean id="car" class="com.fastcampus.ch3.Car" scope="prototype"/>
<bean id="engine" class="com.fastcampus.ch3.Engine" scope="singleton"/>
<bean id="door" class="com.fastcampus.ch3.Door" scope="singleton"/>
바꿔서 실행했더니 car과 car2의 메모리 공간이 달라졌다.
그 다음으로 Car 클래스에 프로퍼티를 몇 개 추가해볼 수 있다. Setter를 만들어 값을 입력한다.
String color;
int oil;
Engine engine;
Door[] doors;
---
car.setColor("black");
car.setOil(100);
car.setEngine(engine);
car.setDoors(new Door[]{ac.getBean(Door.class),ac.getBean(Door.class)});
System.out.println(car);
그런데 Setter를 사용하지 않고 xml 파일로 값을 초기화할 수도 있다.
<bean id="car" class="com.fastcampus.ch3.Car">
<property name="color" value="black/"/>
<property name="oil" value="100"/>
<property name="engine" ref="engine"/>
<property name="doors">
<array value-type="com.fastcampus.ch3.Door">
<ref bean="door"/>
<ref bean="door"/>
</array>
</property>
</bean>
받는 자료형이 기본자료형이거나 String일 때만 value를 주고, 그 외엔 전부 ref로 받는다.
property 태그는 Setter 메서드들을 사용하므로, 태그를 사용하여 초기화하려면 Setter들이 전부 필요하다.
마찬가지로 생성자 메서드를 사용하는 태그로 constructor가 있다.
component scanning를 사용하려면, 사용할 클래스들에 @Component 애너테이션을 붙이고, 스캔할 패키지를 주면 된다.
<context:component-scan base-package="com.fastcampus.ch3"/>
context를 정의해주어야 한다. xml파일과 xsi:SchemaLocation에
xmlns:context="http://www.springframework.org/schema/context" --- "http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-3.0.xsd"
를 추가해주면 해결된다.
Conflicting Beans 오류가 나면 하위 패키지의 Car 클래스들을 무시하도록 필터를 추가한다.
<context:component-scan base-package="com.fastcampus.ch3">
<context:exclude-filter type="regex" expression="com.fastcampus.ch3.diCopy*.*"/>
</context:component-scan>
객체를 자동으로 연결하는 @Autowired와 @Resource도 사용할 수 있다. 클래스 내부에 애너테이션을 추가하면 자동으로 연결이 된다. color와 oil 같은 프로퍼티에는 @Value 애너테이션에 값을 줘서 초기화할 수 있다(타입은 알아서 조정되기 때문에 문자열로 값을 준다).
@Component
class Car{
@Value("black")
String color;
@Value("100")
int oil;
@Autowired
Engine engine;
@Autowired
Door[] doors;
* 공간 상 이하 setter, toString()은 포함하지 않았다.
원래 Autowired 애너테이션을 사용하려면 context:annotation-config 태그가 필요하다. 하지만 compnent-scan태그가 이를 자동으로 전부 등록해주기 때문에 따로 작성하지 않았다.
Engine을 상속한 SuperEngine, TurboEngine이 있다고 하자. SuperEngine 객체를 byName이 아닌 byType으로 bean을 생성한다고 하면 오류가 난다. 자바는 한 개의 bean을 예상했는데 3개를 받기 때문이다. 같은 타입의 클래스가 여러 개면 타입이 아닌 이름으로 받아야 한다.
그런데 @Autowired 애너테이션을 붙인 Engine은 오류가 없이 잘 생성이 된다. 그 이유는 @Autowired 애너테이션이 먼저 타입으로 검색을 하고, 중복된 타입이 있으면 이름으로 검색하기 때문이다(@Qualifier 애너테이션에 이름을 넘겨준다). 이때 @Autowired를 제거하면 타입이 여러 개라고 오류가 난다.
@Resource를 사용해 (name="이름")을 넘겨줄 수도 있다.
주로 한 타입의 빈을 많이 사용하기 때문에 @Autowired를 많이 사용하며, 같은 타입의 빈이 여러 개인 경우에는 @Reqource를 사용해 이름으로 찾거나, @Autowired와 @Qualifier를 함께 사용한다.
이름보다 타입을 쓰는 이유는 오타가 나거나 이름이 바뀔 가능성이 더 높기 때문이다.