wintertreey 님의 블로그
@Scope, @Autowired @Resoruce, @PreConstruct @PreDestroy 본문
@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에 의한 매핑.
스프링 빈의 라이프사이클
- 스프링 컨테이너 생성
- 스프링 빈 생성
- 의존관계 주입
- 초기화 콜백 (빈이 생성되고, 빈의 의존관계 주입이 완료된 후 호출)
- 사용
- 소멸전 콜백 (빈이 소멸되기 직전에 호출)
- 스프링 종료
객체의 생성과 초기화를 분리하는 것이 좋다.
생성자는 필수 정보는 받고, 메모리를 할당해서 객체를 생성한다.
반면에 초기화는 생성된 값을 활용해서 외부 커넥션에 연결하는 역할을 한다.
생성자 안에서 무거운 작업을 하는 것은 유지보수면에서 좋지 않다.
스프링은 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
https://dahliachoi.tistory.com/24
https://goddaehee.tistory.com/46
'백엔드 > Spring' 카테고리의 다른 글
AOP란? Proxy란? (0) | 2024.07.08 |
---|---|
@Qualifier, @Resource, @Value (0) | 2024.07.07 |
MyBatis란? Pooling기법 (0) | 2024.07.04 |
constructor/setter injection, scope 다형성 캐스팅 (0) | 2024.07.04 |
@Component , @Autowired, @Configuration, @ComponentScan (0) | 2024.07.04 |