Spring Boot - AOP
· 3 min read
AOP는 Aspect Orient Programming 관점 지향 프로그래밍으로, 기능을 비지니스 로직과 공통 모듈로 구분한 후에 필요한 시점에 비지니스 로직에 삽입하여 실행되게끔 도와준다.
언제 사용되는가?
- 성능 검사
- 트랜잭션 처리
- 로깅
- 예외 반환
- 검증
- ...
실 예로, @Transactional
, @Cache
같은 애노테이션들은 AOP를 활용하여 동작하게 된다.
구성요소
JoinPoint
: 모듈의 기능이 삽입되어 동작할 수 있는 실행 가능한 특정 위치PointCut
: 어떤 클래스의 어느 JoinPoint를 사용할 것인지를 결정Advice
: 각 JoinPoint에 삽입되어져 동작할 수 있는 코드Interceptor
: InterceptorChain 방식의 AOP 툴에서 사용하는 용어로 주로 한개의 호출 메소드를 가지는 AdviceWeaving
: PointCut에 의해서 결정된 JoinPoint에 지정된 Advice를 삽입하는 과정(CrossCutting)Introduction
: 정적인 방식의 AOP 기술Aspect
: PointCut + Advice + (Introduction)
Dependency
JDK DynamicProxy를 이용하여 AOP 기능을 사용할 수 있지만 가장 많이 사용되는 AspectJ
를 사용해보도록 한다.
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
@Configuration
@EnableAspectJAutoProxy
public class AspectJConfig {
}
Aspect
구성요소들을 적절히 활용하여 로그를 찍어볼 수 있도록 Aspect
를 만든다.
@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
딱히 Controller
와 Service
가 필요한 것은 아니지만, 로그 결과를 보기 위해서 간단하게 만들어 보자.
Controller
@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
@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;
}
}
결과
http://localhost:8080/aop
~: =============== onBeforeThing
~ : Hello, Spring Boot AOP
~ : =============== onAfterHandler
~ : @AfterReturning : Hello, Spring Boot AOP
~ : =============== onAfterReturningHandler
http://localhost:8080/noAop
~ : Hello, Spring Boot No AOP