본문 바로가기
Back-End/토비의 스프링

토비의 스프링 | 1장. 오브젝트와 의존관계2

by Havi 2017. 1. 9.

애플리케이션 컨텍스트의 동작방식

애플리케이션 컨텍스트는 애플리케이션에서 IOC를 적용해서 관리할 모든 오브젝트에 대한 생성과 관계설정을 담당한다. 대신 직접적으로 DaoFactory와 관계를 맺어주는 코드는 따로 없고 그런 생성정보와 연관관계 정보를 별도의 설정정보를 통해 얻는다. 때로는 외부의 오브젝트 펙토리에 그 작업을 위임하고 그 결과를 가져다가 사용하기도 한다.

애플리케이션 컨텍스트의 장점

  • 클라이언트는 구체적인 팩토리 클래스를 알 필요가 없다.
    • 다양한 오브젝트가 생기더라도 이를 일일이 알 필요없이 애플리케이션 컨텍스트를 이용하여 일관된 방식으로 원하는 오브젝트를 가져올 수 있다.
  • 애플리케이션 컨텍스트는 종합 IOC 서비스를 제공해준다.
    • 단순히 생성과 관계설정의 제공뿐 아니라 만들어지는 방식, 시점과 전략, 자동생성, 후처리, 정보의 조합, 설정방식의 다변화 등 다양한 부가기능을 제공한다.
  • 애플리케이션 컨텍스트는 빈을 검색하는 다양한 방법을 제공한다.

스프링 IOC의 용어 정리

  • 빈: 스프링이 IOC방식으로 관리하는 오브젝트라는 뜻이다. 주의할 점은 만들어지는 모든 오브젝트가 빈은 아니다. 그중에서 스프링이 직접 그 생성과 제어를 담당하는 오브젝트만을 빈이라 한다.
  • 빈 팩토리: 스프링의 IOC를 담당하는 핵심 컨테이너를 가리킨다. 빈을 등록하고, 생성하고, 조회하고, 돌려주고 등 부가적인 빈을 관리하는 기능을 담당한다.
  • 애플리케이션 컨텍스트
    • 빈 팩토리를 확장한 IOC 컨테이너다. 빈을 등록하고 관리하는 기본적인 기능은 빈 펙토리와 비슷하다. 여기에 각종 부가 서비스를 추가로 제공한다.
    • 스프링이 제공하는 애플리케이션 지원 기능을 모두 포함해서 이야기하는 것이다.
    • ApplicationContext는 BeanFactory를 상속한다.
  • 설정정보/설정 메타정보: 애플리케이션 컨텍스트 or 빈 팩토리가 IOC를 적용하기 위해 사용하는 메타정보를 말한다.(configuration)
  • 컨테이너 or IOC컨테이너: IOC방식으로 빈을 관리한다는 의미. 애플리케이션 컨텍스트 오브젝트는 하나의 애플리케이션에서 보통 여러 개가 만들어져 사용되는데 이를 통틀어서 스프링 컨테이너라고 부를 수 있다.
  • 스프링 프레임워크: IOC 컨테이너, 애플리케이션 컨텍스트를 포함해서 스프링이 제공하는 모든 기능을 통틀어 말할 때 주로 사용한다.

빈 < 컨테이너 < 빈 팩토리 < 애플리케이션 컨텍스트 < 스프링 프레임워크

1.6 싱글톤 레지스트리와 오브젝트 스코프

오브젝트의 동일성과 동등성: 자바에서 두 개의 오브젝트가 동일한 오브젝트라 하는 것은 == 연산자로 완전히 같음을 얘기한다. 동등하다고 얘기하는 것은 equals() 메소드를 이용해 비교한다. 두 개의 오브젝트가 동일하지는 않지만 동등한 경우는 각각 다른 메모리상에 존재하는 것이다.

  • 기존의 DaoFactory의 userDao() 메소드를 두 번 호출해서 리턴되는 오브젝트와 빈으로 userDao()를 가져오는 차이점은 무엇일까?
  • 그 차이점은 동일성에 있다. 아래 코드를 보자.
DaoFactory factory = new DaoFactory();
UserDao dao1 = factory.userDao();
UserDao dao2 = factory.userDao();

System.out.println(dao1); //springbook.dao.UserDao@11
System.out.println(dao2); //springbook.dao.UserDao@22
ApplicationContext context = new AnnotationConfigApplicationContext(DaoFactory.class);
UserDao dao3 = context.getBean("UserDao", UserDao.class);
UserDao dao4 = context.getBean("UserDao", UserDao.class);

