Spring

@Scope, @Autowired @Resoruce, @PreConstruct @PreDestroy

wintertreey 2024. 7. 7. 17:48

@Scope("Singleton") vs @Scope("prototype")

스코프의 기본값이 싱글톤인건 앞서 공부했다. 

그렇다면 프로토타입, 싱글톤은 어떤 상황에서 구분해서 사용할 수 있을까

 

Bank.java

package pack.bank;

public interface Bank {
	void inputMoney(int money); //입금
	void outputMoney(int money); //출금
	int getMoney(); //잔고확인
}

HanaBank.java

package pack.bank;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
//@Scope("prototype")
public class HanaBank implements Bank{
	private int money= 1000;
	
	@Override
	public void inputMoney(int money) {
		this.money += money;
		
	}
	@Override
	public void outputMoney(int money) {
		this.money -= money;
		
	}
	@Override
	public int getMoney() {
		return money;
		
	}
}

SinhanBank.java

package pack.bank;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component("sin")
//@Scope("prototype") //기본은  싱글톤 
public class SinhanBank implements Bank{
	private int money= 5000;
	
	@Override
	public void inputMoney(int money) {
		this.money = this.money+ money;
		
	}
	@Override
	public void outputMoney(int money) {
		int imsi = money;
		this.money -= imsi;
		
	}
	@Override
	public int getMoney() {
		return money;
		
	}
	
}

 

Gogek.java

package pack.gogek;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

import pack.bank.Bank;
import pack.bank.HanaBank;
import pack.bank.SinhanBank;

@Service
@ComponentScan("pack.bank")
//@Scope("prototype")
public class Gogek {
	private Bank bank; 
	
	@Autowired(required=false)//type으로 연결
	//있으면 사용 없으면 사용안하겠다. true일때 없으면 에러떨어짐. 
	private SinhanBank sinhanBank;
	
	@Resource(name="hanaBank") //name으로 연결
	private HanaBank hanaBank;
	
	public void selectBank(String sel) {
		if(sel.equals("sinhan")) {
			bank = sinhanBank;
		}else if(sel.equals("hana")) {
			bank = hanaBank;
		}
	}
	
	public void playInputMoney(int money) {
		bank.inputMoney(money);
	}
	
	public void playOutputMoney(int money) {
		bank.outputMoney(money);
	}
	
	private String msg;
	
	@PostConstruct //생성자 처리 후 자동호출: 초기화 작업가능
	public void abc() {
		msg = "계좌 잔고: ";
	}
	
	@PreDestroy //웹서비스 종료직전 자동호출: 마무리 작업 가능
	public void def() {
		if(sinhanBank != null) sinhanBank = null;
		if(hanaBank != null) hanaBank = null;
	}
	
	public void showMoney() {
		//System.out.println("계좌잔고: "+bank.getMoeny());
		System.out.println(msg+ bank.getMoney());
	}
	
	
}

 


스코프를 다루기전 간단하게 확인하고 넘어가자. 

 

@Autowired- 타입에 의한 매핑 @Resource- 이름에 의한 매핑

 

@Autowired

field, setter, constructor injection 에 사용.

type에 의한 매핑. 

동일한 타입의 빈이 여러개 존재할경우, 기본적으로 참조변수의 이름과 동일한 빈을 찾아 주입.

이때 Qualifier 사용할 수 도있다.

 

@Resource

field, setter injection 에 사용. costructor 불가!!

기본적으로 참조벼누의 이름과 동일한 빈이 존재하면 해당 빈 주입.

name에 의한 매핑.

 


스프링 빈의 라이프사이클

  1. 스프링 컨테이너 생성
  2. 스프링 빈 생성
  3. 의존관계 주입
  4. 초기화 콜백 (빈이 생성되고, 빈의 의존관계 주입이 완료된 후 호출)
  5. 사용
  6. 소멸전 콜백 (빈이 소멸되기 직전에 호출)
  7. 스프링 종료

객체의 생성과 초기화를 분리하는 것이 좋다.

생성자는 필수 정보는 받고, 메모리를 할당해서 객체를 생성한다.

