Spring

[Spring] 예제로 보는 Annotation-based Container Configuration

KEMON 2020. 5. 12. 00:57
728x90

Are annotations better than XML for configuring Spring?

위의 질문은 Spring 공식 문서에서 해당 파트에서 가장 처음에 등장하는 질문이다.

과연 어노테이션으로 설정하는 것이 XML로 설정하는 것 보다 더 좋은 방법일까??

 

① XML 설정 방식의 장점 

  • XML만 보고도 스프링의 설정을 한눈에 확인 가능
  • 설정과 소스코드의 분리 가능 -> POJO(Plain Old Java Object)

② XML 설정 방식의 단점

  • 'XML 헬' 이라고 불릴 정도로 XML이 너무 커진다.
  • 방대해진 XML을 관리하기 힘들어진다.

위의 XML 단점을 보면 어노테이션이 더 나은 방법이 아닌가? 라는 생각이 든다.

하지만!

어노테이션을 사용하면 소스코드와 설정의 분리가 애매해진다. -> 완벽한 POJO가 아님 

 

따라서 상황에 따라 다르다.

 

1. @Required

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Required
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}

위의 예제 코드를 보면 Required는 '필수'라는 뜻 그대로 해당 코드에서는 movieFinder 값이 들어오지 않으면 예외를 발생시킨다.

@Required 는 Spring Framework 5.1부터 사용되지 않으므로 대충 넘어가자.

 

2. @Autowired

빈과 빈 사이의 관계에서 스프링 컨테이너가 자동으로 관계를 연결시켜줌

빈의 이름 or 타입  여러 정보로 연결시켜준다.

public class A {
    private B b;
    //@Autowired(required=false)
    @Autowired
    public A(B b){
        this.b = b;
    }

    @Autowired
    public void setB(B b) {
        this.b = b;
    }
}

위의 예제로 보면 생성자주입Setter주입 방식으로 주입을 받는다.

Debuging 모드로 보면 모두 B객체를 주입받는 것을 확인할 수 있다.

@Autowired 할 때 빈이 하나 없으면 프로그램이 종료된다.

이렇게 프로그램 종료되는 것을 막기 위해 @Autowired(required = false) 로 해준다

(required = true)가 default 임. 

사용되지는 않지만 가끔 의도에 의해서 사용 

 

※Spring이 버전이 올라가면서 생성자 주입의 경우 @Autowired를 쓰지 않아도 자동으로 주입된다.

public class A {
    @Autowired private B b;

    @PostConstruct
    void init(){
        log.info(""+b);
	}
}

위의 예제와 같이 필드에도 어노테이션을 붙일 수 있다.

@PostConstruct 는 뒤에서 확인할 예정이다. 미리 대충 설명하자면 어노테이션의 라이프사이클 관련된 어노테이션이다.

@PostConstruct를 이용해서 필드의 어노테이션이 잘 되는지 확인할 수 있다.

 


public class A_applicationContextAware implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public void init(){
        log.error("applicationContext::::"+applicationContext);
    }
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

또한 위의 예제 코드처럼

특정 bean에서 applicationContext 사용할 경우 ApplicationContextAware 인터페이스를 implements하고 setApplicationContext을 구현하는 대신 

public class A_applicationContextAware implements ApplicationContextAware {

    @Autowired
    private ApplicationContext applicationContext;

    public void init(){
        log.error("applicationContext::::"+applicationContext);
    }
}

다음과 같이 @Autowired를 통해 코드를 간단히 할 수 있다.

 

3. @Primary

@Configuration
public class AppConfig {
    @Bean
    @Primary
    public B b1(){
        return new B();
    }

    @Bean
    public B b2(){
        return new B();
    }
}

@Configuration : 빈 설정으로 등록이 되면서 동시에 이것은 스프링 설정에 관련있는 빈이다라는것을 의미 해준다

@Bean : xml에서 빈을 설정하는것 대신 자바에서 어노테이션으로 빈을 설정

@Primary

위에서 같은 타입의 Bean이 있어서 다른 곳에서 해당 Bean을 주입할 때 어떤것을 주입해야 할 지 모르기 때문에 

어떤걸 먼저 주입할지 스프링에게 알려주는 어노테이션

(xml에서는 primary=true설정)

 

4. @Qualifier

@Configuration
public class AppConfig {
    @Bean
    @Qualifier("b1")
    public B b1(){
        return new B();
    }

    @Bean
    @Qualifier("b2")
    public B b2(){
        return new B();
    }
}
public class A {
    @Autowired 
    @Qualifier("b2") 
    private B b;

    @PostConstruct
    void init(){
        log.info(""+b);
	}
}

@primary 비슷하게 내가 원하는 빈을 주입 사용한다.

위의 예제에서는 b2를 주입받고 있다.

5. @Resource

@Configuration
public class AppConfig {
    @Bean
    @Qualifier("b1")
    public B appB1(){
        return new B();
    }

    @Bean
    @Qualifier("b2")
    public B appB2(){
        return new B();
    }
}
public class A {
    @Resource(name="appB1") 
    private B b;

    @PostConstruct
    void init(){
        log.info(""+b);
	}
}

Autowired와 똑같은 효과 차이점 = > name이라는 파라미터를 가지고 

함수의 이름을 넣어주면 해당 빈을 설정한다. (@autowired + @qualifier 느낌)

 

6. @Value

어플리케이션에서 xml 말고 application.properties 파일을 만들어서 그안의 properties 파일을 읽어드린다.

 

보통 main인 경우 java, resources 폴더에 application.properties 잡힌다. 설정을 

resources에서 하므로 거기에다가 application.properties파일을 만들어서 사용

catalog.name = MovieCatalog

application.properties 설정 (properties 파일은 key = value 이런 식으로 사용된다)

public class A {
    @Value("${catalog.name}") String catalogName;
}
public class A {
    @Value("${catalog.name}") String catalogName;
    @Value("${catalog.aljksdhfljk}") String test;

    @PostConstruct
    void init(){
        log.info(""+catalogName); //출력 값 : MovieCatalog
        log.info(""+test); //출력 값 : ${catalog.aljksdhfljk}
	}
}

※여기서 주의해야할 사항이 있다!!

위의 예제에서 String test의 @Value값을 확인해보자.

@Value() 파라미터에 제대로 된 key값을 넣지 않으면 null이 아니라 파라미터값이 들어가게된다. 

728x90