티스토리 뷰

1. Spring Annotation - Inversion of Control

1) Java Annotation이란 ?

간단히 말하면 class에 대한 meta-data라 보면 된다.

  • special labels / markers added to java classes
  • provide meta-data about the class
  • processed at compile time or run-time for special processing
* Meta-data(메타데이터)란? (출처 : wikipedia)
데이터에 관한 구조화된 데이터로 다른 데이터를 설명해 주는 데이터이다.
대량의 정보 가운데에서 찾고 있는 정보를 효율적으로 찾아내서 이용하기 위해 일정한 규칙에 따라 콘텐츠에 대하여 부여되는 데이터이다.
어떤 데이터 즉 구조화된 정보를 분석, 분류하고 부가적 정보를 추가하기 위해 그 데이터 뒤에 함께 따라가는 정보를 말한다.

예를들어 @override 또한 annotation 중 하나인데, compilation time에 처리된다.

컴파일러에게 이 메소드가 우리는 compliant이고 부모클래스나 인터페이스의 method를 override할 것이라고 알려주는것. -> 그리고 compilation time에 complie하면서 클래스를 체크하고 실제로 method를 override하는지 확인한다.

 

2) Annotation으로 spring configuration을 하는 이유

xml file로 configuration을 하면 매우 큰 프로젝트에 있어서 너무 장황해진다.

예를들어 30개의 bean, 100개의 bean이 있는 스프링 프로젝트에서, 모든 bean의 리스트들을 config file에 적어야 한다.

이 과정을 annotation을 사용함 으로서 xml configuration을 최소화 시켜준다.

(상자에 도착지를 적어주는 것이라 생각하면된다. 이 클래스는 어디에 쓰일것이다 어디에 쓰일것이다..)

 

3) @Component

Spring이 특별한 annotation이 붙어 있는 나의 java 클래스들을 scan할것이고, 찾으면 자동으로 spring container에서 bean을 등록해 줄 것이다.

 

Development Process

1. Enable component scanning in spring config file.

2. add @Component annotation to your java classes.

3. retrieve bean from spring container.

 

사용방법

클래스 이름위에 @Component 또는 @Component("bean ID")를 적어준다.

특정 bean ID를 정해주지 않으면 자동생성되는 default bean ID는 클래스 이름에서 맨 첫번째 글자를 소문자로 바꾼것이다. 만약 URL처럼 두번째 글자또한 대문자이면, 클래스 이름 그대로가 default bean ID이다.

e.g) Class name :  TennisCoach -> Default bean ID : tennisCoach

 

* 코드 예시)

<!-- add entry to enable component scanning  -->
    <context:component-scan base-package="com.luv2code.springdemo"/>
package com.luv2code.springdemo;

import org.springframework.stereotype.Component;

@Component
public class TennisCoach implements Coach {

	@Override
	public String getDailyWorkout() {
		return "Practice your backhand volley";
	}

	@Override
	public String getDailyFortune() {
		return fortuneService.getFortune();
	}

}
package com.luv2code.springdemo;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AnnotationDemoApp {

	public static void main(String[] args) {
		
		///read spring config file
		ClassPathXmlApplicationContext context = 
				new ClassPathXmlApplicationContext("applicationContext.xml");
		
		//get the bean from spring container
		Coach theCoach = context.getBean("tennisCoach", Coach.class);
		//thatSillyCoach(explicit bean id)-> tennisCoach (default bean id)
		
		//call a method on the bean
		System.out.println(theCoach.getDailyWorkout());
		
		//call method to get the daily fortune 
		System.out.println(theCoach.getDailyFortune());
		
		// close the context
		context.close();
	}
}

* 기존 코드)

<bean id="myCoach" 
    class="com.luv2code.springdemo.TennisCoach">
</bean>

위 코드에서 확인 할 수 있듯, 

 <context:component-scan base-package="com.luv2code.springdemo"/>로 component를 Spring이 스캔할 패키지만 xml에 파일에 등록하여 주면, bean을 spring이 뒤에서 자동으로 등록시켜 주는것을 알 수 있다.

 

2. Spring Annotation - Dependency Injection

지난 xml을 통한 dependency injection을 했을때와 마찬가지로, Coach interface는 dailyFortuneService()를 제공하기 위해 새로운 helper인 fortuneService 인터페이스를 만든다(이것이 dependency)

 

(1) Spring autowiring이란?

Dependency injection을 위해서, Spring은 autowiring을 사용할 수 있는데, Spring이 해당 property에 맞는 클래스를 찾아서 자동으로 injection을 시켜주는 기능이다. 따라서 auto wired되는것이다.

