Web/스프링부트(SpringBoot Framework)

(1) @Autowired와 @Bean 차이점

심플블루 2024. 10. 5. 06:30
반응형

@Autowired 와 @Bean 어노테이션의 구분

@Autowired와 @Bean의 역할은 다르지만, 함께 작동하여 의존성 주입과 객체 생성을 처리합니다. 

하지만 두 어노테이션의 차이를 명확히 알아야 합니다

 

  • @Autowired의 역할:
    • @Autowired는 의존성 주입을 위한 것이며, 직접적으로 객체를 생성하지 않습니다.
    • 이 어노테이션은 Spring 컨테이너에게 해당 타입의 빈을 찾아 주입하라고 지시합니다.
  • 객체 생성 과정:
    • 객체 생성은 주로 Spring 컨테이너에 의해 이루어집니다.
    • @Component, @Service, @Repository, @Controller 등의 어노테이션이 붙은 클래스들은 자동으로 빈으로 등록되고 생성됩니다.
  • @Bean의 역할:
    • @Bean은 메서드 레벨에서 사용되며, 해당 메서드가 반환하는 객체를 Spring 컨테이너에 빈으로 등록합니다.
    • @Configuration 클래스 내에서 사용되며, 직접적으로 객체를 생성하고 구성할 수 있습니다.
  • 자동 구성(Auto-configuration):
    • Spring Boot의 자동 구성 기능으로 인해, 많은 경우 개발자가 직접 @Bean으로 객체를 생성하지 않아도 필요한 빈들이 자동으로 생성됩니다.
  • 컴포넌트 스캔:
    • @SpringBootApplication 어노테이션은 @ComponentScan을 포함하고 있어, 애플리케이션의 모든 컴포넌트를 자동으로 스캔하고 빈으로 등록합니다.

 

아래의 코드를 예로 들어 설명하겠습니다.

// 자동으로 빈으로 등록되는 서비스
@Service
public class UserService {
    public void doSomething() {
        System.out.println("UserService is doing something");
    }
}

// 컨트롤러에서 @Autowired 사용
@RestController
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping("/user")
    public String userAction() {
        userService.doSomething();
        return "Action performed";
    }
}

// 수동으로 빈을 구성하는 경우
@Configuration
public class AppConfig {
    @Bean
    public EmailService emailService() {
        return new EmailService();
    }
}

// 다른 컴포넌트에서 수동 구성 빈 사용
@Component
public class NotificationManager {
    @Autowired
    private EmailService emailService;

    public void sendNotification() {
        emailService.sendEmail();
    }
}

// 메인 애플리케이션 클래스
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

 

  1. UserService는 @Service 어노테이션으로 인해 자동으로 빈으로 등록되고 생성됩니다.
  2. UserController에서 @Autowired를 사용하여 UserService주입받습니다. 여기서 new UserService()를 하지 않아도 됩니다.
  3. EmailService는 @Bean 어노테이션을 통해 수동으로 구성되고 생성됩니다.
  4. NotificationManager는 @Autowired를 사용하여 EmailService를 주입받습니다.
결론적으로, @Autowired 자체는 객체를 생성하지 않지만, Spring의 자동 구성컴포넌트 스캔 기능 덕분에 많은 경우 개발자가 직접 new 키워드로 객체를 생성하지 않아도 됩니다. @Autowired는 이미 생성된 (또는 생성될) 빈을 찾아 주입하는 역할을 합니다.

 

 

필드 주입 방식으로 사용된 @Autowired  어노테이션의 빈 주입에 대하여

@Autowired
private UserService userService

 

  • 빈 등록:
    • UserService 클래스가 @Service 어노테이션이나 다른 방법으로 빈으로 등록되어 있다고 가정합니다.
    • Spring 컨테이너는 애플리케이션 시작 시 이 UserService 타입의 빈을 생성하고 관리합니다.
  • 빈 주입:
    • @Autowired를 사용하면, Spring은 컨테이너에서 UserService 타입의 빈을 찾습니다.
    • 찾은 빈을 해당 필드(userService)에 할당합니다.
  • 매개변수와의 차이:
    • 이는 매개변수를 통한 전달과는 다릅니다.
    • 매개변수 주입은 메서드 호출 시 값을 전달하는 것이지만, 이 경우는 Spring이 관리하는 객체를 필드에 직접 할당하는 것입니다.
  • 객체 생성과 호출:
    • 새로운 객체를 생성하는 것이 아닙니다 (new 키워드 사용 안 함).
    • 이미 생성되어 컨테이너에 존재하는 UserService 빈의 참조가져오는 것입니다.
  • 실제 동작:
    • Spring은 컨트롤러 객체를 생성할 때, userService 필드에 적절한 빈을 주입합니다.
    • 이후 컨트롤러의 메서드에서 this.userService로 해당 빈을 사용할 수 있게 됩니다.
@RestController
public class UserController {
    @Autowired
    private UserService userService;  // Spring이 이미 생성된 UserService 빈을 여기에 주입

    @GetMapping("/users")
    public List<User> getUsers() {
        return userService.getAllUsers();  // 주입된 userService 사용
    }
}

 

이 경우, userService는 새로 생성된 객체가 아니라, Spring 컨테이너가 관리하고 있는 기존 UserService 빈의 참조입니다. 이를 통해 싱글톤 패턴과 유사한 효과를 얻을 수 있으며, 애플리케이션 전체에서 동일한 UserService 인스턴스를 공유하게 됩니다.

@Autowired"이미 빈으로 등록된 userService 객체를 해당 컨트롤러로 호출(정확히는 '주입')한다"는 의미로 이해하면 됩니다.

 

 

