public class Sample {
public void innerMethod1() {
innerMethod2();
}
public void innerMethod2() {
System.out.println("innerMethod2");
}
}
Sample ํด๋์ค ๋ด์ innerMethod1์ด๋ผ๋ ๋ด๋ถ ๋ฉ์๋๊ฐ innerMethod2๋ฅผ ํธ์ถํ๊ณ ์์ต๋๋ค.
์ด๋ ๊ฒ ํ ํด๋์ค ๋ด์์ ๋ด๋ถ ๋ฉ์๋๊ฐ ๋ ๋ค๋ฅธ ๋ด๋ถ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ๊ฒ์ Self Invocation, ์ฆ ์๊ฐ ํธ์ถ์ด๋ผ๊ณ ํฉ๋๋ค.
์ด๋ฐ Self Invocation์ ๋ณดํต์ ๊ฒฝ์ฐ๋ผ๋ฉด ์๋ฌด ๋ฌธ์ ์๋ ์ฝ๋์ด์ง๋ง ๋ง์ฝ @Transactional, @Async์ ๊ฐ์ด ํ๋ก์๋ฅผ ํตํด ๋์ํ๋ ์ด๋
ธํ
์ด์
์ ๋ง๋๋ฉด ์๋๋๋ก ๋์ํ์ง ์์ ์๋ ์์ต๋๋ค.
@Service
@RequiredArgsConstructor
public class SampleService {
private final SampleRepository sampleRepository;
@Transactional(readOnly = true)
public void makeNewSample(long id) {
sample sample = sampleRepository.findById(id);
if(sample != null)
throw new DuplicateException();
Sample sample = new Sample();
saveSample(sample);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveSample(Sample sample) {
sampleRepository.save(sample);
}
}
์์ ์ฝ๋๋ฅผ ๋ณด๋ฉด makeNewSample() ๋ฉ์๋ ๋ด๋ถ์์ saveSample() ๋ฉ์๋๋ฅผ ํธ์ถํ๋๋ฐ
์ด๋ @Transactional(propagation = Propagation.REQUIRES_NEW) ์ค์ ์ด ๋ฌด์๋๊ณ makeNewSample() ๋ฉ์๋์ readOnly = true ํธ๋์ญ์
์ปจํ
์คํธ๊ฐ ๊ทธ๋๋ก ์ ์ง๋์ด sampleRepository.save(sample);์ด ๋์ํ์ง ์๊ฒ ๋ฉ๋๋ค.
@Transactional, @Async ์ ๊ฐ์ AOP ์ด๋
ธํ
์ด์
๋ค์ ํ๋ก์ ๋ฐฉ์์ ์ฌ์ฉํฉ๋๋ค.
๊ทธ๋ฌ๋ฏ๋ก @Transactional ์ด๋
ธํ
์ด์
์ด ์ ์ธ๋ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด ํ๋ก์๋ฅผ ํตํด ํธ๋์ญ์
์ด ์์๋๊ณ ์ดํ์ ์ค์ ๊ฐ์ฒด์ makeNewSample() ๋ฉ์๋๊ฐ ํธ์ถ๋ฉ๋๋ค.
์ด๋ makeNewSample() ๋ฉ์๋์์ saveSample() ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด ์ด๋ ํ๋ก์๊ฐ ์๋ this ์ฐธ์กฐ์ ๋ํด ํธ์ถ๋ฉ๋๋ค.
์ด๋ ๊ฒ Self Inovcation์ด ์ผ์ด๋๋ฉด ํด๋น ๋ฉ์๋์ ์ ์ฉ๋ ์ด๋๋ฐ์ด์ค(Advice)๊ฐ ์คํ๋์ง ์์ต๋๋ค. ์์ ์์์์๋ Transactional(propagation = Propagation.REQUIRES_NEW) ์ค์ ์ด ๋ฌด์๋์ด์ ๋ฐ์ดํฐ๊ฐ ์ ์ฅ๋์ง ์๋ ์ํฉ์ด ๋ฐ์ํ๊ฒ ๋ฉ๋๋ค.
๋ณดํต ๋ณ๋์ ํด๋์ค๋ก ๋ถ๋ฆฌํด Self Invocation์ด ์ผ์ด๋์ง ์๋๋ก ํฉ๋๋ค.
์ด๋ก ์ธํด ํด๋์ค๊ฐ ๊ณผ๋ํ๊ฒ ๋ง์์ง ์ ์๊ธฐ ๋๋ฌธ์ ์ ์ ํ ์ฑ
์ ๋ถ๋ฆฌ๊ฐ ํ์ํฉ๋๋ค.
Self Injection์ผ๋ก this ๋์ ์ ์ฃผ์
๋ Proxy ์ฐธ์กฐ๋ฅผ ํตํด ๋ฉ์๋๋ฅผ ํธ์ถํฉ๋๋ค.
์ํ ์ฐธ์กฐ๊ฐ ๋ฐ์ํ ์ ์๊ธฐ ๋๋ฌธ์ @Lazy๋ก ์ฌ์ฉ ์ ์ด๊ธฐํํ ์ ์๋๋ก ํด์ผ ํฉ๋๋ค.
@Service
@RequiredArgsConstructor
public class SampleService {
private final SampleRepository sampleRepository;
@Lazy
@Autowired
private SampleService sampleService;
@Transactional(readOnly = true)
public void makeNewSample(long id) {
sample sample = sampleRepository.findById(id);
if(sample != null)
throw new DuplicateException();
Sample sample = new Sample();
sampleService.saveSample(sample);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveSample(Sample sample) {
sampleRepository.save(sample);
}
}
AopContext.currentProxy()AopContext์ currentProxy() ๋ฉ์๋๋ฅผ ์ด์ฉํด ํธ์ถํฉ๋๋ค.
ํด๋น ๋ฐฉ๋ฒ์ Spring Docs์ ๊ถ์ฅ๋์ง ์๊ณ , ์ตํ์ ์๋จ์ผ๋ก ๊ฐ๊ธ์ ํผํด์ผ ํ๋ค๊ณ ์์ฑ๋์ด ์์ต๋๋ค.
public class SampleServiceImpl implements SampleService {
@Transactional(readOnly = true)
public void makeNewSample() {
// This works, but it should be avoided if possible.
((SampleService) AopContext.currentProxy()).saveSample(sample);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveSample(Sample sample) {
// some logic...
}
}