서론
이전 시간에 제어의 역전과 의존성 주입에 대해서 알아보았다.
https://beudicri.tistory.com/99
[Spring] 15. 제어의 역전 & 의존성 주입 (IoC & DI)
서론 이전시간에 알아보았던 자바 빈과 스프링 컨테이너를 통해 제어의 역전, 의존성 주입을 하는 방법을 알아보자. 1. IoC (Inverse of Control : 제어의 역전) 기존의 프로그램은 클라이언트 구현 객
beudicri.tistory.com
그렇다면 이번에는 스프링에서 어떻게 빈을 등록하고 의존성을 주입하는지를 알아보도록 하자.
1. Bean 등록 방법 및 사용법
package javaBean;
public class A {
public String keyA; //public
private String valueA; //private
}
해당 클래스를 사용하여 빈을 등록해보도록 하자.
빈을 등록하는 방법에는 여러가지가 있지만, 대체로 XML파일과 자바를 이용한 2가지 방법을 주로 사용한다.
1) XML
1-1) <bean>
src/main/resources/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">
<bean id="a" class="javaBean.A">
<property name="keyA" value="1"/>
<property name="valueA" value="A"/>
</bean>
</beans>
config.xml파일에 등록할 빈의 정보를 넣어준다.
이때, keyA와 valueA (property)에 값을 넣어줄 때 setter를 사용해서 넣어주기 때문에
setter가 존재하지 않으면 아래와 같이 나온다.
package javaBean;
public class A {
public String keyA;
private String valueA;
public void setKeyA(String keyA) {
this.keyA = keyA;
}
public void setValueA(String valueA) {
this.valueA = valueA;
}
public String getKeyA() {
return keyA;
}
public String getValueA() {
return valueA;
}
}
setter를 생성해주면 오류가 사라진 것을 확인할 수 있다. (추가적으로 이후 사용을 위해 getter추가)
package javaBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext ac = new GenericXmlApplicationContext("config.xml");
A a = (A) ac.getBean("a");
System.out.println("keyA : " + a.getKeyA());
System.out.println("valueA : " + a.getValueA());
}
}
빈이 잘 등록된 것을 확인할 수 있다.
1-2) @Component + <component-scan>
이번에는 component-scan을 사용하여 빈을 등록해보자.
<?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 http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="javaBean"/>
</beans>
component-scan에 base-package를 설정해주면,
해당 패키지 내부에 @Component 어노테이션이 붙은 클래스를 모두 빈으로 만들어준다.
package javaBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext ac = new GenericXmlApplicationContext("config.xml");
A a = (A) ac.getBean("a");
a.setKeyA("1");
a.setValueA("A");
System.out.println("keyA : " + a.getKeyA());
System.out.println("valueA : " + a.getValueA());
}
}
여기서 알아두어야 할 것은 Spring을 공후하다보면 @Service, @Controller, @Repository를 한번쯤 볼 수 있다
이를 살펴보면
3개의 어노테이션안에 @Component가 포함되어 있는 것을 확인할 수 있다.
따라서, 해당 어노테이션 들도 component-scan시 자동으로 빈으로 등록된다.
2) Java
2-1) @Configcation & @Bean
이번에는 자바에서 빈을 등록하는 방법에 대해서 알아보자.
아래는 A클래스에 생성자를 추가한 코드이다.
package javaBean;
public class A {
public String keyA;
private String valueA;
public A() {
this.keyA = "1";
this.valueA = "A";
}
public void setKeyA(String keyA) {
this.keyA = keyA;
}
public void setValueA(String valueA) {
this.valueA = valueA;
}
public String getKeyA() {
return keyA;
}
public String getValueA() {
return valueA;
}
}
package javaBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ApplicationConfig {
@Bean
public A a(){
return new A();
}
}
자바로 빈을 등록하는 방법은 @Configuration 어노테이션과 @Bean어노테이션을 사용하면 된다.
@Configuration 어노테이션이 붙은 클래스는 스프링 컨테이너가 해당 클래스를 빈으로 등록하고
해당 클래스 내부의 @Bean들을 모두 생성해준다.
@Bean어노테이션이 붙은 메소드는 아래와 같은 형식을 가져야 한다.
public //반환타입 빈 id
public A a(){}
위와 같은 메소드이 경우 Bean ID는 a가 된다.
package javaBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
// ApplicationContext ac = new GenericXmlApplicationContext("config.xml");
ApplicationContext ac = new AnnotationConfigApplicationContext(ApplicationConfig.class);
A a = (A) ac.getBean("a");
System.out.println("keyA : " + a.getKeyA());
System.out.println("valueA : " + a.getValueA());
}
}
이제 이를 확인해보기 위해 GenericXmlApplicationContext를 AnnotaionConfigApplicationContext로 변경해주고,
ApplicationConfig.class를 매개변수로 넘겨주고, 위와 동일하게 확인해보면 결과는 아래와 같다.
생성자에서 초기화 해준 keyA, valueA가 제대로 들어가있으며, 빈이 잘 등록된 것을 확인할 수 있다.
2-2) @Configuration + @ComponentScan + @Component
package javaBean;
import org.springframework.stereotype.Component;
@Component
public class A {
public String keyA;
private String valueA;
public A() {
this.keyA = "1";
this.valueA = "A";
}
public void setKeyA(String keyA) {
this.keyA = keyA;
}
public void setValueA(String valueA) {
this.valueA = valueA;
}
public String getKeyA() {
return keyA;
}
public String getValueA() {
return valueA;
}
}
위의 1-2) <component-scan>을 수행할 빈 등록할 클래스에 @Component 어노테이션을 붙혀준다.
@Component : 빈으로 등록할 클래스에 직접 붙혀주는 어노테이션!
그러면 @Bean은 왜 사용할까?
→ 내가 작성하지 않은 라이브러리 클래스 등은 내가 직접 어노테이션을 붙혀줄 수 없다
→ 따라서 이러한 클래스를 빈으로 등록하려면 xml(<bean>) or java(@Configuration + @Bean)을 사용해야한다.
package javaBean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "javaBean")
public class ApplicationConfig {
}
이후, ComponentScan(basePackages or basePackageClasses)를 사용하여
@Component 어노테이션이 붙은 클래스를 찾아 빈으로 등록해준다.
이를 다시 실행하면 위와 같은 결과를 얻을 수 있다.
2. 스프링 의존성 주입 (@Autowired)
- @Autowired : 스프링 컨테이너에 등록한 빈에게 의존관계 주입이 필요할 때, 의존성 주입을 도와주는 어노테이션
- 스프링 컨테이너에 빈들을 모두 등록한 후에, 의존성 주입 단계가 이루어진다.
- 생성자, setter, 필드에 사용 가능하다.
스프링 부트를 사용하며, 많이 보았던 Service와 Repository를 통해 이를 알아보도록 하자.
1) 생성자 (Constructor)
package javaBean.User;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
생성자에 어노테이션을 붙히는 방식이다.
특징
- 객체가 생성될 때 딱 한번만 호출된다
: 의존관계가 변하지 않는 경우, 필수 의존 관계에 사용 - 의존 관계에 있는 객체들을 final로 선언할 수 있다는 장점이 있다.
: 생성자에서 무조건 설정해주어야 하므로, 누락이 발생하지 않는다. - 생성자가 하나일 경우 @Autowired를 생략할 수 있다.
2) 수정자 (Setter)
package javaBean.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
Setter를 사용한 방식이다.
특징
- 스프링 빈을 모두 등록한 후에 @Autowired가 붙은 수정자를 모두 찾아서 의존 관계를 주입한다.
- 선택적이고, 변화가능한 의존관계에 사용한다.
- final을 사용할 수 없다. (final을 사용하면 setter를 통해 변화가 불가능하다.)
3) 필드 (Field)
package javaBean.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
}
필드에 어노테이션을 붙혀서 사용한 방법이다.
특징
- 변수에 @Autowired를 붙혀서 사용한다.
- 마찬가지로 final이 불가능하다.
- 단점
1. 클래스가 의존하는 빈에 대한 결합도가 높아진다. (클래스가 특정 빈을 직접적으로 의존하기 때문)
따라서, 유지보수와 테스트가 어려워질 수 있다.
2. 테스트 시 Mock객체 또는 테스트용 빈으로 교체하기 어려울 수 있다.
정리
@Autowired를 사용하는 3가지 방법에 대해서 알아보았다.
여기서 가장 좋은 방법은 생성자를 사용한 방식이다.
- 대부분의 의존관계는 어플리케이션 종료까지 변경될 일이 없다. (불변)
1. 따라서 Setter 사용 X (setter는 public이기 때문에 접근이 쉬워 실수로 변경할 가능성 존재0
2. 생성자 주입을 사용하면 생성자 호출 시점에 1번만 호출되어 의존성 주입 (불변하게 설계 가능) - final 키워드 사용 가능
생성자 주입 사용 시 final 키워드를 사용할 수 있다.
만약, 생성자를 만들지 않은 경우, 오류로 인해 의존성 주입이 누락되지 않는다.
결론
이번에는 xml과 자바를 통해 빈을 생성하고 사용하는 방법에 대해 알아보았고,
추가적으로 Spring에서 의존성 주입을 도와주는 @Autowired 어노테이션 사용법에 대해서 알아보았다.
스프링 부트로 프로젝트를 할때에는 왜 사용하는지 제대로 알지 못하고 사용했었는데,
그때부터 궁금했지만, 이제서야 제대로 이해하고 사용할 수 있게 되어서 뿌듯하고 재미있다!.
참고
https://m42-orion.tistory.com/100
'프로그래밍 > Spring' 카테고리의 다른 글
[Spring] Spring Security 토큰 기반 인증 구현 (1) | 2024.07.22 |
---|---|
[Spring] Spring Security (2) | 2024.07.22 |
[Spring] 15. 제어의 역전 & 의존성 주입 (IoC & DI) (0) | 2023.11.06 |
[Spring] 14. 자바 빈 & 스프링 컨테이너 (Java Bean & Spring Container) (0) | 2023.09.15 |
[Spring] 13. 프로젝트 설정 (IntelliJ) (0) | 2023.08.25 |