여기서 주입의 의미를 조금 더 명확히 해보겠습니다

  1. 일반적인 프로그래밍에서의 "주입":
    • 보통 메서드 호출 시 매개변수를 통해 값이나 객체를 전달하는 것을 의미합니다.
  2. 의존성 주입에서의 "주입":
    • 객체의 생성과 설정을 외부(여기서는 Spring 컨테이너)에서 관리하고, 필요한 의존성을 제공하는 것을 의미합니다.
    • 이는 필드, 생성자, 또는 setter 메서드를 통해 이루어질 수 있습니다.
  3. @Autowired의 경우:
    • Spring 컨테이너가 관리하는 빈 객체의 참조를 해당 필드에 설정(할당)하는 것입니다.
    • 이는 매개변수를 통한 전달이 아니라, 객체의 상태를 직접 설정하는 것입니다.

 

이해를 돕기 위해, 의존성 주입의 과정을 일반적인 Java 코드로 표현하면 다음과 같습니다.

// Spring의 내부 동작을 간단히 표현한 의사 코드
public class SpringContainer {
    private UserService userServiceBean = new UserService(); // 컨테이너가 빈 생성

    public void injectDependencies(UserController controller) {
        // 이 부분이 @Autowired와 유사한 역할을 합니다
        controller.userService = this.userServiceBean;
    }
}

public class UserController {
    private UserService userService; // @Autowired가 붙을 필드

    // Spring 컨테이너가 이와 유사한 작업을 수행합니다
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
}

이 예시에서 injectDependencies 메서드가 하는 일이 바로 Spring의 @Autowired가 수행하는 "주입" 작업과 유사합니다. 이는 매개변수를 통한 전달이 아니라, 객체의 필드에 직접 값을 할당하는 방식입니다.

 

만약 이 코드에서

@Configuration, @Bean, 그리고 @Autowired를 사용하지 않고 EmailService 메서드를 사용하려면, 순수 자바 방식으로 객체를 생성하고 관리해야 합니다.

// 수동으로 빈을 구성하는 경우
@Configuration
public class AppConfig {
    @Bean
    public EmailService emailService() {
        return new EmailService();
    }
}

// 다른 컴포넌트에서 수동 구성 빈 사용
@Component
public class NotificationManager {
    @Autowired
    private EmailService emailService;

    public void sendNotification() {
        emailService.sendEmail();
    }
}

// 메인 애플리케이션 클래스
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

 

위의 코드를 순수 자바로 작성된 코드로 바꾸면 @Configuration, @Bean, 그리고 @Autowired 어노테이션의 기능을 명확히 알 수 있습니다.

// EmailService 클래스
public class EmailService {
    public void sendEmail() {
        System.out.println("Sending email...");
    }
}

// NotificationManager 클래스
public class NotificationManager {
    private EmailService emailService;

    // 생성자를 통한 의존성 주입
    public NotificationManager(EmailService emailService) {
        this.emailService = emailService;
    }

    public void sendNotification() {
        emailService.sendEmail();
    }
}

// 메인 애플리케이션 클래스
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
        
        // 수동으로 객체 생성 및 의존성 주입하기
        // 1. EmailService 인스턴스 생성
        EmailService emailService = new EmailService();

        // 2. NotificationManager 인스턴스 생성 및 EmailService 주입
        NotificationManager notificationManager = new NotificationManager(emailService);

        // 3. NotificationManager 사용
        notificationManager.sendNotification();
    }
}

 

@Configuration, @Bean, 그리고 @Autowired를 사용하지 않고, 순수 자바 코드로 구현을 한 경우 주요 포인트

 

  1. 의존성 주입:
    • NotificationManager 클래스는 생성자를 통해 EmailService를 주입받습니다.
    • 이는 생성자 주입 패턴으로, 스프링의 @Autowired 없이 구현한 것입니다.
  2. 객체 생성 및 조립:
    • main 메소드에서 직접 EmailService 인스턴스를 생성합니다.
    • 생성된 EmailService 인스턴스를 사용하여 NotificationManager 인스턴스를 생성합니다.
  3. 스프링 의존성 제거:
    • @Service, @Component, @Autowired 등의 스프링 어노테이션을 사용하지 않습니다.
    • @Configuration과 @Bean도 사용하지 않습니다.
  4. 수동 객체 관리:
    • 개발자가 직접 객체의 생명주기를 관리해야 합니다.
    • 필요한 곳에서 new 키워드를 사용하여 객체를 생성합니다.
  5. 유연성과 제어:
    • 이 방식은 개발자에게 객체 생성과 의존성 관리에 대한 완전한 제어권을 제공합니다.
    • 하지만 애플리케이션이 커질수록 관리가 복잡해질 수 있습니다.

이 방식의 장단점:

  • 장점: 간단한 애플리케이션에서는 이해하기 쉽고 직관적입니다.
  • 단점: 대규모 애플리케이션에서는 객체 생성과 의존성 관리가 복잡해질 수 있습니다.

이 예제는 스프링의 DI 컨테이너 없이 순수 자바로 의존성을 관리하는 방법을 보여줍니다. 실제 애플리케이션에서는 복잡성 관리를 위해 스프링과 같은 DI 프레임워크를 사용하는 것이 일반적이지만, 기본 개념을 이해하는 데는 이러한 순수 자바 구현이 도움이 될 수 있습니다.


따라서

1. 자동 의존성 주입: @Autowired 는 의존성(한 클래스가 다른 클래스를 사용하거나 필요로 하는 관계, 즉 클래스 간의 관계) 주입에 관련된 것이고,

2. 자동 객체 생성: @Bean 은 빈(객체) 생성하고 관리하는 방식

과 관련이 있습니다.

 

반응형