Spring DI, AOP

5. Spring 애너테이션 - 패스트캠퍼스 백엔드 부트캠프 3기

gkss2tpt 2025. 2. 24. 12:43

1. @ComponentScan과 @Component

  • @ConponentScan으로 @Component를 자동 검색해서 빈으로 등록
  • @Configuration이 붙은 자바 설정 클래스에 붙일 수 있다.
@Configuration
// @ComponentScan("com.fastcampus.ch3") - 패키지 지정
// @ComponentScan(basePackage = {"com.fastcampus.ch3"}) - 패키지 지정(다중)
// @ComponentScan(basePackageClasses = AppConfig.class) - 클래스 지정
@ConponentSCan	// 이 애너테이션이 붙은 클래스의 패키지를 자동스캔
public class AppConfig {
    ...
}
// @Bean(name="superEngine") Engine superEngine() { return new SuperEngine(); }
// @Component("superEngine")
@Component
class SuperEngine extends Engine {}
  • @Controller, @Service, @Repository, @ControllerAdvice의 메타 에너테이션
@Configuration
public class AppConfig {
//    @Bean
//    Car car(){
//        return new Car();
//    }
//
//    @Bean
//    @Scope("prototype")
//    Engine engine(){
//        return new Engine();
//    }
//
//    @Bean
//    Door door(){
//        return new Door();
//    }
}
  • 등록된 Bean들을 전부 주석처리 후 Main을 실행해 보면...

  • Bean을 찾을 수 없다고 나오는데...
public class Main {
    public static void main(String[] args) {
        // AC를 생성 - AC의 설정파일은 AppConfig.class 자바설정
        ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
//        Car car = (Car)ac.getBean("car"); // byName 객체(빈)을 조회
//        Car car = ac.getBean("car", Car.class); // 위와 동일
//        System.out.println("car = " + car);
//        Engine engine = ac.getBean(Engine.class);
//        Engine engine2 = ac.getBean(Engine.class);
//        Engine engine3 = ac.getBean(Engine.class);
//        System.out.println("engine = " + engine);
//        System.out.println("engine2 = " + engine2);
//        System.out.println("engine3 = " + engine3);

        // Bean이 몇개나 등록되어있는가
        System.out.println("ac.getBeanDefinitionCount() = " + ac.getBeanDefinitionCount());
        // 등록된 Bean의 이름
        System.out.println("ac.getBeanDefinitionNames() = " + Arrays.toString(ac.getBeanDefinitionNames()));
//        // engine이라는 BeanDefinition이 있는지
//        System.out.println("ac.containsBeanDefinition(\"engine\") = " + ac.containsBeanDefinition("engine"));
//        // Bean 객체 Car의 @Scope가 Singleton인지아닌지
//        System.out.println("ac.isSingleton(\"car\") = " + ac.isSingleton("car"));
//        // Bean 객체 Engine의 @Scope가 prototype인지 아닌지
//        System.out.println("ac.isPrototype(\"engine\") = " + ac.isPrototype("engine"));
    }
}
  • Main도 주석처리를해주면...

  • appConfig가 등록되어있는데
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
  • 여기서 등록해줘서 그렇다
@Configuration
@ComponentScan
public class AppConfig {
//    @Bean
//    Car car(){
//        return new Car();
//    }
//
//    @Bean
//    @Scope("prototype")
//    Engine engine(){
//        return new Engine();
//    }
//
//    @Bean
//    Door door(){
//        return new Door();
//    }
}
  • @ComponentScan을 붙여주면 이 패키지 안에서 @Component가 붙은 클래스를 찾는다.
@ToString
@Component
class Car{
    Engine engine;
    Door door;
}
@Component
class Engine {}
@Component
class Door {}
  • class에 @Component를 붙여주면...

  • 등록이 되었다.

2. @Value와 @PropertySource

  • @PropertySource - 읽어올 파일 지정
  • @Value - 읽어올 Key값 지정
  • SysInfo.java
@Component
@PropertySource("setting.properties")
@ToString
public class SysInfo {

    @Value("#{systemProperties['user.timezone']}")
    String timeZone;
    @Value("#{systemEnvironment['PWD']}")
    String currDir;

