본문 바로가기

🌈 Spring Framework/🌱 Spring-boot

IoC - ApplicationContext / Bean

🌿 스프링 애노테이션

@SpringBootApplication

⇒ 스프링 부트 어플리케이션은 Bean을 2번 등록한다. 처음에 ComponentScan으로 등록하고, 그 후에 EnableAutoConfiguration으로 추가적인 Bean들을 읽어서 등록

  • @EnableAutoConfiguration

    • jar 의존성에 따라 자동적으로 스프링 애플리케이션의 환경 설정 (automatically configure)

    • AutoConfiguration은 결국 Configuration이다. 즉, Bean을 등록하는 자바 설정 파일

    • @EnableAutoConfiguration에 의해 spring.factories 안에 들어있는 수많은 자동 설정들이 조건에 따라 적용이 되어 수 많은 Bean들이 생성되고, 스프링 부트 어플리케이션이 실행

  • @ComponentScan: enable @Component scan on the package where the application is located

    • 해당 패키지에서 @Component 어노테이션을 가진 Bean들을 스캔해서 등록하는 것이다.(@Configuration, @Repository, @Service, @Controller, @RestController 포함)

  • @Configuration: allow to register extra beans in the context or import additional configuration classes

    • IOC container 클래스를 Bean 구성 Class

    • - @Bean vs. @Component*

      : Spring(IoC Container)에 Bean 을 등록하도록 하는 메타데이터를 기입하는 어노테이션

      @Component : 개발자가 직접 작성한 Class 를 Bean으로 만드는것

      @Bean : 개발자가 작성한 Method를 통해 반환되는 객체를 Bean으로 만드는것

    •  

       

       

       

       

       

       

       

      @Bean : 개발자가 직접 제어가 불가능한 외부 라이버리등을 Bean으로 만들때 사용

      @Configuration
      public class ApplicationConfig {
            @Bean(name="myarray")
            public ArrayList<String> array() {
                    return new ArrayList<String>();
            }
      }
    • ArrayList 와 같은 라이버리등을 Bean으로 등록하기 위해서 객체반환 메소드를 만들고 @Bean을 붙여주면 된다.

    • @Bean 어노테이션에 name이라는 값에 사용하면 자신아 원하는 id 로 Bean 등록 가능 / 어노테이션 name 입력하지않을 경우 메소드의 이름 ⇒ Bean 의 id (ex.array)

       

       

      @Component : 개발자가 직접 작성한 class를 Bean으로 등록하기 위한 어노테이션

      **@Component(value = "mystudent")**
      public class Student {
           public Student() {
                   System.out.println("hi");
           }
      }

       

    • Student class 는 개발자가 사용하기 위해 직접 작성한 클래스/ Bean으로 등록하기 위해 상단에 @Component

    • @Component 추가 정보가 없다면 Class의 이름을 Bean id로 사용.

    • @Bean 과 다르게 @Component 는 name 이 아닌 value를 이용해 Bean 이름 지정

       

       

      @Autowired : 형(타입) 을 통해 해당 자리에 들어올 객체를 판별하여 주입.

      **@Component // IOC Container 에 Bean 으로 등록** 
      public class Pencil {  }
      
      **@Component(value = "mystudent")**
      public class Student {
      
           **@Autowired //의존성 자동 주입** 
           private Pencil pencil;
      
           public Student() {
                   System.out.println("hi");
           }
      }

       

    • @Component 를 이용한 Bean의 의존성 주입은 @Autowired 어노테이션 사용

       

      @Qualifier
    • @Autowired와 같이 쓰이며, 같은 타입의 빈 객체가 있을 때 해당 아이디를 적어 원하는 빈이 주입될 수 있도록 하는 애노테이션

      • 해당 자리에 들어올 수 있는 객체가 여러개인 경우, 즉 다형성을 띄고 있는 객체타입에 @Autowired 를 사용할 경우에는 @Qualifier("Bean이름") 을 이용하여 해당 자리에 주입될 Bean 명시

🌈🌈 ApplicationContext 와 BeanFactory

ApplicationContext는 Pre-loading을 하고 즉, 즉시 인스턴스를 만들고 BeanFactory는 lazy-loading을 하여 실제 요청 받는 시점에서 인스턴스를 만든다고 한다.

⇒ 차이점은 인스턴스화 시점이 다르다는 것

*** 특별한 이유가 없다면 BeanFactory 보다는 ApplicationContext를 써라

특별한이란 메모리 소비가 중요하거나 리소스가 제한된 장치들을 말한다. 일반적인 엔터프라이즈 어플리케이션은 ApplicationContext를 사용하는 게 낫다. 그 이유는 BeanPostProcessor 확장 포인트를 사용 할 수도 있고, 트랜잭션과 AOP와 같은 상당한 양의 지원을 받을 수 있다

 

⭐ ApplicationContext

: 오브젝트 생성, 관계설정, 만들어지는 방식, 자동생성, 후처리등 여러가지 일들을 한다. 또 한 BeanFactory를 상속받고 있다.

// 1. IoC 컨테이너 생성 
ApplicationContext context = 
                new AnnotationConfigApplicationContext(**Config.class**);

