Tools/Spring

ApplicationContext-2 (Autowired)

칼쵸쵸 2020. 8. 6. 23:39

Autowired : 스프링의 의존성 주입 어노테이션 , 등록되어 있는 빈들을 명시된 객체에 주입한다. (생성자, 클래스 필드)

 

기본적인 사용법 - 1 (클래스 변수 주입)

package main;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class repo2{
	
	@Autowired
	repo1 rp1;
	
	public repo2(repo1 rp1)
	{
		this.rp1=rp1;
	}
}

클래스 변수에 Autowired를 통한 의존성 주입 방식

 

기본적인 사용법 - 2 (생성자를 통한 주입)

package main;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class repo2{

	repo1 rp1;
	@Autowired
	public repo2(repo1 rp1)
	{
		this.rp1=rp1;
	}
}

 자바의 객체 의존관계는 인터페이스를 활용한 수정자 주입이 가장 활용도가 높다.

테스트코드를 작성하거나 새로운 객체를 기존객체에 주입하려고 할 때 인터페이스 형태로 객체를 만들고 이를 Setter로 주입하면 이를 통해 보다 유연하게 객체 주입이 가능해 진다.

그러나 스프링의 Autowired를 사용한다면 Bean으로 등록시에는 주입하여야 할 객체가 아직 주입되지 않았으므로 해당 객체가 사용될때 Setter가 지정한 객체를 찾지 못해 NullPointException이 발생할 가능성이 있다. 

따라서 생성자를 통한 의존성 주입이 가장 적절하다. 이를 통해

1. 느슨한 결합이 가능하다.

2. 객체 생성에 의존관계를 강제하여 의존성 주입에 오류가 발생시 컴파일 타임에서 잡아 낼 수 있게 된다.

 

주입될 객체가 Bean으로 등록이 안된경우

package main;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class repo2{

	repo1 rp1;
	@Autowired(required = false)
	public repo2(repo1 rp1)
	{
		this.rp1=rp1;
	}
}

 위 코드에서 만약 repo1이라는 객체가 실제로 존재하지 않을 경우 컴파일 타임에서 오류가 발생한다.

존재 할 수도 있고 없을 수도 있는 위와 같은 경우에는 Autowired 뒤에 required=false를 통해 없을 경우에도 동작하도록 할 수 있다.

 

인터페이스를 통한 주입 ( 인터페이스를 implements한 객체가 여러개 일 경우)

Repo1

@Component
public class Repo1 implements Repo{
	
	public Repo3 giveA(Repo3 repo)
	{
		return repo;
	}
}

 

Repo3

package main;

import org.springframework.stereotype.Component;
@Component
public class Repo3 implements Repo{

}

 

Repo2

package main;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class Repo2{

	Repo rp1;
	@Autowired
	public Repo2(Repo rp1)
	{
		this.rp1=rp1;
	}
}

 

위와 같이 Repo2가 Repo 인터페이스를 implements한 객체가 2개 이상이고 객체들이 Bean으로 등록되어 있는 경우 의존성을 주입할 객체를 정하지 못하고 Parameter 0 of constructor in main.Repo2 required a single bean, but 2 were found 이라는 오류가 발생한다.

 

package main;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

@Component @Primary
public class Repo1 implements Repo{
	
	public Repo3 giveA(Repo3 repo)
	{
		return repo;
	}
}

이를 해결하기 위해 Repo1을 다음과 같이 @Primary 어노테이션을 붙여 주면 Repo1과 Repo3중 Repo1을 우선적으로 주입한다.

 

이때 Primary를 사용하지 않고 원하는 타입의 객체를 주입하려고 한다면 다음과 같이한다.

package main;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class Repo2{
	
	@Autowired @Qualifier("Repo1")
	Repo rp1;
	
	public Repo2(Repo rp1)
	{
		this.rp1=rp1;
	}
}

@Qualifier 어노테이션을 활용하여 원하는 객체의 이름을 정해주면 해당 객체를 주입하게 된다.

이때 주의해야할 점은 Qaulifier 어노테이션은 생성자를 통한 주입이 불가하고 필드 주입만 가능하다.

 

한가지 객체만 필요한 것이 아닌 인터페이스를 implements한 모든 빈들을 사용할 경우 다음과 같이 주입이 가능하다.

package main;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class Repo2{
	
	@Autowired
	List<Repo> repolist;
	
}

List의 형태로 저장하여 Repo로 저장될 수 있는 모든 빈들을 담을 수도 있다.

 

BeanPostProcessor

Bean들은 다음과 같은 라이프 사이클을 따르게 된다.

  1. BeanNameAware's setBeanName
  2. BeanClassLoaderAware's setBeanClassLoader
  3. BeanFactoryAware's setBeanFactory
  4. EnvironmentAware's setEnvironment
  5. EmbeddedValueResolverAware's setEmbeddedValueResolver
  6. ResourceLoaderAware's setResourceLoader (only applicable when running in an application context)
  7. ApplicationEventPublisherAware's setApplicationEventPublisher (only applicable when running in an application context)
  8. MessageSourceAware's setMessageSource (only applicable when running in an application context)
  9. ApplicationContextAware's setApplicationContext (only applicable when running in an application context)
  10. ServletContextAware's setServletContext (only applicable when running in a web application context)
  11. postProcessBeforeInitialization methods of BeanPostProcessors
  12. InitializingBean's afterPropertiesSet
  13. a custom init-method definition
  14. postProcessAfterInitialization methods of BeanPostProcessors

Bean으로 등록될 객체가 InitializingBean을 implements 하면 afterPropertiesSet 메서드를 override 하게 되고 이 메서드를 통해 객체가 Bean으로 등록되고 initailizing된 직후에 동작을 지정할수 있게 된다.

package main;

import java.util.List;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class Repo2 implements InitializingBean{
	
	@Autowired
	List<Repo> repolist;

	@Override
	public void afterPropertiesSet() throws Exception {
		// TODO Auto-generated method stub
		
	}
	
}

이러한 동작을 간단히 해주는 어노테이션이 @Postconstruct 이며 이는 메서드 위에 붙여 사용한다.

package main;

import java.util.List;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class Repo2 {
	
	@Autowired
	List<Repo> repolist;

	@PostConstruct
	public void domorething()
	{
		
	}
	
}

 

이때 Bean들이 이니셜라이징 하기 이전과 이후에 BeanPostProcessors라는 인터페이스에 postProcessBeforeInitialization, postProcessAfterInitialization 라는 메서드로 감싸지게 되는데

 

@Autowired 어노테이션이 붙으면 동작하는 원리는 Autowired 어노테이션의 동작을 처리하는AutowiredAnnotationBeanPostProcessor BeanPostProcessors 인터페이스를 구현하고 여기에 들어있던  postProcessBeforeInitialization 메서드를 overriding하여 Bean을 주입하게 된다.

 

이때의 동작을 정리하면

1. BeanFactory (applicationContext)가 BeanPostProcessors를 구현한 객체들을 찾는다.

2. BeanPostProcessors 그 중 AutowiredAnnotationBeanPostProcessor 를 찾는다

3. AutowiredAnnotationBeanPostProcessor 를 통해 @Autowired 어노테이션의 로직을 처리한다.

 

'Tools > Spring' 카테고리의 다른 글

스프링 AOP 적용 예제  (0) 2021.02.28
스프링 AOP 기본 동작 원리  (0) 2021.02.27
Component와 Component Scan  (0) 2020.08.09
ApplicationContext-1 (Bean을 등록하는 방법)  (0) 2020.08.04
Spring Ioc 컨테이너와 Bean  (0) 2020.08.04