스프링 어노테이션을 활용한 DI
Spring framework 설정 (어노테이션)
이전 글에서는 외부 설정파일(setting.xml)을 통한 DI 주입 방식을 설명했습니다.
이번에는 어노테이션을 활용한 주입방식을 설명하겠습니다.
어노테이션을 활용한 DI
설정파일 형식인 setting.xml 같은 지시서로 DI하는 방식이 아닌 어노테이션을 활용한 DI 방식도 있습니다.
설정파일을 열어서 수정하는 것도 귀찮아서 설정을 코드에다가 메타데이터로 심는 방법으로 진화했다.
@Component라는 어노테이션이 붙은 클래스를 스프링이 읽게 되면 객체화 합니다.
<bean id="exam" class="패키지명.NewlecExam">
<bean id="console" class="패키지경로.InlineExamConsole" >
<property name="exam" ref="exam"></ property >
</bean>
여기서 <property name="exam" ref="exam"></ property > 이 부분을 어노테이션으로 바꾼다면
exam을 인젝션 하는 부분이 설정파일에서 사라지게 되므로
InlineExamConsole의 setExam Setter에 @Autowired 라는 어노테이션으로 설정을 하게 됩니다.
@Autowired
@Override
public void setExam(Exam exam){
this.exam = exam;
}
스프링이 <bean id="console" class="패키지경로.InlineExamConsole" > 이 객체를 읽으면서 객체안에 @Autowired
어노테이션을 감지합니다.
그럼으로서 자동으로 객체와 연결이 됩니다. 하지만 어떠한 명령을 내려야 객체 안에서 어노테이션을 찾고 감지를 합니다,
스프링 지시서(setting.xml) 안에다가 context라는 패키지가 필요합니다.
xmlns:context="http://www.springframework.org/schema/context"
이것을 <beans>에 설정해준다. 그리고 태그안에 아래 태그를 통해 명령을 내린다.
<context:annotation-config /> 해당 태그의 설정으로 객체안에서 @Autowired를 감지하고 찾아낸다.
여기서 @Autowired는 setExam(Exam exam)에서의 참조타입인 Exam을 기준으로
Exam 타입의 NewlecExam과 자동설정을 해준다,
<bean class="패키지명.NewlecExam" p:kor="10" p:eng="10">
<bean class="패키지명.NewlecExam" p:kor="20" p:eng="20" >
이렇게 같은 모양의 두 객체가 있다면 어떤 것을 인식 할까? ----> 모호한 설정으로 인해 오류가 발생합니다.
여기서 둘 중 하나에 id="exam"을 부여하게 되면 해당 객체를 인식할 수 있게 됩니다. (id="exam1" 안됨)
만약 빈의 id를 exam1로 하고 싶다면 @Qualifier("exam1") 을 Setter메서드에 설정해주면 된다. (Setter 주입 형식)
필드 주입형태
어노테이션을 활용한 DI 방법에는 또 다른 방법으로 필드 주입 형태가 존재한다.
public class InlineExamConsole implements ExamConsole{
@Autowired
@Qualifier("exam2")
private Exam exam;
..... 기타 로직들
}
.위와 같이 필드선언부에서 설정하는 방식이다. 얘는 기본생성자를 호출하면서 인젝션이 됩니다.
이럴땐 기본 생성자가 존재해야 합니다. 아니면 기본생성자도 없다면 오버로드된 생성자 또한 없어야 합니다.
그래야 자바 컴파일러가 자동으로 기본 생성자를 생성해줄테니까요
생성자 주입 형태
public class InlineExamConsole implements ExamConsole{
private Exam exam;
@Autowired
//@Qualifier("exam2")
public InlineExamConsole(@Qualifier("exam2") Exam exam){
this.exam = exam;
}
..... 기타 로직들
}
위와 같은 경우는 @Qualifier("exam2")가 매개변수 앞에 붙어야 합니다. 오버로드 생성자의 매개변수로 복수의 변수가 올 수 있으므로 명시적으로 직접 매개변수 앞에 어노테이션을 명시하여 줍니다.
만약 @Autowired(required = false) 로 선언한다면 @Qualifier("exam2")로 선언했던 bean의 id가 exam2인 객체가 없어도 일반 다음 로직을 실행한다.
이제는 <bean id="console" class="패키지경로.InlineExamConsole" > 이 부분도 제외를 합니다.
그러면 스프링에서는 해당 객체가 사라지게 됩니다. 이 객체를 어노테이션으로 생성을 해보겠습니다.
@Component
class InlineExamConsole{
}
이런식으로 클래스에 @Component을 붙혀서 해당 클래스를 객체화 하여 빈으로 컨테이너에 주입합니다.
이것도 사용하기전에 설정파일에서 설정 태그가 필요합니다.
<context:component-scan base-package=" 패키지경로" />
<context:annotation-config />
<bean id="exam" class="패키지경로.NewlecExam" /> //이것은 상관없음
이렇게 설정을 해주게 되면 스프링이 설정파일을 읽으면서 컴포넌트 스캔으로 등록한 해당 패키지경로에서 @Component 가 붙은 클래스를 찾습니다.
이 설정을 하면 자동으로 나머지 어노테이션을 인식할 수 있게 되므로 <context:annotation-config /> 부분도 생략가능
하지만 이렇게 설정해도 앞에서 작성한 프로그램에서
ExamConsole console = (ExamConsole)context.getBean("console") 이 부분에서 오류가 발생한다.
설정파일에서 console이라고 네이밍한 빈을 지웠으므로 컨텍스트에서 찾지 못해서 발생하는 오류입니다.
ExamConsole console = context.getBean(ExamConsole.class); 이러한 형식으로 찾는다면 괜찮을 것입니다.
명시적으로 ExamConsole이라는 인터페이스에 부합하는 객체를 찾으라고 적어줬기 때문입니다.
또다른 방법으로는 우리가 "console"이라고 네이밍을 해주면 될 것입니다. @Component("console") 이렇게 네이밍을 해주면됩니다.
(이제는 Auowired가 될수 있도록 NewlecExam객체도 Component를 설정하여 객체화를 해주어야 합니다.)
이렇게 해서 어노테이션을 통해 DI를 완료하였습니다.
값 설정을 위한 @Value
값에 기본적인 값을 설정하기 위한 어노테이션입니다.
public class NewlecExam implements Exam {
@Value("20")
private int kor;
@Value("20")
private int eng;
}
@Component 는 나중에는 스프링MVC 관점에서 의미론적으로 @Controller, @Service, @Repository 이렇게 나뉘게 됩니다.
@Controller는 웹 요청을 처리하기 위한 애너테이션( 웹 요청을 처리후 뷰를 반환)이고,
@Service는 비즈니스 로직을 처리하기 위한 애너테이션입니다.
@Repository는 데이터베이스와의 상호작용을 담당하며, 데이터의 영속성을 관리합니다.
이제는 아예 setting.xml을 어노테이션으로 대체하기위해
스프링 지시서를 지웁니다. 그렇다면 대신할 수 있는 설정파일을 자바클래스로 변환할 것입니다.
이 설정클래스는 설정을 위한 클래스이므로 @Configuration을 명시해줍니다.
이 어노테이션은 스프링 설정파일에 내용을 클래스에 추가해줍니다.
@ComponentScan({"패키지경로", "패키지경로2"})
@Configuration
public Class AppConfig{
@Bean
public Exam exam(){
return new NewlecExam();
}
}
@ComponentScan({"패키지경로", "패키지경로2"})을 통해 컴포넌트 설정을 읽어야 하는 범위를 지정합니다.
이런식으로 exam함수를 bean객체로 컨테이너에 등록하여 관리할 수 있게 됩니다.
@Bean 어노테이션은 라이브러리나 개발자가 제어할 수 없는 메서드 혹은 기존 클래스의 메서드를 수동으로 Bean으로 등록 하고싶은 클래스를 빈에 등록하기 위해 사용합니다.
@Component 같은 클래스 레벨 어노테이션과는 다르게, @Bean은 주로 특정 로직을 가진 메서드에서 사용하여 객체를 생성하고 반환할 때 쓰입니다.
이제는 그럼 마지막으로 프로그램부분에서 설정파일 경로를 읽어오는
ApplicationContext context = new ClassPathxmlApplicationContext("패키지경로.setting.xml");
이 부분을
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); 로 바꿔주시면 됩니다.
<출처: Yotube 뉴렉처 : https://www.youtube.com/@newlec1 >