본문 바로가기

Spring

[Spring] 스프링 빈 설정 (XML, JAVA, Component Scan)

1. XML

스프링의 애플리케이션 컨텍스트는 XML에 담긴 DI 정보를 활용할 수 있습니다. DI 정보가 담긴 XML 파일은 <beans>를 루트 엘리먼트로 사용하고 <beans> 안에는 여러 개의 <bean>을 정의할 수 있습니다.

<bean> 태그에 사용되는 속성들

속성 이름 설명
id 빈 객체의 고유 이름으로, 빈 id를 이용해 빈에 접근합니다.
name 객체의 별칭입니다.
class 생성할 클래스입니다. 패키지 이름까지 입력해야 합니다.
constructor-arg 생성자를 이용해 값을 주입할 때 사용합니다.
property setter를 이용해 값을 주입할 때 사용합니다.

setter를 이용한 DI 기능

PersonService

public interface PersonService {
    public void sayHello();
}

PersonServiceImpl

public class PersonServiceImpl implements PersonService {

    private String name;

    private int age;

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void sayHello() {
        System.out.println("이름: " + name);
        System.out.println("나이: " + age);
    }
}

application.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">

    <bean id="personService" class="com.example.demo.PersonServiceImpl">
        <property name="name">
            <value>홍길동</value>
        </property>
    </bean>

</beans>

resources 폴더에 application.xml 파일을 생성한 후 PersonService 빈에 접근할 수 있도록 빈 id를 personService로 지정하고 <property> 태그를 이용해 PersonServiceImpl 클래스 객체의 name 속성에 <value> 태그의 값으로 초기화 합니다.

public class DemoApplication {

   public static void main(String[] args) {
      ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
      PersonService person = (PersonService) context.getBean("personService");
      person.sayHello();
   }

}

위와 같이 application.xml 파일을 읽어와서 context 객체를 생성하고 getBean 메서드를 이용하여 id가 personService인 빈을 생성합니다. 그리고 나서 person 객체의 sayHello() 메서드를 실행하면 아래와 같이 출력되는 것을 확인할 수 있습니다.

 

생성자를 이용한 DI 기능

PersonServiceImpl

public class PersonServiceImpl implements PersonService {

    private String name;

    private int age;

    public PersonServiceImpl(String name) {
        this.name = name;
    }

    public PersonServiceImpl(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public void sayHello() {
        System.out.println("이름: " + name);
        System.out.println("나이: " + age);
    }
}

application.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">

    <bean id="personService1" class="com.example.demo.PersonServiceImpl">
        <constructor-arg value="이순신"/>
    </bean>
    
    <bean id="personService2" class="com.example.demo.PersonServiceImpl">
        <constructor-arg value="이순신"/>
        <constructor-arg value="23"/>
    </bean>

</beans>

인자가 한 개인 생성자로 id가 personService1인 빈을 생성하고 인자가 두 개인 생성자로 id가 personService2인 빈을 생성합니다.

public class DemoApplication {

   public static void main(String[] args) {
      ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
      PersonService person1 = (PersonService) context.getBean("personService1");
      person1.sayHello();
      System.out.println();

      PersonService person2 = (PersonService) context.getBean("personService2");
      person2.sayHello();

   }

}

수정자 때와 마찬가지로 context 객체를 생성하고 id가 personService1인 person1 빈과 id가 personService2인 person2 빈을 생성하고 각각 sayHello() 메서드를 호출하면 아래와 같이 출력되는 것을 확인할 수 있습니다.

 

의존 관계에 있는 다른 빈을 주입하는 경우

PersonServiceImpl

public class PersonServiceImpl implements PersonService {

    private PersonRepository personRepository;

    public void setPersonDAO(PersonRepository personRepository) {
        this.personRepository = personRepository;
    }

    @Override
    public void listPersons() {
        personRepository.listPersons();
    }
}

PersonRepository

public class PersonRepository {

