끄적끄적 코딩일지

[Spring 기초] Bean 등록 및 사용하기(Autowired) 본문

Spring

[Spring 기초] Bean 등록 및 사용하기(Autowired)

BaekGyuHyeon 2022. 5. 30. 10:37

Bean 등록방법

Spring에서 Bean을 등록하는 방법은 크게 xml 파일을 사용하는 방법과 Annotation을 사용하는방법 2가지로 나뉜다.(최근에는 Annotation을 사용하는 방법이 주로 쓰인다.)


xml 파일으로 등록하는 방법

스프링은 시작시에 webapp/web.xml과 webapp/WEB-INF/spring/root-context.xml , webapp/WEB-INF/spring/servlet-context.xml 으로 bean을 등록한다.

 

1.web.xml : Spring 동작을 위한 설정정보이다. Spring 버전, 프로젝트명, 배포자, Bean을 등록하는 root-context.xml 혹은 servlet-context.xml 위치, welcom page등을 설정할 수 있다.

 

2. servlet-context : 말 그대로 servlet에 대한 context 설정을 의미하며 Controller,Handler,ViewResolver와 같은 웹과 연관되어 있는 bean을 정의한다.

※ Servlet : 자바를 사용하여 웹을 만들기 위해 필요한 기술. 즉 Java를 사용하여 사용자 요청 처리 및 html상에서 동적 element를 생성할 수 있도록 지원

 

3. root-context.xml : 데이터 저장소(Repository)나 비즈니스 로직이 담겨있는 서비스(service)와 같은 여러 요청에 대해 공유해야 하는 bean들을 정의한다. (로그인에 대한 처리를 할시 사용자가 어떤값을 입력하든 해당 값으로 계정정보를 조회하고 결과를 반환하는 로직은 변하지 않는다. 이를 여러 요청에 대해서 공유한다고 표현한다.)

 

servlet-context 와 root-context는 용도에 따른 구분일뿐 해당 파일에서 bean을 등록하는 방법은 동일하다.

 

public class Member{
    String id;
    String pw;
    String name;
}
pulbic class MemberRepository{
    public Member find(long id){
    	//......(대충 DB 연결해서 입력한 id으로 Member정보 찾는 로직)
        return member;
    }
    public List<Member> findAll(){
    	//......(대충 DB 연결해서 Member테이블 전체 조회하는 로직)
        return members;
    }
    public Member save(Member member){
    	//......(대충 DB 연결해서 Member정보 저장 또는 업데이트하는 로직)
        return member;
    }
    public void delete(Member member){
    	//......(대충 DB 연결해서 Member정보 삭제하는 로직)
    }
}

예를들어 위와같이 회원정보 class와 해당 class를 DB에 저장하거나 조회, 삭제하는 Repository가 있다고 해보자. Repository는 모든 요청에 데이터를 조회,수정,삭제하는 로직은 변함없이 사용될 것이다. 

 

A.이 Repository 처럼 단순 객체를 Bean으로 등록하는 경우

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	<bean id="memberRepository" class="thisis.project.package.MemberRepository"/>
</beans>

위와같이 id를 정의하고 (기본 규칙은 해당 class이름의 첫글자를 소문자로 하는것) 해당 class가있는 package를 포함한 경로를 입력한다.

 

그럼 만약 해당 클레스를 생성할때 간단한 매개변수를 입력받는 경우는 어떻게 할까

 

B. Bean으로 등록하는 객체가 단순 값을 매개변수로 입력받는경우

pulbic class MemberRepository{
    String tableName;
    int tableNumber;
    
    public MemberRepository(String tableName,int tableNumber){
        this.tableName = tableName;
        this.tableNumber = tableNumber;
    }
    public Member find(String id){
    	//......(대충 DB 연결해서 입력한 id으로 Member정보 찾는 로직)
        return member;
    }
    public List<Member> findAll(){
    	//......(대충 DB 연결해서 Member테이블 전체 조회하는 로직)
        return members;
    }
    public Member save(Member member){
    	//......(대충 DB 연결해서 Member정보 저장 또는 업데이트하는 로직)
        return member;
    }
    public void delete(Member member){
    	//......(대충 DB 연결해서 Member정보 삭제하는 로직)
    }
}

