티스토리 뷰

1. Java Configuration

XML File을 이용하여 Srping container를 설정하는 대신, java code만으로 Spring container를 설정 할 수 있다. 
-> 여기에는 XML 파일이 필요가 없다. 순수 자바 코드로 Spring container configuration을 진행 할 것이다.

(1) 3 ways of configuring spring container

1) full XMLconfig
2) XML component Scan
3) java configuration class -> no use of XML File.

1)과 2)는 이미 알아 보았다. 3)을 이번에 알아 볼 것이다.


(2) Development Process

1) Create java class and annotate as @Configuration
- 자바클래스를 만들고 @Configuration을 붙힌다.
2) add component scanning support: @ComponentScan(optional)
- @ComponentScan("package name")  ->@Component를 찾아서 bean으로 등록시켜줌.
3) Read Spring Java configuration class
 - AnnotationConfigApplicationContext 객체 생성을 통해 spring container 사용
4) Retrieve bean from Spring container

* 코드예시)

package com.luv2code.springdemo;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@ComponentScan("com.luv2code.springdemo")
public class SportConfig {

}
package com.luv2code.springdemo;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class JavaConfigDemoApp {

	public static void main(String[] args) {
		
		///read spring config java class
		AnnotationConfigApplicationContext context = 
				new AnnotationConfigApplicationContext(SportConfig.class);
		
		//get the bean from spring container
		Coach theCoach = context.getBean("tennisCoach", Coach.class);
	
		
		//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();
	}
}

 

위와 같이 xml을 연결하는 것이 아닌, SportConfig 클래스에 @Configuration을 붙히고 @ComponentScan("package이름")을 붙혀준다.

@ComponentScan("package이름")은 xml에 스캔을 위해 추가해주었던

<context:component-scan base-package="com.luv2code.springdemo"/>과 기능이 동일하다.

xml config file을 읽어들일때엔, App에서 ClassPathXmlApplicationContext객체를 사용하였는데, config Java class를 읽을때에는 AnnotationConfigApplicationContext객체를 사용하여 해당 클래스를 넘겨준다.

 

2. Defining Beans in Spring

Bean을 @Component를 통해서 자동으로 등록해 @ComponentScan으로 읽어오는 방법도 있지만 각각의 bean을 config file에 정의해주는 방법을 알아보겠다.

 

(1) Development Process

1) Define method to expose bean
 - @Bean method name이 bean id가 되는 것이다.
define each bean individually in this config class.
2) Inject bean dependencies

3) Read spring java configuration class

4) Retrieve bean from spring container

 

* 코드예시)

1) FortuneService implement받은 SadFortuneService 클래스를 만든다.

package com.luv2code.springdemo;

public class SadFortuneService implements FortuneService{

	@Override
	public String getFortune() {
		return "Today is a sad day";
	}
}

2) SwimCoach 클래스를 만든다. 그리고 FortuneService를 constructor injection 시킨다.

package com.luv2code.springdemo;

import org.springframework.beans.factory.annotation.Value;

public class SwimCoach implements Coach {
	
	private FortuneService fortuneService;

	//Constructor injection
	public SwimCoach(FortuneService theFortuneService) {
		fortuneService = theFortuneService;
	}
	
	@Override
	public String getDailyWorkout() {
		return "Swim 1000 meters as a warm up.";
	}

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

3) SportConfig에 SadFortuneService를 @Bean을 통해 등록시켜준다. (method이름이 bean ID가 된다)

package com.luv2code.springdemo;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
//@ComponentScan("com.luv2code.springdemo")
public class SportConfig {

	// define bean for our sad fortune service
	@Bean
	public FortuneService sadFortuneService() {
		return new SadFortuneService();
	}
	
	// define bean for our swim coach AND inject dependency
	@Bean
	public Coach swimCoach() {
		return new SwimCoach(sadFortuneService());
	}
}

Bean 이름이 method의 이름이므로 sadFortuneService, swimCoach가 각 bean의 이름이 되는 것이다.

Bean method를 부름으로서 spring이 intercept해서 actual bean에 reference를 주는 것이다.

 

4) SwimJavaConfigDemoApp에서 실행 똑같이 해주되, 직접 등록한 bean만 호출 하기 위해 SportConfig의 @ComponentScan을 없앤다. (위 코드 참고)

 

<@Bean이 백그라운드에서 작동되는 방식>

  @Bean 
  public Coach swimCoach() {   
   SwimCoach mySwimCoach = new SwimCoach();   
   return mySwimCoach; 
  }

예를들어 위 코드가 백그라운드에서 어떻게 작용되는지 살펴보자면,

Spring이 bean Component를 직접 생성한다. Scope를 설정해주지 않았으니, default로 singleton 방식으로 생성된다.

따라서 swimCoach bean을 호출 할 때마다, 같은 bean의 인스턴스를 갖게 된다.

한줄 한줄 살펴보자면,

@Bean은 Spring에게 bean component를 직접 만든다는 표시다. Scope에 대한 표시가 특별히 없으니 singleton으로 생성한다는 의미이다.

