Framework

스프링(Spring) AOP : AspectJ Pointcut 표현식 (Feat. 프로젝트에서 꼭 활용할 내용들)

매료매료 2020. 3. 31. 15:01
1. AOP(Aspect Oriented Programming)
- 주기능과 보조기능을 분리한 후 선택적으로 메소드에 적용
- XML(aound advice) 스키마 기반
- aspect(클래스) 애노테이션 기반

// XML 설정파일
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
       
       
<!-- 공통 기능을 제공할 클래스를 빈으로 등록 -->
 <bean id="profiler" class="spring.apo.ex01.advice.ProfilerAdvice" />

 <!-- Aspect 설정: Advice를 어떤 Pointcut에 적용할 지 설정 -->
 <aop:config>
  <aop:aspect id="traceAspect" ref="profiler">
   // Aspect J의 표현식
   <aop:pointcut id="publicMethod" expression="execution(public * spring.apo.ex01..*(..))" />
   // pointcut 이라는 호출할 메소드의 범위를 결정하고 / * - 모든 파라미터를 가진 / (..) - 하위 패키지까지 포함.
   <aop:around pointcut-ref="publicMethod" method="trace" />
   // around - 호출시점 / trace - 공통기능
  </aop:aspect>
 </aop:config>

 <bean id="readArticleService" class="spring.apo.ex01.component.ReadArticleServiceImpl">
  <property name="articleDao" ref="articleDao" />
 </bean>

 <bean id="articleDao" class="spring.apo.ex01.component.ArticleDaoImpl" />       
       
</beans>

// Advice 클래스
// Aound Advice
// 다른 advice는 대상객체 호출을 spring FW이 실행
public class ProfilerAdvice {
// ProceedingJoinPoint joinPoint 파라미터이면 -> Around Advice이고,
 public Object trace(ProceedingJoinPoint joinPoint) throws Throwable {
  String signatureString = joinPoint.getSignature().toShortString();
  System.out.println(signatureString + " 시작");
  long start = System.currentTimeMillis();
  try {
  // Around Advice이면, proceed() 메소드 제공한다.
   Object result = joinPoint.proceed(); // aspect가 적용될 대상 객체 호출
   return result;
  } finally {
   long finish = System.currentTimeMillis();
   System.out.println(signatureString + " 종료");
   System.out.println(signatureString + " 실행 시간 : " + (finish - start) + "ms");
  }
 }
}

2. AspectJ의 Pointcut 표현식

execution(public void set*(..)) : 리턴타입이 void이고 메소드이름이 set으로 시작하고 파라미터가 0개이상인 매소드 호출. 파라미터 부분에 ..을 사용하여 0개 이상인 것을 표현
execution(*.net.spring.*.*()) : spring 패키지의 파라미터가 없는 모든 메소드, 모든 리턴타입
execution(*.net.spring..*.*(..)) : spring 패키지의 파라미터가 0개 이상이고 패키지 부분에 ..을 표현하여 하위패키지를 표현
execution(Integer net.spring.boid.WriteArticleService.write(..)) : 리턴타입이 Integer인 WriteArticleService IF의 write() 메소드 호출
execution(* get*(*)) : 이름이 get으로 시작하고 1개의 파라미터를 갖는 메소드 호출
execution(* get*(*,*)) : 이름이 get으로 시작하고 2개의 파라미터를 갖는 메소드 호출
execution(* read*(Integer, ..)) : 이름이 read로 시작하고, 첫번째 파라미터 타입이 Integer이며, 1개의 파라미터를 갖는 메소드 호출

스프링(Spring) AOP : AspectJ Pointcut 표현식

execution 명시자
- Advice를 적용할 메서드 지정

- 기본 형식 :
-> "*" 는 모든 값을 의미
-> ".." 는 0개 이상 의미

 execution([수식어] [리턴타입] [클래스이름] [이름]([파라미터])

 수식어
 - 생략가능
 - public, protected 등등

리턴타입
 - 메서드의 리턴타입 지정

클래스이름, 이름
 - 클래스의 이름 및 메서드의 이름 지정

 파라미터
 - 메서드 파라미터 지정

ex)

execution(* some.package.*.*())
- some.package 패키지 내
- 파라미터가 없는 모든 메서드 호출

execution(* some.package..*.*(..))
- some.package 패키지와 하위 패키지에 있는
- 파라미터가 0개 이상인 모든 메서드 호출

execution(String some.package.SomeService.someMethod(..))
- 리턴 타입이 String,
- some.package.SomeService 인터페이스 내
- 파라미터가 0개 이상인 someMethod 메서드 호출

execution(* some*(*))
- 메서드 이름이 some으로 시작되고,
- 파라미터가 1개인 메서드 호출

execution(* some*(*, *))
- 메서드 이름이 some으로 시작되고,
- 파라미터가 2개인 메서드 호출

execution(* some*(String, ..))
- 메서드 이름이 some으로 시작되고,
- 첫번째 파라미터 타입이 String,
- 파라미터가 1개 이상인 메서드 호출

within 명시자 
- 특정 타입(Interface, Class)에 속하는 메서드를 Pointcut으로 지정

ex)
within(some.package.SomeService)
- SomeService 인터페이스 내 모든 메서드 호출

within(some.package.*)
- some.package 패키지 내 모든 메서드 호출

within(some.package..*)
- some.package 패키지 및 하위 패키지 내 모든 메서드 호출

bean 명시자
- Spring 2.5부터 제공
- 스프링 빈 이름을 이용하여 Pointcut 지정
- 빈 이름의 패턴을 설정

ex)
bean(someServiceBean)
- 이름이 someServiceBean인 빈의 메서드 호출

bean(*SomeService)
- 이름이 SomeService로 끝나는 빈의 메서드 호출

- 표현식에는 '&&' 및 '||' 연산자를 이용
-> 각각의 표현식을 연결
-> and 연산과 or 연산
-> and면 양쪽 표현식을 모두 만족하는
-> or이면 양쪽 표현식중 어느 하나를 만족하는

ex)
@Before("execution(public !void get*(..)) || execution(public !void set*(..))")
public void someBeforeAdviceMethod(JoinPoint joinPoint){
	// 대상 객체 메서드 실행전 수행할 공통기능
}

@After("execution(public !void get*(..)) && execution(public * get*(..))")
public void someAfterAdviceMethod(JoinPoint joinPoint){
	// 대상 객체 메서드 실행후 수행할 공통기능
}

- XML 스키마에서 Aspect를 설정할 경우
-> '&&', '||' 사용가능 ('&amp;&amp;' 식으로)
-> 'and', 'or'로 사용가능
<aop:pointcut id="somePointcut" 
       expression="execution(public !void get*(..)) &amp;&amp; execution(public * get*(..))" />

<aop:pointcut id="somePointcut" 
       expression="execution(public !void get*(..)) and execution(public * get*(..))" />

출처: https://groovysunday.tistory.com/204?category=312452 [성냥의 불친절한 IT 이야기]