Spring AOP

떡ol·2022년 10월 5일
0

1. Spring AOP란?

AOP는 Aspect Oriented Programming의 약자로 관점 지향 프로그래밍이라고 불린다. 관점 지향은 쉽게 말해 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 각각 모듈화하겠다는 것이다. 여기서 모듈화란 어떤 공통된 로직이나 기능을 하나의 단위로 묶는 것을 말한다. 

예로들어 핵심적인 관점은 결국 우리가 적용하고자 하는 핵심 비즈니스 로직이 된다. 또한 부가적인 관점은 핵심 로직을 실행하기 위해서 행해지는 데이터베이스 연결, 로깅, 파일 입출력 등을 예로 들 수 있다.

AOP에서 각 관점을 기준으로 로직을 모듈화한다는 것은 코드들을 부분적으로 나누어서 모듈화하겠다는 의미다. 이때, 소스 코드상에서 다른 부분에 계속 반복해서 쓰는 코드들을 발견할 수 있는 데 이것을 흩어진 관심사 (Crosscutting Concerns)라 부른다. 

2. AOP 간략한 개념

  • Aspect : 위에서 설명한 흩어진 관심사를 모듈화 한 것. 주로 부가기능을 모듈화함.
  • Target : Aspect를 적용하는 곳 (클래스, 메서드 .. )
  • Advice : 실질적으로 어떤 일을 해야할 지에 대한 것, 실질적인 부가기능을 담은 구현체
  • JointPoint : Advice가 적용될 위치, 끼어들 수 있는 지점. 메서드 진입 지점, 생성자 호출 시점, 필드에서 값을 꺼내올 때 등 다양한 시점에 적용가능
  • PointCut : JointPoint의 상세한 스펙을 정의한 것. 'A란 메서드의 진입 시점에 호출할 것'과 같이 더욱 구체적으로 Advice가 실행될 지점을 정할 수 있음

여기까지 AOP의 간략한 개념을 설명하였고 소스를 살펴보자.

3. AOP 구문

xml 구문

spring bean config.xml 파일에 다음과 같이 작성하였다 하나씩 알아보자.

	<context:component-scan base-package="com">
		<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
	</context:component-scan>
	
	<aop:config>
		<aop:pointcut expression="execution(* com.service..*.*(..))" id="comAopMethod"/>
		
		<aop:aspect ref="aopReference">
			<aop:before pointcut-ref="comAopMethod" method="before"/>
			<!--  <aop:after pointcut-ref= "comAopMethod" method="after"/> -->
			<!-- <aop:around pointcut-ref= "comAopMethod" method="around"/> -->
			<aop:after-throwing throwing="ex" pointcut-ref="comAopMethod" method="afterThrowing"/>
		</aop:aspect>
	</aop:config>
	
	<bean id ="aopReference" class="com.config.AopReference"/>
  • context:component-scan : 그냥 컨트롤단을 제외한 서비스단들에만 반응하겠금 작성해둔 코드이다.
  • aop:config : 본격적으로 aop를 사용하기 위해서 선언하는 코드이다. 작성하기 전에는 bean namespaces에도 등록을 해야 정상 작동 가능하다.

Java 구문

@Aspect
public class MyAopConfig {

    @Pointcut("execution(* com.service..*.*(..))")
    public void comAopMethod(){}

    @Before("comAopMethod()")
    public void before(){
        System.out.println("before AOP");
    }
    
    //@After("comAopMethod()")
    public void after(){
        System.out.println("after AOP");
    }

    @AfterThrowing(pointcut = "comAopMethod()", throwing = "ex")
    public void afterThrowing(Exception ex){
        System.out.println("aop send error:: " + ex);
    }

    //@Around("comAopMethod()")
    public void around() {
        System.out.println("around AOP");
    }

}

자바구문은 파일을 만든 후 @ComponenetScan으로 Bean등록을 해주거나 따로 @Import하면 된다.

3.1 조인포인트(JoinPoint) 및 포인트컷(Pointcut)

클라이언트가 호출하는 모든 비즈니스 메소드, 모든 메소드를 조인포인트로 생각하면 된다. 이 중에서 포인트컷으로 선택적으로 aop를 설정하게 한다.

<aop:pointcut expression="execution(* com.service..*.*(..))" id="comAopMethod"/>

✔ 리턴타입 지정

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

✔ 패키지 지정

표현식설명
com.service정확하게 com.service 패키지만 선택
com.service..com.service 패키지로 시작하는 모든 패키지 선택
com.service..implcom.service 패키지로 시작하고 마지막 패키지 이름이 impl로 끝나는 패키지 선택

✔ 클래스 지정

표현식설명
Printer정확하게 Printer 클래스만 선택
*Printer이름이 Printer로 끝나는 클래스만 선택 (ColorPrinter, MonoPrinter)
Printer+해당 클래스로 파생된 모든 자식 클래스 선택, 인터페이스 구현된 모든 클래스 선택
BasicObject클래스 이름 뒤에 '+'가 붙으면 해당 클래스로부터 파생된 모든 자식 클래스 선택,
인터페이스 이름 뒤에 해당 인터페이스를 구현한 모든 클래스 선택

✔ 메서드 지정

표현식설명
*(..)모든 메서드 선택
print*(..)메서드명이 print로 시작하는 모든 메서드 선택

✔ 매개변수 지정

