Spring에서 AOP (Aspect Oriented Programming)
여러 곳에서 사용되는 Method를 한 곳에서 Spring이 유지보수 가능하도록 도와줌
AOP는 메소드에 대한 이야기 (공통 관심사항'과 '핵심 관심사항'의 Bind를 스프링이 해줌)
- 공통 관심사항, 핵심 관심사항 개념을 알아야함
- 공통 관심사항(cross-cutting concern) - 공통으로 처리되는 사항 (포괄적 개념)
- 핵심 관심사항(core concern) - 핵심으로 처리되는 사항 - 대표적인 사용 용도
1. 패키지 내 메소드 Logging
2. 패키지 내 메소드 로직 수행 시간 확인
주요 용어 설명
Aspect
- 여러 곳에서 사용되는 '공통 관심 사항' 들을(cross-cutting concern) 모듈화
- @AspectTarget
- Aspect 가 적용되는 곳
- 어떤 대상에 부가 기능을 부여할 것인가
Advice
- Aspect 에서 실질적인 기능에 대한 구현체
- 어떤 부가 가능 인지 (Before, AfterReturning, AfterThrowing, After, Around)
Joint point
- Advice 가 Target 에 적용되는 시점메서드 진입할 때, 생성자 호출할 때, 필드에서 값을 꺼낼 때 등등
- 어디에 적용할 것인가? 메서드, 필드, 객체, 생성자 등Pointcut
- Joint point 의 상세 스펙을 정의한 것
- 실제 Advice가 적용될 지점, Spring AOP에서는 advice가 적용될 메서드를 선정
- @Pointcut
Aspect
- 여러 곳에서 사용되는 '공통 관심 사항' 들을(cross-cutting concern)를 모듈화
Advice
- '공통 관심 사항'을 위해 만들어 놓은 클래스
before
- 핵심 관심사항 처리 전 '공통 관심사항' 동작
- @Before
After Returning
- 핵심 관심사항이 성공적으로 마무리 되었을 경우 '공통 관심사항' 동작
- @AfterReturning
- 대상 객체가 리턴한 값을 사용하고자 할 때는 returning="ret" 에 담아서 사용
After Throwing
- '핵심 관심사항' 실패했을 때 '공통 관심사항' 동작 (예외)
- @AfterThrowing
- 예외에 접근하기 위해 throwing="ex" 에 담아서 사용
After
- 핵심 관심사항성공여부 관계 없이무조건 공통 관심사항 동작
Around
- 핵심 관심사항이 처리되기 전 후 or 예외 발생 시 공통 관심사항 동작
- JProceedingJoinPoint 타입의 joinPoint를 반드시 매개변수로 가져야함
JoinPoint
- '공통 관심 사항'이 호출되는 지점
- 필요한 메소드가 호출되는 부분을 jointPoint 라고 부름
- 대상 객체 및 호출되는 메서드의 정보를 담고 있음.
joinpoint.getSignature()
- 호출되는 메서드의 정보를 담고 있음
@After("test()")
public void afterError(JoinPoint joinPoint) {
//toShortString() 메소드 이름 반환
String methodName = joinPoint.getSignature().toShortString();
System.out.println(methodName + "메소드가 완료되었습다.");
}
Obejct getTarget()
- 대상 객체를 반환
Object[] getArgs()
- 파라미터 목록을 반환 (아래에 추가 설명 있음)
JoinPoint의 getArgs() 메소드
- 언제 사용하냐면, 해당 서비스를 요청한 클라이언트의 정보를 받아서 사용할 수 있음
getArgs()는 타겟 메소드들의 매개변수들을 배열로 알려줌
PointCut
- JoinPoint를 여러 개 묶어 놓은 것
- 어디에 적용할 것 인지를 정한다(excution ..... )
- PointCut이 적용된 메소드의 리턴 값을 void이여야 한다.
@Pointcut("execution(public * com.example.demo.controller..*(..))")
속성1) execution
- Advice를 적용할 메서드를 명시할 때 사용
execution([수식어] 리턴타입 [클래스이름].이름(파라미터)
execution(* aaa.bbb.ccc.*.*())
- aaa.bbb.ccc 패키지의 파라미터가 없는 모든 메서드 호출
execution(* aaa.bbb.ccc..*.*(..))
- aaa.bbb.ccc패키지 및 하위 패키지에 있는 파라미터가 0개 이상인 메서드
execution(Integer aaa.bbb.ccc..WriteArticleService.write(..))
- 리턴 타입이 Integer인 WriteArticleService 인터페이스의 write 메서드 호출
execution(* get*(*))
- 이름이 get으로 시작하고 1개의 파라미터를 갖는 메서드 호출
execution(* get*(*,*))
- 이름이 get으로 시작하고 2개의 파라미터를 갖는 메서드 호출
execution(* read*(Integer, ..))
- 메서드 이름이 read로 시작하고 첫 번째 파라미터 타입이 Integer이며 1개 이상의 파라미터를 갖는 메서드 호출
속성2) within
- 메서드가 아닌 특정 타입에 속하는 메서드를 Pointcut으로 설정 할 때 사용
within(aaa.bbb.ccc.board.service.WriteArticleService)
- WriteArticleService 인터페이스의 모든 메서드 호출
within(aaa.bbb.ccc.board.service.*)
- aaa.bbb.ccc.board.service 패키지에 있는 모드 메서드호출
within(aaa.bbb.ccc.board..*)
- aaa.bbb.ccc.board 패키지 및 하위 패키지에 있는 모든 메서드 호출
속성3) bean
bean 명시자
bean(beanname)
- 이름이 beanname인 빈의 모든 메서드
bean(bean*)
- 빈의 이름이 bean으로 시작하는 빈의 모든 메서드
Weaving
- join point에 Advice를 적용하는 방법 (3가지가 있는데 스프링 에서는 프록시 방식을 사용한다)
Spring AOP Dependency 설정
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.7.0</version>
</dependency>
코드설명
더보기
package com.example.demo.advice;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect // AOP 환경설정한다는 의미
@Component // Autoscan을 위해
public class LoggingAdvice {
//Advice가 동작할 타겟을 설정합니다.
//해당 패키지 하위의 모든 클래스들의 모든 메소드들을 타겟으로 설정
//-----------핵심 관심사항-----------
@Pointcut("execution(public * com.example.demo.controller..*(..))")
public void test() {
// 해당 타겟을 대표하는 ID 정도로 생각
// 바디 값에는 값을 적어도 의미가 없음
// 무조건 void 타입 이어야함
}
//정상, 비정상일 때 모두 동작
//test() 아이디를 갖는 pointcut에 설정된 메소드들이 동작한 후에 다음의 메소드가 동작
//-----------공통 관심사항-----------
@After("test()")
public void afterError(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().toShortString();
System.out.println(methodName + "메소드가 완료되었습다.");
}
//정상일 때 동작
@AfterReturning("test()")
public void AfterReturning() {
System.out.println("타겟 메소드가 정상적으로 완료되었습니다. ");
}
//비정상일때 동작
@AfterThrowing("test()")
public void AfterThrowing(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().toShortString();
System.out.println(methodName + " 타겟 메소드가 비정상적 . ");
}
@Before("test()")
public void before(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().toShortString();
System.out.println(methodName + " 타깃 메소드 동작하기 전 입니다.");
}
@Around("test()")
public Object pro(ProceedingJoinPoint joinPoint) {
Object re = null;
String methodName = joinPoint.getSignature().toShortString();
long start = System.currentTimeMillis();
System.out.println(methodName + "이 동작하기 전입니다.");
try {
//이 문장을 전 후로//
//위에는 타겟 실행 전
//아래는 타겟 실행 후
re = joinPoint.proceed(); //타깃 메소드를 동작
}catch (Throwable e) {
}
long end = System.currentTimeMillis();
System.out.println(methodName + "이 동작한 후입니다.");
System.out.println("걸린시간:" + (end-start));
return re;
}
}
참고 블로그
https://chung-develop.tistory.com/67
반응형
'개발 > 개념 설명' 카테고리의 다른 글
형상관리 SVN ) 연동방법 (Server - Client) (0) | 2022.09.14 |
---|---|
(Spring) DI 개념 (0) | 2022.06.26 |
세션(Session), 쿠키(Cookie) (0) | 2022.02.17 |
HTTP/ Ajax / WebSocket 의 등장 (feat. Active X) (0) | 2022.02.16 |
데이터베이스에서 시스템 카탈로그란? (0) | 2021.12.31 |
댓글