System.out.println(dao3); //springbook.dao.UserDao@33
System.out.println(dao4); //springbook.dao.UserDao@33
  • 차이점이 느껴지는가? 빈을 통해 가져오는 방식은 새로운 객체를 생성하지 않고 동일한 객체를 가져온다.

싱글콘 레지스트리로서의 애플리케이션 컨텍스트

애플리케이션 컨텍스트는 싱글톤을 저장하고 관리하는 싱글톤 레지스트리이다. 스프링은 별다른 설정을 하지 않으면 기본적으로 싱글톤으로 만든다. 왜 스프링은 싱글톤으로 빈을 만들까?

  • 스프링이 주요 적용 대상이 자바 엔터프라이즈 기술을 사용하는 서버환경이기 때문이다.
  • 하나의 요청을 처리하기 위해 데이터 액세스 로직, 서비스 로직, 비즈니스 로직, 프레젠테이션 로직 등의 다양한 기능을 담당하는 보잡한 로직으로 이루어져 있다.
  • 그런데 매번 클라이언트 요청이 올 때마다 새로운 오브젝트를 모두 만들어 내면 제아무리 성능이 뛰어난 가비지 컬렉션이 있더라도 서버의 부하는 피할 수 없다.
  • 그래서 서비스 오브젝트라는 개념을 일찍부터 사용해 왔다.
  • 이렇게 애플리케이션 안에 제한된 수, 보통은 한 개의 오브젝트만 만들어서 사용하는 것이 싱글톤 패턴의 원리이다.

싱글톤 패턴: GOF 소개한 디자인패턴의 하나이다. 가장 많이 활용되기도 하지만 가장 많이 비판을 받는 패턴이다. 싱글톤 패턴은 어떤 클래스를 애플리케이션 내에서 제한된 인스턴스 개수 이름처럼 주로 하나만 존재하도록 강제하는 패턴이다.

싱글톤 패턴의 한계

자바에서의 일반적인 싱글톤 구현방법

  • 클래스 밖에서는 사용 못하도록 private 타입으로 만든다.
  • 생성된 싱글톤 오브젝트를 저장할 수 있는 자신과 같은 타입의 스태틱 필드 정의.
  • 스태틱 펙토리 메소드인 getInstance()를 만들고 이 메소드가 최초로 호출시 한번만 오브젝트가 만들어지도록 설정. 생성된 오브젝트는 스태틱 필드에 저장 or 스태틱 필드의 초기값으로 오브젝트를 미리 만들 수 있다.
  • 한번 오브젝트가 만들어진 후 getInstance() 메소드를 통해 이미 만들어져 있는 오브젝트를 사용한다.
public class UserDao {
    private static UserDao INSTANCE;
    ...
    private UserDao(ConnectionMaker connectionMaker) {
        this.connectionMaker = connectionMaker;
    }

    public static synchronized UserDao getInstance() {
        if(INSTANCE == null) INSTANCE = new UserDao(???);
        return INSTANCE;
    }
    ...
}

싱글톤방식으로 변경한 UserDao의 소스는 다음과 같은 문제가 있다.

  • private 생성자를 갖고 있기 때문에 상속할 수 없다.
    • private 생성자를 가진 클래스는 다른 생성자가 없다면 상속이 불가능하다. 객체지향의 최대 장점인 상속과 다형성을 적용할 수 없다.
  • 싱글톤은 테스트하기 힘들다.
    • 싱글톤은 만들어지는 방식이 제한적이기 때문에 다이나믹하게 사용하며 테스트에서 사용될 오브젝트를 대체하기 힘들다.
  • 서버환경에서 싱글톤이 하나만 만들어지는 것을 보장하지 못한다.
    • 여러 개의 JVM에 분산돼서 설치가 되는 경우에도 각각 독립적으로 오브젝트가 생기기 때문에 싱글톤으로서 가치가 떨어진다.
  • 싱글톤의 사용은 전역 상태를 만들 수 있기 때문에 바람직하지 못하다.
    • 싱글톤의 클라이언트는 정해져 있지 않다.
    • 싱글톤의 스태틱 메소드(getInstance())를 사용해 언제든지 싱글톤에 접근 가능하기에 자연스럽게 전역 객체로 사용되기 쉽다.
    • 객체지향에서 아무 객체나 자유롭게 접근하고 공유할 수 있는 것은 권장되지 않기에 차라리 스태틱 필드와 메소드로만 구성된 클래스를 사용하는게 낫다.