위와같은경우 해당 객체를 생성할때 매개변수를 입력받아야 생성이 가능하다. 그런경우는 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="memberRepository" class="thisis.project.package.MemberRepository">
    	<constructor-arg name="tableName" value="member_table"/>
        <constructor-arg name="talbeNumber" value="1"/>
    </bean>
</beans>

으로 constructor-arg를 주어 매개변수를 주입할 수 있다. 여러개의 매개변수를 입력받는경우 기본적으로는 입력한  순서대로 매개변수를 전달하나 index 혹은 name 값을 주어 입력할 매개변수를 지정할 수 있다.

 

그럼 Bean으로 등록하는 객체가 다른 Bean을 의존하는 경우는 어떻게 해야할까

 

C. 다른 Bean을 매개변수로 받는 경우

public class MemberService{
    MemberRepository repo;
    public MemberService(MemberRepository repo){
        this.repo = repo;
    }
    public Member saveMember(Member member){
        return repo.save(member);
    }
    public Member login(String id, String pw){
        Member m = repo.find(id);
        if(m != null){
            if(m.pw.equals(pw))
                return m;
            else
                return null;
        }else
            return null;
    }
}

위의 MemberService 내부 로직에는 MemberRepository가 필요하다. 위의 경우처럼 내부 로직에서 다른 객체를 의존하고 있어 객체 생성시 필요한 다른객체를 입력받아야 하는 경우가 있다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="memberRepository" class="thisis.project.package.MemberRepository">
    	<constructor-arg name="tableName" value="member_table"/>
        <constructor-arg name="talbeNumber" value="1"/>
    </bean>
    <bean id="memberService" class="thisis.project.package.MemberService">
    	<constructor-arg name="repo" ref="memberRepository"/>>
    </bean>
</beans>

그럴경우 constructor-arg에 ref="{다른 bean id}"처럼 주입할 Bean의 id를 입력하면된다.


Java Annotation을 사용하는 방법

Spring 2.0부터 Java Annotation을 사용해서 Bean등록, 설정등의 등록을 지원하면서 요즘은 Annotation을 사용해서 Bean을 등록한다. Java Annotation을 사용한 Bean등록은 @Component, @Repository,@Service,@Controller 등을 class에 적용해서 자동으로 Bean을 등록하거나  @Bean을 사용하여 수동으로 등록하는 방법이 있다.

@Repository
public class MemberRepository{
   ....
}
@Service
public class MemberService{
   ....
}
@Controller
public class MemberController{
   ....
}

 

위처럼 각각 역할에 따라 @Service, @Repository, @Controller, @Component등을 class위에 선언하면 자동으로 bean 등록이 된다.

 

반대로 수동으로 bean을 등록하는 방법은

 

@Configuration
public class ResourceConfig{
    @Bean
    public MemberRepository getMemberRepo(){
        return new MemberRepository();
    }
}

위처럼 @Configuration등을 사용하여 설정정보를 Bean으로 등록하고 bean으로 등록할 객체를 return하는 Method를 @Bean을 사용하여 선언한다. 

※ Controller,Service,Componet등 자동으로 bean을 등록해주는것을 사용해도 가능하다. 하지만 보통 역할의 구분때문에 Configuration을 쓴다. 

 


Bean 등록한 객체 사용하기

위에서는 Spring상에서 여러가지 방법으로 Bean을 등록하는 방법을 알아보았다. 그럼 Bean으로 등록한 객체를 다시 조회하는 방법을 알아보자.

 

Bean으로 등록하는 객체를 개발자가 Method등을 호출해서 객체를 가져오는것이 아니라 Spring이 직접 외부에서 (객체를 사용하는 class 밖을 의미한다.) 주입한다. (DI, Dependency Injection,의존관계주입) 이처럼 프레임워크가 직접 객체를 생성하고 관리하는것을 IoC(Inverse Of Controll,제어의 역전)이라고 한다.

 

Spring에서 의존관계를 주입받을때는 @Autowired라는 Annotation을 사용한다. 하지만 해당 Annotation을 어떻게 사용하느냐에 따라 의존관계를 조금씩 다르게 설정할 수 있다. 그 방법은 크게 4가지로 분류한다.

 


1. 생성자 주입