// 2. Member Bean 가져오기 
Member member1 = (Member) context.**getBean**("member1");
member1.print();
  • 즉시 인스턴스 만듬. 구현체는 ApplicationContext 이지만 실제 사용하는 getBean 메서드는 BeanFactory 인터페이스의 메서드이다

  • 빈 팩토리에 여러가지 컨테이너 기능을 추가한 것 → ApplicationContext

⭐ BeanFactory

: 설정 프레임워크와 베이직한 기능 담당 (initiates and configures bean)

💚 Spring Bean 세팅 예시

@Configuration
class AnnotationConfig {

    @Bean(name = {"tiger", "kitty"})
    @Scope(value = "prototype")
    Tiger getTiger(String name) {
        return new Tiger(name);
    }

    @Bean(name = "lion")
        //Lion has the default singleton scope
        // lion 이름의 빈 생성 
    Lion getLion() {
        return new Lion("Hardcoded lion name");
    }

    interface Animal {}
}

▶️ getBean()

  • 이름으로 호출하기
Object lion = context.getBean("lion");
  • 이름과 타입으로 불러오기

: specify both the name and type of the requested bean

Lion lion = context.getBean("lion", Lion.class);

assertThrows(BeanNotOfRequiredTypeException.class, () -> 
    context.getBean("lion", Tiger.class));
}
  • 타입으로 빈 불러오기
Lion lion = context.getBean(Lion.class);

assertThrows(NoUniqueBeanDefinitionException.class, () -> 
    context.getBean(Animal.class));
}
  • 빈을 이름과 생성자 매개 변수로 불러오기
Tiger tiger = (Tiger) context.getBean("tiger", "Siberian");
Tiger secondTiger = (Tiger) context.getBean("tiger", "Striped");

assertEquals("Siberian", tiger.getName());
assertEquals("Striped", secondTiger.getName());
  • 빈의 타입과 매개 변수
Tiger tiger = context.getBean(Tiger.class, "Shere Khan");

assertEquals("Shere Khan", tiger.getName())

 

💚 공부 예제 :

▶️ Example1Application.java

//@SpringBootApplication
public class Example1Application {

    public static void main(String[] args) {
        //SpringApplication.run(Example1Application.class, args);

        // 1. IoC 컨테이너 생성 
        ApplicationContext context = new AnnotationConfigApplicationContext(**Config.class**);

        // 2. Member Bean 가져오기 
        Member member1 = (Member) context.getBean("member1");
        member1.print();

        // 3. Member bean 가져오기
        Member member2 = context.getBean("hello", Member.class);
            // Bean id = "hello" 라는 member2 빈을 불러오기 - Member.class 존재 
        member2.print();

        // 4. PrintB Bean 가져오기
        Printer printer = context.getBean("printerB", Printer.class);
        member1.setPrinter(printer);
        member1.print();

        // 5. 싱글톤인지 확인 
//        if(member1 == member2) {
//            System.out.println("동일한 객체 입니다");
//        } else {
//            System.out.println("서로 다른 객체입니다. ")
//        }


    }

}

▶️ Config.java

@Configuration //어노테이션은 어노테이션기반 환경구성
public class Config {

    // Bean : Spring 의 IoC방식으로 관리하는 객체
    // 빈 팩토리 (Beanfactory) : 스프링의 IoC 를 담당하는 핵심 컨테이너 
    // 어플리케이션 컨텍스트 (applicationContext): 빈 팩토리를 확장한 IoC Container 

    @Bean
    public Member member1() {
        //Setter injection (setter메서드를 이용한 의존성 주입)
        Member member1 = new Member();
        member1.setName("홍길동");
        member1.setNickname("도사" );
        member1.setPrinter(new PrinterA());
        return member1;
    }

    @Bean(name = "hello")
    // hello 라는 이름을 가진 bean 생성 
    public Member member2() {
        //Constructor Injection (생성자를 이용한 의존성 주입) 
        return new Member("전우치", "도사" , new PrinterA());
    }

    @Bean
    public PrinterA printerA() {
        return new PrinterA();
    }

    // 이름을 지정하지 않으면 메서드의 이름이 빈의 이름을 등록된다. 
    @Bean
    public PrinterB printerB() {
        return new PrinterB();
    }

}

▶️ Member.java

public class Member {

    private String name;
    private String nickname;
    private Printer printer;

    public Member() {

    }

    public Member(String name, String nickname, Printer printer) {
        this.name = name;
        this.nickname = nickname;
        this.printer = printer;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public void setPrinter(Printer printer) {
        this.printer = printer;
    }

    public void print() {
        printer.print("Hello" + name + ":" + nickname);
    }
}

▶️ Printer Interface

public interface Printer {
    public void print(String message);
}

▶️ PrinterA

▶️ PrinterB

public class PrinterA  implements Printer{

    @Override
    public void print(String message) {
        System.out.println("Printer A :" + message);

    }

}
public class PrinterB implements Printer{

    @Override
    public void print(String message) {
        System.out.println("Printer B: " + message);

    }

}

 

'🌈 Spring Framework > 🌱 Spring-boot' 카테고리의 다른 글

Lombok 사용시 주의사항  (0) 2021.05.03
Validator - BindingResult  (0) 2021.02.27
@SpringBootApplication  (0) 2021.02.18