싱글톤 레지스트리

자바의 기본적인 싱글톤 패턴 구현방식은 여러 문제가 있기에 스프링이 직접 싱글톤 형태의 오브젝트를 만들고 관리하는 기능을 제공하는데 이것이 바로 싱글톤 레지스트리이다. 스프링 컨테이너는 싱글톤을 생성, 관리, 공급하는 싱글톤 관리 컨테이너이기도 하다. 장점은 다음과 같다.

  • 스태틱 메소드, private 생성자를 사용해야 하는 비정상적인 클래스가 아니라 평범한 자바 클래스를 싱글톤으로 생성과 관계설정, 사용 등에 대한 제어권을 컨테이너에게 넘기면 손쉽게 싱글톤 방식으로 만들어져 관리할 수 있다.
    • 오브젝트 생성에 관한 모든 권한은 IOC 기능을 제공하는 애플리케이션 컨텍스트에 있기 때문이다.
  • 싱글톤 방식으로 사용될 애플리케이션 클래스라도 public 생성자를 가질 수 있다.
  • 싱글톤 패턴과 달리 스프링이 지지하는 객체지향적인 설계 방식과 원칙, 디자인 패턴 등을 적용하는 데 아무런 제약이 없다는 점이다.

싱글톤과 오브젝트의 상태

멀티스레드 환경이라면 여러 스레드가 동시에 접근할 수 있기에 싱글톤 사용에 주의를 기울여야 한다. 특히 서비스 형태의 오브젝트로 사용되는 경우에는 상태정보를 내부에 갖고 있지 않은 무상태 방식으로 만들어져야 한다. 다중 사용자의 요청을 한꺼번에 처리하는 스레드들이 동시에 싱글톤 오브젝트의 인스턴스 변수를 수정하는 것은 매우 위험하다.

public class UserDao {
    private ConnectionMaker connectionMaker; //초기에 설정하면 바뀌지 않는 읽기전용 인스턴스 변수
    /*
     * 매번 새로운 값으로 바뀌는 정보를 담는 인스턴스 변수(심각)
     */
    private Connection c;
    private User user;

    public User get(String id) throws ClassNotFoundException, SQLExcepion {
        this.c = connectionMaker.makerConnection();
        ....
    }
}
  • 변수 c, user는 계속 수정되는 변수이기에 멀티스레드 환경에서는 심각한 데이터 혼란을 야기시킬수 있는 소스이다.
  • 이를 해결하기 위해서는 매번 바뀌는 변수인 c와 user를 메소드 안에 선언해 사용해야 한다.
    • 메소드 안에서 생성되는 로컬 변수는 매번 새로운 값을 저장할 독립적인 공간이 만들어지기 때문이다.
  • connectionMaker 변수는 빈에 한번 생성되고 재사용되며 호출되는 인스턴스 변수이므로 차라리 static final이나 final로 선언하는 편이 나을 것이다.

스프링 빈의 스코프

빈이 생성, 존재, 적용되는 범위를 빈의 스코프라 한다. 스프링 빈의 기본 스코프는 싱글톤이다. 싱글톤 스코프는 컨테이너 내에 한 개의 오브젝트만 만들어져서, 강제로 제거하지 않는 한 스프링 컨테이너가 존재하는 동안 계속 유지된다. 경우에 따라 싱글톤 외의 스코프를 가질 수 있다. 대표적으로 프로토타입 스코프가 있다. 프로토타입은 컨테이너에 빈을 요청할 때마다 매번 새로운 오브젝트가 만들어 진다. 이 외에도 웹을 통해 새로운 HTTP 요청이 생길때마다 생성되는 요청 스코프, 웹의 세션과 스코프가 유사한 세션 스코프도 있다.

1.7 의존관계 주입(DI)

IOC라는 용어는 매우 느슨하게 정의되어 있다. 때문에 스프링을 IOC 컨테이너라고만 해서는 스프링이 제공하는 기능의 특징을 명확히 설명하지 못한다. 그래서 스프링이 제공하는 IOC방식의 핵심을 짚어주는 의존관계 주입이라는 용어를 사용하기 시작했다.

Point: 'Dependency Injection'은 여러 해석어가 있다. 그중에서 가장 흔히 사용하는 용어는 의존성 주입이다. 또, 의존 오브젝트 주입이라고 하는데, 이는 DI의 의미를 잘 나타내주지 못한다. 사실 오브젝트는 다른 오브젝트에 주입할 수 있는게 아니라 오브젝트의 레퍼런스가 전달될 뿐이다. DI는 오브젝트 레퍼런스를 외부로부터 제공(주입)받고 이를 통해 다른 오브젝트와 다이내믹하게 의존관계가 만들어지는 것이 핵심이다.즉, 의존관계 주입 혹은 의존관계 설정이라는 용어가 적절한 것이다.