@Controller
public class PageController{
   private final MemberService memberService;
   private final AnotherService anotherService;
   @Autowired
   private MemberController(MemberService memberService,AnotherService anotherService){
       this.memberService = memberService;
       this.anotherService = anotherService;
   }
   ....
}

이름 그대로 위처럼 생성자를 만들고 @Autowired를 선언하면 Spring이 DI Container에서 해당 객체를 꺼내서 주입한다.

 

※ Spring 4.7 이후버전에서는 생성자가 하나일때 @Autowired를 생략해도 자동으로 DI를 수행해 준다. 때문에 Lombok의 @RequredArgConstructor와 함께 사용하면 따로 생성자와 @Autowired를 사용하지 않아도 쉽게 의존관계를 주입받을 수 있다. 

 

또한 final을 사용할 수 있어 Spring에서 해당 객체를 주입해주지 않는 오류가 발생해도 이를 컴파일시에 잡아준다.(컴파일시에 나는 오류는 가장 빠르게 해결할 수 있는 좋은 오류이다.)

생성자 호출시점에 딱 1번 호출되는것이 보장된다. 때문에 불변, 필수 의존관계에 주로 사용된다. 즉 Spring이 오류가 난다해도 코드 설계상 객체가 바뀌거나 두번 주입되는 경우가 없다.(버그가 일어날 가능성이 줄어든다.)

 

2. 수정자 주입(setter 주입)

@Component
public class ThisIsBean{
    private AnotherBean bean1;
    private OtherBean bean2;
    @Autowired
    public void setBean1(AnotherBean bean1){ this.bean1 = bean1;}
    
    @Autowired
    public void setBean2(OtherBean bean2){ this.bean2 = bean2;}
}

위와같이 setter 메소드로 의존관계를 주입받는것을 의미한다. 해당 객체를 여러번 주입받을수도 있고 interface를 사용하면 어떤 bean을 주입받을지 선택도 가능하다. 때문에 선택, 변경 가능성이 있는 의존관계에 사용에 사용된다. 즉 필요시 외부에서 강제로 호출하게만 하면 변경이 가능하다.

 

※ 생성자 주입과도 같이 사용하는 경우가 있다. 그럴경우 생성자 주입이 가장 먼저 일어나고 그 후에 수정자 주입이 이루어진다.

※ @Autowired의 기본 동작은 주입할 대상이 없으면 오류를 발생한다. 주입한 대상이 없어도 동작하게 하려면 @Autowired(required = false)를 사용하면 된다.

 

3. 필드 주입

@Controller
public class PageController{
   @Autowired
   private MemberService memberService;
   
   @Autowired
   private AnotherService anotherService;

   ....
}

사용할 객체에다가 직접 주입하는 방법이다. 코드가 간결하지만 외부에서 변경이 불가능해서 테스트 하기 힘들고 DI 프레임워크가 없으면 아무것도 할 수 없다는 특징이 있다.

실제로도 테스트 코드 혹은 @Configuration에서 간혹 특수한 목적으로 사용하는 정도이다. 

 

4. 일반 Method 주입

@Component
public IamBean{
    private AnotherBean anotherBean;
    private OtherBean otherBean;
     
    @Autowired
    public void iamCustomMethod(AnotherBean anotherBean, OtherBean otherBean){
        this.anotherBean = anotherBean;
        this.otherBean = otherBean;
    }
}

위처럼 일반 Method를 사용해서 주입받는 방법이다. 한번에 선택적으로 여러 필드에 주입받을수 있지만 잘 사용되지는 않는다.

 


위처럼 @Autowired를 통해 자동으로 의존관계를 주입받을때는 @Controller,@Service,@Configuration,@Component처럼 Bean으로 등록된 객체만 가능하다. 그 외의 경우에는 SpringContext에서 필요한 객체를 직접 조회해야 한다.

 

'Spring' 카테고리의 다른 글

[Spring 기초] Hibernate 다루기  (0) 2022.06.05
[Spring 기초] Spring Security 사용하기  (0) 2022.06.04
[Spring 기초] JPA 사용하기  (0) 2022.06.01
[Spring 기초] @Qualifier, @Primary  (0) 2022.05.30
[Spring 기초] IoC, DI, Bean 개념  (0) 2022.05.30