보충 설명 블로그 정리 글: https://velog.io/@guscjf0903/Bean-추가-내용
Bean은 자바 Bean과 스프링 Bean으로 나뉜다.
자바 BEAN
자바 BEAN이란?
- 자바 빈은 자바 언어의 표준 명세서를 따르는 일반적인 자바 클래스를 가르키는 용어이다.
- 자바 빈은 데이터를 캡슐화하고 재사용 가능한 소프트웨어 컴포넌트를 개발하기 위해 사용된다.
자바 Bean의 규약
- public 클래스 : 자바 빈은 'public'으로 선언된 클래스여야한다.
- 기본 생성자 : 자바 빈은 파라미터가 없는 기본생성자를 가지고 있어야 한다. 기본 생성자는 명시적으로 정의되거나 컴파일러에 의해 자동으로 생성될 수 있다.
- 속성(Properties) : 자바 빈은 속성을 표현하기 위해 필드를 사용한다. 이러한 필드는 'Private'접근제어자로 선언되며, 데이터의 캡슐화를 하는데 사용된다.
- Getter, Setter : 각 속성에 대한 게터와 세터 메서드를 제공한다. 게터는 속상값을 변환하는 메서드이고, 세터는 속성 값을 설정하는 메서드이다.
- 프로퍼티가 boolean이면 get이 아니라 is라고 사용해도 된다.
public class PersonBean implements Serializable {
private String name;
private int age;
public PersonBean() {
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
}
스프링 BEAN
스프링 BEAN이란?
- Bean은 스프링 IoC컨테이너에 의해 관리되는 재사용가능한 자바 객체이다.
- XML, Annotation, Java config 파일을 기반으로 한 Configuration 메타데이터와 Bean으로 등록할려는 POJO를 통해 Bean을 생성할 수 있다. (예전엔 어노테이션이 없어서 XML밖에 쓸게 없었다. 지금은 어노테이션으로 거의다 처리한다.)
![업로드중..]()
- Configuration 메타데이터 : XML, 자바코드, 어노테이션으로 표현되며, 컨테이너의 명령과 인스턴스화, 설정, 조립할 객체등을 정의한다.
- 사진에서 처럼 Configuration과 POJO가 합쳐져 Bean을 생성하고 다양한 기능들을 설정한다.
Dependency Injection
- 의존성(Dependency)이란 말 그대로 하나의 코드가 다른 코드에 의존하는 상태를 뜻한다.
![업로드중..]()
- 의존성 주입(Dependency Injection)이란 의존성이 있는 코드 객체를 넣어준다는 뜻이다.
- 사진에서처럼 A라는 코드가 B라는 코드를 만들지않고, 인스턴스를 생성해서 사용하고 있다면, A는 B에 의존하고 있다고 말할 수 있다.
![업로드중..]()
- 의존성 주입이 계속해서 일어난다면 어디선가 new 키워드가 사영되여 객체를 생성한다.
- 그럼 위 사진과 같이 하나의 객체를 만들기 위해 굉장히 복잡하게 얽혀 의존성이 생성될 수 있다. 또한 많은 객ㅊ레가 중복생성되게 된다. 이같은 문제를 해결하기 위해 스플이 IoC컨테이너를 사용한다.
IoC 컨테이너란?
- IoC란 Inversion Of Control의 약자로 제어의 역전이다.
- 프로그램의 실행흐름, 객체의 생명주기를 개발자가 아닌 외부에서 관리해준다.
- Spring이 직접 자바 객체를 생성하고 관리하기 때문에 이 관리 위임의 주체는 Spring이 된다.
![업로드중..]()
- 그냥 의존성 주입을 하게 되면 사진의 '일반적인 경우'처럼 일직선으로 이어짐과 달리 중간의 매개체가 끼어들어 더이상 제어를 개발자가 아닌 매개체(=IoC 컨테이너)가 일임하게 된다.
- IoC컨테이너는 의존성 관리 뿐만아닌, 인스턴스 주입, 더나아가 메모리 해제의 역할까지 해준다.
- 떄문에 의존성 주입이 필요한 객체를 Bean으로 등록하여 스프링 IoC컨테이너가 생성과 의존성 주입을 관리하도록 해야한다.
Bean으로 등록하는 방법
하나의 예시를 두고 다양한 방법을 알아보자
- BookService.java 파일을 Bean으로 등록해보자.
public class BookRepository {
}
public class BookService {
BookRepository bookRepository;
public void setBookRepository(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
}
1.xml-bean 태그 이용
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
<bean id="bookService"
class="com.springstudy.springapplicationcontext.BookService">
<property name="bookRepository" ref="bookRepository"/>
</bean>
<bean id="bookRepository"
class="com.springstudy.springapplicationcontext.BookRepository">
</bean>
</beans>
- id는 bean의 이름, class는 빈을 등록하고자하는 클래스가 위치한 패키지명이 포함된 위치
- 하지만 xml을 이용한 의존성 주입은 현재 거의 사용되지 않는다.
- java Configuration 사용
@Configuration
public class ApplicationConfig {
@Bean
public BookRepository bookRepository() {
return new BookRepository();
}
@Bean
public BookService bookService() {
BookService bookService = new BookService();
return bookService;
}
}
- @Configuration이 부으면 자바 클래스 파일을 자바 설정파일로 등록할 수 있다.
- @Bean 어노테이션으로 빈으로 등록할 수 있다.
- 이 방법 역시 xml 파일과 마찬가지로 잘 쓰이지 않는다. 수동으로 Bean을 등록해줘야 하기 떄문에 생산성이 많이 떨어진다.
- 하지만 외부에서 만든 라이브러리 등록은 이 방법으로 사용된다.
- ComponentScan 사용
AppConfig.java파일 생성 및 Bean을 생성한다.
@Configuration
@ComponentScan(basePackageClasses = DemoApplication.class)
public class ApplicationConfig {
}
- 컴포넌트 스캔은 컴포넌트 어노테이션을 가진 모든 대상을 가져와서 빈을 등록하기 위해 찾는 과정.
- 베이스 패키지,베이스 패키지 클래스 라는 속성을 이용해서 탐색하는 위차를 지정해 줄 수 있다.
- basePackages : 해당 패키지에 속한 컴포넌트를 스캔한다.
- basePackageClasses : 해당 클래스가 속한 패키지를 스캔한다.
IoC 컨테이너에 의해 빈으로 등록되는 대상 어노테이션
1. @Configuration : 자바 기반의 설정 클래스를 나타낸다. 메서드에 @Bean어노테이션을 사용해서 빈에 등록할 수 있다.
2. @Component : 붙은 모든 클래스들을 자동으로 빈에 등록해준다.
3. @Controller : 웹 어플리케이션의 컨트롤러 클래스로 사용된다. 스프링은 이 어노테이선이 붙은 클래스를 컨트롤러로 인식하고 URL을 요청 처리한다.
4. @Service : 서비스 클래스로 사용된다. 스프링 비즈니스 로직에서 사용된다.
5. @Repository : 데이터 액세스 객체(DAO)로 사용된다. 스프링 데이터 접근 계층에서 사용된다.
6. @Autowired : 의존성 주입을 위해 사용되는 어노테이션이다 해당 필드또는 메서드에 필요한 빈을 자동으로 주입시켜준다.
BEAN과 싱글톤 패턴
- '빈은 싱글톤이다'라는 말이 있다. 만약 객체를 스프링이 아닌 직접 싱글톤으로 만들어 사용한다면 다양한 부작용이 있다.
1. 다형성을 사용하지 못한다 : 싱글톤은 생성자의 접근제어자를 private으로 만들기 때문에 상속하지 못한다.
2. 안정적인 어플리케이션에서 단위테스트가 어렵다. : 싱글톤 패턴으로 구현할 경우 해당 객체는 공유 객체가 되므로, 단위테스트 순서에 따라 결과가 달라질 수 있다.
- 하지만 스프링은 이같은 단점들을 해결하면서 모든 빈을 싱글톤 객체로 사용한다.
쬐끔 디테일한 Bean 등록 과정(IoC 컨테이너의 라이플 사이클, 관리 방법)
- 객체 생성 + Property 설정 : 어노테이션, 자바 Config, XML 메타데이터를 사용하여 Bean Definition을 생성, Bean으로 등록할 POJO와 Bean Definition을 이용해서 Bean을 생성한다.
※ Bean Definition : 스프링 컨테이너에게 빈을 어떻게 생성하고, 설정해야 하는지에 대한 메타데이터를 제공
위 과정에선 평범한 자바클래스를 사용하여 객체를 생성합니다.
- IoC 컨테이너, 싱글톤 레지스트리 :스프링 IoC 컨테이너 는 빈 스코프가 싱글톤인 객체에, 빈의 이름을 Key객체를 Value로 저장한다. 그래서 의존성이 주입 되어야하는 객체가 빈으로 등록 되어 있을 때, Key를 이용해서 항상 동일한 Single Object를 반환하게 된다.
※ 싱글톤 레지스트리 : 스프링 컨테이너가 관리하는 빈 중에서, 싱글톤 범위로 관리되는 빈들을 저장하고 관리하는 메커니즘.
- 싱글톤 레지스트리 특징
- 전역적인 접근 : 모든 빈을 전역적으로 관리한다. 따라서 어디서든지 동일한 싱글톤 빈에 접근 가능
- 스프링 컨테이너가 빈 관리 : 컨테이너가 빈 라이프사이클을 관리하므로, 빈의 생성 및 소멸과 같은 관리작업을 사용자가 할 필요가 없다.
- 싱글톤 패턴과 다름 : 일반적인 디자인패턴의 싱글톤과 다르다. 스프링의 싱글톤은 스프링 컨테이너 내에서만 적용되며, 컨테이너가 관리,생성을 하기 때문에 여러 스레드에서 안전하게 사용할 수 있다.
- 의존설정 : 빈 객체가 생성 된 뒤, IoC컨테이너는 의존설정을 한다. 이 때 의존성 자동 주입이 일어나게 된다.
- 초기화 -> 사용 -> 소멸 : 커넥션 풀처럼 초기화 과정이 필요한 객체들은 초기화를 진행하고, 빈을 사용자가 사용하게 된다. 그리고 스프링 컨테이너가 종료될 때 빈 스코프가 싱글톤이 객체도 함께 소멸된다.
요약
1. 설정과 POJO사용해서 객체를 생성한다.
2. IoC컨테이너가 빈 이름 key , 객체는 Value로 저장해서 싱글톤을 반환한다.
3. 의존설정하고 자동주입한다
Bean Scope
- 빈 스코프란 빈 객체의 생명주기와 범위를 정의하는 것을 의미한다. 스코프가 언제 생성되고 소멸되며, 어떤 범위내에서 빈 객체를 공유할 것인지 결정한다.
- Singleton Scope(기본값)
- 싱글톤 스코프는 컨테이너에서 단일 인스턴스를 생성하고, 어플리케이션 내에서 모든 요청에 동일한 인스턴스를 반환한다.
- 하나의 Bean 객체가 모든 어플리케이션 전체에 공유된다
- @Scope(”singleton”)또는 @Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)로 설정한다.
- 거의 싱글톤만 쓴다고 생각해도 무방하다. 그외에 것들은 거의 잘 쓰이지 않는다
- Prototype Scope
- 프로토타입 스코프는 매번 빈을 요청할때마다 새로운 인스턴스를 생성한다.
- 요청마다 별도의 빈 인스턴스가 생성되고, 각 빈 인스턴스는 독립적으로 관리된다.
- @Scope(”prototype”)또는 @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)로 설정한다.
- Request Scope
- 요청 스코프는 웹 어플리케이션에서 HTTP 요청마다 새로운 인스턴스를 생성한다
- 주로 웹 어플리케이션 개발에 사용, 각 HTTP요청마다 별도의 빈 인스턴스를 생성, 요청 범위 내에서 공유한다.
- @Scope(”request”)로 설정된다.
- Session Scope
- 세션 스코프는 웹 어플리케이션에서 세션마다 새로운 인스턴스를 생성
- 각 세션마다 별도의 빈 인스턴스를 생성하여 세션범위 내에서 공유한다.
- @Scope(”session”)로 설정된다.
- Application Scope(ServletContext Scope)
- 어플리케이션 스코프는 웹 어플리케이션의 생명주기 동안 단일 인스턴스를 생성한다.
- 웹 어플리케이션 내에 모든 요청및 세션에세 동일한 빈 인스턴스를 공유한다.
- @Scope(”application”)로 설정된다.
Bean 의존성 주입 방법
- 스프링에서 Bean 주입 방법은 다른 빈에게 빈 객체를 제공하고 의존성을 관리하는 프로세서를 나타낸다.
- 스프링에서 빈 주입은 주로 의존성 주입을 통해 이루어진다.
- 필드 주입 방식
@Autowired private MemberRepository memberRepository;
- 장점
1. 직관적 필드에 @Autowired만 사용하면 되기 때문에 코드가 단순하다.
- 단점
1. 의존성이 외부에 변경할 수 없어서 테스트하기 어렵다. 다른 클래스와 교체하거나 모의 객체 주입이 어렵다.
2. 의존성 주입을 외부에서 명시적으로 설정하기 어렵기 때문에 컴파일시 문제를 잡기 어렵다.
- Setter 주입 방식
private MemberRepository memberRepository;
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
- 장점
1. 객체를 생성 후에도 의존성을 변경할 수 있어서 유연성이 높다. 실행중에도 의존성 교체가 가능하다.
- Setter 메서드는 public으로 선언되어야 하므로 외부에서 임의로 수정될 위험이 있다. 또 중간에 의존성을 변경하는 경우가 드물기 때문에 너무 유연할 수 있다.
- 생성자 주입 방식
@Service
public class MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
- 장점
1. 의존성을 불변하게 만들어서 객체 생성 이후에 변경할 수 없도록 한다. 이로써 의존성을 더 안정적으로 관리할 수 있다.
2, 컴파일 시에 의존성 주입 오류를 쉽게 감지할 수 있다.
- 단위 테스트와 모의 객체를 사용한 테스트가 쉽고 효과적으로 수행된다.
- 단점
1. 코드가 다소 길어질 수 있다. 객체가 생성 될 때 의존성을 주입해야 하므로 생성자의 파라미터가 많아질 경우 코드의 가독성이 감소 할 수 있다.
번외. Lombok 사용
@Service
@RequiredArgsConstructor
public class UserSignupService {
private final UserRepository userRepository;
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Transactional
public void registerUser(SignupDTO signupDTO) {
User user = toEntity(signupDTO);
userRepository.save(user);
}
}
- 위 코드처럼 롬복의 @RequiredArgsConstructor 어노테이션을 사용해서 final필드로 되어있는 변수에 빈을 삽입한다.
- 가장 코드가 깔끔하고, 자주사용되는 방식이다.
출처및 참고
https://velog.io/@ldb0820/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-BEAN%EB%93%B1%EB%A1%9D-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85-%EB%B0%A9%EC%8B%9D
https://www.youtube.com/watch?v=3gURJvJw_T4
https://seongmun-hong.github.io/spring/Spring-Bean-Create
https://engineerinsight.tistory.com/44
https://www.youtube.com/watch?v=UcpLNgko8lg
https://steady-coding.tistory.com/594