Spring AOP
Aspect-Oriented Programming는 OOP를 보완하는 수단이다.
로깅, 트랜잭션 등 서비스 로직 내에서 동일하게 사용되는 특별한 관심사(Aspect)를 모듈화하는 기법이다.
개요
트랜잭션을 활성화한 로직은 아래와 같은 공통 사항을 포함할 수 있다.
- 트랜잭션 활성화
- 서비스 로직 수행
- Commit / Rollback
- 트랜잭션 종료
AOP가 없다면 트랜잭션이 필요한 로직을 아래처럼 수행할 수 있다.
void doLogic() {
transactionManager.enable();
boolean result = service.execute1();
if (result) {
transactionManager.commit();
} else {
transactionManager.rollback();
}
transactionManager.exit();
}
void doLogic2() {
transactionManager.enable();
boolean result = service.execute2();
if (result) {
transactionManager.commit();
} else {
transactionManager.rollback();
}
transactionManager.exit();
}
boolean result = service.execute*();
를 제외하고 나머지가 모두 동일한 코드로 되어 있다.
이 작업이 반복되면 관리 포인트가 늘어나게 되고 개발자에겐 귀찮은 일이 될 것이다.
AOP는 위와 같은 문제를 모듈화하는 것으로 해결한다.
서비스 로직에는 주 로직만 작성하고 부가 모듈은 따로 빼내어 관리하는 방법으로 주 로직에만 집중할 수 있게 도와준다.
주요 용어
-
Aspect
- 관심사를 의미한다.
- 핵심 로직에서 따로 빼내어 관리하고 싶은 보조 로직을 Aspect로 생각하자.
-
Target
- Aspect를 적용할 대상
-
Advice
- Aspect가 어떠한 로직을 수행할 것인지 정의
-
Join point
- Advice를 적용할 위치
- 다양한 시점에 적용이 가능하다. (메소드 실행 전,후/생성자/필드 등)
-
Point cut
- Join point의 상세 정의
- 적용 위치나 침투 시점 등을 상세하게 정의할 수 있다.
- 예시로
A 클래스의 모든 메소드
실행 전 으로 지정 가능
사용 예시
AOP를 실제로 구현해본다.
CRUD 기능을 수행하는 서비스 클래스를 대상으로 작동하는 AOP를 만들었다.
CrudService
@Service
public class CrudService implements CrudManager {
@Clock
public void create() {
System.out.println("create");
}
@Override
public void read() {
System.out.println("read");
}
@Clock
public void update() {
System.out.println("publish");
}
@Clock
public void delete() {
System.out.println("delete");
}
}
Clock
- 이 annotation이 붙은 메소드는 실행 시간을 측정한다.
@Target(ElementType.METHOD)
public @interface Clock {}
CrudAspect
- Aspect 정의
@Around
는 대상 메소드 실행 전/후, 예외 발생 시점에 수행한다.- 시간 측정의 Point cut은
@Clock
이 부착된 메소드의@Around
로 설정했다. @Before
는 대상 메소드 실행 전에 수행한다.- bean으로도 대상을 선택할 수 있다. 이름을 넣어서 실행하면 모든 메소드가 대상이 된다.
@Component
@Aspect
public class CrudAspect {
@Around(“@annotation(com.example.demo.Clock)”)
public Object timeMeasure(ProceedingJoinPoint joinPoint) throws Throwable {
long begin = System.currentTimeMillis();
Object ret = joinPoint.proceed();
System.out.println("걸린 시간 : " + (System.currentTimeMillis() - begin) + " ms");
return ret;
}
@Before("bean(crudService)")
public void beforeExecute() {
System.out.println("This is CRUD!");
}
}
- AppRunner
- 로직 수행
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
private CrudManager crudManager;
@Override
public void run(ApplicationArguments args) throws Exception {
crudManager.create();
crudManager.read();
crudManager.update();
crudManager.delete();
}
}
결과
This is CRUD!
create
걸린 시간 : 12 ms
This is CRUD!
read
This is CRUD!
publish
걸린 시간 : 1 ms
This is CRUD!
delete
걸린 시간 : 0 ms
모든 메소드에서 실행한 aspect,
read를 제외한 메소드에만 실행한 aspect를 확인할 수 있다.
전체 소스코드
https://github.com/dhmin5693/Spring-core-study/tree/master/SpringAOP
'Java > Spring framework' 카테고리의 다른 글
Spring framework 소스코드 읽어보기 - Bean 생성 원리 (3) (0) | 2020.02.23 |
---|---|
Spring framework 소스 코드 읽어보기 - Bean 생성 원리 (2) (1) | 2020.01.27 |
Spring framework core (11) - SpEL (0) | 2020.01.24 |
Spring framework core (10) - Data binding (0) | 2020.01.17 |
Spring framework core (9) - Validation (0) | 2020.01.15 |