온 코딩

[Spring] AOP - 관심분리, 포인트컷, 어드바이스, 위빙, 애스팩트 + 표현식 모음 본문

복습 ARCHIVE/모델별 프로젝트

[Spring] AOP - 관심분리, 포인트컷, 어드바이스, 위빙, 애스팩트 + 표현식 모음

SummerON 2021. 6. 17. 15:27

AOP(Aspect Oriented Programming) 관점 지향 프로그래밍

- 관심분리

횡단관심 : 메소드마다 공통으로 등장하는 로깅이나 예외, 트랜잭션 처리 같은 코드 들 

핵심관심 : 요청에 따라 실제로 수행되는 핵샘 비즈니스 로직 

객체지향 프로그래밍에서는 횡단관심을 완벽하게 독립적으로 분리해내기가 어렵다 


AOP 사용 전 준비사항

pom.xml 에 dependecy추가

<!-- AspectJ -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>${org.aspectj-version}</version>
		</dependency>	
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.8.8</version>
		</dependency>

Library > maven dependency 확인

applictaionContext.xml 파일에서 Namespace aop 선택

applicationContext.xml에 bean 추가, 포인트컷 환경설정 

앨리먼트 관계

	<bean id="log" class="com.hhw.biz.common.LogAdvice"></bean>
	
	<aop:config>
		<aop:pointcut expression="execution(* com.hhw.biz..*Impl.*(..))" id="allPointcut"/>
		<aop:aspect ref="log">
			<aop:before method="pringLog" pointcut-ref="allPointcut"/>
		</aop:aspect>
	</aop:config>

aop:pointcut expression 설명~~

<aop:pointcut expression="execution(* com.hhw.biz..*Impl.get*(..))" id="getPointcut"/>

execution(* com.hhw.biz..*Impl.get*(..))
(* 리턴타입 : 모든 타입
이후로는 메서드 로케이션 
com.hhw.biz.. 패키지 구조
*Impl. ~~~Impl로 끝나는 파일 
get*(..)) get으로 시작하는 모든 메서드
//참고 : *(..)) 모든 메서드


<aop:aspect ref="log">
	<aop:before method="printLog" pointcut-ref="allPointcut"/>
</aop:aspect>

allPointcut에 정의된 메서드들이 실행되기 전에 log 빈의 printLog메서드를 실행함 
printLog()메서드를 어드바이스라고 함! -공통으로 실행되는 코드

expression 뜻

포인트 컷 

1. 조인 포인트

: 클라이언트가 호출하는 모든 비즈니스 메서드

: 조인포인트 중에서 포인트컷이 선택되기 때문에 포인트컷 대상 또는 포인트컷 후보라고 한다.

 

2. 포인트 컷

: 필터링 된 조인포인트

: 수많은 비즈니스 메서드 중에서 우리가 원하는 특정 메서드에서만 횡단 관심에 해당하는 공통기능을 수행시키기 위해서 포인트 컷이 필요

: 메서드가 포함된 클래스와 패키지, 메서드 시그니처까지 정확하게 지정할 수 있다.

*위의 예제에서는 getPointCut이 get과 관련된 메서드를 잘라낸 포인트컷!

 

3. 어드바이스

: 횡단 관심에 해당하는 공통 기능의 코드 - 언제 동작할지 스프링 설정파일을 통해 지정 가능

: before, after, after-returning, after-throwing, around 다섯가지 위치가 있음

 

4. 위빙

: 핵심 관심 메서드가 호출 될 때, 어드바이스에 해당하는 횡단 관심 메서드가 삽입되는 과정

: 위빙 과정을 통해 관심분리가 가능한 것!

: 컴파일, 로딩, 런타임 위빙 중 스프링에서는 런타임위빙을 지원! (구동할 때 위빙한다~~)

 

5.애스팩트(어드바이저)

: 포인트컷+어드바이스

<aop:aspect ref="어드바이스클래스">

<aop:언제 method="어드바이스이름" pointcut-ref="어떤 포인트컷" />

</aop:aspect>

- 어떤 포인트컷에서 어떤 어드바이스 가져올건지!

*<aop:adviser> : 클래스를 임포트한 경우, 어드바이저를 사용! 메서드를 따로 지정하지 않아도 된다

 

+어드바이스 JoinPoint

- before 

package com.hhw.biz.common;

import org.aspectj.lang.JoinPoint;

public class BeforeAdvice {

	public BeforeAdvice() {
	}
	
	public void beforeLog(JoinPoint jp){
		String method = jp.getSignature().getName();  //메서드 이름
		Object[] args = jp.getArgs();  //전달받는 매개변수
		System.out.println("[사전처리] 비즈니스 로직 수행 전 동작  :  "+method+" / args정보  : "+args[0].toString());
	}
}

- after

package com.hhw.biz.common;

public class AfterAdvice {

	public AfterAdvice() {
		// TODO Auto-generated constructor stub
	}

	public void finallyLog(){ //JoinPoint를 매개변수로 받지 않아도 실행 가능
		System.out.println("[사후처리] 비즈니스 로직 수행 후 무조건 동작");
	}
}

