AOP๋ ์คํ๋ง 3๋ ๊ธฐ๋ฐ๊ธฐ์ ์ค ํ๋์ด๋ค.(IoC/DI, ์๋น์ค ์ถ์ํ)
OOP๋ฅผ ๋์ฒดํ๋ ค๋ ๊ฒ ์ฒ๋ผ ๋ณด์ด๋ AOP์ ๋ฑ์ฅ๋ฐฐ๊ฒฝ๊ณผ ์คํ๋ง์ด ์ด๊ฒ์ ๋์ ํ ์ด์ , ์ฅ์ ์ ์ดํดํด์ผ ํ๋ค.
1. ๋ฑ์ฅ๋ฐฐ๊ฒฝ : ์ฌ๋ฌ ํต์ฌ๊ธฐ๋ฅ๋ค์ ์ค๋ณต๋๋ ๋ถ๊ฐ๊ธฐ๋ฅ๋ค์ด ์์์ 2. ์ด์ : ๋ถ๊ฐ๊ธฐ๋ฅ๋ค์ ๊น๋ํ๊ฒ ๋ฝ์๋ด๊ณ ์ถ์์ 3. ์ฅ์ : ์ค๋ณต ์์ด ๋ ๋ฆฝ์ ์ธ ๋ชจ๋๋ก ๊ตฌ๋ถํด๋์. ํต์ฌ๊ธฐ๋ฅ์ ์์ํ๊ฒ ๊ทธ ๊ธฐ๋ฅ์ ๋ด์ ์ฝ๋๋ก๋ง ์กด์ฌํ๊ณ ๋ ๋ฆฝ์ ์ผ๋ก ์ดํด๋ณผ ์ ์๋๋ก ๊ตฌ๋ถ๋ ๋ฉด์ ์กด์ฌํ๊ฒ ๋จ
์ด๋๋ฐ์ด์ค : ์คํ๋ง์์ ํ๊น ๊ฐ์ฒด์ ์ ์ฉํ ๋ถ๊ฐ๊ธฐ๋ฅ์ ๋ด์ ๊ฐ์ฒด
ํฌ์ธํธ์ปท : ๋ฉ์๋ ์ ์ ์๊ณ ๋ฆฌ์ฆ์ ๋ด์ ๊ฐ์ฒด
์๋น์ค ๊ณ์ธต์ ํธ๋์ญ์ ์ด ์์๋๊ณ ์ข ๋ฃ๋๋ ๊ฒฝ๊ณ๋ก ์ ํ๋ค๋ฉด, ํ ์คํธ์ ๊ฐ์ ํน๋ณํ ์ด์ ๊ฐ ์๋๊ณ ๋ ๋ค๋ฅธ ๊ณ์ธต์ด๋ ๋ชจ๋์์ DAO์ ์ง์ ์ ๊ทผํ๋ ๊ฒ์ ์ฐจ๋จํด์ผ ํ๋ค.
ํต์ฌ : 504์ชฝ AOP: ์ ์คํํธ ์งํฅ ํ๋ก๊ทธ๋๋ฐ
Aspect : ๊ทธ ์์ฒด๋ก ์ ํ๋ฆฌ์ผ์ด์ ์ ํต์ฌ ๊ธฐ๋ฅ์ ๋ด๊ณ ์์ง๋ ์์ง๋ง, ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ฑํ๋ ์ค์ํ ํ ๊ฐ์ง ์์์ด๊ณ ํต์ฌ๊ธฐ๋ฅ์ ๋ถ๊ฐ๋์ด ์๋ฏธ๋ฅผ ๊ฐ๋ ํน๋ณํ ๋ชจ๋์ ๊ฐ๋ฆฌํจ๋ค.
- ์ฝ๋๋ฅผ ์ ๋ฆฌํด๋ณธ๋ค :
- ํ ๋ฉ์๋๊ฐ ํ๋์ ์ฑ ์๋ง ๊ฐ๋๋ก : UserService.upgradeLevelsInternal()
- ํ ํด๋์ค๊ฐ ํ๋์ ์ฑ ์๋ง ๊ฐ๋๋ก : UserService์์ ํธ๋์ญ์ ๊ฒฝ๊ณ์ค์ ์ฝ๋
- ๋ฉ์๋ ๋ถ๋ฆฌ
- upgradeLevels()์์ ํธ๋์ญ์ ๊ฒฝ๊ณ์ค์ ์ฝ๋์ ๋น์ฆ๋์ค ๋ก์ง ์ฝ๋๋ฅผ ๋ถ๋ฆฌํ๋ค.
-
์ฝ๋ ์ ๊ธฐ/ํผ์น๊ธฐ
// ํธ๋์ญ์ ๊ฒฝ๊ณ์ค์ ์ฝ๋๋ง ๋จ์ ๋ฉ์๋ public void upgradeLevels() { TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); try { upgradeLevelsInternal(); transactionManager.commit(status); } catch (Exception e) { transactionManager.rollback(status); throw e; } } // upgradeLevels์์ ๋น์ฆ๋์ค๋ก์ง๋ง ๋ถ๋ฆฌ private void upgradeLevelsInternal() { List userList = userDao.getAll(); userList.forEach(user -> { if (canUpgradeLevel(user)) { upgradeLevel(user); } }); }
- DI๋ฅผ ์ด์ฉํ ํด๋์ค์ ๋ถ๋ฆฌ
- ํธ๋์ญ์ ์ ๋ด๋นํ๋ ์ฝ๋๋ UserService์ ์์ ํ์๊ฐ ์์ผ๋ ํด๋์ค ๋ฐ์ผ๋ก ๋ฝ์๋ธ๋ค.
- UserService์ ์ธํฐํ์ด์ค๋ฅผ ๋์ ํ๊ณ ๊ตฌํํด๋์ค๋ฅผ 2๊ฐ ๋๋ค(UserServiceImpl, UserServiceTx)
- UserServiceTx๋ ํธ๋์ญ์ ๊ฒฝ๊ณ์ค์ ์ ๋ํ ์ฑ ์๋ง ๊ฐ๊ฒ๋๋ค.
- UserServiceTx๋ ๋น์ฆ๋์ค ๋ก์ง์ ์ ํ ๊ฐ์ง ์๊ณ ๋ค๋ฅธ UserService ๊ตฌํ ์ค๋ธ์ ํธ์ ์์๋ง ํ๋ค.
- ํธ๋์ญ์ ์ด ์ ์ฉ๋ UserServiceTx
-
์ฝ๋ ์ ๊ธฐ/ํผ์น๊ธฐ
@RequiredArgsConstructor public class UserServiceTx implements UserService { private final UserService userService; private final PlatformTransactionManager transactionManager; ... public void upgradeLevels() { TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); try { userService.upgradeLevels(); transactionManager.commit(status); } catch (Exception e) { transactionManager.rollback(status); throw e; } } ... }
- ํธ๋์ญ์
๊ฒฝ๊ณ์ค์ ์ฝ๋ ๋ถ๋ฆฌ์ ์ฅ์
- UserServiceImpl์ ํธ๋์ญ์ ๋ด์ฉ์ ์ ๊ฒฝ์ฐ์ง ์์๋ ๋๋ค.
- ๋น์ฆ๋์ค ๋ก์ง์ ๋ํ ํ ์คํธ๋ฅผ ์์ฝ๊ฒ ๋ง๋ค์ด๋ผ ์ ์๋ค.(๋ค์์ ์์ ํ์ธ)
- @Transactional์ด ๋ ํธ๋ฆฌํด ๋ณด์ธ๋ค.
- ๊ฐ์ฅ ํธํ๊ณ ์ข์ ํ
์คํธ ๋ฐฉ๋ฒ์ ๊ฐ๋ฅํ ํ ์์ ๋จ์๋ก ์ชผ๊ฐ์ ํ
์คํธํ๋ ๊ฒ์ด๋ค.
- ํ ์คํธ๊ฐ ์คํจํ์ ๋ ์์ธ์ ์ฐพ๊ธฐ ์ฝ๋ค.
- ๋ณต์กํ ์์กด๊ด๊ณ ์์ ํ
์คํธ
- UserService๋ ๊ฝค ๋ณต์กํ๋ค
- UserService ๋ค์ ์กด์ฌํ๋ ํจ์ฌ ๋ ๋ง์ ๊ฐ์ฒด, ํ๊ฒฝ, ์๋น์ค, ์๋ฒ, ๋คํธ์ํฌ๊น์ง ํ ์คํธ ํ๋ ์ ์ด๋ค.
- ํ
์คํธ ๋์ ๊ฐ์ฒด ๊ณ ๋ฆฝ์ํค๊ธฐ
- ๊ทธ๋์
- ์ธ๋ถ ์์๋ค์ ์ํฅ์ ๋ฐ์ง ์๋๋ก ๊ณ ๋ฆฝ์ํฌ ํ์๊ฐ ์๋ค.
- ์ฅ์
- ๋ค๋ฅธ ์์๋ฅผ ๋ฐฐ์ ํ UserService์ ๊ธฐ๋ฅ๋ง์ ํ ์คํธํ ์ ์๋ค.
- ๋ค๋ฅธ ์์๋ค์ ํ์ํ ์์๋ค์ ์๋ชจํ์ง ์์ผ๋ฏ๋ก ์ํ ์ฑ๋ฅ์ด ์ข๋ค.(์๋ ํฅ์)
- ํ
์คํธ๋ฅผ ์ํ UserServiceImpl ๊ณ ๋ฆฝ
- MockUserDao, MockMailSender ์ฌ์ฉํ๋ฉด ๋จ
- ๊ณ ๋ฆฝ๋ ๋จ์ํ
์คํธ ํ์ฉ
- upgradeLevels()์ ์ ์ฉ
- UserDao ๋ชฉ ๊ฐ์ฒด
- UserServiceTest์ ์ฉ์ด๋ฏ๋ก ์คํํฑ ๋ด๋ถ ํด๋์ค๋ก ์ ์ธ
-
์ฝ๋ ์ ๊ธฐ/ํผ์น๊ธฐ
@Test public void upgradeLevels() throws Exception { // ๊ณ ๋ฆฝ๋ ํ ์คํธ์ด๋ฏ๋ก ๋์ ๊ฐ์ฒด๋ฅผ ์ง์ ์์ฑํ๋ค. UserServiceImpl userService = new UserServiceImpl(); // ๋ชฉ ๊ฐ์ฒด๋ฅผ setํด์ค๋ค. MockUserDao mockUserDao = new MockUserDao(this.userList); userService.setUserDao(mockUserDao); MockMailSender mockMailSender = new MockMailSender(); userService.setMailSender(mockMailSender); // ๋ชฉ ๊ฐ์ฒด๋ฅผ ๊ฐ์ง UserService์ ๋ฉ์๋๋ฅผ ์ํํ๋ค. userService.upgradeLevels(); // UserService ๊ฒฐ๊ณผ๊ฐ ๋ด๊ธด ๋ชฉ ๊ฐ์ฒด์ ๊ฐ๋ค์ ํ์ธํ๋ค. List updated = mockUserDao.getUpdated(); assertThat(updated.size()).isEqualTo(3); checkUserAndLevel(updated.get(0), userList.get(1).getId(), Level.SILVER); checkUserAndLevel(updated.get(1), userList.get(3).getId(), Level.GOLD); checkUserAndLevel(updated.get(2), userList.get(4).getId(), Level.GOLD); List requests = mockMailSender.getRequests(); assertThat(requests.size()).isEqualTo(3); assertThat(requests.get(0)).isEqualTo(userList.get(1).getEmail()); assertThat(requests.get(1)).isEqualTo(userList.get(3).getEmail()); assertThat(requests.get(2)).isEqualTo(userList.get(4).getEmail()); }
- ๊ทธ๋์
- ๋จ์ ํ
์คํธ์ ํตํฉ ํ
์คํธ
- ์ด ์ฑ ์์ ๋จ์ ํ ์คํธ : ์์กด ์ค๋ธ์ ํธ๋ ์ธ๋ถ ๋ฆฌ์์ค๋ฅผ ์ฌ์ฉํ์ง ์๋๋ก ๊ณ ๋ฆฝ์ํจ ํ ์คํธ
- ์ด ์ฑ ์์ ํตํฉ ํ ์คํธ : ์ธ๋ถ์ DB, ํ์ผ ๋ฑ์ ๋ฆฌ์์ค๊ฐ ์ฐธ์ฌํ๋ ํ ์คํธ
- ์ ํ ๊ฐ์ด๋๋ผ์ธ
- ํญ์ ๋จ์ํ ์คํธ ๋จผ์ ๊ณ ๋ ค
- ์ธ๋ถ๋ฆฌ์์ค๋ฅผ ์ฌ์ฉํด์ผ๋ง ํ๋ ๊ฒฝ์ฐ์๋ ํตํฉ ํ ์คํธ
- ๋จ์ ํ ์คํธ๋ฅผ ๋ง๋ค๊ธฐ๊ฐ ๋๋ฌด ๋ณต์กํ๋ฉด ์ฒ์๋ถํฐ ํตํฉํ ์คํธ๋ฅผ ๊ณ ๋ คํด๋ณธ๋ค.
- ๋ชฉ ํ๋ ์์ํฌ
- Mockito ํ๋ ์์ํฌ
- ํน์ง : ๋ชฉ ํด๋์ค๋ฅผ ์ค๋นํ ํ์ ์์
- ์ค... ์ข์๋ฐ
- ์์ฑ์ ์์กด์ฑ ์ฃผ์ ์ผ๋ก ๋ฐ๊พธ๋ฉด ๋ ์ข๊ฒ ๋ค.
- ์ข์๋ฐ ์์ผ๋ก ์์ด๋จ๋ค. ์์์ฐ์ง;;
-
์ฝ๋ ์ ๊ธฐ/ํผ์น๊ธฐ
@Test public void upgradeLevelsWithMockito() { UserServiceImpl userService = new UserServiceImpl(); UserDao mockUserDao = mock(UserDao.class); when(mockUserDao.getAll()).thenReturn(this.userList); userService.setUserDao(mockUserDao); MailSender mockMailSender = mock(MailSender.class); userService.setMailSender(mockMailSender); userService.upgradeLevels(); // ๋ชฉ ๊ฐ์ฒด๊ฐ ์ ๊ณตํ๋ ๊ฒ์ฆ ๊ธฐ๋ฅ ํ์ฉ // ์ด๋ค ๋ฉ์๋๊ฐ ๋ช ๋ฒ ํธ์ถ๋๋์ง, ํ๋ผ๋ฏธํฐ๋ ๋ฌด์์ธ์ง ํ์ธํ ์ ์๋ค. verify(mockUserDao, times(3)).update(any(User.class)); verify(mockUserDao).update(userList.get(1)); assertThat(userList.get(1).getLevel()).isEqualTo(Level.SILVER); verify(mockUserDao).update(userList.get(3)); assertThat(userList.get(3).getLevel()).isEqualTo(Level.GOLD); verify(mockUserDao).update(userList.get(4)); assertThat(userList.get(4).getLevel()).isEqualTo(Level.GOLD); // ํ๋ผ๋ฏธํฐ๋ฅผ ์ ๋ฐํ๊ฒ ๊ฒ์ฌํ๊ธฐ ์ํด ์บก์ฒ ArgumentCaptor mailMessageArgs = ArgumentCaptor.forClass(SimpleMailMessage.class); verify(mockMailSender, times(3)).send(mailMessageArgs.capture()); List mailMessages = mailMessageArgs.getAllValues(); assertThat(mailMessages.get(0).getTo()[0]).isEqualTo(userList.get(1).getEmail()); assertThat(mailMessages.get(1).getTo()[0]).isEqualTo(userList.get(3).getEmail()); assertThat(mailMessages.get(2).getTo()[0]).isEqualTo(userList.get(4).getEmail()); }
- Mockito ํ๋ ์์ํฌ
- ํ๋ก์์ ํ๋ก์ ํจํด, ๋ฐ์ฝ๋ ์ดํฐ ํจํด
- ํ๋ก์
- ํด๋ผ์ด์ธํธ๋ ์ธํฐํ์ด์ค๋ง ๋ณด๊ณ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ํต์ฌ๊ธฐ๋ฅ์ ๊ฐ๋ ํด๋์ค(ํ๊น)๋ฅผ ์ฌ์ฉํ ๊ฒ์ผ๋ก ๊ธฐ๋ํ์ง๋ง ์ค์ ์ ์ผ๋ก๋ ๋ถ๊ฐ๊ธฐ๋ฅ(ํ๋ก์)์ ํตํด ํต์ฌ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ ๊ตฌ์ฑ
- ํด๋ผ์ด์ธํธ -> ํ๋ก์ -> ํ๊น
- ํ๋ก์ : ๋ง์น ์์ ์ด ์ค์ ๋์์ธ ๊ฒ์ฒ๋ผ ์์ฅํด์ ํด๋ผ์ด์ธํธ์ ์์ฒญ์ ๋๋ฆฌ์ธ์ฒ๋ผ ๋์ ๋ฐ์์ค
- ํ๊น ๋๋ ์ค์ฒด : ํ๋ก์๋ฅผ ํตํด ์ต์ข ์ ์ผ๋ก ์์ฒญ์ ์์๋ฐ์ ์ฒ๋ฆฌํ๋ ์ค์ ๊ฐ์ฒด
- ํ๋ก์์ ๋ชฉ์
- ํด๋ผ์ด์ธํธ๊ฐ ํ๊น์ ์ ๊ทผํ๋ ๋ฐฉ๋ฒ์ ์ ์ดํ๊ธฐ ์ํจ
- ํ๊น์ ๋ถ๊ฐ์ ์ธ ๊ธฐ๋ฅ์ ๋ถ์ฌํด์ฃผ๊ธฐ ์ํจ
- ๋ฐ์ฝ๋ ์ดํฐ ํจํด
- ํ๊น์ ๋ถ๊ฐ์ ์ธ ๊ธฐ๋ฅ์ ๋ฐํ์ ์์ ์ ๋์ ์ผ๋ก ๋ถ์ฌํด์ฃผ๊ธฐ ์ํด ํ๋ก์๋ฅผ ์ฌ์ฉํ๋ ํจํด
- ๋์ ์ธ ์ด์ : ์ปดํ์ผ ์์ ์๋ ํ๋ก์์ ํ๊น์ ๊ด๊ณ๊ฐ ์ ํด์ ธ์์ง ์๊ธฐ ๋๋ฌธ
- ๋ฐ์ฝ๋ ์ดํฐ์ธ ์ด์ : ์ฌ๋ฌ ๊ฒน์ ํฌ์ฅ์ง์ ์ฅ์์ฒ๋ผ ์ค์ ๋ด์ฉ๋ฌผ์๋ ๋ณํจ ์์ง๋ง ๋ถ๊ฐ์ ์ธ ํจ๊ณผ๋ฅผ ๋ถ์ฌํ๊ธฐ ๋๋ฌธ
- ๋ํ์ ์ธ ์ : InputStream(๋ด์ฉ๋ฌผ) - FileInputStream(๋ถ๊ฐ๊ธฐ๋ฅ + ๋ด์ฉ๋ฌผ)
- ์์ ์์ UserService์ UserServiceTx๋ ๋ฐ์ฝ๋ ์ดํฐ ํจํด
- ํ๋ก์ ํจํด
- ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉํ๋ ํ๋ก์์ ๋์์ธ ํจํด ํ๋ก์ ํจํด์์์ ํ๋ก์๋ ๊ตฌ๋ถ์ด ํ์ํ๋ค.
- ์ ์ : ํด๋ผ์ด์ธํธ์ ์ฌ์ฉ ๋์ ์ฌ์ด์ ๋๋ฆฌ ์ญํ ์ ๋งก์ ๊ฐ์ฒด๋ฅผ ๋๋ ๋ฐฉ๋ฒ์ ์ด์นญ
- ํ์ : ํ๋ก์๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ ์ค ํ๊น์ ๋ํ ์ ๊ทผ ๋ฐฉ๋ฒ์ ์ ์ดํ๋ ค๋ ๋ชฉ์ ์ ๊ฐ์ง ๊ฒฝ์ฐ
- ํ๋ก์ ํจํด์์ ํ๋ก์๋
- ํ๊น์ ๊ธฐ๋ฅ์ ํ์ฅํ๊ฑฐ๋ ์ถ๊ฐํ์ง ์๋๋ค.
- ํด๋ผ์ด์ธํธ๊ฐ ํ๊น์ ์ ๊ทผํ๋ ๋ฐฉ์์ ๋ณ๊ฒฝํด์ค๋ค.
-
ํ๊น ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ธฐ๊ฐ ๋ณต์กํ๊ฑฐ๋ ๋น์ฅ ํ์ํ์ง ์์ ๊ฒฝ์ฐ์๋ ๊ผญ ํ์ํ ์์ ๊น์ง ๊ฐ์ฒด๋ฅผ ์์ฑํ์ง ์๋ ํธ์ด ์ข๋ค. ๊ทธ๋ฐ๋ฐ ํ๊น ๊ฐ์ฒด์ ๋ํ ๋ ํผ๋ฐ์ค๊ฐ ๋ฏธ๋ฆฌ ํ์ํ ์ ์๋ค. ์ด๋ด ๋ ํ๋ก์ ํจํด์ ์ ์ฉํ๋ค. ์ค์ ํ๊น ๊ฐ์ฒด๋ฅผ ๋ง๋๋ ๋์ ํ๋ก์๋ฅผ ๋๊ฒจ์ค๋ค. ํ๋ก์์ ๋ฉ์๋๋ฅผ ํตํด ํ๊น์ ์ฌ์ฉํ๋ ค๊ณ ์๋ํ๋ฉด? ๊ทธ๋ ํ๋ก์๊ฐ ํ๊น ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ์์ฒญ์ ์์ํ๋ ์์ด๋ค.
- ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉํ๋ ํ๋ก์์ ๋์์ธ ํจํด ํ๋ก์ ํจํด์์์ ํ๋ก์๋ ๊ตฌ๋ถ์ด ํ์ํ๋ค.
- ์์ผ๋ก๋...
- ํ๊น๊ณผ ๋์ผํ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๊ณ
- ํด๋ผ์ด์ธํธ์ ํ๊น ์ฌ์ด์ ์กด์ฌํ๋ฉด์
- ๊ธฐ๋ฅ์ ๋ถ๊ฐ ๋๋ ์ ๊ทผ ์ ์ด๋ฅผ ๋ด๋นํ๋ ๊ฐ์ฒด
- ๋ฅผ ๋ชจ๋ ํ๋ก์๋ผ๊ณ ๋ถ๋ฅด๊ฒ ๋ค.
- ํ๋ก์
- ๋ค์ด๋ด๋ฏน ํ๋ก์
- ํ๋ก์๋ ๊ธฐ์กด ์ฝ๋์ ์ํฅ์ ์ฃผ์ง ์์ผ๋ฉด์ ๊ธฐ๋ฅ์ ํ์ฅํ๊ฑฐ๋ ์ ๊ทผ ๋ฐฉ๋ฒ์ ์ ์ดํ๋ ์ ์ฉํ ๋ฐฉ๋ฒ์ด๋ค.
- ๊ทธ๋ฌ๋ ๋ฒ๊ฑฐ๋กญ๊ธฐ ๋๋ฌธ์ ์์ด ์๊ฐ๋ค.
- ํ๋ก์๋ ๋ชฉ ํ๋ ์์ํฌ ์ฒ๋ผ ํธ๋ฆฌํ๊ฒ ์ฌ์ฉํ ์ ์๋ ๋ฐฉ๋ฒ์ด ์์๊น?
- ํ๋ก์์ ๊ตฌ์ฑ๊ณผ ํ๋ก์ ์์ฑ์ ๋ฌธ์ ์
- ํ๋ก์์ ๊ตฌ์ฑ
- ํ๊น๊ณผ ๊ฐ์ ๋ฉ์๋๋ฅผ ๊ตฌํํ๊ณ ์๋ค๊ฐ ๋ฉ์๋๊ฐ ํธ์ถ๋๋ฉด ํ๊น ๊ฐ์ฒด๋ก ์์ํ๋ค.
- ์ง์ ๋ ์์ฒญ์ ๋ํด์๋ ๋ถ๊ฐ๊ธฐ๋ฅ์ ์ํํ ์ ์๋ค.
- UserServiceTx์์ ํ๋ก์์ ๊ตฌ์ฑ
-
์ฝ๋ ์ ๊ธฐ/ํผ์น๊ธฐ
public class UserServiceTx implements UserService { @Autowired private PlatformTransactionManager transactionManager; @Autowired private UserServiceImpl userService; // ํ๊น ๊ฐ์ฒด๋ก ์์ public void add(User user) { userService.add(user); } public void upgradeLevels() { // ๋ถ๊ฐ๊ธฐ๋ฅ ์ํ ~ TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); try { userService.upgradeLevels(); // ํ๊น ๊ฐ์ฒด๋ก ์์ transactionManager.commit(status); } catch (Exception e) { transactionManager.rollback(status); throw e; } // ~ ๋ถ๊ฐ๊ธฐ๋ฅ ์ํ } }
- ํ๋ก์ ์์ฑ์ ๋ฌธ์ ์
- ํ๊น์ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๊ณ ์์ํ๋ ์ฝ๋๋ฅผ ์์ฑํ๊ธฐ ๋ฒ๊ฑฐ๋กญ๋ค. ๋ฉ์๋๊ฐ ์ถ๊ฐ๋๊ฑฐ๋ ๋ณ๊ฒฝ๋ ๋ ๋ง๋ค ํด์ค์ผ๋๋ค.
- ๋ถ๊ฐ๊ธฐ๋ฅ ์ฝ๋ ์ค๋ณต์ด ๋ชน์ ์ฌํด์ง๋ค.
- ํ๋ก์์ ๊ตฌ์ฑ
- ๋ฆฌํ๋ ์
- JDK์ ๋ค์ด๋ด๋ฏน ํ๋ก์๋ ๋ฆฌํ๋ ์ ๊ธฐ๋ฅ์ ์ด์ฉํด์ ํ๋ก์๋ฅผ ๋ง๋ค์ด ์ค๋ค.
-
์ฝ๋ ์ ๊ธฐ/ํผ์น๊ธฐ
@Test public void invokeMethod() throws Exception { String name = "Spring"; // length int nameLength = name.length(); Method lengthMethod = String.class.getMethod("length"); assertThat(lengthMethod.invoke(name)).isEqualTo(nameLength); // charAt int index = 0; char charAt = name.charAt(index); Method charAtMethod = String.class.getMethod("charAt", int.class); assertThat(charAtMethod.invoke(name, index)).isEqualTo(charAt); }
- ํ๋ก์ ํด๋์ค
- ๋ค์ด๋ด๋ฏน ํ๋ก์๋ฅผ ์ด์ฉํ ํ๋ก์๋ฅผ ๋ง๋ค์ด๋ณธ๋ค.
- HelloProxyTest.simpleProxy()๋ ํ๋ก์ ์ ์ฉ์ ์ผ๋ฐ์ ์ธ ๋ฌธ์ ์ ๋๊ฐ์ง๋ฅผ ๋ชจ๋ ๊ฐ๋๋ค.
- ๋ค์ด๋ด๋ฏน ํ๋ก์ ์ ์ฉ(UppercaseHandler, HelloProxyTest.dynamicProxy())
- HelloUppercaseProxy๋ฅผ ๋ค์ด๋ด๋ฏน ํ๋ก์๋ฅผ ์ด์ฉํด ๋ง๋ค์ด๋ณธ๋ค.
- Hello ์ธํฐํ์ด์ค์ ๋ฉ์๋๊ฐ ์๋ฌด๋ฆฌ ๋ง์๋ InvocationHandler์์ invoke๋ฉ์๋ ํ๋๋ก ์ฒ๋ฆฌํ๋ค.
- ์ถฉ๋ถํ ์ฌ์ฌ์ฉํ ์ ์๋ค.
- ๋ฉ์๋๋ฅผ ์ ๋ณํด์ ์ฌ์ฉํ ์๋ ์๋ค.
- ํ๋ก์๋ ๊ธฐ์กด ์ฝ๋์ ์ํฅ์ ์ฃผ์ง ์์ผ๋ฉด์ ๊ธฐ๋ฅ์ ํ์ฅํ๊ฑฐ๋ ์ ๊ทผ ๋ฐฉ๋ฒ์ ์ ์ดํ๋ ์ ์ฉํ ๋ฐฉ๋ฒ์ด๋ค.
- ๋ค์ด๋ด๋ฏน ํ๋ก์๋ฅผ ์ด์ฉํ ํธ๋์ญ์
๋ถ๊ฐ๊ธฐ๋ฅ
- UserServiceTx๋ฅผ ๋ค์ด๋ด๋ฏน ํ๋ก์ ๋ฐฉ์์ผ๋ก ๋ณ๊ฒฝ
- ๋ณ๊ฒฝํ๋ ์ด์ ?
- ์๋น์ค ์ธํฐํ์ด์ค์ ๋ฉ์๋๋ฅผ ๋ชจ๋ ๊ตฌํํด์ผ ํจ
- ํธ๋์ญ์ ์ด ํ์ํ ๋ฉ์๋ ๋ง๋ค ํธ๋์ญ์ ์ฒ๋ฆฌ์ฝ๋๊ฐ ์ค๋ณต
- ํธ๋์ญ์
InvocationHandler
- ํธ๋์ญ์ ๋ถ๊ฐ๊ธฐ๋ฅ์ ๊ฐ๋ ํธ๋ค๋ฌ
-
์ฝ๋ ์ ๊ธฐ/ํผ์น๊ธฐ
public class TransactionHandler implements InvocationHandler { // ๋ถ๊ฐ๊ธฐ๋ฅ์ ์ ๊ณตํ ํ๊น ๊ฐ์ฒด. ์ด๋ค ํ์ ์ ๊ฐ์ฒด๋ ์ ์ฉํ ์ ์๋ค. @Setter private Object target; // ํธ๋์ญ์ ์ ์ ๊ณตํ๊ธฐ ์ํ ํธ๋์ญ์ ๋งค๋์ @Autowired private PlatformTransactionManager transactionManager; // ํธ๋์ญ์ ์ ์ ์ฉํ ๋ฉ์๋ ์ด๋ฆ ํจํด @Setter private String pattern; public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().startsWith(pattern)) { return invokeInTransaction(method, args); } else { return method.invoke(target, args); } } private Object invokeInTransaction(Method method, Object[] args) throws Throwable { TransactionStatus status = this.transactionManager.getTransaction(new DefaultTransactionDefinition()); try { Object ret = method.invoke(target, args); this.transactionManager.commit(status); return ret; } catch (InvocationTargetException e) { this.transactionManager.rollback(status); throw e.getTargetException(); } } }
- TransactionHandler์ ๋ค์ด๋ด๋ฏน ํ๋ก์๋ฅผ ์ด์ฉํ๋ ํ
์คํธ
- UserServiceTest์ ์ ์ฉ
-
์ฝ๋ ์ ๊ธฐ/ํผ์น๊ธฐ
@Test public void upgradeAllOrNothing() { TestUserService testUserService = new TestUserService(userList.get(3).getId()); testUserService.setUserDao(this.userDao); testUserService.setMailSender(mailSender); TransactionHandler txHandler = new TransactionHandler(); txHandler.setTarget(testUserService); txHandler.setTransactionManager(transactionManager); txHandler.setPattern("upgradeLevels"); UserService txUserService = (UserService)Proxy.newProxyInstance( getClass().getClassLoader(), new Class[] { UserService.class }, txHandler ); userDao.deleteAll(); userList.forEach(user -> userDao.add(user)); try { txUserService.upgradeLevels(); fail("TestUserServiceException expected"); } catch (TestUserServiceException e) { } checkLevelUpgraded(userList.get(1), false); }
- ๋ค์ด๋ด๋ฏน ํ๋ก์๋ฅผ ์ํ ํฉํ ๋ฆฌ ๋น
- ์ด์ TransactionHandler์ ๋ค์ด๋ด๋ฏน ํ๋ก์๋ฅผ ์คํ๋ง์ DI๋ฅผ ํตํด ์ฌ์ฉํ ์ ์๋๋ก ๋ง๋ค ์ฐจ๋ก๋ค.
- ๊ทธ๋ฐ๋ฐ ์ผ๋ฐ์ ์ธ ๋น์ผ๋ก ๋ฑ๋กํ ์ ์๋๊ฒ ๋ฌธ์
- ์คํ๋ง ๋น์ ํด๋์ค ์ด๋ฆ๊ณผ ํ๋กํผํฐ๋ก ์ ์๋๋๋ฐ?
- ์คํ๋ง์ ์ง์ ๋ ํด๋์ค ์ด๋ฆ์ ๊ฐ์ง๊ณ ๋ฆฌํ๋ ์ ์ ์ด์ฉํด์ ํด๋ฌ ํด๋์ค์ ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด์
- ๋ค์ด๋ด๋ฏน ํ๋ก์ ๊ฐ์ฒด๋ ์ด๋ฐ ์์ผ๋ก ํ๋ก์ ๊ฐ์ฒด๊ฐ ์์ฑ๋์ง ์๋๋ค๋ ์ ์ด๋ค.
- ๋ค์ด๋ด๋ฏน ํ๋ก์๋ Proxyํด๋์ค์ newProxyInstance()๋ผ๋ ์คํํฑ ๋ฉ์๋๋ฅผ ํตํด์๋ง ๋ง๋ค ์ ์๋ค.
- ํฉํ ๋ฆฌ ๋น
- ์คํ๋ง์ ๋์ ํด์ ๊ฐ์ฒด์ ์์ฑ๋ก์ง์ ๋ด๋นํ๋๋ก ๋ง๋ค์ด์ง ํน๋ณํ ๋น
- ํฉํ ๋ฆฌ ๋น์ ๋ฑ๋กํ๋ ๊ฐ์ฅ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ ์คํ๋ง์ FactoryBean์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ ๋ฐฉ๋ฒ
- ์์ฑ์๋ฅผ ์ ๊ณตํ์ง ์๋ Messageํด๋์ค๋ฅผ ํฉํ ๋ฆฌ ๋น์ผ๋ก ๋ฑ๋กํ๋ ์์
-
์ฝ๋ ์ ๊ธฐ/ํผ์น๊ธฐ
// ์ฐธ๊ณ : https://www.baeldung.com/spring-factorybean @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = FactoryBeanConfig.class) public class MessageFactoryBeanTest { @Autowired private ApplicationContext context; @Qualifier("message") @Autowired private Message message; @Resource(name = "&message") private MessageFactoryBean messageFactoryBean; @Test public void getMessageFromFactoryBean() throws Exception { Object messageFromContext = context.getBean("message"); assertThat(messageFromContext).isInstanceOf(Message.class); assertThat(((Message) messageFromContext).getText()).isEqualTo("Factory Bean"); assertThat((message).getText()).isEqualTo("Factory Bean"); Message messageFromFactoryBean = messageFactoryBean.getObject(); assertThat(messageFromFactoryBean.getText()).isEqualTo("Factory Bean"); } }
- ๋ค์ด๋ด๋ฏน ํ๋ก์๋ฅผ ๋ง๋ค์ด์ฃผ๋ ํฉํ ๋ฆฌ ๋น
-
ํฉํ ๋ฆฌ ๋น์ ์ฌ์ฉํ๋ฉด ์คํ๋ง์ ๋น์ผ๋ก ๋ง๋ค์ด ์ค ์ ์๊ตฐ(ํฉํ ๋ฆฌ ๋น์ getObject ๋ฉ์๋)
-
FactoryBean์ฝ๋ ์ ๊ธฐ/ํผ์น๊ธฐ
@Setter public class TxProxyFactoryBean implements FactoryBean<Object> { private Object target; private PlatformTransactionManager transactionManager; private String pattern; private Class<?> serviceInterface; // FactoryBean ๊ตฌํ ๋ฉ์๋ @Override public Object getObject() throws Exception { TransactionHandler txHandler = new TransactionHandler(); txHandler.setTarget(target); txHandler.setTransactionManager(transactionManager); txHandler.setPattern(pattern); return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{serviceInterface}, txHandler); } public Class<?> getObjectType() { return serviceInterface; } public boolean isSingleton () { // ์ฑ๊ธํค ๋น์ด ์๋๋ผ๋ ๋ป์ด ์๋ // getObject๊ฐ ๋งค ๋ฒ ๊ฐ์ ๊ฐ์ฒด๋ฅผ ๋ฆฌํดํ์ง ์๋๋ค๋ ์๋ฏธ return false; } }
-
Config ์ฝ๋ ์ ๊ธฐ/ํผ์น๊ธฐ
@Configuration public class FactoryBeanConfig { @Autowired private UserServiceImpl userServiceImpl; @Autowired private PlatformTransactionManager transactionManager; @Bean(name = "txProxy") public TxProxyFactoryBean txProxyFactoryBean() { TxProxyFactoryBean factoryBean = new TxProxyFactoryBean(); factoryBean.setTarget(userServiceImpl); factoryBean.setTransactionManager(transactionManager); factoryBean.setPattern("upgradeLevels"); factoryBean.setServiceInterface(UserService.class); return factoryBean; } }
-
ํ ์คํธ ์ฝ๋ ์ ๊ธฐ/ํผ์น๊ธฐ
@RunWith(SpringRunner.class) @SpringBootTest @Slf4j class UserServiceTest { @Autowired private TxProxyFactoryBean txProxy; @Test public void upgradeAllOrNothing() throws Exception { TestUserService testUserService = new TestUserService(userList.get(3).getId()); testUserService.setUserDao(this.userDao); testUserService.setMailSender(mailSender); txProxy.setTarget(testUserService); UserService txProxyUserService = (UserService)txProxy.getObject();
userDao.deleteAll(); userList.forEach(user -> userDao.add(user)); try { txProxyUserService.upgradeLevels(); fail("TestUserServiceException expected"); } catch (TestUserServiceException e) { } checkLevelUpgraded(userList.get(1), false);
} static class TestUserService extends UserServiceImpl { private String id; public TestUserService(String id) { this.id = id; } protected void upgradeLevel(User user) { if (user.getId().equals(this.id)) { throw new TestUserServiceException(); } super.upgradeLevel(user); } } }
-
- ํ๋ก์ ํฉํ ๋ฆฌ ๋น ๋ฐฉ์์ ์ฅ์ ๊ณผ ํ๊ณ
- ํ๋ก์ ํฉํ ๋ฆฌ ๋น์ ์ฌ์ฌ์ฉ
- TransactionHandler๋ฅผ ์ด์ฉํ๋ TxProxyFactoryBean์ ์ฝ๋ ์์ ์์ด ๋ค์ํ ํด๋์ค์ ์ ์ฉํ ์ ์๋ค.
- ํ๋ก์ ํฉํ ๋ฆฌ ๋น ๋ฐฉ์์ ์ฅ์
- ๋ฐ์ฝ๋ ์ดํฐ ํจํด์ด ์ ์ฉ๋ ํ๋ก์๋ฅผ ์ฌ์ฉํ ๋ ๋ฐ์ํ๋ ๋ฌธ์ ๋ ๊ฐ์ง๋ฅผ ํด๊ฒฐํ ์ ์๋ค.
- ํ๋ก์ ๋์ ํด๋์ค๋ฅผ ์ผ์ผ์ด ๋ง๋ค์ด์ผ ํ๋ ๋ฒ๊ฑฐ๋ก์
- ๋ถ๊ฐ๊ธฐ๋ฅ ์ฝ๋ ์ค๋ณต
- ๋ฐ์ฝ๋ ์ดํฐ ํจํด์ด ์ ์ฉ๋ ํ๋ก์๋ฅผ ์ฌ์ฉํ ๋ ๋ฐ์ํ๋ ๋ฌธ์ ๋ ๊ฐ์ง๋ฅผ ํด๊ฒฐํ ์ ์๋ค.
- ํ๋ก์ ํฉํ ๋ฆฌ ๋น์ ํ๊ณ
- ์ฌ๋ฌ ํด๋์ค์ ๊ณตํต์ ์ธ ๋ถ๊ฐ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ค๋ฉด ์ค๋ณต์ด ๋ฐ์ํ๋ค.
- ํ ํด๋์ค ์์ ์๋ ์ฌ๋ฌ ๋ฉ์๋์ ๋ถ๊ฐ๊ธฐ๋ฅ์ ์ ๊ณตํ๋๊ฑด ๊ฐ๋ฅํ๋ค.
- ๋น์ทํ ์ค์ ์ด ๊ณ์ ๋ฐ๋ณต๋ผ์ ๋ํ๋๊ฒ ๋ ๊ฒ
- TransactionHandler ๊ฐ์ฒด๊ฐ ํ๋ก์ ํฉํ ๋ฆฌ ๋น ๊ฐ์๋งํผ ๋ง๋ค์ด์ง ๊ฒ์ด๋ค.
- ํ๊น ๊ฐ์ฒด๋ฅผ ํ๋กํผํฐ๋ก ๊ฐ๊ณ ์๋ค(UserServiceImpl)
- ๊ทธ๋์ ํ๊น ๊ฐ์ฒด๊ฐ ์ฌ๋ฌ๊ฐ๋ผ๋ฉด ๊ทธ ๊ฐ์๋งํผ ๋น์ ์ ์ธํ๊ณ ๊ทธ๋งํผ ๊ฐ์ฒด๊ฐ ์์ฑ ๋๋ค.
- ์ฑ๊ธํค ๋น์ผ๋ก ๋ง๋๋๊ฒ ์ข๊ฒ ์ง?
- ์ฌ๋ฌ ํด๋์ค์ ๊ณตํต์ ์ธ ๋ถ๊ฐ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ค๋ฉด ์ค๋ณต์ด ๋ฐ์ํ๋ค.
- ํ๋ก์ ํฉํ ๋ฆฌ ๋น์ ์ฌ์ฌ์ฉ
- ์ฌ๊ธฐ๊น์ง ์์ฝ
AOP ๋์๊ฐ ๋๊ธฐ ์์ํ๋ค.
1 ํธ๋์ญ์
์ฝ๋์ ๋ถ๋ฆฌ : AOP๋ค์ด๊ฐ๊ธฐ์ํ ์ค๋น
2 ๊ณ ๋ฆฝ๋ ๋จ์ํ
์คํธ
- 1์์ ๋ถ๋ฆฌํ ๋ด์ฉ์ ์ฅ์ ์ ํ
์คํธ์ฝ๋์ ์ ์ฉ
- ์ฅ์ : ์ฝ๋ ๋ถ๋ฆฌ๋ฅผ ํตํด ๋น์ฆ๋์ค ๋ก์ง์ ๋ํ ํ
์คํธ๋ฅผ ๋ ์ฝ๊ฒ ๋ง๋ค ์ ์์(๋น์ฆ๋์ค ๋ก์ง UserService ์ฝ๋์ ๋ถ๊ฐ๊ธฐ๋ฅ ํธ๋์ญ์
๊ฒฝ๊ณ์ค์ ์ฝ๋ ๋ถ๋ฆฌ)
- mockito๋ ๋ด์ฉ ์ค๋ช
ํ๋ค ์๊ฐ๋์ ๋ผ์๋ฃ์๊ฑฐ ๊ฐ์๋ฐ ์ฐธ ์ข์๋ณด์๋ค. ์์ด๋ค๋ ์์ฝ๋ค.
3. ๋ค์ด๋ด๋ฏน ํ๋ก์์ ํ๋ก์ ํจํด
- ํ๋ก์ ํจํด์ ์ ์ฉํด์ ๋ถ๊ฐ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ค.
- ๊ธฐ์กด ์ฝ๋์ ์ํฅ ์์ด ํ์ํ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ค.
- ํ๋ก์๋ฅผ ํฉํ ๋ฆฌ ๋น์ผ๋ก ๋ฑ๋กํ๋ค.
- ํ์ํ ๊ธฐ๋ฅ์ด ์ค๋ณต๋๋ ๊ฒฝ์ฐ ์ค๋ณต์ ์ด๋์ ๋ ๋ง์ ์ ์๋ค.
- ํ์ง๋ง ํ๊น ๊ฐ์ฒด๊ฐ ์ฌ๋ฌ๊ฐ๊ฐ ๋๋ ๊ฒฝ์ฐ ์ค์ ์ค๋ณต์ ํผํ ์ ์๋ค.
- AOP ๋์๊ฐ ๋๋ค.
AOP? ์ค๋ณต๋๋ ๊ธฐ๋ฅ์ ์ฝ๋ ์ค๋ณต ์์ด ๋์ํ๊ฒ ํ๋ ๋ฐฉ๋ฒ?
- ProxyFactoryBean
- ์คํ๋ง์ ์ผ๊ด๋ ๋ฐฉ๋ฒ์ผ๋ก ํ๋ก์๋ฅผ ๋ง๋ค ์ ์๊ฒ ๋์์ฃผ๋ ์ถ์๋ ์ด์ด๋ฅผ ์ ๊ณตํ๋ค.
- ProxyFactoryBean : ํ๋ก์๋ฅผ ์์ฑํด์ ๋น ๊ฐ์ฒด๋ก ๋ฑ๋กํ๊ฒ ํด์ฃผ๋ ํฉํ ๋ฆฌ ๋น์ด๋ค.
- ProxyFactoryBean์ ์ด์ฉํ ๋ค์ด๋ด๋ฏน ํ๋ก์ ํ ์คํธ(6-41)
- ProxyFactoryBean์ ํ๊น ๊ฐ์ฒด๋ฅผ ์ค์ ํ๊ณ ๋ถ๊ฐ๊ธฐ๋ฅ์ ์ถ๊ฐํ ๋ค์ ProxyFactoryBean์์ ์ค์ ํ ๊ฐ์ฒด๋ฅผ ๋นผ์ค๋์
-
ํ ์คํธ ์ฝ๋ ์ ๊ธฐ/ํผ์น๊ธฐ
public class HelloProxyTest { @Test public void proxyFactoryBean() { ProxyFactoryBean pfBean = new ProxyFactoryBean(); pfBean.setTarget(new HelloTarget()); //ํ๊น ์ค์ pfBean.addAdvice(new UppercaseAdvice()); //๋ถ๊ฐ๊ธฐ๋ฅ ์ถ๊ฐ Hello proxiedHello = (Hello) pfBean.getObject(); //FacotryBean์ด๋ฏ๋ก ์์ฑ๋ ํ๋ก์๋ฅผ ๊ฐ์ ธ์จ๋ค. assertThat(proxiedHello.sayHello("Toby")).isEqualTo("HELLO TOBY"); assertThat(proxiedHello.sayHi("Toby")).isEqualTo("HI TOBY"); assertThat(proxiedHello.sayThankYou("Toby")).isEqualTo("THANKYOU TOBY"); } static class UppercaseAdvice implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { String ret = (String)invocation.proceed(); //ํ๊น์ ์๊ณ ์๊ธฐ์ ํ๊น ์ค๋ธ์ ํธ๋ฅผ ์ ๋ฌํ ํ์๊ฐ ์๋ค. return ret.toUpperCase(); //๋ถ๊ฐ๊ธฐ๋ฅ ์ ์ฉ } } }
- ์ด๋๋ฐ์ด์ : ์ด๋๋ฐ์ด์ค(๋ถ๊ฐ๊ธฐ๋ฅ)์ ํฌ์ธํธ์ปท(๋ฉ์๋ ์ ์ ์๊ณ ๋ฆฌ์ฆ)์ ๋ฌถ์ ๊ฐ์ฒด
- ์ด๋๋ฐ์ด์ค : ํ๊น์ด ํ์ ์๋ ์์ํ ๋ถ๊ฐ๊ธฐ๋ฅ
- UppercaseAdvice์๋ ํ๊น ๊ฐ์ฒด๊ฐ ๋ฑ์ฅํ์ง ์๋๋ค.(setTarget ํ๊ธฐ ๋๋ฌธ)
- addAdvice : ์ฌ๋ฌ ๊ฐ์ MethodInterceptor(๋ถ๊ฐ๊ธฐ๋ฅ)๋ฅผ ์ถ๊ฐํ ์ ์๋ค.
- ์ด๋๋ฐ์ด์ค : ํ๊น ๊ฐ์ฒด์ ์ข ์๋์ง ์๋ ์์ํ ๋ถ๊ฐ๊ธฐ๋ฅ์ ๋ด์ ๊ฐ์ฒด
- ํฌ์ธํธ์ปท : ๋ถ๊ฐ๊ธฐ๋ฅ ์ ์ฉ ๋์ ๋ฉ์๋ ์ ์ ๋ฐฉ๋ฒ
- TxProxyFactoryBean์์๋ ๋์ ๋ฉ์๋๋ฅผ setPattern์ผ๋ก ์ค์
- MethodInterceptor ๊ฐ์ฒด๋ ํ๊น ์ ๋ณด๋ฅผ ๊ฐ๊ณ ์์ง ์์ผ๋ฏ๋ก ๋ฉ์๋ ํจํด์ผ๋ก ๋์ ๋ฉ์๋๋ฅผ ์ ํํ๋ ๋ฐฉ์์ ์ฌ์ฉํ ์ ์๋ค.
- ํ๋ก์์ ๋ถ๊ฐ๊ธฐ๋ฅ ์ ์ฉ ๋์ ๋ฉ์๋๋ฅผ ์ ํํ๋ ๊ธฐ๋ฅ์ ๋ฃ์ผ๋ฉด ๋๋ค.
- ์ด๋๋ฐ์ด์ค๊ฐ ํ ํ๋ฆฟ์ด ๋๊ณ MethodInvocation ๊ฐ์ฒด๊ฐ ์ฝ๋ฐฑ์ด ๋๋ ์
- ์คํ๋ง์ด ์ ๊ณตํ๋ NameMatchMethodPointcut๊ณผ UppercaseAdvice ํ์ฉ ํ ์คํธ ์ฝ๋
-
ํ ์คํธ ์ฝ๋ ์ ๊ธฐ/ํผ์น๊ธฐ
@Test public void pointcutAdvisor() { ProxyFactoryBean pfBean = new ProxyFactoryBean(); pfBean.setTarget(new HelloTarget()); // ๋ฉ์๋ ์ด๋ฆ์ ๋น๊ตํด์ ๋์์ ์ ์ ํ๋ ์๊ณ ๋ฆฌ์ฆ์ ์ ๊ณตํ๋ ํฌ์ธํธ์ปท ๊ฐ์ฒด NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut(); pointcut.setMappedName("sayH*"); // ํฌ์ธํธ์ปท๊ณผ ์ด๋๋ฐ์ด์ค๋ฅผ advisor๋ก ๋ฌถ์ด์ ํ ๋ฒ์ ์ถ๊ฐ // ๋ฌถ๋ ์ด์ ? ์ด๋ค ์ด๋๋ฐ์ด์ค์ ๋ํด ์ด๋ค ํฌ์ธํธ์ปท์ ์ฌ์ฉํ ์ง ๋ช ํํ๊ฒ ๊ตฌ๋ถํ๊ธฐ ์ํด // ์ฌ๋ฌ ๊ฐ์ ์ด๋๋ฐ์ด์ค์ ํฌ์ธํธ์ปท์ด ์ถ๊ฐ๋ ์ ์๊ธฐ ๋๋ฌธ pfBean.addAdvisor(new DefaultPointcutAdvisor(pointcut, new UppercaseAdvice())); Hello proxiedHello = (Hello) pfBean.getObject(); assertThat(proxiedHello.sayHello("Toby")).isEqualTo("HELLO TOBY"); assertThat(proxiedHello.sayHi("Toby")).isEqualTo("HI TOBY"); assertThat(proxiedHello.sayThankYou("Toby")).isEqualTo("ThankYou Toby"); }
- ProxyFactoryBean ์ ์ฉ
- JDK ๋ค์ด๋ด๋ฏน ํ๋ก์์ ๊ตฌ์กฐ๋ฅผ ๊ทธ๋๋ก ์ด์ฉํด์ ๋ง๋ค์๋ TxProxyFactoryBean์ ์คํ๋ง์ด ์ ๊ณตํ๋ ProxyFactoryBean์ ์ด์ฉํ๋๋ก ์์
- ์ด๋๋ฐ์ด์ค์ ํฌ์ธํธ์ปท์ ์ฌ์ฌ์ฉ
- ProxyFactoryBean์ ์คํ๋ง์ DI, ํ ํ๋ฆฟ/์ฝ๋ฐฑ ํจํด, ์๋น์ค ์ถ์ํ ๋ฑ์ ๊ธฐ๋ฒ์ด ๋ชจ๋ ์ ์ฉ๋ ๊ฒ์ด๋ค.
- ์๋ ์์ ์ฝ๋์์๋ UserService๋ง ์ ์ฉํ์ง๋ง ๊ทธ ์ธ์ ๋ค๋ฅธ ํด๋์ค๋ ์ด๋ฆ ํจํด๋ง ์ง์ ํด์ ProxyFactoryBean์ ๋ฑ๋กํด์ฃผ๋ฉด ๋๋ค.
- TransactionAdvice
- ์์ ์ฝ๋
-
ํ ์คํธ ์ฝ๋ ์ ๊ธฐ/ํผ์น๊ธฐ
@Setter public class TransactionAdvice implements MethodInterceptor { PlatformTransactionManager transactionManager; // ํ๊น์ ํธ์ถํ๋ ๊ธฐ๋ฅ์ ๊ฐ์ง ์ฝ๋ฐฑ ๊ฐ์ฒด๋ฅผ ํ๋ก์๋ก๋ถํฐ ๋ฐ๋๋ค. // ๋๋ถ์ ์ด๋๋ฐ์ด์ค๋ ํน์ ํ๊น์ ์์กดํ์ง ์๊ณ ์ฌ์ฌ์ฉํ ์ ์๋ค. public Object invoke(MethodInvocation methodInvocation) throws Throwable { TransactionStatus status = this.transactionManager.getTransaction(new DefaultTransactionDefinition()); try { // ์ฝ๋ฐฑ์ ํธ์ถํด์ ํ๊น์ ๋ฉ์๋๋ฅผ ์คํ // ํ๊น ๋ฉ์๋ ํธ์ถ ์ ํ๋ก ํ์ํ ๋ถ๊ฐ๊ธฐ๋ฅ์ ๋ฃ์ ์ ์๋ค. // ๊ฒฝ์ฐ์ ๋ฐ๋ผ ํ๊น์ด ์์ ํธ์ถ๋์ง ์๊ฒ๋ ํ๊ฑฐ๋ ์ฌ์๋๋ฅผ ์ํ ๋ฐ๋ณต์ ํธ์ถ๋ ๊ฐ๋ฅํ๋ค. Object ret = methodInvocation.proceed(); this.transactionManager.commit(status); return ret; // JDK ๋ค์ด๋ดํฝ ํ๋ก์๊ฐ ์ ๊ณตํ๋ Method์ ๋ฌ๋ฆฌ // ์คํ๋ง์ MethodInvocation์ ํตํ ํ๊น ํธ์ถ์ ์์ธ๊ฐ ํฌ์ฅ๋์ง ์๊ณ ํ๊น์์ ๋ณด๋ธ ๊ทธ๋๋ก ์ ๋ฌ๋๋ค. } catch (RuntimeException e) { this.transactionManager.rollback(status); throw e; } } }
- ์คํ๋ง XML ์ค์ ํ์ผ
- ์ฝ๋๋ ์๋ณผ๊ณณ์ด ์๊ณ ์ค์ ๋ง ํด์ฃผ๋ฉด ๋๋ค.
- ์ด๋๋ฐ์ด์ค๋ฅผ ๋ฑ๋กํ๋ค.
- ์ค์ ํ์ผ(Java Config)
-
ํ ์คํธ ์ฝ๋ ์ ๊ธฐ/ํผ์น๊ธฐ
@Configuration @RequiredArgsConstructor public class AopConfig { private final PlatformTransactionManager transactionManager; private final UserService userService; @Bean public TransactionAdvice transactionAdvice() { return new TransactionAdvice(transactionManager); } @Bean public NameMatchMethodPointcut transactionPointcut() { NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut(); pointcut.setMappedName("upgrade*"); return pointcut; } @Bean public DefaultPointcutAdvisor transactionAdvisor() { DefaultPointcutAdvisor pointcutAdvisor = new DefaultPointcutAdvisor(); pointcutAdvisor.setAdvice(transactionAdvice()); pointcutAdvisor.setPointcut(transactionPointcut()); return pointcutAdvisor; } @Bean public ProxyFactoryBean userServiceProxyFactoryBean() { ProxyFactoryBean factoryBean = new ProxyFactoryBean(); factoryBean.setTarget(userService); factoryBean.setInterceptorNames("transactionAdvisor"); return factoryBean; } }
- ํ ์คํธ์ฝ๋
-
ํ ์คํธ ์ฝ๋ ์ ๊ธฐ/ํผ์น๊ธฐ
@Test public void upgradeAllOrNothing() { TestUserService testUserService = new TestUserService(userList.get(3).getId()); testUserService.setUserDao(this.userDao); testUserService.setMailSender(mailSender); ProxyFactoryBean proxyFactoryBean = context.getBean("&userServiceProxyFactoryBean", ProxyFactoryBean.class); proxyFactoryBean.setTarget(testUserService); UserService txUserService = (UserService) proxyFactoryBean.getObject(); try { txUserService.upgradeLevels(); fail("TestUserServiceException expected"); } catch (TestUserServiceException e) { } checkLevelUpgraded(userList.get(1), false); }
- ์ง๊ธ๊น์ง : ๋ฐ๋ณต์ ์ผ๋ก ๋ฑ์ฅํ๋ ํธ๋์ญ์
์ฝ๋๋ฅผ ๊น๋ํ๊ณ ํจ๊ณผ์ ์ผ๋ก ๋ถ๋ฆฌ
- ํฌ๋ช ํ ๋ถ๊ฐ๊ธฐ๋ฅ์ด๋ค : ๊ธฐ์กด ์ค๊ณ์ ์ฝ๋์๋ ์ํฅ์ ์ฃผ์ง ์์(์๋ ๋ฏ ์๋ ๋ฏ)
- ์๋ ํ๋ก์ ์์ฑ
- ์์ง ๋จ์ ๋ฌธ์ : ์ค์ ์ ๋ณด๋ฅผ ๊ณ์ ์ถ๊ฐํด์ฃผ๋ ๋ฒ๊ฑฐ๋ก์
- ์ค๋ณต ๋ฌธ์ ์ ์ ๊ทผ ๋ฐฉ๋ฒ
- ์ง๊ธ๊น์ง ์ดํด๋ณธ ๋ฐฉ๋ฒ์๋ ํ ๋ฒ์ ์ฌ๋ฌ ๋น์ ํ๋ก์๋ฅผ ์ ์ฉํ ๋งํ ๋ฐฉ๋ฒ์ ์์๋ค.
- ๋น ํ์ฒ๋ฆฌ๊ธฐ๋ฅผ ์ด์ฉํ ์๋ ํ๋ก์ ์์ฑ๊ธฐ
- ๋น ํ์ฒ๋ฆฌ๊ธฐ : ๋น ๊ฐ์ฒด๋ก ๋ง๋ ํ์ ๊ทธ ๋น ๊ฐ์ฒด๋ฅผ ๋ค์ ๊ฐ๊ณตํ ์ ์๊ฒ ํด์ค๋ค.
- ๋น ํ์ฒ๋ฆฌ๊ธฐ๋ฅผ ๋น์ผ๋ก ๋ฑ๋กํ๋ฉด ์ฌ์ฉํ ์ ์๋ค.
- ๋น ๊ฐ์ฒด ์ผ๋ถ๋ฅผ ํ๋ก์๋ก ํฌ์ฅํ๊ณ ํ๋ก์๋ฅผ ๋น์ผ๋ก ๋์ ๋ฑ๋กํ ์๋ ์๋ค.
- ๋น ํ์ฒ๋ฆฌ๊ธฐ์ ๋์
-
1. ๋น ํ์ฒ๋ฆฌ๊ธฐ๊ฐ ๋ฑ๋ก๋ผ์์ผ๋ฉด ์คํ๋ง์ ๋น ๊ฐ์ฒด๋ฅผ ๋ง๋ค ๋ ๋ง๋ค ํ์ฒ๋ฆฌ๊ธฐ์๊ฒ ๋น์ ๋ณด๋ธ๋ค. 2. ๋น ํ์ฒ๋ฆฌ๊ธฐ๋ ๋ฑ๋ก๋ ์ด๋๋ฐ์ด์ ์ ํฌ์ธํธ์ปท์ ์ด์ฉํด ์ ๋ฌ๋ฐ์ ๋น์ด ํ๋ก์ ์ ์ฉ ๋์์ธ์ง ํ์ธํ๋ค. 3. ์ ์ฉ ๋์์ด๋ฉด? ๋ด์ฅ๋ ํ๋ก์ ์์ฑ๊ธฐ์๊ฒ ํ์ฌ ๋น์ ๋ํ ํ๋ก์๋ฅผ ๋ง๋ค๊ฒ ํ๊ณ ์ด๋๋ฐ์ด์ ๋ฅผ ์ฐ๊ฒฐํ๋ค. 4. ํ๋ก์๋ฅผ ๋ง๋ค์์ผ๋ฉด? ์ ๋ฌ๋ฐ์ ๋น ๊ฐ์ฒด ๋์ ๋ง๋ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ๋๋ ค์ค๋ค. 5. ์คํ๋ง์ ๋๋ ค๋ฐ์ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ๋น์ผ๋ก ๋ฑ๋กํ๊ณ ์ฌ์ฉํ๋ค.
- ํ์ฅ๋ ํฌ์ธํธ์ปท
- ํฌ์ธํธ์ปท์ ํด๋์ค ํํฐ์ ๋ฉ์๋ ๋งค์ฒ ๋ ๊ฐ์ง๋ฅผ ๋๋ ค์ฃผ๋ ๋ฉ์๋๋ฅผ ๊ฐ๊ณ ์๋ค.
- ๋ชจ๋ ๋น์ ๋ํด ํ๋ก์ ์ ์ฉ ๋์์ธ์ง ํ๋จํด์ผ ํ๋ ์ํฉ์์๋ ํด๋์ค์ ๋ฉ์๋ ์ ์ ์๊ณ ๋ฆฌ์ฆ์ ๋ชจ๋ ๊ฐ๋ ํฌ์ธํธ์ปท์ด ํ์ํ๋ค.
- ํฌ์ธํธ์ปท ํ
์คํธ
- ํ์ฅ ํฌ์ธํธ์ปท ํ์ต ํ ์คํธ ์ฝ๋
-
ํ ์คํธ ์ฝ๋ ์ ๊ธฐ/ํผ์น๊ธฐ
@Test @Description("ํ์ฅ ํฌ์ธํธ์ปท ํ ์คํธ 480p") public void classNamePointcutAdvisor() { // ํฌ์ธํธ์ปท ์ค๋น NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut() { public ClassFilter getClassFilter() { // ํด๋์ค ์ด๋ฆ์ด HelloT๋ก ์์ํ๋ ๊ฒ๋ง ์ ํ return aClass -> aClass.getSimpleName().startsWith("HelloT"); } }; // sayH๋ก ์์ํ๋ ๋ฉ์๋๋ง ์ ํ pointcut.setMappedName("sayH*"); checkAdviced(new HelloTarget(), pointcut, true); checkAdviced(new HelloWorld(), pointcut, false); checkAdviced(new HelloToby(), pointcut, true); } class HelloWorld extends HelloTarget { } class HelloToby extends HelloTarget { } private void checkAdviced(Object target, Pointcut pointcut, boolean adviced) { ProxyFactoryBean pfBean = new ProxyFactoryBean(); pfBean.setTarget(target); pfBean.addAdvisor(new DefaultPointcutAdvisor(pointcut, new UppercaseAdvice())); Hello proxiedHello = (Hello) pfBean.getObject(); if (adviced) { assertThat(proxiedHello.sayHello("Toby")).isEqualTo("HELLO TOBY"); assertThat(proxiedHello.sayHi("Toby")).isEqualTo("HI TOBY"); assertThat(proxiedHello.sayThankYou("Toby")).isEqualTo("ThankYou Toby"); } else { assertThat(proxiedHello.sayHello("Toby")).isEqualTo("Hello Toby"); assertThat(proxiedHello.sayHi("Toby")).isEqualTo("Hi Toby"); assertThat(proxiedHello.sayThankYou("Toby")).isEqualTo("ThankYou Toby"); } }
- DefaultAdvisorAutoProxyCreator์ ์ ์ฉ
- ํด๋์ค ํํฐ๋ฅผ ์ ์ฉํ ํฌ์ธํธ์ปท ์์ฑ
- ์ฃผ์ด์ง ์ด๋ฆ ํจํด์ ๊ฐ์ง๊ณ ํด๋์ค๋ฅผ ์ฐพ์๋ด๋ ClassFilter๋ฅผ ์ถ๊ฐํ๋๋ก ๋ง๋ค ๊ฒ์ด๋ค.
-
์ฝ๋ ์ ๊ธฐ/ํผ์น๊ธฐ
public class NameMatchClassMethodPointcut extends NameMatchMethodPointcut { public void setMappedClassName(String mappedClassName) { // ๋งค๊ฐ๋ณ์๋ก ๋ฐ์ ํด๋์ค ์ด๋ฆ์ ์ด์ฉํ ํํฐ this.setClassFilter(new SimpleClassFilter(mappedClassName)); } static class SimpleClassFilter implements ClassFilter { private final String mappedName; public SimpleClassFilter(String mappedName) { this.mappedName = mappedName; } @Override public boolean matches(Class clazz) { if (mappedName.startsWith("Test") || clazz.getSimpleName().startsWith("Test")) { System.out.println("!!!!!!!!!!!"); } if (mappedName.startsWith("User") || clazz.getSimpleName().startsWith("User")) { System.out.println("!!!!!!!!!!!"); } System.out.println(String.format("mappedName : %s, classSimpleName : %s", mappedName, clazz.getSimpleName())); return PatternMatchUtils.simpleMatch(mappedName, clazz.getSimpleName()); } } }
- ์ด๋๋ฐ์ด์ ๋ฅผ ์ด์ฉํ๋ ์๋ ํ๋ก์ ์์ฑ๊ธฐ ๋ฑ๋ก, ํฌ์ธํธ์ปท ๋ฑ๋ก
-
์ฝ๋ ์ ๊ธฐ/ํผ์น๊ธฐ
@Bean public NameMatchClassMethodPointcut transactionPointcut() { NameMatchClassMethodPointcut pointcut = new NameMatchClassMethodPointcut(); // ํด๋์ค ์ด๋ฆ ํจํด pointcut.setMappedClassName("*ServiceImpl"); // ๋ฉ์๋ ์ด๋ฆ ํจํด pointcut.setMappedName("upgrade*"); return pointcut; } @Bean public DefaultPointcutAdvisor transactionAdvisor() { DefaultPointcutAdvisor pointcutAdvisor = new DefaultPointcutAdvisor(); pointcutAdvisor.setAdvice(transactionAdvice()); pointcutAdvisor.setPointcut(transactionPointcut()); return pointcutAdvisor; } @Bean public ProxyFactoryBean userServiceProxyFactoryBean() { transactionAdvisor(); ProxyFactoryBean factoryBean = new ProxyFactoryBean(); factoryBean.setTarget(userService); factoryBean.setInterceptorNames("transactionAdvisor"); return factoryBean; }
-
- ํ
์คํธ์ฝ๋
-
์ฝ๋ ์ ๊ธฐ/ํผ์น๊ธฐ
@Test public void upgradeAllOrNothing() { userDao.deleteAll(); userDao.addAll(userList); testUserServiceImpl.setMailSender(mailSender); try { testUserServiceImpl.upgradeLevels(); fail("TestUserServiceException expected"); } catch (TestUserServiceException e) { } checkLevelUpgraded(userList.get(1), false); }
-
- ์๋์์ฑ ํ๋ก์ ํ์ธ
- ํฌ์ธํธ์ปท ํด๋์ค ์ด๋ฆ ํํฐ๋ฅผ *NotServiceImpl๋ก ๋ฐ๊ฟ์ ํ์ธ
- ์๋์์ฑ๋ ํ๋ก์ ํ์ธ(testUserService๊ฐ ํ๋ก์๋ก ๋ณ๊ฒฝ๋ ๊ฐ์ฒด์ธ์ง)
- ํด๋์ค ํํฐ๋ฅผ ์ ์ฉํ ํฌ์ธํธ์ปท ์์ฑ
- ํฌ์ธํธ์ปท ํํ์์ ์ด์ฉํ ํฌ์ธํธ์ปท
- ๋ ํธ๋ฆฌํ ํฌ์ธํธ์ปท ์์ฑ ๋ฐฉ๋ฒ
- ํฌ์ธํธ์ปท ํํ์
- ์ ๊ท์ ๊ฐ์ ์ผ์ข ์ ํํ์ ์ธ์ด๋ฅผ ์ฌ์ฉํด์ ํฌ์ธํธ์ปท์ ์์ฑํ๋ ๋ฐฉ์
- AspectJExpressionPointcut ํด๋์ค ์ฌ์ฉ
- ํฌ์ธํธ์ปท ํํ์ ๋ฌธ๋ฒ
-
[] : ์ต์ (์๋ต๊ฐ๋ฅ) | : or์กฐ๊ฑด execution([์ ๊ทผ์ ํ์ ํจํด] ํ์ ํจํด1 [ํ์ ํจํด2.]์ด๋ฆํจํด (ํ์ ํจํด3 | "..", ...) [throws ์์ธ ํจํด]) ์ ๊ทผ์ ํ์ : public, private ๋ฑ ํ์ ํจํด1 : ๋ฆฌํด ๊ฐ์ ํ์ ํจํด ํ์ ํจํด2 : ํจํค์ง์ ํด๋์ค ์ด๋ฆ์ ๋ํ ํจํด. ์ฌ์ฉํ ๋๋ '.'์ผ๋ก ์ฐ๊ฒฐ ์ด๋ฆํจํด : ๋ฉ์๋ ์ด๋ฆ ํจํด ํ์ ํจํด3 : ํ๋ผ๋ฏธํฐ์ ํ์ ์ ์์๋๋ก ๋ฃ์ ์ ์๋ค. ์์ผ๋ ์นด๋ ์ฌ์ฉ ๊ฐ๋ฅ ์์ธ ํจํด : ์์ธ ์ด๋ฆ ํจํด
- ํฌ์ธํธ์ปท ํํ์ ํ ์คํธ
-
ํ ์คํธ ์ฝ๋ ์ ๊ธฐ/ํผ์น๊ธฐ
@Test public void methodSignaturePointcut() throws SecurityException, NoSuchMethodException { // aspectjweaver ๋ํ๋์ ํ์ AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.setExpression("execution(public int toby.common.learningtest.Target.minus(int,int)) "); // Target.minus() : ์ฑ๊ณต assertThat(pointcut.getClassFilter().matches(Target.class) && pointcut.getMethodMatcher().matches(Target.class.getMethod("minus", int.class, int.class), null)).isTrue(); // Target.plus() : ๋ฉ์๋ ๋งค์ฒ์์ ์คํจ assertThat(pointcut.getClassFilter().matches(Target.class) && pointcut.getMethodMatcher().matches(Target.class.getMethod("plus", int.class, int.class), null)).isFalse(); // Bean.method() : ํด๋์ค ๋งค์ฒ์์ ์คํจ assertThat(pointcut.getClassFilter().matches(Bean.class) && pointcut.getMethodMatcher().matches(Target.class.getMethod("method"), null)).isFalse(); }
-
- ํฌ์ธํธ์ปท ํํ์ ํ
์คํธ
-
// ๋ ๊ฐ์ ์ ์ํ ํ๋ผ๋ฏธํฐ๋ฅผ ๊ฐ๋ minus๋ผ๋ ์ด๋ฆ์ ๋ชจ๋ ๋ฉ์๋๋ฅผ ์ ์ ํ๋ ํฌ์ธํธ์ปท execution(int minus(int, int)) // ๋ฆฌํดํ์ ์๊ด์๋ ํฌ์ธํธ์ปท execution(* minus(int, int)) // ํ๋ผ๋ฏธํฐ ๊ฐ์์ ํ์ ์ ๋ฌด์ํ๋ ํฌ์ธํธ์ปท execution(* minus(..)) // ๋ชจ๋ ๋ฉ์๋ ๋ค ํ์ฉ execution(* *(..))
-
ํ ์คํธ ์ฝ๋ ์ ๊ธฐ/ํผ์น๊ธฐ
@Test public void pointcut() throws NoSuchMethodException { targetClassPointcutMatches("execution(* *(..))", true, true, true, true, true, true); targetClassPointcutMatches("execution(* *(int, int))", false, false, true, true, false, false); targetClassPointcutMatches("execution(* *..*get.*(..))", true, true, true, true, true, false); targetClassPointcutMatches("execution(void *(..))", true, true, false, false, true, true); } // ํ๊น ํด๋์ค ๋ฉ์๋์ ๋ํด ํฌ์ธํธ์ปท ์ ์ ์ฌ๋ถ๋ฅผ ๊ฒ์ฌํ๋ ํฌํผ ๋ฉ์๋ public void targetClassPointcutMatches(String expression, boolean... expected) throws NoSuchMethodException { pointcutMatches(expression, expected[0], Target.class, "hello"); pointcutMatches(expression, expected[1], Target.class, "hello", String.class); pointcutMatches(expression, expected[2], Target.class, "plus", int.class, int.class); pointcutMatches(expression, expected[3], Target.class, "minus", int.class, int.class); pointcutMatches(expression, expected[4], Target.class, "method"); pointcutMatches(expression, expected[5], Bean.class, "method"); } // ํฌ์ธํธ์ปท๊ณผ ๋ฉ์๋๋ฅผ ๋น๊ตํด์ฃผ๋ ํ ์คํธ ํฌํผ ๋ฉ์๋ private void pointcutMatches(String expression, Boolean expected, Class clazz, String methodName, Class... args) throws NoSuchMethodException { AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.setExpression(expression); assertThat(pointcut.getClassFilter().matches(clazz) && pointcut.getMethodMatcher().matches(clazz.getMethod(methodName, args), null)).isEqualTo(expected); }
-
- ํฌ์ธํธ์ปท ํํ์์ ์ด์ฉํ๋ ํฌ์ธํธ์ปท ์ ์ฉ
-
@Bean public AspectJExpressionPointcut transactionPointcut() { AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.setExpression("execution(* *..*ServiceImpl.upgrade*(..))"); return pointcut; }
- ์ฝ๋์ ์ค์ ์ด ๋จ์ํด์ง๋ค.
- ๋ฌธ์์ด๋ก ๋ ํํ์์ด๊ธฐ ๋๋ฌธ์ ๋ฐํ์ ์์ ๊น์ง ๋ฌธ๋ฒ์ ๊ฒ์ฆ์ด๋ ๊ธฐ๋ฅ ํ์ธ์ด ๋ถ๊ฐ๋ฅํ๋ค.
-
- ํ์
ํจํด๊ณผ ํด๋์ค ์ด๋ฆ ํจํด
- TestUserServiceImpl ํด๋์ค ์ด๋ฆ์ TestUserService๋ก ๋ฐ๊พธ๋ฉด ํ ์คํธ๋ ์คํจํ๋๊ฐ?
- ์ํผํด๋์ค๊ฐ UserServiceImpl์ด๊ณ ์ธํฐํ์ด์ค๋ UserService์ด๊ธฐ ๋๋ฌธ์ ServiceImpl๋ก ๋๋๋ ํ์ ํจํด์ ์กฐ๊ฑด์ ์ถฉ์กฑํ๋ค.
- ํ์ ํจํด์ ์กฐ๊ฑด์ ์ถฉ์กฑํ๊ธฐ ๋๋ฌธ์ ํ ์คํธ๋ ์คํจํ์ง ์๋๋ค.
- ์ฌ๊ธฐ๊น์ง ์์ฝ
AOP ๋์๊ฐ ๋๊ธฐ ์์ํ๋ค.
1 ํธ๋์ญ์
์ฝ๋์ ๋ถ๋ฆฌ : AOP๋ค์ด๊ฐ๊ธฐ์ํ ์ค๋น
2 ๊ณ ๋ฆฝ๋ ๋จ์ํ
์คํธ
- 1์์ ๋ถ๋ฆฌํ ๋ด์ฉ์ ์ฅ์ ์ ํ
์คํธ์ฝ๋์ ์ ์ฉ
- ์ฅ์ : ์ฝ๋ ๋ถ๋ฆฌ๋ฅผ ํตํด ๋น์ฆ๋์ค ๋ก์ง์ ๋ํ ํ
์คํธ๋ฅผ ๋ ์ฝ๊ฒ ๋ง๋ค ์ ์์(๋น์ฆ๋์ค ๋ก์ง UserService ์ฝ๋์ ๋ถ๊ฐ๊ธฐ๋ฅ ํธ๋์ญ์
๊ฒฝ๊ณ์ค์ ์ฝ๋ ๋ถ๋ฆฌ)
- mockito๋ ๋ด์ฉ ์ค๋ช
ํ๋ค ์๊ฐ๋์ ๋ผ์๋ฃ์๊ฑฐ ๊ฐ์๋ฐ ์ฐธ ์ข์๋ณด์๋ค. ์์ด๋ค๋ ์์ฝ๋ค.
3. ๋ค์ด๋ด๋ฏน ํ๋ก์์ ํ๋ก์ ํจํด
- ํ๋ก์ ํจํด์ ์ ์ฉํด์ ๋ถ๊ฐ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ค.
- ๊ธฐ์กด ์ฝ๋์ ์ํฅ ์์ด ํ์ํ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ค.
- ํ๋ก์๋ฅผ ํฉํ ๋ฆฌ ๋น์ผ๋ก ๋ฑ๋กํ๋ค.
- ํ์ํ ๊ธฐ๋ฅ์ด ์ค๋ณต๋๋ ๊ฒฝ์ฐ ์ค๋ณต์ ์ด๋์ ๋ ๋ง์ ์ ์๋ค.
- ํ์ง๋ง ํ๊น ๊ฐ์ฒด๊ฐ ์ฌ๋ฌ๊ฐ๊ฐ ๋๋ ๊ฒฝ์ฐ ์ค์ ์ค๋ณต์ ํผํ ์ ์๋ค.
- AOP ๋์๊ฐ ๋๋ค.
AOP? ์ค๋ณต๋๋ ๊ธฐ๋ฅ์ ์ฝ๋ ์ค๋ณต ์์ด ๋์ํ๊ฒ ํ๋ ๋ฐฉ๋ฒ?
4. ์คํ๋ง์ ํ๋ก์ ํฉํ ๋ฆฌ ๋น
- ํ๋ก์ ํฉํ ๋ฆฌ ๋น์ ํ๊น๊ฐ์ฒด๋ฅผ ์ค์ ํ๊ณ ๋ถ๊ฐ๊ธฐ๋ฅ์ ์ถ๊ฐํ ๋ค์ ์ค์ ํ ๊ฐ์ฒด๋ฅผ ํ๋์ฉ ๋นผ์ค๋ ์
- ์ด๋๋ฐ์ด์ค์ ํฌ์ธํธ์ปท์ ๋ฌถ์ด์ ์ด๋๋ฐ์ด์ ๋ฅผ ๋ง๋ค๊ณ ํฉํ ๋ฆฌ ๋น์ ๋ฑ๋กํ๋ค.
- ๊ด์ฐฎ๊ธด ํ๋ฐ ์ค์ ์ ๋ณด๋ฅผ ๊ณ์ ์ถ๊ฐํด์ฃผ๋ ๋ฒ๊ฑฐ๋ก์์ด ์๋ค.
5. ์คํ๋ง AOP
- ๋น ํ์ฒ๋ฆฌ๊ธฐ๋ฅผ ์ฌ์ฉํด์ ํฌ์ธํธ์ปท์ ๋ง๋ ํ๊น์ ๋ถ๊ฐ๊ธฐ๋ฅ์ ์ ์ฉํ๋ ๋ฐฉ๋ฒ ์ฌ์ฉ(NameMatchClassMethodPointcut)
- ๋ ํธํ ํฌ์ธํธ์ปท ํํ์ ํ์ฉ(aspectjweaver)
- ๊ฒฐ๊ตญ ๋น์ด ์๊ธธ ๋ ๋ง๋ค ํฌ์ธํธ์ปท์ ๋ง๋ ๋์์ ํธ๋์ญ์
๊ธฐ๋ฅ์ด ์ถ๊ฐ๋๊ณ ๋ถ๊ฐ๊ธฐ๋ฅ์ด ์ถ๊ฐ๋ ๋น์ ํ๋ก์๋ก ์กด์ฌํ๊ฒ๋จ
- AOP๋ ๋ฌด์์ธ๊ฐ?
- UserService์ ํธ๋์ญ์
์ ์ ์ฉํด์จ ๊ณผ์ ์ ์ ๋ฆฌํด๋ณด๋ฉด
- ํธ๋์ญ์
์๋น์ค ์ถ์ํ
- ํธ๋์ญ์ ์ฒ๋ฆฌ์ ๋ํ ๊ตฌ์ฒด์ ์ธ ๊ตฌํ ๋ฐฉ๋ฒ์ ์์ ๋กญ๊ฒ ๋ฐ๊ฟ ์ ์๋๋ก ์ถ์ํ ๊ธฐ๋ฒ ์ ์ฉ
- ์๋ฒํ๊ฒฝ์์ ์ข ์๋์ง ์์
- ํ๋ก์์ ๋ฐ์ฝ๋ ์ดํฐ ํจํด
- ๋น์ฆ๋์ค ๋ก์ง ์ฝ๋์ ๋ถ๊ฐ๊ธฐ๋ฅ(ํธ๋์ญ์ ) ์ฝ๋๊ฐ ๋ณด์ด์ง ์๊ฒ๋์
- ๋ค์ด๋ด๋ฏน ํ๋ก์์ ํ๋ก์ ํฉํ ๋ฆฌ ๋น
- ๋ถ๊ฐ๊ธฐ๋ฅ ๋ถ์ฌ ์ฝ๋ ์ค๋ณต๋๋ ๋ฌธ์ ํด๊ฒฐ(JDK ๋ค์ด๋ด๋ฏน ํ๋ก์)
- ํฌ์ธํธ ์ปท๊ณผ ๋ถ๊ฐ๊ธฐ๋ฅ์ ๋ถ๋ฆฌํด์ ์ฌ๋ฌ ํ๋ก์์์ ๊ณต์ ํ๋๋ก ์ค์ (ํ๋ก์ ํฉํ ๋ฆฌ ๋น)
- ์๋ ํ๋ก์ ์์ฑ ๋ฐฉ๋ฒ๊ณผ ํฌ์ธํธ์ปท
- ํธ๋์ญ์ ๋์ ๋น ๋ง๋ค ํ๋ก์ ํฉํ ๋ฆฌ๋น์ ์ค์ ํด์ฃผ๋ ๋ฌธ์ ํด๊ฒฐ(๋น ํ์ฒ๋ฆฌ๊ธฐ)
- ํฌ์ธํธ์ปท ํํ์ ํ์ฉ(aspectjweaver)
- ๋ถ๊ฐ๊ธฐ๋ฅ์ ๋ชจ๋ํ
- TransactionAdvice๋ผ๋ ์ด๋ฆ์ผ๋ก ๋ชจ๋ํ
- ํธ๋์ญ์
์๋น์ค ์ถ์ํ
- AOP : Aspect Oriented Programmin
-
- ์ผ์ชฝ : 2์ฐจ์์ ์ธ ํ๋ฉด ๊ตฌ์กฐ์ ์ค๋ณต๋ ๋ถ๊ฐ๊ธฐ๋ฅ๋ค ๋ถํฌ
- ์ค๋ฅธ์ชฝ : 3์ฐจ์ ๊ตฌ์กฐ์์ ๋ถ๊ฐ๊ธฐ๋ฅ์ ๋ค๋ฅธ ๋ฉด์ ์กด์ฌํ๋๋ก ์ค๊ณ
- AOP
- ๋ถ๊ฐ์ ์ธ ๊ธฐ๋ฅ์ ๋ถ๋ฆฌํด์ Aspect๋ผ๋ ๋ ํนํ ๋ชจ๋๋ก ๋ง๋ค์ด์ ์ค๊ณํ๊ณ ๊ฐ๋ฐํ๋ ๋ฐฉ๋ฒ
- OOP๋ฅผ ๋๋ ๋ณด์กฐ์ ์ธ ๊ธฐ์
- ๊ด์ ์งํฅ ํ๋ก๊ทธ๋๋ฐ : ์ ํ๋ฆฌ์ผ์๋ ์ ํน์ ํ ๊ด์ ์ ๊ธฐ์ค์ผ๋ก ๋ฐ๋ผ๋ณผ ์ ์๊ฒ ํด์ค๋ค๋ ์๋ฏธ
- Aspect
- ๊ทธ ์์ฒด๋ก ์ ํ๋ฆฌ์ผ์ด์ ์ ํต์ฌ ๊ธฐ๋ฅ์ ๋ด๊ณ ์์ง๋ ์์ง๋ง, ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ฑํ๋ ์ค์ํ ์์์ด๋ค.
- ํต์ฌ๊ธฐ๋ฅ์ ๋ถ๊ฐ๋์ด ์๋ฏธ๋ฅผ ๊ฐ๋ ํน๋ณํ ๋ชจ๋์ด๋ค.
- ๋ถ๊ฐ๊ธฐ๋ฅ์ด ์ ์๋ ์ด๋๋ฐ์ด์ค์ ์ด๋๋ฐ์ด์ค์ ๋์์ ๊ฒฐ์ ํ๋ ์ ๋ณด๋ฅผ ๋ด์ ํฌ์ธํธ์ปท์ ๊ฐ๋๋ค.
-
- UserService์ ํธ๋์ญ์
์ ์ ์ฉํด์จ ๊ณผ์ ์ ์ ๋ฆฌํด๋ณด๋ฉด
- AOP ์ ์ฉ๊ธฐ์
- ํ๋ก์๋ฅผ ์ด์ฉํ AOP
- ํ๋ก์๋ก ๋ง๋ค์ด์ DI๋ก ์ฐ๊ฒฐ๋ ๋น ์ฌ์ด์ ์ ์ฉํด ํ๊น์ ๋ฉ์๋ ํธ์ถ ๊ณผ์ ์ ์ฐธ์ฌํด์ ๋ถ๊ฐ๊ธฐ๋ฅ์ ์ ๊ณต
- ์ด๋๋ฐ์ด์ค๊ฐ ์ ์ฉ๋๋ ๋์์ ๊ฐ์ฒด์ ๋ฉ์๋์ด๋ค.
- ์คํ๋ง์ AOP๋ ํ๋ก์ ๋ฐฉ์์ AOP๋ผ๊ณ ํ ์ ์๋ค. : ํ๋ก์๋ ๋ ๋ฆฝ์ ์ธ ๋ถ๊ฐ๊ธฐ๋ฅ์ ํ๊น ๋ฉ์๋์ ๋ค์ด๋ด๋ฏนํ๊ฒ ์ ์ฉํด์ฃผ๊ธฐ ์ํ ๊ฐ์ฅ ์ค์ํ ์ญํ ์ ๋งก์
- ๋ฐ์ดํธ์ฝ๋ ์์ฑ๊ณผ ์กฐ์์ ํตํ AOP
- ํ๋ก์ ๋ฐฉ์์ด ์๋ AOP๋? AspectJ ํ๋ ์์ํฌ
- ์ปดํ์ผ๋ ํ๊น์ ํด๋์ค ํ์ผ ์์ฒด๋ฅผ ์์ ํ๊ฑฐ๋, JVM์ ๋ก๋ฉ๋๋ ์์ ์ ๊ฐ๋ก์ฑ์ ๋ฐ์ดํธ์ฝ๋๋ฅผ ์กฐ์ํ๋ ๋ฐฉ๋ฒ ์ฌ์ฉ
- ๋๋ถ๋ถ์ AOP๋ ํ๋ก์ ๋ฐฉ์์ผ๋ก๋ ์ถฉ๋ถํ๋ค.
- AspectJ๋ ๋ณต์กํ์ง๋ง ๋ ๊ฐ๋ ฅํ๊ณ ์ ์ฐํจ
- ํ๋ก์๋ฅผ ์ด์ฉํ AOP
- AOP์ ์ฉ์ด
- ํ๊น : ๋ถ๊ฐ๊ธฐ๋ฅ์ ๋ถ์ฌํ ๋์
- ์ด๋๋ฐ์ด์ค : ๋ถ๊ฐ๊ธฐ๋ฅ์ ๋ด์ ๋ชจ๋
- ์กฐ์ธ ํฌ์ธํธ : ์ด๋๋ฐ์ด์ค๊ฐ ์ ์ฉ๋ ์ ์๋ ์์น. ํ๋ก์ AOP์์ ์กฐ์ธ ํฌ์ธํธ๋ ๋ฉ์๋์ ์คํ๋จ๊ณ๋ฟ
- ํฌ์ธํธ์ปท : ์ด๋๋ฐ์ด์ค๋ฅผ ์ ์ฉํ ์กฐ์ธ ํฌ์ธํธ๋ฅผ ์ ๋ณํ๋ ์์ ๋๋ ๊ทธ ๊ธฐ๋ฅ์ ์ ์ํ ๋ชจ๋
- ํ๋ก์ : ํด๋ผ์ด์ธํธ์ ํ๊น ์ฌ์ด์ ํฌ๋ช ํ๊ฒ ์กด์ฌํ๋ฉด์ ๋ถ๊ฐ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ๊ฐ์ฒด
- ์ด๋๋ฐ์ด์ : ํฌ์ธํธ์ปท๊ณผ ์ด๋๋ฐ์ด์ค๋ฅผ ํ๋ ์ฉ ๊ฐ๋ ๊ฐ์ฒด
- ์ ์คํํธ : AOP์ ๊ธฐ๋ณธ ๋ชจ๋. ์คํ๋ง์ ์ด๋๋ฐ์ด์ ๋ ์์ฃผ ๋จ์ํ ์ ์คํํธ๋ผ๊ณ ๋ณผ ์๋ ์๋ค.
- AOP ๋ค์์คํ์ด์ค
- ์คํ๋ง AOP๋ฅผ ์ ์ฉํ๊ธฐ ์ํด ์ถ๊ฐํ๋ ์ด๋๋ฐ์ด์ , ํฌ์ธํธ์ปท, ์๋ ํ๋ก์ ์์ฑ๊ธฐ ๊ฐ์ ๋น๋ค์ ์คํ๋ง ์ปจํ ์ด๋์ ์ํด ์๋์ผ๋ก ์ธ์๋ผ์ ํน๋ณํ ์์ ์ ์ํด ์ฌ์ฉ๋๋ค.
- ์คํ๋ง์ ํ๋ก์ ๋ฐฉ์ AOP๋ฅผ ์ ์ฉํ๋ ค๋ฉด ์ต์ํ ๋ค ๊ฐ์ง ๋น์ ๋ฑ๋กํด์ผ ํ๋ค.
- ์ด๋๋ฐ์ด์ค ์ธ์๋ ์คํ๋ง์ด ์ง์ ์ ๊ณตํ๋ ํด๋์ค๋ฅผ ๋น์ผ๋ก ๋ฑ๋กํ๊ณ ํ๋กํผํฐ ์ค์ ๋ง ํด์ค ๊ฒ
- ์๋ ํ๋ก์ ์์ฑ๊ธฐ
- ์ด๋๋ฐ์ด์ค
- ํฌ์ธํธ์ปท
- ์ด๋๋ฐ์ด์
- AOP ๋ค์์คํ์ด์ค
- ์คํ๋ง์ AOP์ ๊ด๋ จ ํ๊ทธ๋ฅผ ์ ์ํด์ค aop์คํค๋ง๋ฅผ ์ ๊ณตํ๋ค.
-
transaction-config.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"/> ... </beans>
AOP ์ค์ ๋น <aop:config> <aop:pointcut id="transactionPointcut" expression="execution(* *..*ServiceImpl.upgrade*(..))" /> <aop:advisor advice-ref="transactionAdvice" pointcut-ref="transactionPointcut"/> </aop:config> ์ด์ ์ค์ @Bean public TransactionAdvice transactionAdvice() { return new TransactionAdvice(transactionManager); } @Bean public AspectJExpressionPointcut transactionPointcut() { AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.setExpression("execution(* *..*ServiceImpl.upgrade*(..))"); return pointcut; } @Bean public DefaultPointcutAdvisor transactionAdvisor() { DefaultPointcutAdvisor pointcutAdvisor = new DefaultPointcutAdvisor(); pointcutAdvisor.setAdvice(transactionAdvice()); pointcutAdvisor.setPointcut(transactionPointcut()); return pointcutAdvisor; }
- ์ด๋๋ฐ์ด์ ๋ด์ฅ ํฌ์ธํธ์ปท
-
<aop:config> <aop:advisor advice-ref="transactionAdvice" pointcut="execution(* *..*ServiceImpl.upgrade*(..))"/> </aop:config>
-
-
ํธ๋์ญ์ ์์ฑ๋ค์ด ์ด๋ฐ๊ฒ ์๋ค ์ ๋๋ง ์๋ฉด ๋ ๋ฏ ํฌ์ธํธ์ปท ๋ง๋ค ์์ฑ์ ๋ค๋ฅด๊ฒ ํ๋ฉด ์ ์ง๋ณด์์ฑ์ด ๋ชน์ ๋จ์ด์ง ๋ฏ get~์๋ค๊ฐ readOnly๋ถ์ด๋ ์ ๋๊น์ง๋ ์ข๋ค๊ณ ์๊ฐํจ.
- DefaultTransactionDefinition์ ์ฉ๋๋?
- ํธ๋์ญ์
์ ์
- ํธ๋์ญ์ ์ด๋ผ๊ณ ๋ชจ๋ ๊ฐ์ ๋ฐฉ์์ผ๋ก ๋์ํ๋ ๊ฒ์ ์๋๋ค.
- ํธ๋์ญ์
์ ํ
- ํธ๋์ญ์ ๊ฒฝ๊ณ์์ ์ด๋ฏธ ์งํ ์ค์ธ ํธ๋์ญ์ ์ด ์์ ๋ ๋๋ ์์ ๋ ์ด๋ป๊ฒ ๋์ํ ๊ฒ์ธ๊ฐ๋ฅผ ๊ฒฐ์ ํ๋ ๋ฐฉ์
- B๋ Aํธ๋์ญ์ ์ ์ข ์๋ผ์ผ ํ๋๊ฐ, ๋ ๋ฆฝ์ ์ผ๋ก ์๋ํด์ผ ํ๋๊ฐ๋ฅผ ๊ฒฐ์
- PROPAGATION_REQUIRED
- ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉ๋๋ ์ ํ ์์ฑ
- ์งํ ์ค์ธ ํธ๋์ญ์ ์ด ์์ผ๋ฉด ์๋ก ์์ํ๊ณ , ์ด๋ฏธ ์์๋ ํธ๋์ญ์ ์ด ์์ผ๋ฉด ์ฐธ์ฌํ๋ค.
- PROPAGATION_REQUIRES_NEW
- ํญ์ ์๋ก์ด ํธ๋์ญ์ ์ ์คํํ๋ค.
- PROPAGATION_NOT_SUPPORTED
- ์งํ ์ค์ธ ํธ๋์ญ์ ์ด ์์ด๋ ๋ฌด์ํ๋ค.
- ํฌ์ธํธ์ปท์ ๋ณต์กํ๊ฒ ๋ง๋ค์ง ์๊ณ ํน์ ๊ธฐ๋ฅ์๋ง ํธ๋์ญ์ ์ ์ฉ์ด ์๋๋๋ก ์ค์ ํ๊ณ ์ ํ ๋ ์ฌ์ฉ
- ์ด๋ฌ๋ฉด ๋ ๋ณต์กํ ๊ฑฐ ๊ฐ์๋ฐ... config์ชฝ ๋ณด๋๊ฒ ์๋ ์ด์ ์ด ๋ฉ์๋์ ํธ๋์ญ์ ์ด ๊ฑธ๋ ค์๋์ง ์๊ฑธ๋ ค์๋์ง ์ด๋ป๊ฒ์์
- ๊ฒฉ๋ฆฌ ์์ค(Isolation level)
- ๋ชจ๋ DB ํธ๋์ญ์ ์ ๊ฒฉ๋ฆฌ์์ค์ ๊ฐ๊ณ ์์ด์ผ ํ๋ค.
- ๊ธฐ๋ณธ์ ์ผ๋ก๋ DB์ ์ค์ ๋์ด ์๊ณ DefaultTransactionDefinition๋ ISOLATION_DEFAULT์ด๋ค.
- ํน๋ณํ ์์ ์ ์ํํ๋ ๋ฉ์๋์ ๊ฒฝ์ฐ ๋ ์์ ์ธ ๊ฒฉ๋ฆฌ์์ค์ ์ง์ ํ ํ์๊ฐ ์๋ค.
- ์ ํ์๊ฐ
- ํธ๋์ญ์ ์ํ ์ ํ์๊ฐ์ ์ค์ ํ ์ ์๋ค.
- DefaultTransactionDefinition์ ๊ธฐ๋ณธ ์ค์ ์ ์ ํ์๊ฐ์ด ์๋ ๊ฒ์ด๋ค.
- ์ฝ๊ธฐ์ ์ฉ
- ์ฝ๊ธฐ์ ์ฉ ํธ๋์ญ์ ์ ํธ๋์ญ์ ๋ด์์ ๋ฐ์ดํฐ์กฐ์ ์๋๋ฅผ ๋ง์์ค ์ ์๋ค.
- ํธ๋์ญ์ ์ธํฐ์ ํฐ์ ํธ๋์ญ์ ์์ฑ
-
๋ฉ์๋ ๋ณ๋ก ๋ค๋ฅธ ํธ๋์ญ์ ์ ์๋ฅผ ์ ์ฉํ๋ ค๋ฉด ์ด๋๋ฐ์ด์ค์ ๊ธฐ๋ฅ์ ํ์ฅํด์ผ ํ๋ค.
- TransactionInterceptor
- ์คํ๋ง์ด ํธ๋์ญ์ ๊ฒฝ๊ณ์ค์ ์ด๋๋ฐ์ด์ค๋ฅผ ํธ๋ฆฌํ๊ฒ ์ฌ์ฉํ ์ ์๋๋ก ๋ง๋ ์ธํฐ์ ํฐ.
- PlatformTransactionMnager์ Properties ํ์ ์ ๋ ๊ฐ์ง ํ๋กํผํฐ๋ฅผ ๊ฐ๋๋ค.
- Properties๋ Mapํ์ ๊ฐ์ฒด๋ค. ๋ฉ์๋ ํจํด์ ๋ฐ๋ผ ๊ฐ๊ธฐ ๋ค๋ฅธ ํธ๋์ญ์ ์์ฑ์ ๋ถ์ฌํ๊ธฐ ์ํด Mapํ์ ์ผ๋ก ๋ฐ๋๋ค.
- ๋ฉ์๋ ์ด๋ฆ ํจํด์ ํตํ ํธ๋์ญ์
์์ฑ ์ง์
-
TransactionInterceptor์ Properties์ ํค
-
PROPAGATION_NAME // ํธ๋์ญ์ ์ ํ ๋ฐฉ์. ํ์๊ฐ , ISOLATION_NAME // ๊ฒฉ๋ฆฌ ์์ค. ์๋ต ๊ฐ๋ฅ(deafult=๋ํดํธ๊ฒฉ๋ฆฌ์์ค) , readOnly // ์ฝ๊ธฐ์ ์ฉ. ์๋ต ๊ฐ๋ฅ(default=์ฝ๊ธฐ์ ์ฉ์๋) , timeout_NNNN // ์ ํ์๊ฐ. NNNN์ ์ด๋จ์ ์๊ฐ. ์๋ต ๊ฐ๋ฅ , -Exception1 // ์ฒดํฌ ์์ธ ์ค ๋กค๋ฐฑ ๋์์ผ๋ก ์ถ๊ฐํ ์์ธ. ํ ๊ฐ ์ด์ ๋ฑ๋ก๊ฐ๋ฅ , +Exception2 // ๋ฐํ์ ์์ธ์ง๋ง ๋กค๋ฐฑ์ํค์ง ์์ ์์ธ. ํ ๊ฐ ์ด์ ๋ฑ๋ก๊ฐ๋ฅ
-
ํธ๋์ญ์ ์์ฑ ์ ์ ์์
@Bean public TransactionInterceptor transactionInterceptor() { TransactionInterceptor interceptor = new TransactionInterceptor(); interceptor.setTransactionManager(transactionManager);
NameMatchTransactionAttributeSource txAttributeSource = new NameMatchTransactionAttributeSource(); Map<String, TransactionAttribute> txMethods = new HashMap();
RuleBasedTransactionAttribute txAttributeForAll = new RuleBasedTransactionAttribute(); txAttributeForAll.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); txMethods.put("*", txAttributeForAll);
RuleBasedTransactionAttribute txAttributeForGet = new RuleBasedTransactionAttribute(); txAttributeForGet.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); txAttributeForGet.setReadOnly(true); txAttributeForGet.setTimeout(30); txMethods.put("get*", txAttributeForGet);
RuleBasedTransactionAttribute txAttributeForUpgrade = new RuleBasedTransactionAttribute(); txAttributeForUpgrade.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); txAttributeForUpgrade.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE); txMethods.put("upgrade*", txAttributeForGet);
txAttributeSource.setNameMap(txMethods); interceptor.setTransactionAttributeSources(txAttributeSource); return interceptor; }
-
- tx ๋ค์์คํ์ด์ค๋ฅผ ์ด์ฉํ ์ค์ ๋ฐฉ๋ฒ
-
// transaction-config.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> ... // ์คํ๋๋ฉด? Enumeration์ผ๋ก ์คํค๋ง์ ๊ฐ์ด ์ ์๋์ด ์๊ธฐ ๋๋ฌธ์ XML ์ ํจ์ฑ๊ฒ์ฌ๋ก ํ์ธ ๊ฐ๋ฅํ๋จ๋ค. <tx:advice // ์ด ํ๊ทธ์ ์ํด TransactionInterceptor ๋น์ด ๋ฑ๋ก๋๋ค. id="transactionAdvice transaction-manger="transactionManager"> // ํธ๋์ญ์ ๋งค๋์ ์ ๋น ์์ด๋๊ฐ transactionManager๋ผ๋ฉด ์๋ต ๊ฐ๋ฅ\ <tx:attributes> <tx:method name="get*" propagation="REQUIRED" read-only="true" timeout="30" /> <tx:method name="upgrade*" propagation="REQUIRES_NEW" isolation="SERIALIZABLE" /> <tx:method name="*" propagation="REQUIRED" /> </tx:attributes> </tx:advice>
-
- TransactionInterceptor
- ํฌ์ธํธ์ปท๊ณผ ํธ๋์ญ์
์์ฑ์ ์ ์ฉ ์ ๋ต
- ํธ๋์ญ์
ํฌ์ธํธ์ปท ํํ์์ ํ์
ํจํด์ด๋ ๋น ์ด๋ฆ์ ์ด์ฉํ๋ค.
- ํธ๋์ญ์ ๊ฒฝ๊ณ๋ก ์ผ์ ํด๋์ค๋ค์ด ๋ชจ์ฌ์๋ ํจํค์ง๋ฅผ ํต์งธ๋ก ์ ํ
- ํด๋์ค ์ด๋ฆ์์ ์ผ์ ํ ํจํด์ ์ฐพ์์ ํํ์ ๊ฒฐ์ : execution(* *..*Service.*(..))
- ์คํ๋ง์ ๋น ์ด๋ฆ์ ์ด์ฉ : bean(*Service)
- ๊ณตํต๋ ๋ฉ์๋ ์ด๋ฆ ๊ท์น์ ํตํด ์ต์ํ์ ํธ๋์ญ์
์ด๋๋ฐ์ด์ค์ ์์ฑ์ ์ ์ํ๋ค.
- ์ค์ ๋ก ํ๋์ ์ดํ๋ฆฌ์ผ์ด์ ์์ ์ฌ์ฉํ ํธ๋์ญ์ ์์ฑ์ ์ข ๋ฅ๋ ๊ทธ๋ค์ง ๋ค์ํ์ง ์๋ค.
- ๊ฐ์ฅ ๊ฐ๋จํ ํธ๋์ญ์ ์์ฑ ๋ถ์ฌ ๋ฐฉ๋ฒ์ ๋ชจ๋ ๋ฉ์๋์ ๋ํด ๋ํดํธ ์์ฑ์ ์ง์ ํ๋ ๊ฒ์ด๋ค. ๊ฐ๋ฐ ์ง์ฒ๋์ ๋ฐ๋ผ ๋จ๊ณ์ ์ผ๋ก ์ถ๊ฐํด์ฃผ๋ฉด ๋จ
- get*, find*๋ฑ ๋ช ๋ช ๊ท์น์ ์ค์ ํด์ ํธ๋์ญ์ ์ ์ ์ฉํ๋ฉด ์ข์
- ํ๋ก์ ๋ฐฉ์ AOP๋ ๊ฐ์ ํ๊น ๊ฐ์ฒด ๋ด์ ๋ฉ์๋๋ฅผ ํธ์ถํ ๋๋ ์ ์ฉ๋์ง ์๋๋ค.
- ๊ทธ๋ ์ง์? ํด๋ผ์ด์ธํธ๋ ํ๋ก์๋ฅผ ํธ์ถํ๊ฒ๋ ์ค์ ๋ผ์๋๋ฐ ํ๊น ๊ฐ์ฒด ๋ด์์๋ ํ๋ก์๋ฅผ ๋ชป๋ถ๋ฅด๋๊น
- ์ฃผ์ํด์ผ ํ๋ค๋ ๋ด์ฉ
- ํธ๋์ญ์
ํฌ์ธํธ์ปท ํํ์์ ํ์
ํจํด์ด๋ ๋น ์ด๋ฆ์ ์ด์ฉํ๋ค.
- ํธ๋์ญ์
์์ฑ ์ ์ฉ
- UserService์ ์ ์ฉํด๋ณด์
- ํธ๋์ญ์
๊ฒฝ๊ณ์ค์ ์ ์ผ์ํ
- *์ค์* ์๋น์ค ๊ณ์ธต์ ํธ๋์ญ์ ์ด ์์๋๊ณ ์ข ๋ฃ๋๋ ๊ฒฝ๊ณ๋ก ์ ํ๋ค๋ฉด, ํ ์คํธ์ ๊ฐ์ ํน๋ณํ ์ด์ ๊ฐ ์๋๊ณ ๋ ๋ค๋ฅธ ๊ณ์ธต์ด๋ ๋ชจ๋์์ DAO์ ์ง์ ์ ๊ทผํ๋ ๊ฒ์ ์ฐจ๋จํด์ผ ํ๋ค.
- ์๋ ๋ฉ์๋๋ฅผ ์ถ๊ฐํ๊ณ ๊ตฌํํด์ User๊ด๋ จ ๋ฐ์ดํฐ ์กฐ์์ UserService๋ผ๋ ํธ๋์ญ์ ๊ฒฝ๊ณ๋ฅผ ํตํด ์งํํ ๊ฒฝ์ฐ ๋ชจ๋ ํธ๋์ญ์ ์ ์ ์ฉํ ์ ์๊ฒ ๋๋ค.
-
UserService์ ๋ฉ์๋ ์ถ๊ฐ
public interface UserService { // ... User get(String id); List getAll(); void deleteAll(); void update(User user); }
- ์๋น์ค ๋น์ ์ ์ฉ๋๋ ํฌ์ธํธ์ปท ํํ์ ๋ฑ๋ก
- ๋น ์ด๋ฆ์ ์ฌ์ฉํ ํํ์์ ๊ฐ๋ ํฌ์ธํธ์ปท๊ณผ ์ด๋๋ฐ์ด์
-
<aop:config> <aop:advisor advice-ref="transactionAdvice" pointcut="bean(*Service)"/> </aop:config>
- ํธ๋์ญ์
์์ฑ์ ๊ฐ์ง ํธ๋์ญ์
์ด๋๋ฐ์ด์ค ๋ฑ๋ก
- ์ด์ ์ ์ค์ ํ๋ transaction-config.xml ๊ทธ๋๋ก ๋๋ฉด ๋
- ํธ๋์ญ์
์์ฑ ํ
์คํธ
- ํฌ์ธํธ์ปท์ Bean์ผ๋ก ์ค์ ํ๊ธฐ ๋๋ฌธ์ testUserServiceImpl๋ก ์ค์ ํ๋ ๋น ์ด๋ฆ testUserService๋ก ๋ณ๊ฒฝ
- ์ฝ๊ธฐ์ ์ฉ ์์ฑ ํ ์คํธ
- ์๋๋๋ฐ..? h2๋ ์๋๋ค๋๋ฐ mariadb๋ก ํด๋ด์ผ๊ฒ ๋ค
- mariadb๋ก ํ๋๊น ๋๋ค(TransientDataAccessResourceException)
- ํธ๋์ญ์
์ ๋
ธํ
์ด์
- ์๋ฐ 5์์ ๋ฑ์ฅ
-
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) // ์ ๋ ธํ ์ด์ ์ ๋ณด๊ฐ ์ธ์ ๊น์ง ์ ์ง๋ ์ง ์ง์ . ์ด๋ ๊ฒ ์ค์ ํ๋ฉด ๋ฐํ์ ๋๋ ๋ฆฌํ๋ ์ ์ ํตํด ์ ๋ ธํ ์ด์ ์ ๋ณด๋ฅผ ์ป์ ์ ์๋ค. @Inherited // ์์์ ํตํด์๋ ์ ๋ ธํ ์ด์ ์ ๋ณด๋ฅผ ์ป์ ์ ์๊ฒ ํ๋ค. @Documented public @interface Transactional { // ๋ํดํธ ๊ฐ์ด ์ค์ ๋์ด ์์ผ๋ฏ๋ก ๋ชจ๋ ์๋ต ๊ฐ๋ฅํ๋ค. @AliasFor("transactionManager") String value() default ""; @AliasFor("value") String transactionManager() default ""; Propagation propagation() default Propagation.REQUIRED; Isolation isolation() default Isolation.DEFAULT; int timeout() default TransactionDefinition.TIMEOUT_DEFAULT; boolean readOnly() default false; Class<? extends Throwable>[] rollbackFor() default {}; String[] rollbackForClassName() default {}; Class<? extends Throwable>[] noRollbackFor() default {}; String[] noRollbackForClassName() default {}; }
- ํธ๋์ญ์
์์ฑ์ ์ด์ฉํ๋ ํฌ์ธํธ์ปท
- ํฌ์ธํธ์ปท๊ณผ ํธ๋์ญ์ ์์ฑ์ ์ ๋ ธํ ์ด์ ํ๋๋ก ์ง์ ํ ์ ์๋ค.
- ๋ฉ์๋ ๋ง๋ค ๋ถ์ฌํ๋ฉด ์ฝ๋๊ฐ ์ง์ ๋ถํด ์ง ์ ์๋ค.
- ๋์ฒด ์ ์ฑ
- ํธ๋์ญ์
์ ๋
ธํ
์ด์
์ฌ์ฉ์ ์ํ ์ค์
- <tx:annotation-driven/>
- spring boot์์๋ ๋ณธ ์ ์ด ์๋๋ฐ? ์๋์ค์ ์ธ๊ฐ๋ด
- ํธ๋์ญ์
์ ๋
ธํ
์ด์
์ ์ฉ
- UserService ์ธํฐํ์ด์ค์ ์ ๋ ธํ ์ด์ ์ ์ฉ
-
UserService ์ธํฐํ์ด์ค
@Transactional public interface UserService { void add(User user); void upgradeLevels(); void createOrIncreaseRecommend(User user); void setMailSender(MailSender mailsender); @Transactional(readOnly = true) User get(String id); @Transactional(readOnly = true) List getAll(); void deleteAll(); void update(User user); }
- ์ฌ๊ธฐ์ ๊ตฌํ์ฒด UserServiceImpl์ @Transactional์ ๋ถ์ด๋ฉด? ๋์ฒด์ ์ฑ ์ ๋ฐ๋ผ readOnly๋ ๋ฌด์๋๋ค.
- ์ ์ธ์ ํธ๋์ญ์
๊ณผ ํธ๋์ญ์
์ ํ ์์ฑ
- ์ ์ธ์ ํธ๋์ญ์ : AOP๋ฅผ ์ด์ฉํด ์ฝ๋ ์ธ๋ถ์์ ํธ๋์ญ์ ์ ๊ธฐ๋ฅ์ ๋ถ์ฌํด์ฃผ๊ณ ์์ฑ์ ์ง์ ํ ์ ์๊ฒ ํ๋ ๋ฐฉ๋ฒ
- ํ๋ก๊ทธ๋จ์ ์ํ ํธ๋์ญ์ : ์์ ๋ฐ๋๋ก ๊ฐ๋ณ ๋ฐ์ดํฐ ๊ธฐ์ ์ ํธ๋์ญ์ API๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ
- ํน๋ณํ ๊ฒฝ์ฐ๊ฐ ์๋๋ผ๋ฉด ์ ์ธ์ ๋ฐฉ์์ ํธ๋์ญ์ ์ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ฐ๋์งํ๋ค.
- ํธ๋์ญ์
ฉ ๋๊ธฐํ์ ํ
์คํธ
- ํธ๋์ญ์ ๋งค๋์ ์ ํธ๋์ญ์ ๋๊ธฐํ
-
3๊ฐ์ ํธ๋์ญ์ ์ด ๋ง๋ค์ด์ง ํ ์คํธ์ฝ๋
@Test public void transactionSync() { userService.deleteAll(); userService.add(userList.get(0)); userService.add(userList.get(1)); }
- ํธ๋์ญ์ ๋งค๋์ ๋ฅผ ์ด์ฉํ ํ ์คํธ์ฉ ํธ๋์ญ์ ์ ์ด
-
3๊ฐ์ ํธ๋์ญ์ ์ ํฉ์น ํ ์คํธ์ฝ๋
@Test public void transactionSync() { // UserService๋ฅผ ํธ์ถํ๊ธฐ ์ ์ ํธ๋์ญ์ ์ ์์ํด์ฃผ๋ฉด ํธ๋์ญ์ ์ด ์ ํ๋์ด ํตํฉ๋๋ค. DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition(); // ํธ๋์ญ์ ๋งค๋์ ์๊ฒ ํธ๋์ญ์ ์ ์์ฒญํ๋ค. // ๊ธฐ์กด์ ์์๋ ํธ๋์ญ์ ์ด ์์ผ๋ฏ๋ก ์๋ก์ด ํธ๋์ญ์ ์ ์์์ํค๊ณ ํธ๋์ญ์ ์ ๋ณด๋ฅผ ๋๋ ค์ค๋ค. // ๋์์ ๋ง๋ค์ด์ง ํธ๋์ญ์ ์ ๋ค๋ฅธ ๊ณณ์์๋ ์ฌ์ฉํ ์ ์๋๋ก ๋๊ธฐํํ๋ค. TransactionStatus txStatus = transactionManager.getTransaction(txDefinition); userService.deleteAll(); userService.add(userList.get(0)); userService.add(userList.get(1)); transactionManager.commit(txStatus); }
- ํธ๋์ญ์ ๋๊ธฐํ ๊ฒ์ฆ์ฉ ํ ์คํธ
-
read-only ํ ์คํธ
@Test(expected = TransientDataAccessResourceException.class) public void transactionSyncReadOnly() { DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition(); txDefinition.setReadOnly(true); TransactionStatus txStatus = transactionManager.getTransaction(txDefinition); userService.deleteAll(); }
-
๋กค๋ฐฑ ํ ์คํธ
@Test public void transactionSyncRollBack() { userDao.deleteAll(); assertThat(userDao.getAll().size()).isEqualTo(0); DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition(); TransactionStatus txStatus = transactionManager.getTransaction(txDefinition); userService.add(userList.get(0)); assertThat(userDao.getAll().size()).isEqualTo(1); transactionManager.rollback(txStatus); assertThat(userDao.getAll().size()).isEqualTo(0); }
- ๋กค๋ฐฑ ํ
์คํธ
- ํ ์คํธ ๋ด์ ๋ชจ๋ DB์์ ์ ํ๋์ ํธ๋์ญ์ ์์์ ๋์ํ๊ฒ ํ๊ณ , ํ ์คํธ๊ฐ ๋๋๋ฉด ๋ฌด์กฐ๊ฑด ๋กค๋ฐฑํ๋ ํ ์คํธ
- ํ ์คํธ์ DB์กฐ์์ผ๋ก ์ธํ ์ฌ์ด๋์ดํํธ๋ฅผ ๋ฐฉ์งํ ์ ์์
- ๊ทผ๋ฐ ๋ฐ์ดํฐ ์กฐ๊ธ๋ง ๋ณต์กํด์ ธ๋ ๋ง๋ค๊ธฐ ๋นก์ผ๋ฐ
- ํ
์คํธ๋ฅผ ์ํ ํธ๋์ญ์
์ ๋
ธํ
์ด์
- @Transactional
- ํ ์คํธ์๋ @Transactional์ ์ ์ฉํ ์ ์๋ค.
-
์์์ ๋ดค๋ read-only ํ ์คํธ
@Test(expected = TransientDataAccessResourceException.class) @Transactional(readOnly = true) public void transactionSyncReadOnly() { userService.deleteAll(); }
- @Rollback
- @Transactional์ด ๋ฌ๋ ค์์ด๋ @Rollback(false)ํด์ฃผ๋ฉด ์์ธ๊ฐ ๋ฐ์ํ์ง ์๋ ํ ํธ๋์ญ์ ์ ์ปค๋ฐ๋๋ค.
- @TransactionConfiguration
- @Rollback์ ๋ฉ์๋์๋ง ๋ถ์ผ ์ ์๋ค.
- @TransactionalConfiguration(defaultRollback = false) ํด์ฃผ๋ฉด ์ฃ @Rollback(false) ๋๋๊ฒ
- ํน์ ํ ์คํธ๋ฉ์๋๋ง ๋กค๋ฐฑ์ํค๊ณ ์ถ์ผ๋ฉด ๊ทธ ๋ฉ์๋์ @Rollback ๋ถ์ด๋ฉด ๋จ
- @TransactionalConfiguration deprecated ๋๋ค.
- ์๋? ํ ์คํธ๋ ๋ฒจ์์ @Transactional์ ํญ์ rollback=true๋ผ์
- ํ ์คํธํด๋์ค์ @Transactional ๋ถ์ด๊ณ ํ ์คํธ์ฝ๋ ๋๋ฆฌ๋ฉด ์ ์ฒด ํ ์คํธ ๋ฉ์๋๋ rollback๋๋ค.
- NotTransactional๊ณผ Propagation.NEVER
- @NotTransactional : @Transactional ์ค์ ์ ๋ฌด์ํ๊ณ ํธ๋์ญ์ ์ ์์ํ์ง ์์ ์ฑ๋ก ํ ์คํธ๋ฅผ ์์ํ๊ฒ ํจ
- @Transactional(propagation = Propagation.NEVER) : ํธ๋์ญ์ ์ด ์์๋์ง ์๋๋ค.
- ํจ๊ณผ์ ์ธ DB ํ
์คํธ
- ํธ๋์ ์ ์ ์ด ์ ๋ ธํ ์ด์ ์ ํตํฉ ํ ์คํธ๋ฅผ ๋ง๋ค ๋ ์์ฃผ ์ ์ฉํ๋ค.
- DB๊ฐ ์ฌ์ฉ๋๋ ํตํฉํ ์คํธ๋ ๋ณ๋์ ํด๋์ค๋ก ๋ง๋ค๊ณ ๋กค๋ฐฑ๋๋๋ก ์ค์ ํ๋๊ฒ ์ข๋ค.
- ํ ๋น ์์ ์จ๋ ํธ๋์ญ์ ์ ์์๋ก AOP๋ฅผ ์ค๋ช ํ๊ณ , ์ค๋ช ์ด ๋ค ๋๋๋ ํธ๋์ญ์ ์ค์ ์ ์ฝ๊ณ ๊น๋ํ๊ฒ ํ ์ ์๋ ๋ฐฉ๋ฒ์ ์๋ ค์ฃผ์ จ๋ค. ๋ฐ์์ง์ง
- @Transactional
- ์ง์ง ๊ธธ์๋ค ์ ๋ง
- 6์ฅ์์๋ ํธ๋์ญ์ ๊ฒฝ๊ณ์ค์ ๊ธฐ๋ฅ์ ์ฑ๊ฒฉ์ด ๋ค๋ฅธ ๋น์ฆ๋์ค ๋ก์ง ํด๋์ค์์ ๋ถ๋ฆฌํ๊ณ ์ ์ฐํ๊ฒ ์ ์ฉํ ์ ์๋ ๋ฐฉ๋ฒ์ ์ฐพ์๋ณด๋ฉด์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฐ์ฌํด์ ๋ํ๋๋ ๋ถ๊ฐ๊ธฐ๋ฅ์ ๋ชจ๋ํํ ์ ์๋ AOP ๊ธฐ์ ์ ์์๋ดค๋ค.
- ํธ๋์ญ์ ์ฒ๋ผ ํ๊ฒฝ๊ณผ ์ธ๋ถ ๋ฆฌ์์ค์ ์ํฅ์ ๋ฐ๋ ์ฝ๋๋ฅผ ๋ถ๋ฆฌํ๋ฉด ๋น์ฆ๋์ค ๋ก์ง์๋ง ์ถฉ์คํ ํ ์คํธ๋ฅผ ๋ง๋ค ์ ์๋ค.
- ๋ชฉ ๊ฐ์ฒด๋ฅผ ์ด์ฉํ๋ฉด ์์กด๊ด๊ณ ์์ ์๋ ๊ฐ์ฒด๋ ์์ฝ๊ฒ ๊ณ ๋ฆฝ๋ ํ ์คํธ๋ก ๋ง๋ค ์ ์๋ค.
- ํฌ์ธํธ์ปท์ AspectJ ํฌ์ธํธ์ปท ํํ์์ ์ฌ์ฉํด์ ์์ฑํ๋ฉด ํธ๋ฆฌํ๋ค.
- AOP๋ OOP๋ง์ผ๋ก๋ ๋ชจ๋ํํ๊ธฐ ํ๋ ๋ถ๊ฐ๊ธฐ๋ฅ์ ํจ๊ณผ์ ์ผ๋ก ๋ชจ๋ํํ๋๋ก ๋์์ฃผ๋ ๊ธฐ์ ์ด๋ค.
- ์คํ๋ง์ ์์ฃผ ์ฌ์ฉ๋๋ AOP ์ค์ ๊ณผ ํธ๋์ญ์ ์์ฑ์ ์ง์ ํ๋ ๋ฐ ์ฌ์ฉํ ์ ์๋ ์ ์ฉ ํ๊ทธ๋ฅผ ์ ๊ณตํ๋ค.
- AOP๋ฅผ ์ด์ฉํด ํธ๋์ญ์
์์ฑ์ ์ง์ ํ๋ ๋ฐฉ๋ฒ
- ํฌ์ธํธ์ปท ํํ์๊ณผ ๋ฉ์๋ ์ด๋ฆ ํจํด์ ์ด์ฉํ๋ ๋ฐฉ๋ฒ
- ํ๊น์ ์ง์ ๋ถ์ฌํ๋ @Transactional ์ ๋ ธํ ์ด์ ํ์ฉ
- @Transactional์ ์ด์ฉํ ํธ๋์ญ์ ์์ฑ์ ํ ์คํธ์ ์ ์ฉํ๋ฉด ์์ฝ๊ฒ DB๋ฅผ ์ฌ์ฉํ๋ ์ฝ๋์ ํ ์คํธ๋ฅผ ๋ง๋ค ์ ์๋ค.