    public void listPersons() {
        System.out.println("listMembers 메서드 호출");
        System.out.println("회원정보를 조회합니다.");
    }
}

application.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">

    <bean id="personService" class="com.example.demo.PersonServiceImpl">
        <property name="personRepository" ref="personRepository"/>
    </bean>

    <bean id="personRepository" class="com.example.demo.PersonRepository"/>

</beans>

id가 personDAO인 빈을 personService 빈에 주입합니다. 주입되는 데이터가 기본형이 아닌 참조형인 경우 ref 속성으로 설정해야 합니다.

public class DemoApplication {

   public static void main(String[] args) {
      ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
      PersonService person = (PersonService) context.getBean("personService");
      person.listPersons();

   }

}

context 객체를 생성하고 id가 personService인 빈을 생성한 후 person 객체를 이용해 listPersons() 메서드를 호출하면 아래와 같이 출력되는 것을 확인할 수 있습니다.

 

하지만 위와 같은 방법은 xml 파일에 bean을 일일이 등록해줘야 해서 매우 번거롭습니다. 그래서 xml 파일에 아래와 같이 component-scan을 등록해주면 base-package에서 지정한 패키지의 하위 패키지를 scanning해서 @Component 어노테이션이 붙은 클래스들을 자동으로 빈으로 등록해줍니다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.example.demo"/>

</beans>

PersonServiceImple와 PersonRepository를 아래와 같이 작성하고 name이 personServiceImpl인 빈을 조회한 후 똑같이 실행하면 같은 결과가 출력됩니다.

@Service
public class PersonServiceImpl implements PersonService {

    @Autowired
    private PersonRepository personRepository;

    public void setPersonRepository(PersonRepository personRepository) {
        this.personRepository = personRepository;
    }

    @Override
    public void listPersons() {
        personRepository.listPersons();
    }
}
@Repository
public class PersonRepository {

    public void listPersons() {
        System.out.println("listMembers 메서드 호출");
        System.out.println("회원정보를 조회합니다.");
    }
}

참고로 @Service와 @Repository 그리고 @Controller 어노테이션은 @Component 어노테이션을 확장시킨 어노테이션이므로 scanning시 스프링 빈으로 자동으로 등록됩니다.

2. JAVA 설정파일

xml 파일이 아닌 JAVA 클래스를 이용해서도 빈을 설정할 수 있습니다.

먼저 ApplicationConfig라는 클래스를 생성한 뒤 아래와 같이 작성해줍니다.

@Configuration
public class ApplicationConfig {

    @Bean
    public PersonRepository personRepository() {
        return new PersonRepository();
    }

    @Bean
    public PersonServiceImpl personServiceImpl() {
        PersonServiceImpl personService = new PersonServiceImpl();
        personService.setPersonRepository(personRepository());
        return personService;
    }
}

자바 설정 파일은 클래스위에 @Configuration 어노테이션을 작성하고 빈으로 등록할 객체를 리턴하는 메소드 위에 @Bean 어노테이션을 추가해줌으로써 빈으로 등록할 수 있습니다. 그리고 나서 PersonServiceImpl과 PersonRepository에 작성했던 @Component 확장 어노테이션을 지워주고 아래 코드를 실행해주면 xml파일로 테스트했을 때와 같은 결과가 출력됩니다.

public class DemoApplication {

   public static void main(String[] args) {
      ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
      PersonService person = (PersonService) context.getBean("personServiceImpl");
      person.listPersons();

   }

}

3. JAVA 설정 파일 + Component Scan

xml 파일은 사용하기 싫은데 Component Scan은 사용하고 싶다면 자바 설정 파일에 Component Scan을 설정해주면 됩니다.

자바 설정 파일을 아래와 같이 작성해주고 다시 PersonServiceImpl과 PersonRepository 클래스에 각각 @Service와 @Repository 어노테이션을 붙여주고 테스트해보면 같은 결과가 출력됩니다.

@Configuration
@ComponentScan(basePackageClasses = ApplicationConfig.class)
public class ApplicationConfig {
}