Spring DI 실습

강서진·2023년 11월 27일
0

Spring

목록 보기
3/18
post-custom-banner

강좌 ch 3. 4강 요약

이전에 사용했던 config.txt 파일을 xml파일로 생성해 사용해본다.

  • 강의에서는 config.xml을 만들 때 Spring xml configuration file 메뉴가 있는데, 나는 안떠서 당황했다. ui가 바뀌면서 옮겨갔나 하고 찾아봤는데 알고보니 IntelliJ 유료 버전에만 있는 기능이었다. 그냥 resource 폴더에 파일을 생성하고, config.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"/>
namespace context is not bound ERROR

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를 함께 사용한다.
이름보다 타입을 쓰는 이유는 오타가 나거나 이름이 바뀔 가능성이 더 높기 때문이다.

post-custom-banner

0개의 댓글