public Coach swimCoach() { 란 말은, bean id를 swimCoach로 명시하는 것이고, method의 return type이 Coach interface 타입이라는 말이다. 이것은 dependency injection시 Coach interface를 implement받는 것을 Spring이 찾을때 매우 유용하다. @Bean은 swimCoach bean의 request를 모두 가로막을것이고, 싱글톤이기에 bean의 같은 인스턴스를 request에 반환해 줄것이다.

SwimCoach mySwimCoach = new SwimCoach(); 는 SwimCoach의 새로운 인스턴스를 생성해주는 기능이고

return mySwimCoach는 생성된 인스턴스를 반환해준다.

따라서, 전체적으로 보자면, method가 @Bean annotation이 있기때문에, 모든 swimCoach()의 콜을 intercept, 가로막을 것이다. Scope가 명시되어 있지 않기에, 싱글톤으로, Spring container(applicationContext)의 메모리를 확인하여 이미 생bean이 생성되었는지 안되었는지 확인할 것이다.

만약, bean이 처음 생성되는것이면 method가 정상 실행된다. 또한 application context에 bean을 등록함으로서 bean이 이미 생성되었는지 안되었는지 알 수 있는것이다.

생성 된 이후에 method가 불려진다면, @Bean annotation이 가로막아 spring container의 메모리를 확인 할 것이고, 이미 bean이 생성된것을 확인 했기 때문에, 메모리의 인스턴스를 즉각적으로 반환할 것이다. 또한, method 안의 code를 실행 하지도 않을것이다(singleton이기 때문에).

따라서 위의  SwimCoach mySwimCoach = new SwimCoach(); return mySwimCoach는 초기 bean 생성되는 한번만 실행될것이다.

	// define bean for our sad fortune service
	@Bean
	public FortuneService sadFortuneService() {
		return new SadFortuneService();
	}
	
	// define bean for our swim coach AND inject dependency
	@Bean
	public Coach swimCoach() {
		return new SwimCoach(sadFortuneService());
	}

 

따라서 위의 코드를 살펴보자면, sadFortuneService로 @Bean을 spring이 보고 bean 생성시 SadFortuneService를 초기화 시킨다. 그리고 swimCoach bean을 생성시에도, SwimCoach 객체가 new 키워드에 의해 생성되고, sadFortuneService()를 생성자를 통해 넘겨주게 되는데, sadFortuneService()가 실행될때는 이미 bean이 생성되었기 때문에 sadFortuneService() method 안의 코드가 실행되지 않기때문에, 객체 자체, bean 자체가 넘어가 효율적으로 dependency injection이 일어나는 것이다.

 

* @Bean을 그러면 언제 사용하는가?

: You can use @Bean to make an existing third-party class available to your Spring framework application context.

 

예를 들어 Spring에서 알지 못하는 어떠한 외부 객체를 Spring application을 통해 사용하고 dependency injection을 @Autowired를 통해 넘기고 싶은데, 코드를 직접 건들일 수 없고 그 객체가 spring을 사용하지 않기에 @Component를 사용할 수 없을때, @Bean을 사용함으로서 외부 객체를 감싸 bean으로 등록 할 수 있는 것이다.

(**third-party란 제조사와 소비자를 연결해주는 제3자라고 볼 수 있다. 프로그래밍 개발과 개발자 사이의 프레임워크, 라이브러리, 플러그 인 등을 third-party 라고 볼수 있다. 제 3자로서 중간다리 역할을 하는 것을 third-party라고 한다.)

 

3. Injecting Values from properties File

(1) Development Process

1) properties File 생성

2) Spring config에 properties file을 로드시켜주기

3) Properties File로 부터의 value값을 참조하기(${name}으로)

 

* 코드예시)

1) Properties file을 다음과 같이 name=value 형태로 만든다.

foo.email=ssmm0205@naver.com
foo.team=Awesome Java Coders

2) @PropertySource를 통해서 properties file을 로딩시켜준다.(Java config file에 로딩시켜주기)

package com.luv2code.springdemo;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@PropertySource("classpath:sport.properties")
public class SportConfig {...

3) Value값을 넘겨주고 싶은 필드를 만들고, @Value("${name of property}")을 통해 name-value pair에 맞게 value를 가지고 온다. 그 값을 가져 올 수 있게 getter method 또한 다음과 같이 만든다.

package com.luv2code.springdemo;

import org.springframework.beans.factory.annotation.Value;

public class SwimCoach implements Coach {
	
	@Value("${foo.email}")
	private String email;
	
	@Value("${foo.team}")
	private String team;

	public String getEmail() {
		return email;
	}

	public String getTeam() {
		return team;
	}
}

4) App에서 getter method 호출을 통해 value값을 출력한다. 

public class SwimJavaConfigDemoApp {

	public static void main(String[] args) {
		...
		//get the bean from spring container
		SwimCoach theCoach = context.getBean("swimCoach", SwimCoach.class);
		
        ...
		
		//call our new swim coach methods... has the props values injected
		System.out.println("email: " + theCoach.getEmail());
		System.out.println("team: " + theCoach.getTeam());
		// close the context
		context.close();
	}
}

해당 value를 getter method로 가지고 오기 위해서, Coach가 아닌 SwimCoach로 가지고 오는 bean type을 변경해 주어야 getEmail()과 getTeam()에 접근 가능하다.

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