'AOP'(Aspect-Oriented Programming)는 'OOP'(Object-Oriented Programming)의 보완적인 개념으로, 애플리케이션의 핵심 로직과는 별개로 로깅, 트랜잭션 처리, 보안 등과 같은 '부가적인 관심사' 를 분리하여 관리하는 기술입니다.
'AOP' 는 관점(Aspect) 지향 프로그래밍으로도 불리며, 핵심 로직과 부가적인 로직을 각각 모듈화하여 개발을 용이하게 합니다. 예를 들어, 로그인 기능이 필요한 웹 애플리케이션을 개발할 때, 핵심 로직은 사용자가 원하는 기능 수행이고, 로그인 기능은 부가적인 관심사입니다. AOP를 이용하면 핵심 로직과 로그인 기능을 각각 모듈화하여 개발하고, 필요한 경우 로그인 기능을 추가하거나 수정할 수 있습니다.
'AOP'를 구현하는 방법으로는 프록시 패턴, 컴파일러, 런타임 등이 있으며, 스프링 프레임워크에서는 프록시 패턴과 'AspectJ' 를 이용한 방식으로 AOP를 구현합니다. 스프링의 AOP는 메소드 호출, 예외 처리, 트랜잭션 처리 등 다양한 측면에서 활용됩니다.
AOP에서 사용하는 어노테이션 종류
- @Aspect: AOP 기능을 정의하는 클래스에 사용되는 어노테이션입니다.
- @Pointcut: 어떤 메소드에 대해 AOP를 적용할지를 정의하는 어노테이션입니다.
- @Before: 메소드 실행 전에 실행될 AOP 어드바이스를 정의하는 어노테이션입니다.
- @After: 메소드 실행 후에 실행될 AOP 어드바이스를 정의하는 어노테이션입니다.
- @AfterReturning: 메소드가 성공적으로 반환된 후 실행될 AOP 어드바이스를 정의하는 어노테이션입니다.
- @AfterThrowing: 예외가 발생한 후 실행될 AOP 어드바이스를 정의하는 어노테이션입니다.
- @Around: 메소드 실행 전/후 또는 예외 발생 시점에 실행될 AOP 어드바이스를 정의하는 어노테이션입니다.
실제 예제를 보여드리겠습니다.
'@Component' 어노테이션을 활용하여 스프링 컨테이너에 Bean으로 등록합니다.
'@Aspect' 어노테이션을 사용하여 AOP를 선언합니다.
'@Pointcut' 어떤 메소드에 AOP를 적용할지 선업합니다.
@Component
@Aspect
public class LoggerAspect {
@Pointcut("execution(* com.a.b..*Service.*(..))")
public void servicePointcut(){
}
}
'@Before' 어노테이션을 사용하여 '@Pointcut' 메소드가 실행되기 전 로그를 찍는 메소드를 실행합니다.
@Before("servicePointcut()")
public void beforeService(JoinPoint jp){
//String aopPrefix = "[Before Service AOP]";
String aopPrefix = "";
Object[] args = jp.getArgs();
if(args != null){
if(args.length > 0){
String className = jp.getTarget().getClass().getName();
String methodName = jp.getSignature().getName();
aopPrefix += className + "." + methodName + ".";
String logMessage;
int k = 1;
for(Object arg : args){
logMessage = aopPrefix + "Argument[" + k + "] : " + arg.toString();
k++;
this.logger.info(logMessage);
}
}
}
}
'@AfterReturning' 어노테이션을 사용하여 메소드가 성공적으로 실행된 뒤 로그를 찍도록 합니다.
@AfterReturning(pointcut = "servicePointcut()", returning = "returnData")
public void afterReturningService(JoinPoint jp, Object returnData){
//String aopPrefix = "[AfterReturning Service AOP]";
String aopPrefix = "";
if(returnData != null){
String className = jp.getTarget().getClass().getName();
String methodName = jp.getSignature().getName();
aopPrefix += className + "." + methodName + ".";
String logMessage = aopPrefix + "returnData : " + returnData.toString();
this.logger.debug(logMessage);
}
}
'@AfterThrowing' 어노테이션을 사용하여 예외가 발생했을 경우 로그를 찍도록 합니다.
@AfterThrowing(pointcut="execution(* com.a.b..*Controller.*(..))", throwing="ex")
public void exceptionControllerPointcut(JoinPoint jp, Throwable ex){
this.logger.error("Exception[els_check_error]------------------------->" + ex.getMessage());
for(final String e : ExceptionUtils.getRootCauseStackTrace(ex)) {
this.logger.error(e);
}
String aopPrefix = "";
Object[] args = jp.getArgs();
if(args != null){
if(args.length > 0){
String className = jp.getTarget().getClass().getName();
String methodName = jp.getSignature().getName();
aopPrefix += className + "." + methodName + ".";
String logMessage;
int k = 1;
for(Object arg : args){
logMessage = aopPrefix + "Argument[" + k + "] : " + arg.toString();
k++;
this.logger.error(logMessage);
}
}
}
}
'Programing > Java & Spring' 카테고리의 다른 글
WebFlux, WebClient 사용하여 API 호출 - 1편 (0) | 2023.09.26 |
---|---|
함수형 인터페이스(Funcational Interface) (0) | 2023.09.22 |
스프링(Spring) 에서 RequestURI, RequestURL 차이 (0) | 2023.09.08 |
Java Default Method (디펄트 메소드) (0) | 2023.09.08 |
스프링(Spring)에서 RestTemplate, Https 통신 (0) | 2023.09.08 |
[JAVA] content-disposition (0) | 2013.12.31 |
[SPRING3.0] 메세지 처리 (0) | 2013.09.12 |
[SPRING3.0] 페이스북 공유하기① (0) | 2013.09.11 |
[SPRING3.0] 데이터 엑세스 기술② (0) | 2013.09.04 |
[SPRING3.0] 트랜잭션 설정 (0) | 2013.09.04 |