AOP
Aspect Oriented Programming의 약자로, 흩어진 코드를 한곳에서 관리할 수 있도록 도와주는 코딩 기법이다.
크게 두가지 방법이 있다.
- 바이트 코드 조작 방법 => 컴파일된 .class를 조작한다.
- 프록시 패턴 방법 => Spring AOP가 사용하는 방법
AOP가 적용된 애노테이션 중에 @Transactional(readOnly = true)을 보자.
보통 트랜잭션 처리는 트랜잭션 매니저가 트랜잭션 오토 커밋을 false로 만들고 작업이 끝나면 true로 변환한다(트랜잭션을 커밋한다). 그리고 이 작업을 내부적으로 try/catch로 묶어놨는데 에러가나면 catch 블럭에서 rollback시키다.
Spring Data JPA 해당하는 메소드들에는 기본적으로 트랜잭션이 적용이 되어있다.
@Transactional(readOnly = true)는 기본설정을 오버라이딩한것뿐이다.
이렇게 트랜잭션은 하나의 클래스에만 적용되어있는 것이 아니라 사방에 적용이 되어있다.
이와 비슷하게 @Transactional도 동작한다. @Transactional를 보고 처리하는 코드가 어딘가에 있다.
애노테이션은 무엇일까?
주석과 같다. 이 주석을 어디에다 달지, 언제까지 유효한지에대한 속성만 가질 뿐이지 기능은 없다. 단지 이 애노테이션들을 처리하는 프로세서들이 있을 뿐이다.
쉬운 이해를 위해 AOP를 흉내내보자.
특정 애노테이션이 붙어있는 메소드가 실행될 때 그 메소드의 로그를 남기는 것을 해보자.
우선 애노테이션을 만든다.
// Annotation 생성
@Target(ElementType.METHOD) // 이 애노테이션을 어디에 붙일 수 있는지 타겟정보 명시
@Retention(RetentionPolicy.RUNTIME) // 이 애노테이션을 사용한 코드를 런타임까지 유지를 한다고 명시
public @interface LogExecutionTime {
}
위에 만든 애노테이션에대한 처리기를 만들어보자.
@Component // Bean만 Aspect가 될 수 있다.
@Aspect // Aspect 역할을 한다고 명시
public class LogAspect {
Logger logger = LoggerFactory.getLogger(LogAspect.class);
// LogExecutionTime 애노테이션 주변으로 메소드에 명시한 일들을 하라.
// @annotation말고 Bean도 있다.특정 Bean에 있는 모든 메소드에 적용하라.
// 또는 복잡한 표현식을 쓸 수 있다. 표현식을 쓰면 @LogExecutionTime을 안붙이고도
@Around("@annotation(LogExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
// spring이 제공하는 stopWatch
StopWatch stopWatch = new StopWatch();
stopWatch.start();
Object result = joinPoint.proceed();
stopWatch.stop();
logger.info(stopWatch.prettyPrint());
return result;
}
}
실제 동작해보자
@RestController
public class SampleController {
@Autowired
String kimjye;
@LogExecutionTime
@GetMapping("/hello")
public String context(){
return "hello " + kimjye;
}
}
PSA
PSA는 Portable Service Abstraction의 약자로, 잘 만든 인터페이스라고 생각하면 된다.
스프링에서 제공하는 대부분의 API는 PSA다.
@Transactional은 AOP의 예제도되지만 PSA의 예제도 된다.
@Transcational를 처리할 Aspect가 어딘가에 있다. 그리고 이 Aspect에서는 Transaction 처리 기술에 독립적인 PlatformTransactionManager라는 인터페이스를 구현했다.
그렇기 때문에 PlatformTransactionManager의 구현체들은 Bean으로 등록되어있다.
따라서 @Transactional은 PlatformTransactionManager을 구현했기 때문에 JpaTransactionManager에서 DataSourceTransactionManager로 변경이 되더라도 @Transactional을 처리하는 Aspect 코드는 변경이 되지 않는다.
이것이 추상화의 장점이다.