관리 메뉴

제뉴어리의 모든것

AOP 복습 및 실제 구현 내용 본문

Spring Boot

AOP 복습 및 실제 구현 내용

제뉴어리맨 2022. 10. 31. 04:12

전체 항목

  • AOP 핵심 개념
  • 기본적인 사용 방법
  • 실제 적용 사례

 

본 내용은 공부용이고 본인이 이해하기 위한 언어로 되어있습니다.

실제와는 차이가 있을 수 있습니다.


AOP 핵심 개념

아래는 AOP의 개념과

사용되는 기본용어이다.

아래의 코드와 보면서 이해하자.

 

 

https://shlee0882.tistory.com/206

 

여기서 계좌이체() , 대출승인() , 이자계산() 이란 핵심기능 메소드가 있는것이다.

그리고 그 핵심 기능마다 공통적으로 해주어야 하는 부가기능이 있다.

그것이 로깅, 보안. 트랜잭션이다.

그리고 계좌이체() , 대출승인() , 이자계산() 은 여러 메소드들 중에 Pointcut의 기준에 충족하여 AOP가 적용되는 것이다.

 

 

  • Spring 에서 사용되는 AOP의 기본 코드 구조
@Aspect
@Component
public class PerformanceAspect {

    private Logger logger = Logger.getLogger(getClass().getName());

    @Pointcut("within(@org.springframework.stereotype.Repository *)")
    public void repositoryClassMethods() {};

    @Around("repositoryClassMethods()")
    public Object measureMethodExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.nanoTime();
        Object retval = pjp.proceed();
        long end = System.nanoTime();
        String methodName = pjp.getSignature().getName();
        logger.info("Execution of " + methodName + " took " + 
          TimeUnit.NANOSECONDS.toMillis(end - start) + " ms");
        return retval;
    }
}

 

Advice

타겟 (특정 메소드) 에 적용 할 부가기능을 말한다.

위 코드에서 보자면 

measureMethodExecutionTime () 메소드를 Advice라고 할 수 있겠다.

그리고 해당 메소드가 Advice임을 나타내는 

Advice 애노테이션의 종류는

“around,” “before” and “after”  가 있다.

 

즉, 

어드바이스는 부가기능의 구현 부분을 의미하고,

어드바이스임을 명시하고 작동 범위를 정의하는 어드바이스 어노테이션의 종류로는

“around,” “before” and “after”  가 있다.

 

Pointcut

어드 바이스를 적용할 타겟을 선정하는 기준이 되는 표현식이다.

위 코드에서 보자면 

@Pointcut("within(@org.springframework.stereotype.Repository *)")

위 부분을 Pointcut이라고 할 수 있다.

쉽게 외우자면, (join) point 의 cut (line) 이라고 생각하면 된다.
그리고 포인트컷은 지시자와 함께 정의가 되는데, 지시자의 종류는 아래와 같다.

  • execution : 메서드 실행 조인트 포인트를 매칭. 스프링 AOP에서 가장 많이 사용하며, 기능도 복잡하다.
  • within : 특정 타입 내의 조인 포인트를 매칭한다.
  • args : 인자가 주어진 타입의 인스턴스인 조인 포인트
  • this : 스프링 빈 객체(스프링 AOP 프록시)를 대상으로 하는 조인 포인트
  • target : Target 객체(스프링 AOP 프록시가 가르키는 실제 대상)를 대상으로 하는 조인 포인트
  • @target : 실행 객체의 클래스에 주어진 타입의 애너테이션이 있는 조인 포인트
  • @within 주어진 애너테이션이 있는 타입 내 조인 포인트
  • @annotation 메서드가 주어니 애너테이션을 가지고 있는 조인 포인트를 매칭
  • @args 전달된 실제 인수의 런타임 타입이 주어진 타입의 애너테이션을 갖는 조인 포인트
  • bean 스프링 전용 포인트컷 지시자이고 빈의 이름으로 포인트컷을 지정한다.

그리고 정말 다양한 Poincut 표현식이 있다.

아래는 execution() 지시자를사용한 포인트컷의 기본적인 문법 구조이다.

execution([접근제한자 패턴] 타입패턴 [타입패턴.]이름패턴 (타입패턴 | "..", ...) [throws 예외 패턴])

 

 

Aspect

Advice와 Pointcut 이 합쳐진 것을 의미한다.

즉, 특정 메소드들에 적용할 부가기능과 해당 부가기능을 적용할 특정 메소드를 결정하는 기준까지

포함하는 개념이다.

 

 

Joinpoint

핵심기능 (메소드) 을 한 지점(호출지점) 으로써 표현되는 개념이다.

위 코드에서는 pjp.proceed(); 을 jointpoint 라고 할 수 있다.

말하자면 아래와 같은 개념이다.

 

 

 

Target

핵심 기능 메소드를 말한다.

즉, Advice가 적용되는 메소드이다.

 

 

 

Advisor

Advice + Pointcut 이 합쳐진 개념이다.

Spring AOP에서만 사용되는 특별한 용어이다.

 

위빙(Weaving)

위빙은 포인트컷에 의해서 결정된 타겟의 조인 포인트에 부가기능(어드바이스)를 삽입하는 과정을 뜻한다.

 

 


기본적인 사용 방법

 

의존성

implementation 'org.springframework.boot:spring-boot-starter-aop'

testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'

 

 

