Spring의 IoC/DI 컨테이너에 대한 동작을 확인하기 위해 Maven을 이용해 프로젝트를 생성한 후, XML 형식의 설정 파일을 만들어 IoC와 DI가 잘 동작하는지 확인하기
Maven을 이용해 스프링 프레임워크 사용하는 프로젝트 생성
Bean이 무엇인지 이해하기
XML형식의 스프링 설정파일의 내용에 대한 이해하기
Bean, ApplicationContext, DI
Java 11, IntelliJ, Maven, Spring 5
예전에는 Visual한 컴포넌트를 Bean이라고 불렀지만, 요즘은 일반적인 Java 클래스를 Bean 클래스라고 함
추가) getName(), setName() 메소드를 name property라고 함
package org.fordevelop;
//Bean클래스
public class UserBean {
private String name;
private int age;
private boolean male;
public UserBean(){ //기본 생성자 생성
}
public UserBean(String name, int age, boolean male){
this.name = name;
this.age = age;
this.male = male;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public boolean isMale() {
return male;
}
public void setMale(boolean male) {
this.male = male;
}
}
<properties>
<!-- 상수처럼 사용 가능-->
<spring.version>5.3.9</spring.version>
</properties>
<dependencies>
<!--Spring 라이브러리 추가-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
→ 빌드 후 설치된 라이브러리 목록을 보면, Spring Context와 연관있는 Spring 라이브러리들이 모두 설치됨
resources 폴더를 생성해 applicationContext.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="userBean" class="org.fordevelop.UserBean"> </bean>
</beans>
id - 참조변수 이름, class - 해당 자바 클래스의 경로
xml파일로 Spring 설정 파일을 만들 때 가장 바깥쪽 태그는 반드시 beans 이어야 함 → 해당 설정파일을 스프링 컨테이너가 읽어들이도록 하기 위해서
스프링 컨테이너에게 생성할 객체에 대한 정보를 전달해줘야 함 → bean 태그를 사용해 bean을 등록함
위에서 작성한 bean 태그의 의미 :
UserBean userBean = new UserBean();
이때 스프링 컨테이너는 등록한 객체를 1개만 가지고 있는데, 이를 '싱글톤 패턴'이라고 함
package org.fordevelop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ApplicationContextExam01 {
public static void main(String[] args){
ApplicationContext ac = new ClassPathXmlApplicationContext(
"classpath:applicationContext.xml"
); //ㄱ
System.out.println("초기화 완료!");
UserBean userBean = (UserBean) ac.getBean("userBean"); //ㄴ
userBean.setName("fordevelop");
System.out.println(userBean.getName());
UserBean userBean2 = (UserBean)ac.getBean("userBean");
if(userBean == userBean2) //ㄷ
System.out.println("동일한 인스턴스임");
}
}
ㄱ. 객체를 생성해줄 스프링 컨테이너를 생성함
ApplicationContext는 인터페이스이므로, 이를 구현한 ClassPathXmlApplicationContext를 사용해 컨테이너를 생성해줌
ClassPathXmlApplicationContext 객체 생성 시 앞서 생성한 bean에 대한 정보를 가진 applicationContext.xml 파일을 읽어들인 후 해당 파일 안에 선언된 bean들을 모두 읽어줌
→ 여러 bean이 존재한다면, 모두 객체 생성하여 메모리에 올림
→ 생성한 resources 폴더는 main 폴더 내에 있으므로 소스폴더임. 해당 폴더 내에 작성한 파일들은 classpath로 지정됨.
ㄴ. getBean()의 return 타입이 Object이므로 UserBean으로 downcasting 해줌
getBean()은 BeanFactory 인터페이스에 선언되어 있는 메소드로, ApplicationContext 인터페이스가 BeanFactory을 상속받기 때문에 사용이 가능함
ApplicationContext spring docs
getBean()의 흐름 : 앞서 생성한 applicationContext.xml 파일에서 메소드의 인자로 전달된 값과 동일한 Bean을 찾고, 해당 클래스의 객체를 생성하여 반환함
ㄷ. 스프링 컨테이너는 특정 클래스의 객체를 1개만 생성하는 싱글톤 패턴을 사용함 → 생성한 Bean을 재사용하는 것
테스트용 클래스 작성
public class Car {
private Engine v8;
public Car(){
System.out.println("Car 생성자");
}
public void setEngine(Engine e){
this.v8 = e;
}
public void run(){
System.out.println("엔진을 이용해 달림");
v8.exec();
}
public static void main(String[] args)
Engine e = new Engine();
Car c = new Car();
c.setEngine(e);
c.run();
}
}
public class Engine {
public Engine(){
System.out.println("Engine 생성자");
}
public void exec(){
System.out.println("엔진이 동작함");
}
}
→ Car 클래스의 main 메서드 내에서 Car과 Engine 객체를 각각 생성자를 사용해 생성해줘야 함
Engine e = new Engine();
Car c = new Car();
c.setEngine(e);
→ 위의 코드의 역할을 Spring 컨테이너에게 하도록 함
<bean id="userBean" class="org.fordevelop.UserBean"> </bean>
<bean id="e" class="org.fordevelop.Engine"></bean>
<bean id="car" class="org.fordevelop.Car">
<property name="engine" ref="e"></property>
</bean>
위의 코드는
Engine e = new Engine();
Car c = new Car();
c.setEngine(e);
동일한 역할을 수행함
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ApplicationContextExam02 {
public static void main(String[] args){
ApplicationContext ac = new ClassPathXmlApplicationContext(
"classpath:applicationContext.xml"
);
Car car = (Car)ac.getBean("car");
car.run();
}
}
→ 2-3에서 작성한 내용과 동일한 과정을 수행함
→ 위의 코드를 통해 알 수 있는 DI의 장점 : Engine 클래스를 몰라도 Car의 Bean을 주입 받아 Engine이 필요한 작업 실행 가능함
Spring컨테이너가 관리하는 객체를 빈(Bean)이라고 말합니다. (여러분들이 직접 new연산자로 생성해서 사용하는 객체는 빈(Bean)이라고 말하지 않습니다.) Spring은 빈을 생성할 때 기본적으로 싱글톤(Singleton)객체로 생성합니다. 싱글톤이란 메모리에 하나만 생성한다는 것입니다. 메모리에 하나만 생성되었을 경우, 해당 객체를 동시에 이용한다면 어떤 문제가 발생할 수 있을까요? 이런 문제를 해결하려면 어떻게 해야할까요? ( 참고로 Spring에서 빈을 생성할 때 스코프(scope)를 줄 수 있습니다. 스코프를 줌으로써 기본으로 설정된 싱글톤 외에도 다른 방법으로 객체를 생성할 수 있습니다. )