- after-returning

package com.hhw.biz.common;

import org.aspectj.lang.JoinPoint;

import com.hhw.biz.board.BoardVO;

public class AfterReturningAdvice {

	public AfterReturningAdvice() {
		// TODO Auto-generated constructor stub
	}

	public void afterLog(JoinPoint jp, Object returnObj){ 
		String method = jp.getSignature().getName(); //메서드이름
		if(returnObj instanceof BoardVO){ //returnOjb가 BoardVO의 객체라면!
			BoardVO board = (BoardVO) returnObj; // 매개변수 returnObj(Context파일에 정의된 이름 사용)를 board값에 넣기
			if(board.getContent().equals("contents")){ 
				System.out.println(board.getTitle()+"내용은 contents");
			}
		}
		System.out.println("[사후로직] 비즈니스 로직 수행 후 동작  :  "+method+"메서드 리턴값"+returnObj.toString());
	}
	
}

applicationContext.xml의

<aop:aspect>에서

<aop:after-returning>태그 안에 returning="returningObj"라는 속성 추가! 속성 값은 매개변수 값과 같아야한다.

 

- after-throwing 

package com.hhw.biz.common;

import org.aspectj.lang.JoinPoint;

public class AfterThrowingAdvice {

	public AfterThrowingAdvice() {
		// TODO Auto-generated constructor stub
	}
	
	public void exceptionLog(JoinPoint jp, Exception exceptionObj){ 
    // exceptionObj는 모든 오류를 받기 위해 Exception 사용, Context.xml파일에서 정의된 매개변수명 사용
	//	exceptionObj에는 오류값, 내용이 저장되어 있음
    
		String method = jp.getSignature().getName();
		System.out.println("[예외처리] 비즈니스 로직 수행 중 예외발생  :  "+method+" / 예외메세지 : "+exceptionObj);
	}

}

applicationContext.xml의

<aop:aspect>에서

<aop:after-throwing>태그 안에 throwing="exceptionObj"라는 속성 추가! 속성 값은 매개변수 값과 같아야한다.

 

- around

: 다른 어드바이스처럼 메인 로직 수행 전/후에 실행 되는 것이 아니라 메인의 로직을 ProceedingJoinPoint pjp 객체로 가로챈 후, 클래스 내에서 proceed() 메서드를 통해 로직 수행을 한다.

package com.hhw.biz.common;

import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.util.StopWatch;

public class AroundAdvice {

	public AroundAdvice() {
		// TODO Auto-generated constructor stub
	}
	
	public Object aroundLog(ProceedingJoinPoint pjp) throws Throwable{
    //ProceedingJoinPoint을 매개변수로 받음!! - JoinPoint를 상속받고, proceed()메서드를 가지고 있음
    
		String method = pjp.getSignature().getName();
		
		StopWatch stopWatch = new StopWatch(); // 스탑워치 클래스
		
		System.out.println("[BEFORE] : 비즈니스 메서드 수행 전 //메서드 시그니처 :"+method);
		stopWatch.start();
		Object returnObj = pjp.proceed(); // 가로 챈 로직을 수행함
		stopWatch.stop();
		System.out.println("[AFTER] : 비즈니스 메서드 수행 후 // 수행에 걸린 시간  : "+stopWatch.getTotalTimeMillis()+"(ms)초");
		
		return returnObj;
	}

}

 

표현식 모음

1. 리턴타입 지정

표현식 설명
* 모든 리턴타입 허용
void 리턴타입이 void인 메서드
!void 리턴타입이 void가 아닌 메서드

2. 패키지 지정

표현식 설명
com.hhw.biz com.hhw.biz 패키지만 선택
com.hhw.biz.. com.hhw.biz 로 시작하는 모든 패키지 선택
com.hhw.biz..impl com.hhw.biz로 시작하면서 마지막 패키지 이름이 impl로 끝나는 패키지 선택

3. 클래스 지정

표현식 설명
BoardServiceImpl BoardServiceImpl 클래스만 선택
*Impl 클래스 이름이 Impl로 끝나는 모든 클래스 선택
BoardService+ 해당 클래스로부터 파생된 모든 자식클래스 선택(구현, 상속 등)

4. 메서드 지정

표현식 설명
*(..) 기본설정으로 모든 메서드를 선택
get*(..) get으로 시작하는 모든 메서드 선택

5. 매개변수 지정

표현식 설명
(..) 기본타입, 매개변수의 갯수와 타입에 제약 없음
(*) 반드시 1개 이상의 매개변수를 가지는 메서듬나 선택
(com.hhw.user.UserVO) 매개변수에 UserVO를 가지는 메서드만 선택(클래스 경로 반드시 적기)
(!com.hhw.user.UserVO) 매개변수에 UserVO를 가지지 않는 메서드만 선택
(Integer, ..) 한 개 이상의 메서드를 가지되 첫번째 매개변수는 무조건 Integer
(Integer, *) 두 개 이상의 메서드를 가지되 첫번째 매개변수는 무조건 Integer
Comments