Spring - SPEL(SPring Expression Language)


AOP(Aspect-Oriented Programming)

  • 프로그래밍에서 발생하는 공통적인 기능을 구현할 때 코드가 중복되지 않도록 모듈화 하는 프로그래밍 기법을 관점지향적 프로그래밍(AOP)라고 한다.
  • 기능별로 Aspect를 만들어 독립적으로 정의하고, 정의된 기능을 어디서 적용해야하는지 명시함으로서 클래스 내부에 구현할 필요 없이 기능을 사용할 수 있다.
  • Java에서는 다중 상속이 불가능하여 AOP가 도입되었다.

AOP의 용어와 개념

  • Aspect - 공통적인 기능을 정의해놓은 하나의 모듈. Advice와 Pointcut이 포함되어 있다.
  • Advice - 모듈이 동작할 기능을 Advice라고 한다. ‘언제’ Aspect가 수행될 것인지 @Around, @Before와 같은 정보를 통해 나타낸다.
  • Pointcut - Advice가 적용될 위치에 대한 정보. Target에 대한 특정 메소드의 실행 시점을 의미한다.
  • Join Point - Advice가 합류하는 지점. pointcut보다 자세한 적용지점을 의미한다.
  • Target - Aspect의 기능이 적용될 객체
  • Weaving - Aspect를 Traget에 연결시켜 OOP로 만드는 과정을 의미.
  • Cross-Cutting-Concern (흩어진 관심사) - 소스 코드상에서 다른 코드에 반복적으로 발생하는 부분을 의미. 이러한 관심사들을 Aspect로 모듈화하고 분리하여 재사용하는 것이 AOP의 주 목표이다.

※ AOP는 흩어진 관심사를 Aspect로 만들어 사용하는 것이므로 다수의 Aspect를 사용하는 것은 좋지 못한 사용방법이다.

자바에서의 AOP

  • 자바에서 사용할 수 있는 AOP는 크게 AspectJ, 스프링 AOP로 나누어 설명할 수 있다.

Spring AOP

  • Spring IOC를 통해 애플리케이션에서 흔히 발생하는 문제에 대해 해결하는 것을 목적으로 사용되는 구현체이다.
  • 모든 AOP기능을 제공하지 않고 스프링 Bean에만 AOP를 적용할 수 있다.
  • Dynamic Proxy(동적으로 Proxy 객체를 생성하는 방법) 기법이 적용된다.(Target 객체에 대한 호출을 Proxy가 Interrupt하여 객체의 기능에 Advice 수행을 추가한다.)
  • 기존 Bean을 대체하는 Dynamic Proxy Bean을 생성하므로 클라이언트 코드에 변경이 없으며 Method Join Point만을 지원한다.(Target의 Method가 호출되는 런타임 시점에만 Weaving이 사용됨)

Spring AOP 적용

  • 스프링 AOP를 사용하기 위해서는 다음의 의존성을 추가해야 한다.
1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  • Aspect 모듈은 XML을 이용한 구현, @Aspect 어노테이션을 이용한 구현 두가지가 있으며 Aspect 빈의 메소드에 Advice와 Pointcut 를 등록하여 모듈이 동작한다.

Spring AOP Advice 타입

  • Spring AOP에서 정의할 수 있는 Advice 어노테이션은 다음과 같다.
  1. @Around - joinPoint 이전/이후에 모두 Advice가 수행되도록 한다.

  2. @Before - joinPoint 이전에 Advice가 수행되도록 한다.

  3. @After - joinPoint 이후 Advice가 수행되도록 한다.

  4. @AfterReturning - joinPoint가 정상적으로 반환/수행된 후 Advice가 수행되도록 한다.

  5. @AfterThrowing - joinPoint 수행 중 예외가 던져질 경우 Advice가 수행되도록 한다.

Spring AOP PointCut

  • Spring AOP에서 자주 사용되는 포인트 컷은 다음과 같으며 각 표현식은 &&, ||, !로 조합할 수 있다.
  1. execution( (접근지정자) (패키지).(클래스/인터페이스).(메소드)(..) ) - 메소드의 실행을 PointCut으로 등록한다.

  2. within((패키지).(클래스/인터페이스)) - 특정 타입에 속하는 매소드를 PointCut으로 등록한다.

  3. bean (빈 이름) - 해당 이름을 갖는 Bean을 PointCut으로 등록한다.

  4. this ((패키지).(인터페이스)) - 해당 인터페이스를 구현하는 프록시 객체 메소드를 PointCut으로 등록한다.

  5. target ((패키지).(인터페이스)) - 해당 인터페이스를 구현하는 대상 객체의 메소드를 PointCut으로 등록한다.

  6. args (타입) - 해당 타입을 인자로 갖는 메소드를 PointCut으로 등록한다.

  7. @annotation (어노테이션) - 해당 어노테이션이 선언된 메소드를 PointCut으로 등록한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Component
@Aspect
public class PerfAspect {
//com.example.demo라는 패키지 안의 모든 EventService 클래스에 대해 이 내용을 적용.
//@Around("execution(* com.example.demo..*.EventService.*(..))")

//Transactional 어노테이션에 대해 적용
//@Around("@annotation(Transactional)")

//Service라는 이름의 빈에 대해 적용
@Around("bean(Service)")
public Object logPerf(ProceedingJoinPoint pip) throws Throwable{ //Advice
long begin = System.currentTimeMillis();
Object retVal = pip.proceed();//메서드 호출 자체를 감싼다
System.out.println( System.currentTimeMillis() - begin);
return retVal;
}
}

AspectJ

  • 완전한 AOP를 제공하는 것을 목적으로 하는 기술
  • AspectJ는 다음과 같은 시간에 Weaving을 사용할 수 있다.
    1. 컴파일 타임 - Aspect 코드와 애플리케이션의 코드를 모두 입력받아 AspectJ 컴파일러가 클래스를 생성
    2. 컴파일 전 - 이미 존재하는 class 파일과 jar 파일을 입력받아 클래스 생성.
    3. 로드 타임 - class파일과 jar파일을 입력받아 클래스를 생성하지만 class 파일이 JVM에 로드된 후 클래스 생성이 이루어진다.
  • 메소드 실행에 대해서만 Join Point를 적용할 수 있는 Spring AOP와 달리 다음의 Join Point를 설정할 수 있다.
    1. 메소드 호출/실행
    2. 생성자 호출/실행
    3. Static 초기화 실행
    4. 객체 초기화
    5. 필드 참조
    6. 필드 값 변경
    7. Handler 실행
    8. Advice 실행
  • 런타임이 아닌 컴파일/로드 타임에 Weaving이 사용되므로 런타임 Weaving을 사용하는 Spring AOP에 비해 빠르지만 AspectJ 컴파일러, Aspect 자바 툴을 요구하고 라이브러리들을 재 패키징해야하는 복잡함이 있다.

댓글