반면에 초기화는 생성된 값을 활용해서 외부 커넥션에 연결하는 역할을 한다.

생성자 안에서 무거운 작업을 하는 것은 유지보수면에서 좋지 않다.

 

스프링은 3가지 방법으로 빈 생명주기 콜백을 지원한다.

  • 인터페이스 (InitializingBean, DisposableBean)
  • 설정 정보에 초기화 메서드, 종료 메서드 지정
  • @PostConstruct, @PreConstruct

그저 초기화와 종료 메서드에 각각 @PostConstruct, @PreDestroy을 붙여주면 된다.

 

@postConstruct

- 객체의 초기화 부분
- 객체가 생성된 후 별도의 초기화 작업을 위해 실행하는 메소드를 선언

 

@PreDestroy

- 마지막 소멸 단계
- 스프링 컨테이너에서 객체(빈)를 제거하기 전에 해야할 작업이 있다면 메소드위에 사용하는 어노테이션.

 

단점

  • 외부라이브러리에 적용하지 못한다.

 

따라서 외부 라이브러리를 초기화, 종료하려면  @Bean의 initMethod, destroyMethod를 사용한다.

 


GogekMain.java

package pack.gogek;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class GogekMain {

	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("bankinit.xml");
		
		Gogek daniel = (Gogek)context.getBean("gogek");
		daniel.selectBank("sinhan");
		daniel.playInputMoney(500);
		daniel.playOutputMoney(200);
		System.out.println("daniel- ");
		daniel.showMoney();
		
		Gogek john = (Gogek)context.getBean("gogek");
		john.selectBank("sinhan");
		john.playInputMoney(500);
		john.playOutputMoney(200);
		System.out.println("john- ");
		john.showMoney();
		
		Gogek oscar = (Gogek)context.getBean("gogek");
		oscar.selectBank("hana");
		oscar.playInputMoney(500);
		oscar.playOutputMoney(100);
		System.out.println("oscar- ");
		oscar.showMoney();
		
		System.out.println("객체주소: "+daniel);
		System.out.println("객체주소: "+john);
		System.out.println("객체주소: "+oscar);
		// 모두 같은 결과출력. 싱글톤. 인스턴스1개.//객체주소: pack.gogek.Gogek@446a1e84
		//신한, 하나, 고객에 스코프프로토타입@걸어주니 모두 주소가 다르게 즉 3개의 객체가 생성됨을 확인할수있다.
		

	}

}

다니엘까지있었을땐 괜찮았다.

 

출력하면 

다니엘-신한 5300

존-신한 5600

오스카-하나 1400

이 나온다. 

 

존이 5300이 나와야 정상! 이유는 존- 신한이 다니엘-신환과 연동되어버린것! 

이는 작업시 싱글톤으로 객체를 생성했기 때문이다. 

이런 경우 scope를 프로토타입으로 해줘야한다. 

 


 

https://codingnojam.tistory.com/13

 

[Spring] @Autowired, @Inject, @Resource 차이점 비교정리

안녕하세요 coding-knowjam입니다. 오늘은 의존성 주입에 사용하는 @Autowired, @Inject, @Resource에 대해서 각각에 대해 어떤 특징과 차이점이 있는지 알아보겠습니다. 예시로 사용할 클래스는 아래와 같

codingnojam.tistory.com

https://dahliachoi.tistory.com/24

 

Spring 핵심원리-기본[ 빈 생명주기 콜백]

데이터 베이스 커넥션 풀이나, 네트워크 소캣처럼 애플리케이션 시작 시점에 필요한 연결을 미리 해두고 애플리케이션 종료 시점에 연결을 모두 종료하는 작업을 하려면 객체의 초기화와 종료

dahliachoi.tistory.com

https://goddaehee.tistory.com/46

 

[Spring] @PostConstruct , @PreDestroy

@postConstruct - 객체의 초기화 부분 - 객체가 생성된 후 별도의 초기화 작업을 위해 실행하는 메소드를 선언한다. - @PostConstruct 어노테이션을 설정해놓은 init 메소드는 WAS가 띄워질 때 실행된다. @Pre

goddaehee.tistory.com