런타임 의존관계 설정

의존관계

  • 의존관계에 있다는 말은 항상 방향성을 부여해줘야 한다.
  • 의존한다는 건 의존대상, B가 변하면 A에 영향을 미친다는 뜻이다.(ex. A가 B에 의존할때 즉, A -> B)
  • 반대로 B는 A에 의존하지 않는다. 즉, B는 A의 변화에 영향을 받지 않는다.

UserDao의 의존관계

  • UserDao는 ConnectionMaker 인터페이스에 의존하기에 ConnnectionMaker 인터페이스가 변하면 영향을 받게 된다.
  • 하지만 DConnectionMaker 등이 변화가 있어도 UserDao에 영향을 주지 않는다.
  • 이렇게 인터페이스에만 의존관계를 두어 관계를 느슨하게 만드는 것은 결합도가 낮다고 설명할 수 있다.

런타임 의존관계

  • == "오브젝트 의존관계"
  • 인터페이스를 통해 의존관계를 갖는 경우 UserDao의 오브젝트가 런타임 시에 사용할 오브젝트가 어떤 클래스로 만든 것인지 미리 알 수 없다.
  • 의존 오브젝트: 런타임 시에 의존관계를 맺는 대상, 즉 실제 사용대상인 오브젝트
  • 의존관계의 충족조건
    • 런타임 시점에 의존관계가 드러나지 않는다. 이를 위해서는 인터페이스에만 의존해야 한다.
    • 런타임 시점의 의존관계는 컨테이너나 팩토리 같은 제3의 존재가 결정한다.
    • 의존관계는 사용할 오브젝트에 대한 레퍼런스를 외부에서 제공해줌으로써 만들어진다.
  • 의존관계의 핵심은 설계 시점에는 알지 못했던 두 오브젝트의 관계를 맺도록 도와주는 제 3의 존재이다.(ex. DI, 애플리케이션 컨텍스트, 빈 팩토리, IOC 컨테이너 등)

UserDao의 의존관계 주입

  • 우리는 제3의 존재에게 런타임 의존관계 결정 권한을 위임하기 위해 DaoFactory를 만들어 UserDao가 사용할 DConnectionMaker를 생성하여 런타임 의존관계를 맺게 해주었다.
  • 이미 DaoFactory를 만든 시점에서 의존관계 주입(DI)를 이용한 것이다. 즉, DaoFactory가 DI 컨테이너인 것이다.
  • DI 컨테이너는 의존관계를 맺어줄 클래스의 오브젝트를 만들고 이 생성자의 파라미터로 오브젝트의 레퍼런스를 전달해준다.

의존관계 검색과 주입

의존관계를 맺는 방법이 외부로부터의 주입이 아니라 스스로 검색하여 이용하는 방법을 의존관계 검색(DL)이라 한다. 의존관계 검색은 자신이 필요로 하는 의존 오브젝트를 능동적으로 찾는다.

public UserDao() {
    DaoFactory daoFactory = new DaoFactory();
    this.connectionMaker = daoFactory.connectionMaker();
}
  • 이렇게 해도 UserDao는 여전히 자신이 어떤 ConnectionMaker 오브젝트를 사용할지 미리 알지 못한다.(DConnectionMaker를 직접적으로 가져오지는 않으므로)
  • 따라서 IOC 개념을 잘 따르고 있으나, 적용 방법은 외부로부터의 주입이 아니라 스스로 IOC 컨테이너인 DaoFactory에게 요청하는 것이다.
  • 스프링의 애플리케이션 컨텍스르에 미리 정해놓은 이름을 전달해서 그 이름을 찾게되면 일종의 검색이라 볼 수 있다.
public UserDao() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DaoFactory.class);
    this.connectionMaker = context.getBean("connectionMaker", ConnectionMaker.class);
}
  • 위의 소스를 통해 getBean()이라는 메소드를 사용하여 의존관계 검색 방식으로 ConnectionMaker 오브젝트를 가져오게 할 수 있다.
  • 같은 IOC원칙에 방법만 다르지만 그래도 의존관계 주입 쪽이 훨씬 깔끔하다.