@Before 를 사용한 기본 AOP 적용 방법

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect //Aspect임을 알리는 애노테이션
@Component //Aspect로써의 역할을 하기 위해선 빈으로 등록하여야 한다
public class TxAspect {

    @Pointcut("execution(* kyh.template.aoptemplate.controller.MoneyController.*Money(..))")
    private void lgg(){};


	//Around가 아닌 Advice 애노테이션은 JoinPoint 만 인자로 받을 수 있다. ProceedingJoinPoint 불가
    @Before("lgg()")
    public void doBefore(JoinPoint joinPoint) { 
        System.out.println("로그 출력");
    }
}

위에 코드에서 중요한 점은,

 

  • Aspect는 Bean으로 등록하여야 한다
  • @Around가 아닌 Advice 애노테이션은 JoinPoint 만 인자로 받을 수 있다. ProceedingJoinPoint 불가


실제 적용 사례

@Pointcut 을 이용하여 타겟의 기준을 만드는 것이 아니라.

특정 애노테이션이 붙은 메소드에서 Advice (부가기능) 를 적용 시킬 수 있다.

 

  • 사용자정의 애노테이션
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedMemberId {
}

 

  • 애노테이션을 어드바이스의 적용 범위로 지정한 코드
package com.preproject.server.tx;

import com.preproject.server.member.wrapper.WrapperUserNamePasswordAuthenticationToken;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import org.aspectj.lang.reflect.MethodSignature;

@Aspect
@Component
@RequiredArgsConstructor
public class TxAspect {

    private final PlatformTransactionManager transactionManager;

    @Around("@annotation(com.preproject.server.tx.NeedMemberId)") //NeedMemberId를 붙인 메소드에
    public Object applyTx(ProceedingJoinPoint joinPoint) throws Throwable {

        TransactionStatus transaction = transactionManager.getTransaction(new DefaultTransactionDefinition());

        try {
            Object[] parameterValues = joinPoint.getArgs(); //우선 현재 Controller로 넘어 온 파라미터 값들을 가져 옴
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); //SecurityContext에 저장된 Authentication 가져 옴

            WrapperUserNamePasswordAuthenticationToken wUNPAT = (WrapperUserNamePasswordAuthenticationToken) authentication;
            Long memberId = wUNPAT.getMemberId(); //SecurityContext에 저장된 서버가 인식하는 실제 MemberId

            //조인 포인트 (호출 되는 메소드를 말한다) 가 호출되는 시점에 넘겨 받은 인자값들
            MethodSignature signature = (MethodSignature) joinPoint.getSignature(); //메소드의 선언 부분에 대한 정보
            Method method = signature.getMethod(); //메소드 자체의 대한 정보를 갖는 클래스
            Parameter[] parameters = method.getParameters(); //메소드가 갖는 파라미터들의 정보

            for (int i = 0; i < method.getParameters().length; i++) {
                if (parameters[i].getName().equals("authMemberId")) { //i번째 파라미터의 이름이 "authMemberId"일 경우
                    parameterValues[i] = memberId; //해당 파라미터에 위에서 Authentication을 이용하여 얻은 "memberId"를 넣음
                    break;
                }
            }

            Object object = joinPoint.proceed(parameterValues); //새롭게 설정된 파라미터 값들을 해당 메소드에 전달

            transactionManager.commit(transaction);
            return object;
        } catch (RuntimeException runtimeException) {
            transactionManager.rollback(transaction);
            throw runtimeException;
        }
    }
}

위에 코드는 @NeedMemberId라는 애노테이션이 붙은 메소드에서 

어드바이스 (applyTx() 메소드) 를 실행하겠다는 코드이다.

컨트롤러의 핸들러 메소드의 인자로, 서버에서 가지고 있는 사용자의

Authentication에서 얻은 memberId를 넣어주는 내용이다.

 

 

 


참조 

https://shlee0882.tistory.com/206

 

Spring AOP, Aspect 개념 특징, AOP 용어 정리

1. Spring AOP의 핵심기능과 부가기능 - 업무 로직을 포함하는 기능을 핵심 기능(Core Concerns) - 핵심 기능을 도와주는 부가적인 기능(로깅, 보안)을 부가기능(Cross-cutting Concerns) 이라고 부른다. 2. AOP..

shlee0882.tistory.com

https://dahye-jeong.gitbook.io/spring/spring/2020-04-09-aop-proxy

 

AOP(2) - Aop Proxy - spring

프록시를 활용하는 부가기능, 접근제어 기능 등은 일반적으로 자주 활용되는 것이 많다. 즉, 다양한 타겟 클래스와 메소드에 중복되어 나타날 가능성이 많다.(ex) Transaction

dahye-jeong.gitbook.io

https://wwlee94.github.io/category/study/toby-spring/aop/basic-concept/

 

[토비의 스프링] 6-2. AOP - 기본 용어

AOP, 포인트컷, 어드바이스, 어드바이저

wwlee94.github.io

https://www.egovframe.go.kr/wiki/doku.php?id=egovframework:rte:fdl:aop:aspectj 

 

egovframework:rte:fdl:aop:aspectj [eGovFrame]

@AspectJ는 Java 5 어노테이션을 사용한 일반 Java 클래스로 관점(Aspect)를 정의하는 방식이다. @AspectJ 방식은 AspectJ 5 버전에서 소개되었으며, Spring은 2.0 버전부터 AspectJ 5 어노테이션을 지원한다. Spring

www.egovframe.go.kr

https://galid1.tistory.com/498