Programmer is the Artist 토비의 스프링 3.1 6.5 스프링 AOP 작업의 목표 트랜잭션 코드를 깔끔하고 료과적으로 분리 트랜잭션 코드는 투명한 부가기능 형태로 제공 기존 설계와 코드에 영향을 주지 않음 6.5.1 자동 프록시 생성 대부분의 문제는 해결함 타깃 코드는 깔끔한 채로 남아있음 부가기능은 한 번만 만들어 모든 타깃과 메서드에 재사용 가능 타깃의 적용 메소드를 선정하는 방식도 독립적으로 작성 가능 남은 문제 부가기능이 타깃 오브젝트마다 새로 만들어짐 ProxyFactoryBean의 어드바이스를 통해 해결 타깃 오브젝트마다 거의 비슷한 내용의 ProxyFactoryBean 설정정보를 추가해야함 중복 문제의 접근 방법 DAO 바뀌는 부분과 바뀌지 않는 부분을 구분해서 분리 템플릿과 콜백, 클라이어트를 분리 프록시 클래스 코드 다이내믹 프록시라는 런타임 코드 자동생성.. 토비의 스프링 3.1 6.4 스프링의 프록시 팩토리 빈 6.4.1 ProxyFactoryBean 스프링은 일관된 방법으로 프록시를 만들 수 있게 도와주는 추상 레이어를 제공 프록시 오브젝트를 생성해주는 기술을 추상화한 팩토리 빈을 제공 ProxyFactoryBean : 프록시를 생성해서 빈 오브젝트로 등록하게 해주는 팩토리 빈 순수하게 프록시 생성을 위한 작업만을 담당하고, 부가 기능은 별도의 빈에 둘 수 있음 MethodInterceptor 인터페이스를 구현하여 만듦 InvocationHandler의 invoke() 메소드는 타깃 오브젝트에 대한 정보를 제공하지 않음 MethodInterceptor의 invoke() 메소드는 타깃 오브젝트에 대한 정보까지 함께 제공받음 타깃 오브젝트에 상관없이 독립적으로 만들 수 있음 싱글톤 빈으로 등록 가능 학습 테스트 .. 토비의 스프링 3.1 6.3 다이내믹 프록시와 팩토리 빈 6.3.1 프록시와 프록시 패턴, 데코레이터 패턴 확장성을 고려한 기능의 분리 전형적인 전략 패턴 사용 가능 트랜잭션을 적용한다는 사실이 코드에 남아있음 적용 사실 자체의 분리가 필요 UserServiceTx 생성 후 UserServieImpl에 트랜잭션 관련 코드를 모두 제거 부가기능 분리 시 부가기능 외 나머지 모든 기능은 원래 핵심기능을 가진 클래스로 위임해주어야 함 부가기능의 사용 클라이언트가 핵심기능을 가진 클래스를 직접 사용하면 부가기능을 사용할 수 없음 클라이언트는 인터페이스를 통해서만 핵심기능을 사용 부가기능은 같은 인터페이스를 구현하여 끼어들어야 함 즉, 클라이언트는 부가기능을 통해 핵심기능을 이용하게 됨 프록시 : 자신이 클라이언트가 사용하려고 하는 실제 대상인 것처럼 위장해서 클라이언.. 토비의 스프링 3.1 6.2 고립된 단위 테스트 가장 좋은 테스트 방법 : 가능한 작은 단위로 쪼개서 테스트 테스트 실패 시 원인을 찾기가 쉽기 때문 불가능한 경우도 있음 : 다른 오브젝트와 환경에 의존하고 있는 경우 6.2.1 복잡한 의존관계 속의 테스트 UserService 매우 간단한 기능만 가지고 있음 그럼에도 불구하고 세 가지 타입의 의존 오브젝트가 필요 UserDao : DB 처리 MailSender : 메일 처리 PlatformTransactionManager : 트랜잭션 처리 UserServiceTest 본래의 목적 : UserService에 대한 테스트 세 가지 의존관계를 갖고 있으므로 테스트가 진행되는 동안 세 가지 오브젝트가 모두 정상이어야 한다. 실제로는 해당 오브젝트의 뒤에 존재하는 훨씬 더 많은 오브젝트와 환경, 서비스, 서버.. 토비의 스프링 3.1 6.1 트랜잭션 코드의 분리 트랜잭션 경계설정 비즈니스 로직보다 코드가 더 길다. 6.1.1 메소드 분리 트랜잭션 적용 코드 구조 트랜잭션 시작 비즈니스 로직 트랜잭션 종료 코드의 특징 두 가지 종류의 코드가 구분되어 있음 트랜잭션 & 비즈니스 로직 두 코드 간 주고받는 정보가 없음 완벽히 독립적인 코드 비즈니스 로직을 메소드로 추출하여 독립 public class UserService { public void upgradeLvls() throws Exception { TransactionStatus status = this.transactionManager.getTransaction(new DefaultTransactionDefinition()); try { upgradeLvlsInternal(); this.transactionM..