Hyemi Lee

Hyemi Lee

주니어 개발자의 삽질과 기록

Spring, Spring Framework AOP

AOP

  • 로깅, 예외, 트랜잭션 처리 같은 부가적인 코드(횡단관심)로 인해서 비즈니스 메소드가 복잡해질때가 있다
  • 핵심관심 : 핵심 비즈니스 로직
  • AOP를 이용하여 관심 분리를 하므로써 클래스의 응집도를 높일 수 있다

예제

  • LogAdvice.java
package com.springbook.biz.common;
public class LogAdvice {
	public void printLog() {
		System.out.println("[공통 로그] 비즈니스 로직 수행 전 동작");
	}
}
  • 스프링컨테이너 설정파일
    • 현재 진행중인 메소드가 expresion의 설정범위에 있으면 method를 실행시킨다
<bean id="log" class="com.springbook.biz.common.LogAdvice"></bean>
<aop:config>
 <aop:pointcut id="allPointcut" expression="execution(* com.springbook.biz..*Impl.*(..))"/>
 <aop:aspect ref="log">
 <aop:before pointcut-ref="allPointcut" method="printLog"/>
 </aop:aspect>
</aop:config>
  • BoardServiceClient.java
    • 해당클래스를 실행시키면 BoardServiceImpl클래스의 insertBoard()메서드가 실생된다.
    • aop:pointcut 태그의 expression에 의해서 aop:aspect에 설정된 printLog()가 실행된다
public class BoardServiceClient {
	public static void main(String[] args) {
 //... 생략
		// 3. 글 등록 기능 테스트
		BoardVO vo = new BoardVO();
		vo.setTitle("제목 테스트얌");
		vo.setWriter("이혜미");
		vo.setContent("임시 내용이라능 spring boot 뿌셔버리자능~~");
		boardService.insertBoard(vo);
 //... 생략
 }
}
  • 결과 aop ex

의미

  • 핵심 관심 메소드(insertBoard())와 횡단 관심 메소드(printLog())사이에서 소스상의 결합은 발생하지 않았다

AOP용어 정리

조인트 포인트(JoinPoint)

  • client가 호출하는 모든 비즈니스 메소드
  • ex) BoardServiceImpl, UserServiceImpl 클래스의 모든 메소드

포인트컷(Pointcut)

  • 필터링된 조인트 포인트
  • 비즈니스 메소드들 중에서 우리가 원하는 특정메소드
  • 이 메소드에서 횡단관심이 발생한다
<aop:pointcut id="allPointcut" expression="execution(* com.springbook.biz..*Impl.*(..))"/>
  • ‘*’ : 리턴타입
  • com.springbook.biz. . : 패키지정보
  • ‘*Impl’ : 클래스명
  • *(..) : 메소드명 및 매개 변수

어드바이스(Advice)

  • 횡당 관심에 해당하는 공통 기능의 코드
  • 독립된 클래스 메소드로 작성한다
  • 동작 시점 : before, after, after-returning, after-throwing, around
<aop:before pointcut-ref="allPointcut" method="printLog"/>

위빙(Weaving)

  • 핵심 관심 메소드(포인트컷)가 호출될 때, 어드바이스에 해당하는 횡단관심 메소드가 삽입되는 과정을 의미한다.
  • 위빙을 통해서 비즈니스 메소드를 수정하지 않고도 횡단 관심의 기능을 추가,변경할 수 있다.

애스팩트(Aspect) 또는 어드바이저(Advisor)

  • 포인트컷 + 어드바이스 를 이어준다
  • 어떤 포인트컷 메소드에 대해서 어떤 어드바이스 메소드를 실행할지 결정한다
<bean id="log" class="com.springbook.biz.common.LogAdvice"></bean>
<aop:config>
 <aop:pointcut id="allPointcut" expression="execution(* com.springbook.biz..*Impl.*(..))"/>
 <aop:aspect ref="log">
 <aop:before pointcut-ref="allPointcut" method="printLog"/>
 </aop:aspect>
</aop:config>
  • 어디서 ? execution에 해당하는 메소드를 실행하는 곳에서
  • 무엇을 ? printLog()메소드를
  • 언제 ? before, 포인트컷 메소드가 실행되기 전에 실행한다

엘리먼트

  • aspect와 같은 기능을한다
  • 트랜잭션 설정 같은 특수한 경우는 advisor를 사용
  • advice 객체의 아이디를 모르거나 메소드 이름을 확인할 수 없는 경우 사용한다

어드바이스 동작 시점

|동작시점| 설명 |–|–| |Before| 비즈니스 메소드 실행 전 동작 |After| - After Returning : 비즈니스 메소드가 성공적으로 리턴되면 동작

  • After Throwing : 비즈니스 메소드가 실행 중 예외가 발생하면 동작 (try~catch 블록에서 catch블록에 해당)
  • After : 비즈니스 메소드가 실행된 후, 무조건 실행(try~catch~finally에서 finally에 블록에 해당) |Around| 비즈니스 메소드 실행 전후에 처리할 로직을 삽입할 수 있음

  • 예제
