Java config를 이용한 설정
Java Config를 이용한 설정
들어가기 전에
Java Config와 어노테이션을 이용해 스프링에서 사용하는 빈을 정의하고 DI하는 방법에 대해 알아보도록 한다.
학습 목표
- JavaConfig형태의 설정파일의 내용을 이해할 수 있다.
- @ComponentScan, @Component, @Autowired 어노테이션의 쓰임새에 대해 이해한다.
핵심 개념
- AnnotationConfigApplicationContext
- @Configuration
- @ComponentScan
- @Component
- @Autowired
Java config를 이용한 설정을 위한 어노테이션
@Configuration
- 스프링 설정 클래스를 선언하는 어노테이션
@Bean
- bean을 정의하는 어노테이션
@ComponentScan
- @Controller, @Service, @Repository, @Component 어노테이션이 붙은 클래스를 찾아 컨테이너에 빈으로 등록
@Component
- 컴포넌트 스캔의 대상이 되는 애노테이션 중 하나로써 주로 유틸, 기타 지원 클래스에 붙이는 어노테이션
@Autowired
- 주입 대상이되는 bean을 컨테이너에 찾아 주입하는 어노테이션
Java Config를 이용해 설정하기
ApplicationConfig.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package kr.or.connect.diexam01;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ApplicationConfig {
@Bean
public Car car(Engine e) {
Car c = new Car();
c.setEngine(e);
return c;
}
@Bean
public Engine engine() {
return new Engine();
}
}
- @Configuration 은 스프링 설정 클래스라는 의미를 가진다. JavaConfig로 설정을 할 클래스 위에는 @Configuration가 붙어있어야 한다.
- ApplicationContext중에서 AnnotationConfigApplicationContext는 JavaConfig클래스를 읽어들여 IoC와 DI를 적용하게 된다.
- 이때 설정파일 중에 @Bean이 붙어 있는 메소드들을 AnnotationConfigApplicationContext는 자동으로 실행하여 그 결과로 리턴하는 객체들을 기본적으로 싱글턴으로 관리를 하게 된다.
- ApplicationContext는 파라미터를 받아들이지 않는 빈 생성 메서드를 먼저 실행해서 반환받은 객체를 관리한다. 그리고나서 파라미터에 생성된 객체들과 같은 타입의 객체가 있을 경우에 파라미터에 전달해서 객체를 생성한다. 빈은 클래스 이름의 첫글자를 소문자로 바꾼 이름을 기본으로 한다.
ApplicationContextExam03.java
- 설정 파일을 읽어 실제 실행시켜주는 자바 파일 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package kr.or.connect.diexam01;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class ApplicationContextExam03 {
public static void main(String[] args) {
// 이전까지 클래스 패스에서 설정을 읽어들였기 때문에 ClassPathXmlApplicationContext를 이용했다면, 이번엔 설정을 가지고 있는 클래스 파일을 읽어들여야 한다.
ApplicationContext ac = new AnnotationConfigApplicationContext(ApplicationConfig.class); // 빈을 생성해서 가진다.
Car car = (Car)ac.getBean(Car.class);
car.run();
}
}
- ApplicationConfig의 메서드명과 동일하게 설정해야 객체를 얻을 수 있다.
파라미터로 요청하는 class 타입으로 지정 가능하다. ApplicationContext가 관리하는 객체 중에서 해당 클래스 타입이 존재하면 해당 객체를 반환해준다.
Car car = ac.getBean(Car.class);
ApplicationContext2.java
1
2
3
4
5
6
7
8
9
10
11
12
package kr.or.connect.diexam01;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("kr.or.connect.diexam01") // 컴포넌트 스캔 패키지를 지정해주면, 사용자가 일일이 알려주지 않아도 어노테이션 붙어있는 것들을 찾아내서 등록해준다.
public class ApplicationConfig2 {
}
- 기존 JavaConfig에서 빈을 생성하는 메소드를 모두 제거했다. 단, @Configuration아래에 @ComponentScan이라는 어노테이션을 추가했다.
- @ComponentScan어노테이션은 파라미터로 들어온 패키지 이하에서 @Controller, @Service, @Repository, @Component 어노테이션이 붙어 있는 클래스를 찾아 메모리에 올리고 DI로 주입하도록 한다.
- 이러한 어노테이션이 붙어있지 않은 객체들은 @Bean 어노테이션을 이용해 직접 생성해주는 방식으로 클래스를 관리할 수 있다. Spring JDBC나 다른 라이브러리가 갖고있는 객체들을 사용하는 경우, 해당 라이브러리를 열어서 직접 어노테이션을 붙일 수 없다. 그럴 경우 @Bean 어노테이션을 이용해 빈을 등록하여 편리하게 사용 가능하다.
Engine.java
- 기존의 Car클래스와 Engine클래스 위에 @Component를 붙이도록 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package kr.or.connect.diexam01;
import org.springframework.stereotype.Component;
@Component
public class Engine {
public Engine() {
System.out.println("Engine 초기화");
}
public void exec() {
System.out.println("엔진이 동작합니다.");
}
}
Car.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package kr.or.connect.diexam01;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Car {
@Autowired // 컨테이너에 Engine 타입의 객체가 생성되어 있으면 알아서 주입해준다.(setter 메서드 불필요)
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();
// }
}
ApplicationContextExam04.java
- 수정된 JavaConfig를 읽어들여 실행하는 클래스를 보도록 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package kr.or.connect.diexam01;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class ApplicationContextExam04 {
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(ApplicationConfig2.class); // 빈을 생성해서 가진다.
Car car = (Car)ac.getBean(Car.class);
car.run();
}
}
Spring에서 사용하기에 알맞게 @Controller, @Service, @Repository, @Component 어노테이션이 붙어 있는 객체들은 ComponentScan을 이용해서 읽어들여 메모리에 올리고 DI를 주입하도록 하고, 이러한 어노테이션이 붙어 있지 않은 객체는 @Bean어노테이션을 이용하여 직접 생성해주는 방식으로 클래스들을 관리하면 편리하다.
다루는 빈의 수가 많아질수록 XML로 설정하는 것보다
@ComponentScan
,@Component
,@Autowired
를 이용하는 것이 유지보수에 더 좋을 것으로 보인다.- XML 설정 vs. 어노테이션 기반 설정:
- XML 설정은 빈의 관리와 의존성 주입이 외부에 명시적으로 표현되어 있어서 가독성이 좋고 관리하기 쉽다. 하지만 빈의 수가 많아질수록 XML 파일이 복잡해지고 유지보수가 어려워질 수 있다.
- 반면에 어노테이션 기반 설정은 자바 코드에 설정이 명시되어 있어서 직관적이고 간단하다. 또한, IDE의 지원을 받아서 리팩토링이나 자동 완성 기능을 활용할 수 있습니다. 하지만 어노테이션 설정만으로는 설정의 전체 구조를 파악하기 어려울 수 있다.
- @Autowired의 활용:
@Autowired
를 이용하면 필요한 의존성을 자동으로 주입할 수 있다. 이는 XML 설정에서 직접 의존성을 설정하는 것보다 편리하고 간단하다.@Autowired
를 사용하는 방법에는 필드, 생성자, 세터 메서드에 사용할 수 있다. 필드 주입은 코드의 간결성을 높이지만 의존성이 숨겨져 있어서 가독성이 떨어질 수 있다. 반면에 생성자 주입은 의존성을 명시적으로 드러내어 가독성과 테스트 용이성을 높일 수 있다. 세터 메서드 주입은 선택적인 의존성을 주입할 때 유용하다. 하지만 일부 빈이 누락될 수 있고, 런타임 시점에서 NullPointerException이 발생할 수 있다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.