AOP: 스프링의 3대 기반기술
현재 코드의 문제점: 비즈니스 로직이 주가 아니라, 트랜잭션 코드가 더 큰 자리를 차지하고 있음
// 비즈니스 로직과 트랜잭션 경계설정의 분리
public void upgradeLevels() throws Exception {
TransactionStatus status = this.transactionManager.getTransaction(
new DefaultTransactionDefinition());
try {
upgradeLevelsInternal();
this.transactionManager.commit(status);
} catch (Exception e) {
this.transactionManager.rollback(status);
throw e;
}
}
// 비즈니스 로직 코드
private void upgradeLevelsInternal(){
List<User> users = userDao.getAll();
for (User user : users) {
if (canUpgradeLevel(user)) {
upgradeLevel(user);
}
}
}
분리는 했으나, 아예 트랜잭션 코드를 숨기려면? → 트랜잭션 코드를 클래스 밖으로 뽑아내기
UserService를 인터페이스로 만들고 기존 코드는 UserService인터페이스의 구현 클래스를 만들어넣기
// 기존의 UserService 클래스를 UserServiceImpl로 이름 변경
// 클라이언트가 사용할 로직을 담은 핵심 메소드만 UserService 인터페이스로 만든 후
// UserServiceImpl이 구현하도록 함
// 트랜잭션 코드를 제거한 UserService
public class UserServiceImpl implements UserService {
UserDao userDao;
MailSender mailSender;
public void upgradeLevels() {
Lise<User> users = userDao.getAll();
for (User user : users) {
if (canUpgradeLevel(user)) {
upgradeLevel(user);
}
}
}
// 비즈니스 트랜잭션 처리
// UserServiceTx 클래스
public class UserServiceTx implements UserService {
// UserService를 구현한 다른 오브젝트를 DI 받음
UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
// DI 받은 UserService 오브젝트에 모든 기능을 위임
public void add(User user) {
userService.add(user);
}
public void upgradeLevels() {
userService.upgradeLevels();
}
}
// 트랜잭션이 적용된 UserServiceTx
public class UserServiceTx implements UserService {
// UserService를 구현한 다른 오브젝트를 DI 받음
UserService userService;
// 추상화된 트랜잭션 구현 오브젝트를 DI 받을 수 있도록 프로퍼티 추가
PlatformTransactionManager transactionManager;
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManger = transactionManager;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
// DI 받은 UserService 오브젝트에 모든 기능을 위임
public void add(User user) {
userService.add(user);
}
public void upgradeLevels() {
TransactionStatus status = this.transactionManager.getTransaction(new
DefaultTransactionDefinition());
try {
userService.upgradeLevels();
this.transactionManager.commit(status);
} catch (RuntimeException e) {
this.transactionManager.rollback(status);
throw.e;
}
}
}
목 오브젝트를 사용해 수동 DI를 적용하는 테스트 → 어떤 클래스의 오브젝트인지 분명하게 알 필요가 있음
⇒ @Autowired UserServiceImpl userServiceImpl;
로 해당 클래스로 만들어진 빈을 주입받도록함(@Autowired 지정)
// 목 오브젝트 설정이 필요한 테스트 코드수정
@Test
public void upgradeLevels() throws Exception {
MockMailSender mockMailSender = new MockMailSender();
userServiceImpl.setMailSender(mockMailSender);