의존관계 검색 vs 의존관계 주입 중요한 차이점은 의존관계 검색 방식에서는 자신이 검색하려는 오브젝트가 스프링의 빈일 필요는 없다. 반면에 의존관계 주입은 UserDao에 ConnectionMaker 오브젝트를 주입하려면 UserDao의 생성과 초기화 권한을 갖고 있어야 하므로 꼭 빈이여야 한다.

Point: DI는 단지 외부에서 오브젝트를 주입시켜줬다 해서 다 DI가 아니다. DI에서의 주입은 다이내믹하게 구현 클래스를 결정해서 제공받을 수 있도록 인터페이스 타입의 파마리터를 통해 이뤄져야 한다. 그래서 우리는 '주입받는다'보다는 'DI 받는다'라 표현하는게 옳바르다.

의존관계 주입의 응용

의존관계의 주입의 장점은 무엇일까? 앞에서 우리는 다음의 장점을 알게 되었다.

  • 런타임 클래스에 대한 의존관계가 나타나지 않는다.
  • 인터페이스를 통해 결합도가 낮은 코드를 만든다.
  • 다른 책임을 가진 의존관계에 있는 대상이 변경되더라도 자신은 영향받지 않는다.
  • 변경을 통한 다양한 확장이 가능하다. 이를 응용했을때를 살펴보자.

기능 구현의 교환
만약 실서버에서 애플리케이션을 운영할 때, 로컬서버에서 개발할 때 등 각각의 DB정보를 다르게 접근하고 싶으면 어떻게 해야 할까? 초난감 DAO에서 했던 방식데로 DI를 사용하지 않았으니 모든 클래스마다 LocalDBConnectionMaker를 일일이 바꿔줘야되고 많으면 수백개의 라인을 수정해야 될지도 모른다. 또 실서버에 배포하게 되면 그때마다 또 수정해 주어야 한다. 반면에 DI 방식을 적용하게 되면 @Configuration이 붙은 DaoFactory를 사용하면 다음과 같이 한줄만 수정하면 가능하다.

@Bean
public ConnectionMaker connectionMaker() {
    return new LocalDBConnectionMaker(); //실서버는 ProductionDBConnectionMaker()
}

위처럼 DI는 공통된 오브젝트를 사용하여 확장성을 용이하게 하는 편리함을 갖고 있다. 여기에 vm option에 환경정보를 추가혀여 environment를 받고 실서버 혹은 개발서버 각각의 환경에 따른 DI주입을 커스터마이징하게 변경후 사용한다면 불편함이 많이 해소될 것이다.

메소드를 이용한 의존관계 주입

지금까지는 의존관계 주입에 있어 생성자만 사용하였지만 생성자 이외의 방법도 있다.

  • 수정자 메소드를 이용한 주입
    • 일반적으로 수정자 메소드는 setter를 이용한 방법이다.
    • 핵심적인 기능은 파라미터로 전달된 값을 보통 내부의 인스턴스 변수에 저장하는 것이다.
    • 객체지향적인 방식에서는 회피해야 될 방법이다.
  • 일반 메소드를 이용한 주입
    • 여러 개의 파라미터를 갖는 일반 메소드를 DI용으로 사용할 수 있다.
    • 필요한만큼의 변수를 받을 수 있기에 한번에 모든 파라미터를 받아야 하는 생성자보다는 장점을 갖고 있다.

1.8 XML을 이용한 설정

DI 컨테이너를 사용하면서 오브젝트들의 의존관계를 일일이 생성해주기 번거로웠다. 대부분 틀에 박힌 동일한 구조가 반복된다. 또한 DI 구성이 바뀔 때마다 자바 코드를 수정하고 다시 컴파일 하는 것도 귀찮은 작업이다. DaoFactory와 같은 자바 클래스 이외에도 다양한 방법이 존재하는데 그 중 대표적인 방법이 XML이다. XML의 장점은 다음과 같다.

  • 단순한 텍스트 파일이라 다루기 쉽다.
  • 별도의 컴파일 혹은 빌드작업이 필요없다.
  • 환경이 달라져도 빠르게 변경사항을 반영할 수 있다.
  • 스키마나 DTD(Document Type Dfinition)를 이용해서 정해진 포맷을 따라 작성했는지 손쉽게 확인 가능하다. > DTD(Document Type Dfinition): SGML 계열의 마크업 언어에서 문서 형식을 정의하는 것이다.

XML 설정

