⚡ 들어가기전
개발을 진행 하면서, 핵심 로직을 작성한 뒤에 공통적으로 따라오는 부분이 존재 하면서 AOP를 공부하게 되었다.
예를 들어서 특정 로직이 실행이 된 경우 이력을 남기는 부분, 특정 로직이 완료된 경우 서버에 프로토콜을 넘기는 경우 매번 동일한 공통 로직을 적어줘야되는 부분이 생겼다.
나처럼 특정 로직이 완성된 경우에 공통로직이 발생하는 경우도 있겟지만, 로직의 시작전, Exception이 떨어진 경우등 특정 시점에 적용시키는 것도 가능하다.
⚡ Spring Boot AOP (Aspect Oriented Programming)
- 측면 / 양상 지향적인 프로그래밍
- 관점 지향 프로그램이다. 중복된 코드를 떼어내서 분리하고 각각의 Method가 자신의 일에만 집중하게 해준다.
- 쉽게 설명하면 핵심 로직 과 공통 로직을 구분하고, 공통 로직의 경우, 필요한 시점에 적요하는 프로그래밍 방법이다.
⚡ AOP 주요 개념
- 공식 문서
- Aspect: 여러 클래스에 흩어진 관심사의 모듈화. 트랜잭션 관리는 엔터프라이즈 Java 애플리케이션에서 크로스 커팅 문제의 좋은 예입니다. Spring AOP에서 Aspect는 일반 클래스(스키마 기반 접근) 또는 @Aspect 어노테이션으로 주석이 달린 일반 클래스(@AspectJ 스타일)를 사용하여 구현됩니다.
- Join point: 메소드 실행이나 예외 처리와 같은 프로그램 실행 중 포인트. Spring AOP에서 조인 포인트는 항상 메소드 실행을 나타냅니다.
- Advice: 특정 조인 포인트에서 aspect에 의해 취해진 조치입니다. 다양한 유형의 advice에는 "around", "before" 및 "after" advice가 포함됩니다. Spring을 포함한 많은 AOP 프레임워크는 advice를 인터셉터로 모델링하고 조인 포인트 주변에 인터셉터 체인을 유지합니다.
- Pointcut: 조인 포인트와 일치하는 술어입니다. Advice는 pointcut 표현식과 연관되며 pointcut과 일치하는 모든 조인 포인트에서 실행됩니다(예: 특정 이름의 메소드 실행). pointcut 표현식에 의해 매칭되는 조인 포인트의 개념은 AOP의 핵심이고 Spring은 기본적으로 AspectJ pointcut 표현식 언어를 사용합니다.
- Introduction: 타입을 대신하여 추가적인 메소드나 필드를 선언합니다. Spring AOP를 사용하면 advice된 객체에 새로운 인터페이스(및 해당 구현)를 도입할 수 있습니다.
- Target object: 하나 이상의 aspect에 의해 advice되는 객체입니다. "advised object"라고도 합니다. Spring AOP는 런타임 프록시를 사용하여 구현되므로 이 객체는 항상 프록시 객체입니다.
- AOP proxy: aspect contracts(메소드 실행에 advice 등)를 구현하기 위해 AOP 프레임워크에 의해 생성된 객체입니다. Spring Framework에서 AOP 프록시는 JDK 동적 프록시 또는 CGLIB 프록시입니다.
- Weaving: aspect를 다른 애플리케이션 유형 또는 객체와 연결하여 advice된 객체를 생성합니다. 이것은 컴파일 시간, 로드 시간 또는 런타임에 수행할 수 있습니다. Spring AOP는 다른 순수 Java AOP 프레임워크와 마찬가지로 런타임에 weaving을 수행합니다.
- 참조자료
⚡ 테스트
테스트 소스 작성 내용
// the pointcut expression
// .. : 하위의 모든 디렉토리를 의미
// *(..) : * - 하위의 모든 메서드, (..) - 모든 매개변수
// Controller
// @Pointcut("execution(* com.example.testweb.controller.*(..))")
// Service
//@Pointcut("execution(* com.example.testweb.service.*(..))")
// 어노테이션이 지정된 곳에서 실행.
@Pointcut("@annotation(com.example.testweb.service.annotation.protocolTest)")
private void protocolEvent1() {} // the pointcut signature
// 메소드 호출전 실행
@Before("@annotation(com.example.testweb.service.annotation.protocolTest)")
public void aspectTestLog_Before() {
log.info("Before");
}
// exception과 상관 없이, 메소드가 정상적으로 실행된 이후에 실행.
@After("@annotation(com.example.testweb.service.annotation.protocolTest)")
public void aspectTestLog_After() {
log.info("After");
}
// exception이 발생하지 않고 정상적으로 실행된 경우, 실행.
@AfterReturning("@annotation(com.example.testweb.service.annotation.protocolTest)")
public void aspectTestLog_AfterReturning_1() {
log.info("After Returning");
}
// 리턴값 받아서 처리하는 부분
@AfterReturning(value ="@annotation(com.example.testweb.service.annotation.protocolTest)", returning = "returnValue")
public void aspectTestLog_AfterReturning_2(Object returnValue) {
log.info("After Returning");
log.info("returnValue : {}", returnValue);
}
// exception이 발생하지 않고 정상적으로 실행된 경우, 실행.
@AfterThrowing("@annotation(com.example.testweb.service.annotation.protocolTest)")
public void aspectTestLog_AfterThrowing() {
log.info("After Throwing");
}
// 메소드 실행 전.후 익셉션 발생 시점 등 다양하게 기능 설정. 및 구현 가능.
@Around("@annotation(com.example.testweb.service.annotation.protocolTest)")
public Object aspectTestLog3(ProceedingJoinPoint pjp) throws Throwable {
// start stopwatch
log.info("Around Start");
/*해당 Object 에는 메소드가 실행뒤의 결과값이 담겨 있다.*/
Object result = pjp.proceed();
// stop stopwatch
log.info("Around End");
return result;
}
나의 경우, Controller에서 @protocolTest 어노테이션을 붙인뒤, aop 기본적인 부분을 작성한 뒤에 테스트를 진행 했다.
아래의 경우 Controller를 호출 했을 경우, aspect가 실행 되는 모습이다.
INFO 22020 --- [io-11040-exec-1] c.e.t.service.aspect.aopTestAspect : Around Start
INFO 22020 --- [io-11040-exec-1] c.e.t.service.aspect.aopTestAspect : Before
INFO 22020 --- [io-11040-exec-1] c.e.testweb.controller.basicController : test Controller Start
INFO 22020 --- [io-11040-exec-1] c.e.testweb.controller.basicController : 걸린시간: 0 ms
INFO 22020 --- [io-11040-exec-1] c.e.testweb.controller.basicController : test Controller End
INFO 22020 --- [io-11040-exec-1] c.e.t.service.aspect.aopTestAspect : After Returning
INFO 22020 --- [io-11040-exec-1] c.e.t.service.aspect.aopTestAspect : returnValue : null
INFO 22020 --- [io-11040-exec-1] c.e.t.service.aspect.aopTestAspect : After Returning
INFO 22020 --- [io-11040-exec-1] c.e.t.service.aspect.aopTestAspect : After
INFO 22020 --- [io-11040-exec-1] c.e.t.service.aspect.aopTestAspect : Around End
⚡ 생각
AOP 의경우, 개발을 하다보면 누구나 한번쯤은 필요하다고 생각한 프로그래밍이라고 생각한다.
간단하게 테스트용으로 작성을 했으며, 나의 경우는 위의 방법을 통해서 회사 프로그램에 적용을 시키고 사용중이다.
소스중에 공통적으로 사용되고 있는 부분이 있다면 한번 사용해보면 좋겟다.
반응형
'Spring' 카테고리의 다른 글
[JPA] QueryDSL에서 서브 쿼리 사용 정리 (0) | 2023.01.29 |
---|---|
[Spring] SpringBoot + RabbitMQ 연동 내용 정리. (0) | 2022.10.21 |
Spring Boot SPQR 개념 및 사용 방법 정리 (0) | 2022.09.15 |
Spring Boot JPA 사용 방법 정리 (0) | 2022.08.04 |
Spring Data JPA에 대한 개념 및 사용 방법 정리 (0) | 2022.06.02 |