표현식설명
(..)모든 매개변수
(*)반드시 1개의 매개변수를 가지는 메서드만 선택
(com.devljh.domain.user.model.User)매개변수로 User를 가지는 메소드만 선택. 꼭 풀패키지명이 있어야함
(!com.devljh.domain.user.model.User)매개변수로 User를 가지지않는 메소드만 선택
(Integer, ..)한 개 이상의 매개변수를 가지되, 첫번째 매개변수의 타입이 Integer인 메서드만 선택
(Integer, *)반드시 두 개의 매개변수를 가지되, 첫번째 매개변수의 타입이 Integer인 메서드만 선택

다시 소스로 돌아가서 아래의 구문을 해석하자면,

<aop:pointcut expression="execution(* com.service..*.*(..))" id="comAopMethod"/>
  • 모든리턴타입을 허용
  • com.service아래의 모든 패키지를 허용 (예: com.service.NameService, com.service.BuildingService)
  • 모든 클래스와 매서드를 허용
  • 모든 매개변수를 허용

해당 조건에서 작동하게 되는 것이다.

3.2 aspect 어드바이스 알아보기

	<bean id ="aopReference" class="com.config.AopReference"/>

	<aop:aspect ref="aopReference">
			<aop:before pointcut-ref="comAopMethod" method="before"/>
			<aop:after pointcut-ref= "comAopMethod" method="after"/>
			<!-- <aop:around pointcut-ref= "comAopMethod" method="around"/> -->
			<aop:after-throwing throwing="ex" pointcut-ref="comAopMethod" method="afterThrowing"/>
    </aop:aspect>
    

위에서 간력히 설명하였듯, 포인트 컷으로 만든 지점에서 어떤걸 하고싶은지 설정하는 단계이다. <aop:aspect> 안에 사용하며, 크게 3가지로 나뉜다.

  • Before : 비지니스 메소드 실행 전 동작 ; <aop:before>
  • After
    • After Returning : 비지니스 메소드가 성공적으로 리턴되면 동작 ; <aop:after-returning>
    • After Throwing : 비지니스 메소드 실행 중 예외가 발생하면 동작(try~catch 블록에서 catch 블록에 해당) ; <aop:after-throwing>
    • After : 실행 된 후 무조건 실행(try~catch~finally 블록에서 finally 블록에 해당) ; <aop:after>
  • Around : 비지니스 메소드 실행 전후에 처리할 로직을 삽입할 수 있음 <aop::after-around>

After에 경우에는 세부적으로 Returning, Throwing, After로 사용가능하다.
props로는 pointcut-ref, method를 기본적으로 가지고 있고 After Throwing, After Returning 추가적인 porps를 가진다.

  • pointcut-ref : 위에서 설정한 포인트컷 id를 선언하면 된다.
  • method : <aop:aspect ref="aopReference">에 선언한 빈에 매서드를 찾아 실행한다.
  • throwing : After Throwing에만 사용하는 프로퍼티이다. 에러 객체를 담아 넘기게 해준다.
  • returning : 마찬가지로 After Returning에서 리턴할 변수 값을 담아 넘길 수 있다.

method에 대해서는 아래 코드를 보자.

public class AopReference {
	
	public void before() {
		System.out.println("before AOP");
	}
	
	public void after() {
		System.out.println("after AOP");
	}
	
	public void afterThrowing(Exception ex) {
		System.out.println("aop send error:: " + ex);
	}
	
	
	public void around() {
		System.out.println("around AOP");
	}

}

위와 같이 bean으로 등록된 AopReference class에서 해당하는 method를 포인트컷과 어드바이스의 종류에 맞게 실행하는것이다. (굳이 before라서 이름을 같게 할필요는 없다.)

4 실습하기

자그럼 테스트를 진행해보자.
포인트컷에서 execution(* com.service...*(..)) com.service 아래 패키지에서 작동하게 했으므로 서비스단을 한개 만들어본다. 간단하므로 설명을 패스

package com.service;

@Service
public class AopServiceTest {

	
	public void aopTest() {
		System.out.println("-서비스가 작동합니다.--");
	}
	
	public void aopTestErr() throws Exception {
		System.out.println("----에러가 작동할땐 throwing aop를 작통할겁니다, 확인해보세요---");
		throw new Exception("AOPError입니다.");
	}
}

Controller다. 위에코드를 불러오는방법은 많고 나는 웹 호출로 테스트 해봤다.

	@RequestMapping(value = "aopTest")
	public String aopTest() throws Exception {
		aopServiceTest.aopTest();
		
		aopServiceTest.aopTestErr();
		
		return null;
	}

!결과

잘 작동하는거 같다. <aop:around>도 주석을 풀어 실행해보면 어떤 의미인지 알 수 있다.

잘보면 AOP Advice는 after에서 지원하는 기능이 많은걸 알 수 있다. 그렇다면 잘써먹는다는건데... 이 중에 throwing를 이용해서 exception 처리를 현업에서 많이 이용한다. 여기서 사용법을 알아보자.

추가로 tx:advice라고, db 트랜젝션처리 관련 Advice도 존재한다.. 여기참고



참고자료들___
(참고) [Spring] 스프링 AOP (Spring AOP) 총정리
(참고) Spring AOP
(참고) [Spring] 스프링 AOP Pointcut 표현식

profile
하이

0개의 댓글