From 8314fb910d5577b6f6d511a3133ec15eb46833fc Mon Sep 17 00:00:00 2001 From: ChaerinYu Date: Sun, 29 May 2022 15:30:25 +0900 Subject: [PATCH] =?UTF-8?q?create:=20[Spring]=20Spring=20Bean=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=ED=95=98=EB=8A=94=20=EB=B0=A9=EB=B2=95=20(#115)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Spring/bean_register_annotations.md | 126 ++++++++++++++++++++++++++ Spring/why_configuration_with_bean.md | 90 ++++++++++++++++++ 2 files changed, 216 insertions(+) create mode 100644 Spring/bean_register_annotations.md create mode 100644 Spring/why_configuration_with_bean.md diff --git a/Spring/bean_register_annotations.md b/Spring/bean_register_annotations.md new file mode 100644 index 0000000..52a7e5d --- /dev/null +++ b/Spring/bean_register_annotations.md @@ -0,0 +1,126 @@ +# Spring Bean 등록 시 사용되는 Annotation 비교 + +기존 Spring MVC에서는 xml을 활용하여 Bean을 등록하고 있었습니다. +하지만 프로젝트 규모가 커짐에 따라 사용하는 요소들을 xml에 등록하는 것이 상당히 번거로워져`Annotation(@)`을 활용한 Bean 등록 방법이 생기게 되었습니다. +Bean 등록할 때 활용 가능한 `@Bean`, `@Component`, `@Configuration`에 대해서 알아봅시다. 😎 + + +## 🤔 Spring Bean? +Spring에서는 Spring의 DI Container에 의해 관리되는 POJO (Plain Old Java Object)를 Bean이라고 부릅니다. +이 Bean들은 Spring을 구성하는 핵심 요소입니다. +- POJO로써 Spring 애플리케이션을 구성하는 핵심 객체 +- Spring IoC 컨테이너 (DI 컨테이너)에 의해 생성 및 관리됨 +- 주요 속성으로 class, id, scope, constructor-arg 등 + +### Spring Bean의 구성요소 +- class: Bean으로 등록할 Java 클래스 +- id: Bean의 고유 식별자 +- scope: Bean을 생성하기 위한 방법 (singleton, prototype 등) +- constructor-arg: Bean 생성 시 생성자에 전달할 파라미터 +- property: Bean 생성 시 setter에 전달할 인수 + +Spring에서는 등록되어 있는 Bean을 싱글톤 객체로 생성하여 관리합니다. +그래서 Bean을 등록하기 위한 다양한 방법들을 제공하고 있는데 각각 방법들에 대해 살펴봅시다. 😎 + + + +## Spring Bean 등록 방법 +## 1. @Bean + @Configuration +예를 들어 다음과 같은 클래스를 Spring Container에 등록한다고 가정해봅시다. +``` java +public class ChallengeClass { + +} +``` +이 클래스를 Bean으로 등록하기 위해서는 설정 클래스에서 `@Bean`을 사용하여 **수동으로 Spring Container에 Bean을 등록할 수 있습니다.** +설정 클래스에는 `@Configuration`을 꼭!! 붙여주어야 합니다. (그래야 싱글톤 보장이 됩니다.) +이렇게 수동으로 등록된 Bean은 **메소드 이름으로 Bean 이름이 결정**되므로 중복되지 않도록 주의해야 합니다. + +``` java +@Configuration +public class ClassConfig { + @Bean + public ChallengeClass challengeClass() { + return new ChallengeClass(); + } +} +``` +Spring Container는 `@Configuration`이 붙어있는 클래스를 자동으로 Bean으로 등록해두고 해당 클래스를 파싱해서 `@Bean`이 있는 메소드를 찾아 Bean을 생성해줍니다. + +`@Bean`을 주로 사용하는 경우는 아래와 같습니다. +- ✅ 개발자가 직접 제어가 불가능한 라이브러리를 활용할 때 +- ✅ 애플리케이션 전범위적으로 사용되는 클래스를 등록할 때 +- ✅ 다형성(polymorphism)을 활용하여 여러 구현체를 등록해주어야 할 때 + +ex) +- 객체를 json으로 변경할 때 gson 라이브러리를 사용할 때 싱글톤 Bean으로 등록해주어 1개의 객체만 생성하여 여러 클래스가 공유함으로써 메모리 상의 이점을 얻을 수 있다. + 우리가 만든 클래스가 아니므로 불가피하게 `@Bean`으로 등록해줘야 한다. +- 여러 구현체를 Bean으로 등록할 때 어떤 구현체들이 Bean으로 등록되는지 `@Configuration` 클래스만 보고 한 눈에 파악하여 유지보수 하기 좋다. + + +* `@Configuration` @interface +``` java +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Component +public @interface Configuration { + // ... +} +``` + + +## 2. @Component +위와 같은 방식은 Bean으로 등록해야 하는 클래스가 많아질 수록 상당히 많은 시간이 걸립니다. +그래서 Spring에서는 특정 어노테이션이 있는 클래스를 찾아서 Bean으로 등록해주는 컴포넌트 스캔 기능을 제공합니다. +컴포넌트 스캔은 `@Component`가 있는 클래스들을 찾아 자동으로 Bean으로 등록해줍니다. +_(컴포넌트 스캔: Bean으로 등록 될 준비를 마친 클래스들을 스캔하여, Bean으로 등록해주는 것)_ + +그래서 우리가 직접 개발한 클래스를 빈으로 편리하게 등록하고자 하는 경우에는 `@Component`를 활용하면 됩니다. +``` java +@Component +public class ChallengeClass { + +} +``` +`@Component`를 갖는 어노테이션은 `@Controller`, `@Service`, `@Repository`등이 있으며 `@Configuration` 역시 갖고 있습니다. +`@Configuration`안에 있는 `@Component`에 의해 설정 클래스 역시 자동으로 Bean으로 등록되고 `@Bean`이 있는 메소드를 통해 Bean 등록을 할 수 있었던 것입니다. + +* `@Component` @interface +``` java +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Indexed +public @interface Component { + // ... +} +``` +`@Component`를 이용할 경우 Main 클래스에서 @ComponentScan으로 컴포넌트 찾는 탐색 범위를 지정해주어야 합니다. (ex. `@ComponentScan(basePackages = "com.test.xxx")`) +하지만 SpringBoot를 이용중이라면 `@SpringBootConfiguration` 하위에 기본적으로 포함되어 있어 별도 설정이 필요없습니다. + + +> Spring은 기본적으로 **컴포넌트 스캔을 이용한 자동 Bean 등록 방식을 권장**합니다. +> 왜냐하면 해당 Bean이 잘 설정되어 있는지 바로 파악할 수 있기 때문입니다. +> 또한 `@Bean`을 이용하면 Bean으로 등록해야 하는 작업과 Bean이 등록되었는지 파악하는 과정도 번거로워질 수 있습니다. +> `@Bean`으로 등록해야 하는 경우를 제외한 나머지의 경우에는 자동 등록 방식을 사용합시다! + + +## @Bean + @Configuration vs. @Component +### @Bean + @Configuration +- 수동으로 Spring Container에 Bean을 등록하는 방법 +- 개발자가 직접 제어 불가능한 library를 Bean으로 등록할 때 사용 +- 유지보수성을 높이기 위해 애플리케이션 전범위적으로 사용되는 클래스나 다형성을 활용하여 여러 구현체를 빈으로 등록할 때 사용 +- 1개 이상의 `@Bean`을 제공하는 클래스의 경우 반드시 `@Configuration`을 명시해 주어야 싱글톤이 보장됨 + +### @Component +- 자동으로 Spring Container에 Bean을 등록하는 방법 +- Spring의 Component Scan 기능이 `@Component`가 있는 클래스를 자동으로 찾아서 Bean으로 등록함 +- 대부분의 경우 `@Component`를 이용한 자동 등록 방식 사용을 추천함 +- `@Component`의 하위 어노테이션: `@Configuration`, `@Controller`, `@Service`, `@Repository` 등 + + +




+ +* Ref. + * [[Spring] 빈 등록을 위한 어노테이션 차이 및 비교](https://mangkyu.tistory.com/75) \ No newline at end of file diff --git a/Spring/why_configuration_with_bean.md b/Spring/why_configuration_with_bean.md new file mode 100644 index 0000000..1d429ea --- /dev/null +++ b/Spring/why_configuration_with_bean.md @@ -0,0 +1,90 @@ +# @Bean이 @Configuration 안에서 사용되어야 하는 이유 +`@Bean`을 이용한 수동 Bean 메소드는 Spring Bean 안에만 구현되어 있다면 모두 동작합니다. +하지만 Spring은 `@Bean`이 반드시 `@Configuration`을 활용하도록 강조하는데 그 이유는 `@Configuration`에 특별한 부가 기능이 적용되기 때문입니다. + +## @Configuration에 적용되는 Proxy Pattern +`@Configuration`은 `@Component`의 하위 어노테이션으로 `@Configuration`이 붙어있는 클래스 역시 Spring Bean으로 등록됩니다. +그럼에도 Spring이 `@Configuration`을 따로 만든 이유는 **CGLib으로 Proxy Pattern을 적용해 수동으로 등록하는 Bean이 반드시 싱글톤으로 생성됨을 보장**하기 위해서입니다. + +예를 들어 다음과 같이 Spring Bean으로 등록하고자 하는 클래스가 있다고 합시다. +``` java +public class TestResourceManager { + +} +``` +``` java +@Configuration +public class MyConfig { + + @Bean + public TestResourceManager testResourceManager() { + return new TestResourceManager(); + } + + @Bean + public FirstBean firstBean() { + return new FirstBean(testResourceManager()); + } + + @Bean + public SecondBean secondBean() { + return new SecondBean(testResourceManager()); + } +} +``` +위와 같이 Bean을 생성하는 메소드를 여러 번 호출한다면 불필요하게 여러 개의 Bean이 생성됩니다. +이러한 문제를 방지하기 위해 Spring은 `@Configuration`이 있는 클래스를 객체로 생성할 때 CGLib 라이브러리를 사용해 Proxy Pattern을 적용합니다. +그래서 **`@Bean`이 있는 메소드를 여러 번 호출하여도 항상 동일한 객체를 반환하여 싱글톤을 보장**합니다. + +이를 이해하기 쉬운 코드로 나타내면 다음과 같습니다. (실제로는 이렇지 않으니 이해를 돕기 위한 코드로 봐주세요.) +``` java +@Configuration +public class MyConfigProxy extends MyConfig { + + private Object source; + + @Override + public TestResourceManager testResourceManager() { + if (testResourceManager == null) { + source = super.testResourceManager(); + } + + return source; + } + + @Override + public FirstBean firstBean() { + return super.firstBean(); + } + + @Override + public SecondBean secondBean() { + return super.secondBean(); + } +} +``` + +## 싱글톤 여부를 제어하기 위한 proxyBeanMethods +대부분의 경우에는 `@Bean`에 의한 수동 빈 등록을 할 때 싱글톤으로 생성되기를 원합니다. +하지만 @Bean 메소드를 호출할 때 의도적으로 매번 다르 객체가 생성되기 원할 때에는 proxyBeanMethods를 false로 주면 됩니다. + +``` java +@Configuration(proxyBeanMethods = false) +public class MyConfig { + // ... +} +``` +위의 설정 클래스에 대해서는 proxy가 적용되지 않으며 모든 @Bean 메소드 호출마다 새로운 객체를 생성해줍니다. +하지만 거의 모든 상황에서 새로운 객체의 생성을 필요로 하는 경우는 거의 없습니다. + + + +`@Configuration`이 아니라면 Bean이 싱글톤임을 보장받을 수 없으므로 반드시 `@Bean` 사용 시 클래스에 `@Configuration`를 적용합시다!! + + + + +




+ +* Ref. + * [[Spring] @Configuration안에 @Bean 사용 이유, proxyBeanMethods](https://mangkyu.tistory.com/234) \ No newline at end of file