본문 바로가기
공부/프로그래밍

[Springboot] spring bean과 싱글톤(singleton)

by demonic_ 2019. 10. 1.
반응형

일정시간마다 데이터를 정재하는 스케쥴이 실행된다. 해당 스케쥴을 실행하기 위해 Service Bean 을 수행하는데 초반에는 별 문제가 되지 않았다가 최근 데이터가 늘어나면서 점점 처리속도가 느려지기 시작했다. 그래서 Schedule 수행 시간을 늘려 처리하려 했지만, 이전 스케쥴러 실행이 종료되지 않을 경우, 새로 만든 스케쥴러가 실행되지 않았다. 그리고 이전 스케쥴러가 끝난 후에야 새로만든 스케쥴러가 수행되기 시작했다. (아마 스택 상태로 대기하고 있었던 듯 하다)

그래서 이름만 바꾼 빈을 주입해서 콘솔에 띄워보았다

@Component
public class AppRunner implements ApplicationRunner {
	@Autowired
	private SampleService sampleService1;

	@Autowired
	private SampleService sampleService2;

	@Override
	public void run(ApplicationArguments args) throws Exception {

	    System.out.println(sampleService1);

	    System.out.println(sampleService2);
	}
}

결과

...
com.example.sample.service.SampleService@439f37c4
com.example.sample.service.SampleService@439f37c4

 

이러한 현상이 발생한 이유는 Spring의 Bean 은 기본적으로 singleton 으로 관리되고 있기 때문이다. 이는 어찌보면 당연한 현상이다. 서버가 실행되는 동안 수도 없이 많이 호출 될 수 있는데, 그때마다 일일히 생성하게 된다면 메모리가 버티지 못할 것이기 때문이다. 만약 초당 10000번의 호출이 있다면 10000개를 일일히 만드는데 엄청난 자원을 사용할 것이다.

 

그럼 sampleService1 안에 있는 변수를 수정하면 sampleService2 에서 호출할떄 그 값이 그대로 불러와질까?

sampleService 클래스를 다음과 같이 추가한 후, 호출한 AppRunner 를 수정했다.

@Service
public class SampleService {

    private String sampleText;

    @PostConstruct
    void init() {
        this.sampleText = "안녕하세요";
    }

    public void setSampleText(String sampleText) {
        this.sampleText = sampleText;
    }

    public String getSampleText() {
        return sampleText;
    }
}

 

ApplicationRunner 수행

@Component
public class AppRunner implements ApplicationRunner {

    @Autowired
    private SampleService sampleService1;

    @Autowired
    private SampleService sampleService2;

    @Override
    public void run(ApplicationArguments args) throws Exception {
    	System.out.println(sampleService1);
	    System.out.println(sampleService2);

        System.out.println("sampleService1 안에 sampleText 의 값: " +  sampleService1.getSampleText());

        // sampleService1 에서 안에 변수값을 수정
        sampleService1.setSampleText("안녕, 반가워");

        // sampleService1과 sampleService2 에서 sampleText 값을 호출
        System.out.println("sampleService1 의 값: " + sampleService1.getSampleText());
        System.out.println("sampleService2 의 값: " + sampleService2.getSampleText());

        // 변경된 값을 둘다 호출하고 있다.(singleton)
    }
}

결과

...
com.example.sample.service.SampleService@439f37c4
com.example.sample.service.SampleService@439f37c4
sampleService1 안에 sampleText 의 값: 안녕하세요
sampleService1 의 값: 안녕, 반가워
sampleService2 의 값: 안녕, 반가워

 

그럼 호출 될 때마다 생성하게 할 순 없을까? 가능하다
SampleService 클래스에 Scope 어노테이션을 추가하고 value 에 prototype 을 선언한다.

@Service
@Scope(value = "prototype")
public class SampleService {
	...
}

이 후 실행하보면 다음과 같이 각각 객체가 생성됨을 확인할 수 있다.

실행결과

...
com.example.sample.service.SampleService@49860486
com.example.sample.service.SampleService@3e3e51a6
sampleService1 안에 sampleText 의 값: 안녕하세요
sampleService1 의 값: 안녕, 반가워
sampleService2 의 값: 안녕하세요

 

끝.

 

 

 

참고:
https://javaslave.tistory.com/45
https://cnpnote.tistory.com/entry/SPRING-Spring-autowired는-non-singleton-컨테이너가-아닌가요

반응형

댓글