DI 정보가 담긴 XML 파일은 를 루트 엘리먼트로 사용한다. 이 안에는 여러 개의 을 정의할 수 있고 @Configuration()과 @Bean()이 붙은 자바 클래스와 설정이 동일하다. @Bean 메소드를 통해 얻는 빈의 DI정보는 다음과 같다.

  • 빈의 이름: getBean()에서 사용되는 이름
  • 빈의 클래스: 어떤 클래스를 이용해서 만들지를 정의
  • 빈의 의존 오브젝트
    • 빈의 생성자나 수정자 메소드를 통해 의존 오브젝트를 넣어준다.
    • 의존 오브젝트도 하나의 빈이므로 해당하는 메소드를 호출하여 의존 오브젝트를 가져온다.
    • 의존 오브젝트는 하나 이상일 수도 있다.

XML에서 을 사용해도 이 세가지 정보를 정의할 수 있다.

자바 코드 설정정보XML 설정정보
빈 설정파일@Configuration
빈의 이름@Bean methodName()<bean id="methodName"
빈의 클래스return new BeanClass();class="a.b.c...BeanClass">

userDao() 전환

자바빈의 관례를 따라서 수정자 메소드는 프로퍼티가 된다. setConnectionMaker() 메소드는 connectionMaker라는 프로퍼티를 갖는다.

userDao.setConnectionMaker(connectionMaker());

위의 소스는 아래와 같이 변경할 수 있다. name은 set을 제외한 나머지 부분의 이름이고 ref는 수정자 메소드를 통해 주입해줄 오브젝트의 빈 이름이다.

<property name="connectionMaker" ref="connectionMaker" />

최종소스는 다음과 같다.

 <beans>
    <bean id="userDao" class="springbook.dao.UserDao">
        <property name="connectionMaker" ref="connectionMaker" />
    </bean>
</beans>

DTD와 스키마: XML 문서의 구조를 정의하는 방법은 DTD와 스키마가 있다. DTD는 보통 "<!DOCTYPE benas PUBLIC~ " 이런식으로 선언하고 스키마는 ""으로 선언한다. 특별한 이유가 없다면 스키마방식을 사용하는게 바람직하다. 이유는 DTD는 독립적인 네임스페이스를 사용해야 되지만 스키마는 태그같은 공용태그를 지원하기 때문이다.

XML을 이용하는 애플리케이션 컨텍스트

어플리케이션 컨텍스트가 XML 설정정보를 활용하기 위해서 GenericXmlApplicationContext를 사용한다.

ApplicationContext context = new GenericXmlApplicationContext("applicationContext.xml"); //'/'가 없으면 루트부터 시작하는 클래스패스

이외에도 더 편리한 기능이 추가된 ClassPathXmlApplicationContext를 이용해 XML로부터 설정정보를 가져올 수 있다.

DataSource 인터페이스로 변환

스프링에서는 DB 커넥션을 가져오는 오브젝트의 기능을 추상화해서 만들어진 DataSource라는 인터페이스가 이미 존재한다. 따라서 ConnectionMaker와 같은 인터페이스를 따로 만들어서 사용할 필요가 없다.

public interface DataSource extends CommonDataSource, Wrapper {
    Connection getConnectioin() throws SQLException;
    ...
}
import javax.sql.DataSource;

public class UserDao {
    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void add(User user) throws SQLException {
        Connection c = dataSource.getConnection();
        ...
    }
    ...
}

다음은 DataSource 구현 클래스가 필요하다. SimpleDriverDataSource처럼 단순한 DataSource 구현 클래스를 하나 가져다 사용하자.

@Bean
public DataSource dataSource() {
    SimpleDriverDataSource dataSource = new SimpleDriverDataSource();

    dataSource.setDriverClass(com.mysql.jdbc.Driver.class);
    dataSource.setUrl("jdbc:mysql://localhost/sprig");
    dataSource.setUsername("spring");
    dataSource.setPassword("book");

    return dataSource;
}

@Bean
public UserDao userDao() {
    UserDao userDao = new UserDao();
    userDao.setDataSource(dataSource());
    return userDao;
}
<beans>
    <bean id="dataSource" class="org.srpingframework.jdbc.datasource.SimpleDirveDataSource">
        <property = name="dirverClass" value="com.mysql.jdbc.Driver" />
        <property = name="url" value="jdbc:mysql://localhost/spring" />
        <property = name="username" value="spring" />
        <property = name="password" value="book" />
    </bean>
    <bean id="userDao" class="springbook.user.dao.UserDao">
        <property name="dataSource" ref="dataSource" />
    </bean>
</beans>


댓글0