ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Camel][Spring] Annotation(어노테이션)을 이용한 의존 자동 연결
    Spring/개념 정리 2020. 4. 22. 22:28

    의존 자동 연결

    프로젝트의 규모가 작으면 그닥 상관 없을지도 모르지만, 프로젝트의 규모가 커지게 되면 스프링 빈 간의 의존 관계를 XML이나 Java 코드로 관리하는 데 상당한 시간이 소요될 수 있습니다. 또한 특정 타입의 빈 객체가 한 개 밖에 존재하지 않는 경우가 많아서 의존 객체가 너무 뻔할 때가 있습니다. 

     

     이런 경우 일일이 의존 관계를 설정할 필요 없이 자동으로 프로퍼티나 생성자 파라미터 값으로 동일 타입의 빈 객체를 전달해주는 기능이 있다면 코드를 간결하게 할 수 있습니다. 스프링에서는 이처럼 일일히 의존 정보를 설정하지 않아도 자동으로 스프링 빈 객체 간의 의존을 설정해주는 기능을 제공하고 있습니다. 

     

     이번 포스팅에서는 의존 자동 설정을 위해 어노테이션을 기반으로한 방법에 대해 설명하겠습니다. 

     

    1. 어노테이션 기반 의존 자동 연결을 위한 설정

     이어서 뒤에 설명할 어노테이션 기반 의존 자동 연결을 위해서는 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"
        	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:annotation-config />
        
        	...
        
    </beans>
    

     

    <context:annotation-config> 태그는 다수의 스프링 전처리기 빈을 등록해 주는 기능을 합니다. 만약 <context:annotation-config> 태그를 사용하지 않고자 한다면 아래의 코드처럼 각각의 전처리기를 일일이 등록해주는 방법도 있습니다. 하지만 전자의 경우가 일반적이긴 합니다. 

    <bean class = 
    	"org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />

     

    2. @Autowired 어노테이션을 이용한 의존 자동 설정

     @Autowired 어노테이션은 의존 관계를 자동으로 설정할 때 사용합니다. @Autowired 어노테이션은 생성자, 필트, 메소드의 세곳에 적용이 가능하며, 사용방법은 아래와 같습니다. 

    public class AnimalAutowired {
    
    	private AnimalFactory animalFactory;
        
        	@Autowired
        	public void setAnimalFactory( AnimalFactory animalFactory ) {
        		this.animalFactory = animalFactory;
        	}
        
        	...
        
    }

    스프링은 위처럼 @Autowired 어노테이션이 적용되어 있으면, 해당하는 타입의 스프링 빈 객체를 알맞게 연결해주게됩니다. 

    ---------------------------------------------------------------------------------------------------------------------------------- 

    <?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:annotation-config />
        
        	<bean id="animalAutowired" class="com.ex.AnimalAutowired">
            </bean>
            
            <bean id="aFactory" class="com.ex.AnimalFactory" factory-method="instance">
            	...
                	...
            </bean>
        
    </beans>
    

     위의 XML 설정을 보면 animalAutowired 빈은 animalFactory 프로퍼티의 값을 설정하고 있지 않습니다. 하지만 setAnimalFactory 메소드에 @Autowired 어노테이션이 적용되어 있기 때문에 스프링은 동일한 타입을 갖는 aFactory 빈을 animalFactory 프로퍼티의 값으로 사용합니다. 

     

    위의 프로퍼티 설정 메소드의 경우뿐만 아니라 임의의 메소드에도 @Autowired 어노테이션을 적용시킬 수 있습니다. 

     

    init 메소드에 @Autowired 어노테이션 적용

    public class AnimalAutowired {
    
    	private AnimalFactory animalFactory;
        	private AnotherFactory anotherFactory;
            
        	@Autowired
        	public void init( AnimalFactory animalFactory, AnotherFactory anotherFactory ) {
        		this.animalFactory = animalFactory;
                	this.anotherFactory = anotherFactory
        	}
        
        	...
        
    }

    생성자에 @Autowired 어노테이션 적용

    public class AnimalAutowired {
    
    	private AnimalFactory animalFactory;
        	private AnotherFactory anotherFactory;
            
        	@Autowired
        	public AnimalAutowired ( AnimalFactory animalFactory, AnotherFactory anotherFactory ) {
        		this.animalFactory = animalFactory;
                	this.anotherFactory = anotherFactory
        	}
        
        	...
        
    }

    필드에 @Autowired 어노테이션 적용

    public class AnimalAutowired {
    	
        	@Autowired
    	private AnimalFactory animalFactory;
        	@Autowired
    	private AnotherFactory anotherFactory;
     
        	...
        
    }

    단, 마지막 예시에서처럼 @Autowired 어노테이션을 필드에 적용하면, 의존 객체를 전달받기 위한 메소드를 추가하지 않아도 됩니다. 하지만 스프링 이외의 환경에서 사용될 수도 있기 때문에 의존 객체를 전달받기 위한 메소드나 생성자는 남겨두는 것이 좋습니다.

     

     

    2-1. @Autowired 어노테이션 적용 프로퍼티의 필수 여부 지정

    스프링은 @Autowired 어노테이션을 발견하게되면 @Autowired에 해당하는 스프링 빈 객체를 찾아서 설정합니다. 하지만 @Autowired 어노테이션에 해당하는 타입의 빈 객체가 존재하지 않는다면 스프링은 컨테이너를 초기화하는 과정에서 Exception을 발생시키게 됩니다. 

     

    만약 @Autowired 어노테이션을 적용했지만 스프링 빈이 존재하지 않는 경우에 Exception을 발생 시키고 싶지 않고 단순히 null 값으로 유지하고 싶은 경우라면, @Autowired 어노테이션의 required 속성 값을 false로 지정해주면 됩니다.

    public class AnimalAutowired {
    	
        	@Autowired(required=false)
    	private AnimalFactory animalFactory;
        	@Autowired
    	private AnotherFactory anotherFactory;
     
        	...
        
    }

     

    2-2. @Qualifier 어노테이션을 이용한 자동 설정 제한

    <bean id="camelFactory" class="com.ex.AnimalFactory">
            ...
    </bean>
    
    <bean id="pigFactory" class="com.ex.AnimalFactory">
            ...
    </bean>
    
    <bean id="animalAutowired" class="com.ex.AnimalAutowired">
    </bean>
    public class AnimalAutowired {
    
    	private AnimalFactory animalFactory;
        
        	@Autowired
        	public void setAnimalFactory( AnimalFactory animalFactory ) {
        		this.animalFactory = animalFactory;
        	}
        
        	...
        
    }

    위의 코드에서는 동일한 타입의 빈 객체를 두개 이상 정의했고, AnimalAutowired는 위의 Java코드처럼 AnimalFactory 타입의 프로터피 설정 메소드인 setAnimalFactory 메소드에 @Autuwired 어노테이션을 적용했습니다. 

     

     이때  AnimalFactory 타입의 빈이 두 개가 존재하게 되면서, 스프링은 setAnimalFactory 메소드에 어떤 빈 객체를 주입해야 하는지 알 수가 없습니다. 그렇기 때문에 초기화 과정에서 Exception을 발생 시키게 됩니다. 이는 즉, @Autowired 어노테이션을 사용한다면, @Autowired 어노테이션의 적용 대상이 되는 빈은 단 한개여야 한다는 것입니다. 

     

    하지만 위와 같은 경우를 해결할 수 있는 방법이 있습니다. 그것은 바로 @Qualifier 어노테이션을 함께 사용하는 것입니다. XML에서는 <qualifier> 태그를 사용해서 한정자를 지정할 수 있습니다. 그리고 Java 코드에서는 @Qualifier 어노테이션을 사용해서 한정자를 지정할 수 있습니다. 

    <bean id="camelFactory" class="com.ex.AnimalFactory">
    	<qualifier value="camel"/>
            ...
    </bean>
    
    <bean id="pigFactory" class="com.ex.AnimalFactory">
         	<qualifier value="pig"/>   
            ...
    </bean>
    
    <bean id="animalAutowired" class="com.ex.AnimalAutowired">
    </bean>

     

    아래의 코드에서 스프링은 animalFactory 필드에 camel 한정자를 갖는 스프링 빈을 할당하게 됩니다. 

    public class AnimalAutowired {
    	
        	@Autowired
        	@Qualifier("camel")
    	private AnimalFactory animalFactory;
        
        	private AnotherFactory anotherFactory;
        
        	...
        
    }

     

    만약 @Autowired 어노테이션이 적용된 메소드나 생성자가 두 개 이상의 파라미터를 갖는다면 아래와 같이 파라미터에 @Qualifier 어노테이션을 적용할 수 있습니다.

    public class AnimalAutowired {
    	    
    	private AnimalFactory animalFactory;
        
        	private AnotherFactory anotherFactory;
     		
            @Autowired
        	public AnimalAutowired ( @Qualifier("camel")AnimalFactory animalFactory, AnotherFactory anotherFactory ) {
        		this.animalFactory = animalFactory;
                	this.anotherFactory = anotherFactory
        	}
        
        	...
        
    }

     

     

    3. @Resource 어노테이션을 이용한 의존 자동 설정

    의존 자동 설정에 관련된 어노테이션으로 @Autowired 어노테이션만이 있는 것은 아닙니다. 또 다른 의존 설정과 관련된 어노테이션인 @Resource 어노테이션이 적용되어 있으면 알맞은 빈 객체를 할당할 수 있습니다.

     

    @Autowired 어노테이션과의 차이점을 설명하자면, @Autowired 어노테이션은 타입을 기준으로 빈 객체를 선택하는 반면에 @Resource 어노테이션은 이름을 기준으로 빈 객체를 선택합니다.

     

    @Resource 어노테이션의 사용방법은 아래와 같습니다. 

    <bean id="camelFactory" class="com.ex.AnimalFactory">
            ...
    </bean>
    
    <bean id="pigFactory" class="com.ex.AnimalFactory">
            ...
    </bean>
    
    <bean id="animalAutowired" class="com.ex.AnimalAutowired">
    </bean>
    public class AnimalAutowired {
    
    	private AnimalFactory animalFactory;
        
        	@Resource(name="camelFactory")
        	public void setAnimalFactory( AnimalFactory animalFactory ) {
        		this.animalFactory = animalFactory;
        	}
        
        	...
        
    }

    @Resource 어노테이션의 name 속성은 사용할 스프링 빈 객체의 이름을 갖습니다. 위의 경우 스프링은 setAnimalFactory 메소드에 이름이 camelFactory인 빈 객체를 전달하게됩니다. 

     

    그리고 만약 지정한 이름을 갖는 빈 객체가 존재하지 않는다면, 스프링은 Exception을 발생 시킵니다.

     

    추가적으로, name 속성의 값을 지정하지 않을 경우 필드 이름이나 프로퍼티 이름을 사용하게됩니다. 

     

     

    4. @Inject, @Named 어노테이션을 이용한 의존 자동 설정

    @Inject 어노테이션은 이름에서 알 수 있듯이 스프링의 DI를 목적으로 만들어진 어노테이션입니다. 이외에도 @Named 어노테이션 역시 의존 자동 설정을 지원합니다. 

     

    @Inject 어노테이션을 사용하기 위해서는 @Inject 어노테이션을 포함한 jar 파일을 개발환경에 추가해주어야 하며, 메이븐 의존을 사용할 결우 아래와 같은 의존 설정을 추가해주어야 합니다.

    <dependency>
    	<groupId>javax.inject</groupId>
        <artifactId>javax.inject</artifactId>
        <version>1</version>
    </dependency>

    @Inject 어노테이션을 사용하면 @Autowired 어노테이션과 마찬가지로 필드, 메소드, 생성자에 적용할수 있습니다. 

     

    @Inject 어노테이션과 @Autowired 어노테이션의 차이점으로는 @Autowired 어노테이션은 required 속성을 이용해서 필수 여부를 지정할 수 있는 반면, @Inject 어노테이션은 반드시 사용할 빈이 존재해야한다는 점입니다. 

     

    @Named 어노테이션은 자동 설정 대상이 두 개 이상일 경우 특정한 빈을 선택할 목적으로 사용된다는 점에서 @Qualifier 어노테이션과 유사한 특징을 가지고 있습니다. 이 둘의 차이가 있다면 @Named 어노테이션은 사용할 빈의 이름을 지정한다는 것입니다. 

    public class AnimalAutowired {
    	    
    	private AnimalFactory animalFactory;
        
        	private AnotherFactory anotherFactory;
     		
            @Inject
        	public AnimalAutowired ( @Named("camelFactory")AnimalFactory animalFactory, AnotherFactory anotherFactory ) {
        		this.animalFactory = animalFactory;
                	this.anotherFactory = anotherFactory
        	}
        
        	...
        
    }

     

     

    5. @Configuration 어노테이션과 의존 설정

    @Configuration 어노테이션을 사용할 경우, 여러 클래스에 빈 정보를 나눠서 설정할 수 있습니다. 

    @Configuration
    public Config2 {
    	@Bean
        	public Camel camel1() { ... }
        
        	@Bean(name="camel2")
        	public Camel camel2() { ... }
        
        	@Bean
        	public CamelRepository camelRepository() { 
        		CamelRepository cRepo = new CamelRepository();
            	cRepo.setAnimals(Arrays.asList(camel1(),camel2()));
            	return cRepo;
        	}
    }
    
    @Configuration
    public Config1 {
    	@Autowired
        	private CamelRepository camelRepository;
        
        	@Bean
        	public PasswordChangeService pwcs() {
        		return new PasswordChangeService(camelRepository);
        	}
        
        	@Bean
        	public AuthFailLogger authFailLogger() { ... }
        
        	@Bean
        	public AuthenticationService as() {
        		AuthenticationService authS = new AuthenticationService();
            	authS.setFailLogger(authFailLogger());
            	authS.setCamelRepository(camelRepository);
            	return authS;
        	}
    }

    위의 코드에서처럼 다른 @Configuration 클래스에 정의한 빈을 참조해야 할 때는 앞서 설명한 @Autowired 어노테이션이나 @Resource 어노테이션 등의 어노테이션을 사용해야합니다. 이러한 어노테이션을 사용하지 않는다면 다른 @Configuration 클래스에 정의된 @Bean의 경우는 사용할 수 없게됩니다. 

    댓글

Camel`s Tistory.