    @Value("${autosaveDir}")
    String autoSaveDir;
    @Value("${autosaveInterval}")
    int autosaveInterval;
    @Value("${autosave}")
    boolean autosave;
}
  • setting.properties
autosaveDir=/autosave
autosave=true
autosaveInterval=30

SysInfo info = ac.getBean(SysInfo.class);
System.out.println("info = " + info);

  • 문자열을 Spring이 타입에 맞게 변경해준다.
  • currDir(현재 디렉토리)가 null로 나와서 확인...
Map<String, String> env = System.getenv();
System.out.println("env = " + env);

  • System.env에 있는 Key, Value값이 나온다.

3. @Autowired

  • Spring container에서 타입으로 빈을 검색해서 참조 변수에 자동 주입(DI)
  • 검색된 빈이 n개이면, 그 중에 참조변수와 이름이 일치하는 것을 주입
  • 주입 대상이 변수일 때, 검색된 빈이 1개 아니면 예외 발생
  • 주입 대상이 배열일 때, 검색된 빈이 n개라도 예외 발생X
  • @Autowired(required=false)일 때, 주입할 빈을 못찾아도 예외 발생X

  • Engine가 3개 있다면...?
@ToString
@Component
class Car{
    @Autowired
    Engine engine;
    @Autowired
    Door door;
}

@Component
class SuperEngine extends Engine{}
@Component
class TurboEngine extends Engine{}

@Component
class Engine {}
@Component
class Door {}

  • class Engine가 주입된다.
  • 이름이 같은걸 우선으로 찾기 때문
@Component
class SuperEngine extends Engine{}
@Component
class TurboEngine extends Engine{}

//@Component
class Engine {}
  • class Engine을 주입 대상에서 제외하면?

  • 에러 발생 single matching bean(한개를 찾으려 했는데 여러개라서 에러가 발생)
@ToString
@Component
class Car{
    @Autowired
    Engine[] engine;
    @Autowired
    Door door;
}
  • Engine을 배열로 받으면?

  • n개의 Engine이 들어간 모습
  • 생성자 주입
@ToString
@Component
class Car{
//    @Autowired
    Engine[] engine;
//    @Autowired
    Door door;

    @Autowired // 생략가능
    Car(Engine[] engine, Door door){
        this.engine = engine;
        this.door = door;
    }
}
  • 기본 생성자가있으면? 
@ToString
@Component
class Car{
//    @Autowired
    Engine[] engine;
//    @Autowired
    Door door;

    Car(){}

//    @Autowired
    Car(Engine[] engine, Door door){
        this.engine = engine;
        this.door = door;
    }
}
  • @Autowired가 없는 생성자 일때

  • @Autowired가 있을때
@ToString
@Component
class Car{
//    @Autowired
    Engine[] engine;
//    @Autowired
    Door door;

    Car(){}

    @Autowired
    Car(Engine[] engine, Door door){
        this.engine = engine;
        this.door = door;
    }
}

 

4. @Resource

  • Spring container에서 이름으로 빈을 검색해서 참조 변수에 자동 주입(DI)
  • 일치하는 이름의 빈이 없으면, 예외 발생
  • byType이 2개 이상일때  @Qualifier를 이용해서 이름이 일치하는 Bean을 찾아서 등록할 수 있다.
  • byType으로 주입후보를 결정하고 그 중 이름으로 일치하는걸 주입
class Car {
    @Autowired
    @Qualifier("superEngine")
    Engine engine;
}
  • byName으로 주입후보를 결정하고 일치하는걸 주입
class Car {
//  @Resource(name="superEngine")
    @Resource
    Engine engine;
}

  • 에러가 나는이유...

  • class Engine의 @Component를 주석처리 해놨기 때문

  • @Resource에 이름을 지정해주고 실행을해주면...

  • SuperEngine이 잘 주입된 모습
  • @Autowired만 해주면 에러가 나는데... 

  • 주입 후보가 2개나 있기 때문...

  • @Qualifier를 붙여주면...

  • 타입으로 검색된 빈이 여러개여도 이름이 일치하는 빈을 주입해준다.

 

5. 스프링 애너테이션(스프링 제공) vs 표준 애너테이션(자바 제공)

  • Maven이 없기때문...

  • javax.inject 버전 1로 해주면...