혜야의 코딩스토리

[Spring] AOP 관점 지향 프로그래밍 본문

꿈 : 멋진 개발자 🧸/Java

[Spring] AOP 관점 지향 프로그래밍

hyeya_ 2022. 9. 30. 23:09

📌 AOP (Aspect Oriented Programming)

- 관점(관심) 지향 프로그래밍

- 횡단 관심사(cross-cutting concern )에 따라 프로그래밍 하는 것

- 객체지향 프로그래밍(Object Oriented Programming, OOP)를 보완하는 확장적인 개념

- 장점 : 중복되는 코드 제거, 효율적인 유지보수, 높은 생산성, 재활용성 극대화, 변화 수용의 용이성

📌 Aspect (측면, 관점, 관심)

    핵심적인 비즈니스 로직은 아니지만 반드시 해야 하는 부가기능(인프라 로직)

    **인프라 로직

        - 애플리케이션의 전 영역에서 나타날 수 있음

        - 중복코드를 만들어낼 가능성때문에 유지보수가 힘들어짐

        - 비즈니스 로직과 함께 있으면 비즈니스 로직을 이해하기 어려워짐

     

     - 이처럼 인프라 로직은 로깅, 트랜잭션, 권한검사, 성능 측정 등 하나의 관심사를 가지게 됨

     - 상단의 이미지처럼 인프라 로직의 중복이 횡단으로 나타남    =>    cross-cutting concern 횡단 관심사

  

    💡 따라서, AOP는 횡단 관심사에 따라 프로그래밍을 하는 것이라고 생각하면 편함

 

빵또아..
Proxy

📌 AOP 용어

-Aspect : 공통 관심사(로깅, 보안, 트랜잭션 등)

-Target : 어떤 대상에 부가 기능을 부여할 것인가
-Advice : 어떤 부가기능? (Join point에서 실행되어야 하는 코드)

-Join point : 어디에 적용할 것인가? 메서드, 필드, 객체, 생성자 등 (Spring AOP에서는 메소드가 실행될 때만으로 한정함)
-Point cut : 실제 advice가 적용될 지점, Spring AOP에서는 advice가 적용될 메서드를 선정
                 @Around("execution ... ") => execution으로 지정하는 부분이 Point cut임

-Proxy : Advice가 적용되었을 때 만들어지는 객체

@Transactional
한 트랜잭션의 연산들은 모두 성공하거나, 반대로 모두 실패된다.

 

📌Advice 종류 (ex. 로그인 전/후의 각 페이지 처리 등)

Before target method 호출 전에 적용
After target method 호출 후에 적용
Around target method 호출 이전과 이후 모두 적용
AfterReturning target method가 실행된 후 제대로 된 결과 값을 리턴했을 때
AfterThrowing target method 실행 시점에서 예외가 발생했을 때
package com.example.spring02.aop;

import java.util.Arrays;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component //스프링에서 관리하는 범용 bean으로 등록
@Aspect //AOP(공통적인 업무를 지원하는 bean으로 등록)
public class LogAdvice {
	//로깅을 위한 변수
	private static final Logger logger=LoggerFactory.getLogger(LogAdvice.class);
	
	/*
	 * @Before(핵심 업무 전), @After(핵심 업무 후), @Around(전,후 모두 사용)
	 * ..은 모든 하위 패키지를 의미, *(..)는 모든 메소드를 의미
	 * @시점("범위" or "범위" or "범위")
	 * @Before, @After => JoinPoint객체
	 * @Around => ProceedingJoinPoint객체
	 */

	@Around("execution(* com.example.spring02.controller..*Controller.*(..)) "
			+ " or execution(* com.example.spring02.service..*Impl.*(..)) "
			+ " or execution(* com.example.spring02.model..dao.*Impl.*(..))")
	public Object logPrint(ProceedingJoinPoint joinPoint) 
			throws Throwable {//핵심업무가 실행되는 시점
		long start=System.currentTimeMillis();//시스템의 밀리세컨드 값
		Object result=joinPoint.proceed();
		String type=joinPoint.getSignature().getDeclaringTypeName();
		String name="";
		if(type.indexOf("Controller") > -1) {
			name="Controller : "; //콘솔창에서 Controller : 표시됨
		}else if(type.indexOf("Service") > -1) {
			name="ServiceImpl : ";
		}else if(type.indexOf("DAO") > -1) {
			name="DAOImpl : ";
		}
		
		//호출한 클래스, method 정보를 로거에 저장
		logger.info(name+type+"."+joinPoint.getSignature().getName()+"()");
		//method에 전달되는 매개변수들을 로거에 저장
		logger.info(Arrays.toString(joinPoint.getArgs()));
		long end=System.currentTimeMillis();
		long time=end-start;
		logger.info("실행시간 : " + time);
		return result;
	}
}

참고

https://www.youtube.com/watch?v=y2JkXjOocZ4&t=2s 

https://www.youtube.com/watch?v=Hm0w_9ngDpM&t=690s