xml 코드를 통해서 bean을 만들고 setter나 constructor injection 통해서 직접 property에 ref로 해당 bean을 넘겨주었어야 했다. 하지만 @Autowired annotation을 통해서 spring이 자동으로 dependency를 찾아서 injection 시켜준다.

 

1) Autowiring 예시

① Coach implementation에 FortuneService interface를 inject 시킬 것이다.

② 그러면 Spring이 @Component를 스캔할 것이다.

③ Spring이 FortuneService interface를 implement 하고있는것을 찾는다.

④ 있다면 그것을 찾아서 inject 시킨다.

 

2) Autowiring Injection Types

① Constructor Injection

② Setter Injection

Field Injection

 

(2) Constructor Injection Development Process

① Define the dependency interface and class

   (FortuneService interface 만들고 HappyFortuneService를 상속받는 클래스로 만든다음 @Component 기입)

② Create constructor in your class for injection

   (TennisCoach에 Constructor통해서 dependency를 넘긴다.)

③ Configure the dependency injection with @Autowired Annotation

   (constructor위에 @Autowired라고 적는다. 이것이 핵심)

-> 알아서 spring이 dependency 찾아서 주입도 해주고 @Component를 찾아서 자동으로 bean도 생성해 주기 때문에 조립할 필요가 없어진다.

 * 코드 예시)

	private FortuneService fortuneService;
    
    // constructor injection
    @Autowired
	public TennisCoach(FortuneService thefortuneService) {
		fortuneService = thefortuneService;
	}

 

TennisCoach 클래스에 다음과 같이 injection 시켜줄 인터페이스를 가지고와서 constructor를 통해 parameter로넘겨주는것은 xml 파일과 동일한데, 따로 xml 파일에 bean을 등록하고 ref로 injection시켜줄 필요없이 @Autowired를 적어주면 FortuneService 인터페이스를 implement 받고 있는 객체를 자동으로 등록시켜 준다. 

implement 받고 있는 객체가 여러개일때는 app 실행시 NoUniqueBeanDefinitionException이라는 예외가 발생하며 @Component로 bean에 등록되어 연결 될 수 있는 bean list 또한 보여준다.

이럴때는 @Qualifier("bean name")을 통해 다음과 같이 문제를 해결 할 수 있다.

	private FortuneService fortuneService;
    
    // constructor injection
    @Autowired
	public TennisCoach(@Qualifier("happyFortuneService")FortuneService thefortuneService) {
		fortuneService = thefortuneService;
	}

(3) Setter Injection Development Process

Create setter method in your class for injections

② Configure the dependency injection with @Autowired Annotation

 

* Setter method 뿐만 아니라 다른 어떠한 method라도 주입가능하다. @Autowired만 적어 주면 된다.

* 코드 예시)

	private FortuneService fortuneService;
    
	//define a setter method
	@Autowired
	@Qualifier("happyFortuneService")
	public void doSomeCrazyStuff(FortuneService fortuneService) {
		System.out.println(">> TennisCoach : inside doSomeCrazyStuff() method");
		this.fortuneService = fortuneService;
	}
	

위 코드는 setsetFortuneService를 doSomeCrazyStuff로 이름을 바꾸어 준것인데, 볼수 있듯이 메소드 이름은 중요 하지 않고, Autowired로 통해 Spring이 자동으로 injection 시켜주는것을 확인할 수 있다.

마찬가지로 FortuneService를 implement받는 bean이 여러개일 때, @Qualifier("bean name")을 위에 써줌으로서 특정 bean을 injection 시킬 수 있다.

 

(4) Field Injection Development Process

① Configure the dependency injection with Autowired annotation

- applied directly to the field
- no need for setter methods

 

1) Field Injection이란 :

dependencies를 class의 필드 값에 세팅해줌으로서 직접적으로 inject시키는 방법(심지어 private 필드도 가능하다)

Java reflection이라는 기술을 사용해 behind the scene에서 일어난다.

FortunService field를 setter method나 constructor, method로 injection을 하지 않고 필드 그 자체로 주입시킬수 있다.

* 코드 예시)

	@Autowired
	@Qualifier("happyFortuneService")
	private FortuneService fortuneService;

간단하게 이렇게 필드 명위에 @Autowired를 써줘도 Java reflection이라는 방법때문에 넘어간다.

또한 @Qualifier("bean name")을 통해서 어떤 bean을 injection시킬지 특정 지을 수 있다.

 

이렇게 @Autowired annotation을 통해 dependency injection을 할 수 있는 세 가지 방법을 알아보았다.

그럼 어떤 걸 써야 할까?

-> 어떤게 더 낫다고 할 수는 없다. 그냥 편한대로 쓰되, 프로젝트에서 일관성있게 쓰면 된다고 한다.

 

최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday