안녕하세요.
https://blog.naver.com/sysysy0302 여니입니다 :)
1. 스프링 빈(bean)이란?
스프링은 스프링컨테이너를 통해 객체를 관리하는데, 스프링 컨테이너에 관리되는 객체를 Bean 이라고 한다.
<servlet-context(source)>
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- @Controller, @Service, @Repository, @Component ... 어노테이션의 기능을 활성화 시켜주는 기능 -->
<annotation-driven /> <!--; xml안에서 annotation을 활성화시키겠다 -->
<!--
정적인 파일들에 대한 요청을 mapping해줌.
/resources/* : /resources/a.jpg, /resources //; resources/* : resources 아래의 파일 1 , resources/* : resources 아래의 파일 모두
/resources/** : /resources/a.jpg, /resources , /resources/img/b.jpg
-->
<resources mapping="/resources/**" location="/resources/" />
<!--
주소 자동완성 도구
매번 같은주소(/WEB-INF/views/)를 추가해야하기 때문에 주소자동완성기능이 기본적으로 등록되어있음.
- view resolver라는 도구를 등록(spring에서 제공)
- class : InternalResourceViewResolver
- prefix : 접두어 / suffix : 접미어
- 문자열값을 return할때 해당 문자열에 접두어, 접미어를 붙여서 포워딩을 시켜준다.
-->
<beans:bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
primary="true"> <!--; ☆InternalResourceViewResolver -->
<beans:property name="prefix" value="/WEB-INF/views/" /> <!--; 접두어 :주소값 자동완성시켜주는 역할 -->
<beans:property name="suffix" value=".jsp" /> <!--; 접미어 :주소값 자동완성시켜주는 역할 -->
</beans:bean>
<!--
base-package에 등록된 패키지와 그 하위에 존재하는 모든 패키지를 scan(감시)해서 bean으로 등록가능한 클래스가 있다면
모두 등록 시키겠다.
bean? 스프링 프레임워크에서 관리하는 자바 객체. 딱 1개만 생성해서 관리.
-->
<context:component-scan base-package="com.kh.spring" /> <!--; com.kh.spring에 들어있는-->
</beans:beans>
2. 스프링 빈(bean)을 스프링 컨테이너에 등록하는 방법
2.1 컴포넌트 스캔과 자동 의존관계 설정
@Component 애노테이션이 있으면 스프링 빈으로 자동 등록된다.
또한, @Component를 포함하는 @Controller, @Service, @Repository 애노테이션도 스프링 빈으로 자동 등록된다.
회원 컨트롤러를 등록하고, 회원 컨트롤러가 회원서비스와 회원 리포지토리를 사용할 수 있게 의존관계를 설정해보자.
package com.kh.spring.member.controller;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.kh.spring.member.model.service.MemberService;
import com.kh.spring.member.model.validator.MemberValidator;
import com.kh.spring.member.model.vo.Member;
//; @Controller: servlet-context.xml이 자동으로 빈 클래스로 등록을 시켜준다
// Controller 타입의 어노테이션을 붙여주면 빈 스캐너가 자동으로 빈(bean)으로 등록해줌.
// (servlet-context.xml안에 있는 <context:component-scan>태그)
// @RequestMapping("/member") -> 공통주소(클래스레벨에 선언)
// localhost:8081/spring/member(공통주소)/login.me(그외주소, 메소드레벨에 선언)
// 단, 클래스레벨에 @RequestMapping이 존재하지 않는 경우 메서드레벨에서 단독으로 요청을 처리한다.
@Controller
public class MemberController {
/*
* 기존객체 생성 방식 private MemberService mService = new MemberService();
*
* 서비스가 동시에 많은 횟수가 요청이되면 그만큼 많은 객체가 생성된다. //; 메모리 관리상 유연성이 없다.
*
* Spring의 DI(Dependency Injection) -> 객체를 스프링에서 직접 생성해서 주입해주는 개념
*
* new 연산자를 쓰지 않고 선언만 한 후 @Autowired어노테이션을 붙이면 객체를 주입받을수 있다.
*/
// @Autowired //; spring 형식의 MemberService객체를 주입받고 있는 방식
private MemberService mService;
private MemberValidator memValidator;
/*
* ; 객체를 주입받는 방식이 여러가지 있다.
*
* 필드주입방식 장점 : 이해하기 편함. 사용하기도 편함.
*
* 필드주입방식의 단점 : 순환 의존성 문제가 발생할 수 있다. 무분별한 주입시 의존관계 확인이 어렵다. final 예약어를 지정할수가 없다.
* //; 즉 객체가 변경될 수 있으므로 위험하다.
*
*/
// 생성자 주입방식
public MemberController() {
}
@Autowired // ; 여기서 붙여주면 위에 중복 @Autowired는 지워주기
public MemberController(MemberService mService, MemberValidator memValidator) { //; memValidator객체를 주입해줄것임.
this.mService = mService;
this.memValidator = memValidator;
}
@Controller
public class MemberController {
private MemberService mService;
private MemberValidator memValidator;
public MemberController() {
}
@Autowired
public MemberController(MemberService mService, MemberValidator memValidator) {
this.mService = mService;
this.memValidator = memValidator;
}
=> 주석을 지운 생성자 주입 방식 부분만 살펴보면 다음과 같습니다.
- @Autowired
생성자에 @Autowired가 있으면 스프링이 연관된 객체를 스프링 컨테이너에 찾아서 넣어준다. 이렇게 객체 의존관계를 외부에서 넣어주는 것을 '의존성 주입(DI)'라 한다. 위 관계 그림에서 -> 의존관계를 표현한 화살표 역할을 해준다. (생성자가 1개만 있으면 생략 가능)
회원 관리 예제 때는 개발자가 테스트에서 직접 주입했고, 여기서는 @Autowired에 의해 스프링이 주입해준다.
바로 memberService가 스프링 컨테이너에 스프링 빈으로 등록되어 있지 않기 때문이다.
memberService도 스프링 빈으로 등록해주어야 한다.
<MemberService>
package com.kh.spring.member.model.service;
import com.kh.spring.member.model.vo.Member;
public interface MemberService {
}
<MemberServiceImpl>
//; MemberServiceImpl : MemberService인터페이스
@Service
public class MemberServiceImpl implements MemberService{
@Autowired //; MemberDao에 빈객체 등록을 해두었기때문에 member 주입받을수 있음
private MemberDao memberDao;
MemberServiceImpl는 MemberService의 interface로 등록해준다.
<MemberDao>
package com.kh.spring.member.model.dao;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.kh.spring.member.model.vo.Member;
@Repository //; MemberDao에 빈객체 등록을 해두었음
public class MemberDao {
}
memberRepository도 스프링 빈으로 등록해야 한다.
memberService와 memberRepository가 스프링 컨테이너에 스프링 빈으로 등록되었다.
참고로, 스프링은 스프링 컨테이너에 스프링 빈을 등록할 때, 기본으로 싱글톤으로 등록한다.(유일하게 하나만 등록해서 공유한다.) 따라서 같은 스프링 빈이면 모두 같은 인스턴스다. 설정으로 싱글톤이 아니게 설정할 수 있지만, 특별한 경우를 제외하면 대부분 싱글톤을 사용한다.
2.2 자바 코드로 직접 스프링 빈 등록하기(Configuration)
@Configuration과 @Bean 어노테이션을 이용해 스프링 빈을 등록한다.
@Configuration을 이용하면 스프링 프로젝터에서 Configuration 역할을 하는 Class를 지정할 수 있다.
SpringConfig 파일을 생성하고 다음과 같이 코드를 작성할 수 있다.
@Configuration
public class SpringConfig {
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
참고
- XML로 설정하는 방식도 있지만 최근에는 잘 사용하지 않으므로 생략한다.
- DI에는 필드 주입, setter 주입, 생성자 주입 이렇게 3가지 방법이 있다. 의존관계가 실행중에 동적으로 변하는 경우는 거의 없으므로 생성자 주입을 권장한다.
- 실무에서는 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용한다. 그리고 정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록한다.
- @Autowired 를 통한 DI는 helloConroller , memberService 등과 같이 스프링이 관리하는 객체에서만 동작한다. 스프링 빈으로 등록하지 않고 내가 직접 생성한 객체에서는 동작하지 않는다.
+ 해당 포스트는 아래 링크의 포스팅을 참고하여 공부하기 위해 작성하였습니다.
https://velog.io/@falling_star3/Spring-Boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B9%88bean%EA%B3%BC-%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84