AOP는 Aspect Orient Programming 관점 지향 프로그래밍으로, 기능을 비지니스 로직과 공통 모듈로 구분한 후에 필요한 시점에 비지니스 로직에 삽입하여 실행되게끔 도와준다.

언제 사용되는가?

  • 성능 검사
  • 트랜잭션 처리
  • 로깅
  • 예외 반환
  • 검증

실 예로, @Transactional, @Cache같은 애노테이션들은 AOP를 활용하여 동작하게 된다.

구성요소

  • JoinPoint: 모듈의 기능이 삽입되어 동작할 수 있는 실행 가능한 특정 위치
  • PointCut: 어떤 클래스의 어느 JoinPoint를 사용할 것인지를 결정
  • Advice: 각 JoinPoint에 삽입되어져 동작할 수 있는 코드
  • Interceptor: InterceptorChain 방식의 AOP 툴에서 사용하는 용어로 주로 한개의 호출 메소드를 가지는 Advice
  • Weaving: PointCut에 의해서 결정된 JoinPoint에 지정된 Advice를 삽입하는 과정(CrossCutting)
  • Introduction: 정적인 방식의 AOP 기술
  • Aspect: PointCut + Advice + (Introduction)

Dependency

JDK DynamicProxy를 이용하여 AOP 기능을 사용할 수 있지만 가장 많이 사용되는 AspectJ를 사용해보도록 한다.

1
2
3
4
5
dependencies { 
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-starter-aop')
testCompile('org.springframework.boot:spring-boot-starter-test')
}

Config

1
2
3
4
@Configuration
@EnableAspectJAutoProxy
public class AspectJConfig {
}

Aspect

구성요소들을 적절히 활용하여 로그를 찍어볼 수 있도록 Aspect를 만든다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Aspect
@Component
public class TestAspect {

private static final Logger logger = LoggerFactory.getLogger(TestAspect.class);

@Before("execution(* com.example.service.*.*Aop(..))")
public void onBeforeHandler(JoinPoint joinPoint) {
logger.info("=============== onBeforeThing");
}

@After("execution(* com.example.service.*.*Aop(..))")
public void onAfterHandler(JoinPoint joinPoint) {
logger.info("=============== onAfterHandler");
}

@AfterReturning(pointcut = "execution(* com.example.service.*.*Aop(..))",
returning = "str")
public void onAfterReturningHandler(JoinPoint joinPoint, Object str) {
logger.info("@AfterReturning : " + str);
logger.info("=============== onAfterReturningHandler");
}

@Pointcut("execution(* com.example.service.*.*Aop(..))")
public void onPointcut(JoinPoint joinPoint) {
logger.info("=============== onPointcut");
}
}

Controller, Service

딱히 ControllerService가 필요한 것은 아니지만, 로그 결과를 보기 위해서 간단하게 만들어 보자.

Controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RestController
public class TestController {

@Autowired
private TestService service;

@GetMapping(value = "/noAop")
public String noAop(){
return service.test();
}

@GetMapping(value = "/aop")
public String aop(){
return service.testAop();
}
}
Service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Service
public class TestServiceImpl implements TestService {

private static final Logger logger = LoggerFactory.getLogger(TestServiceImpl.class);

@Override
public String test() {
String msg = "Hello, Spring Boot No AOP";
logger.info(msg);
return msg;
}

@Override
public String testAop() {
String msg = "Hello, Spring Boot AOP";
logger.info(msg);
return msg;
}
}
결과
1
http://localhost:8080/aop
1
2
3
4
5
~: =============== onBeforeThing
~ : Hello, Spring Boot AOP
~ : =============== onAfterHandler
~ : @AfterReturning : Hello, Spring Boot AOP
~ : =============== onAfterReturningHandler


1
http://localhost:8080/noAop
1
~ : Hello, Spring Boot No AOP

참고