<bean id="afterThrowing" class="com.springbook.biz.common.AfterThrowingAdvice"></bean>
<bean id="after" class="com.springbook.biz.common.AfterAdvice"></bean>
<aop:config>
	<aop:pointcut id="allPointcut" expression="execution(* com.springbook.biz..*Impl.*(..))"/>
	<aop:aspect ref="afterThrowing">
		<aop:after-throwing pointcut-ref="allPointcut" method="exceptionLog"/>
	</aop:aspect>
	<aop:aspect ref="after">
		<aop:after pointcut-ref="allPointcut" method="finallyLog"/>
	</aop:aspect>
</aop:config>
  • Around 예제
    • ProceedingJoinPoint객체의 proceed()를 통해 비즈니스 메소드를 호출할 수 있다.
import org.aspectj.lang.ProceedingJoinPoint;
public class AroundAdvice {
	public Object aroundLog(ProceedingJoinPoint pjp) throws Throwable {
		System.out.println("[Before] : 비즈니스 메소드 수행 전에 처리내용...");
		Object returnObj = pjp.proceed();
		System.out.println("[After] : 비즈니스 메소드 수행 후에 처리내용...");
		return returnObj;
	}
}

애노테이션으로 AOP 설정

  • @Service : 횡단관심의 기능을 가진 클래스의 객체를 생성시킨다(어드바이스)
  • @Aspect : 어드바이스 + 포인트컷
  • @Pointcut : 횡단관심 발생 메소드 위치
  • @Before, @After, @AfterReturing, @AfterThrowing, @Around : 횡단관심 발생시점

예제

  • xml 설정
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

Before

@Service	// 해당 클래스의 객체가 생성되어야 사용할 수 있으므로 @Service를 이용해 생성해준다.
@Aspect		// LogAdvice 어드바이스(공통횡단관심의 기능) + allPointcut 포인트컷(어디서 발생시킬지) 둘을 연결시켜준다
public class LogAdvice {
	@Pointcut("execution(* com.springbook.biz..*Impl.*(..))")
	public void allPointcut() {}
	@Before("allPointcut()")
	public void printLog() {
		System.out.println("[공통 로그] 비즈니스 로직 수행 전 동작");
	}
}

After

  • @AfterReturing (메소드 성공적 수행 후)
    • returning을 이용하여 메소드의 결과값을 받아 올 수 있다
  • @AfterThrowing
    • throwing을 이용하여 예외값 받아올 수 있다
    • @AfterThrowing(pointcut="afterAllPointCut()", throwing="exceptObj")
  • 예제
@Service
@Aspect
public class AfterThrowingAdvice {
	@Pointcut("execution(* com.springbook.biz..*Impl.*(..))")
	public void afterAllPointCut() {}
	// 포인트컷 설정 및 (예외)결과값 받아오기
	@AfterThrowing(pointcut="afterAllPointCut()", throwing="exceptObj")
	public void exceptionLog(JoinPoint jp, Exception exceptObj) {
		String method = jp.getSignature().getName(); // 실행된 메소드 이름 가져오기
		System.out.println(method + "() 메소드 수행 중 예외 발생");
		if(exceptObj instanceof IllegalArgumentException) {
			System.out.println("부적합한 값이 입력되었습니다");
		} else if(exceptObj instanceof NumberFormatException) {
			System.out.println("숫자 형식의 값이 아닙니다");
		} else if(exceptObj instanceof Exception) {
			System.out.println("문제가 발생했습니다");
		}
	}
}

Around

@Service
@Aspect
public class AroundAdvice {
// ProceedingJoinPoint객체의 proceed()를 통해 비즈니스 메소드를 호출할 수 있다.
	@Pointcut("execution(* com.springbook.biz..*Impl.*(..))")
	public void aroundAllPointCut() {}
	@Around("aroundAllPointCut()")
	public Object aroundLog(ProceedingJoinPoint pjp) throws Throwable {
		String method = pjp.getSignature().getName();
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		System.out.println("[Before] : 비즈니스 메소드 수행 전에 처리내용...");
		Object returnObj = pjp.proceed();
		stopWatch.stop();
		System.out.println(method + "() 메소드 수행 시간 : "+ stopWatch.getTotalTimeMillis()+"(ms)초");
		return returnObj;
	}
}

Reference

  • 스프링 퀵 스타트-채규태 지음

Share on

Twitter Facebook LinkedIn

You may also enjoy

Redis Stream

2021年04月28日

Stream Stream은 로그 데이터를 처리하게위해 5.0에서 새로 도입된 데이터 타입입니다. 대량의 데이터가 연속적으로 발생할때 처리하기 위해 등장했습니다. 기존 데이터를 수정하지 않고 오직 추가로 발생합니다. 이런 종류의 데이터를 stream or log데이터...

Study, Object, chapter2&3 presentation

2021年04月20日

chapter03. 역할, 책임, 협력 객체지향 설계란, 올바른 객체에게 올바른 책임을 할당하면서 낮은 결합도와 높은 응집도를 가진 구조를 창조하는 활동이다.

Spring, chatting 프로그램 만들기, Reactive란?

2020年06月16日

Reactive 막힘없이 흘러다니는 data(event)를 통해 사용자에게 자연스러운 응답을 주고 규모 탄력적으로 리소스를 사용하며 실패에 있어서 유연하게 대처한다 모든 지점에서 블럭 되지 않게 하자 oop와 같은 패러다임 모든 것을 비동기적인 data의 strea...