Spring

Spring AOP

AOP란?

Aspect-Oriented Programming의 약자이다.

흩어진 Aspect들을 모아서 모듈화 하는 기법이다.

서로 다른 클래스라고 하더라도 비슷한 기능을 하는 부분이 있다. DB를 연결하거나 끊거나 하는 작업이 대표적인 예시이다.

이로한 부분을 횡단 관심사라고 불린다.

이런 횡단 관심사와 비즈니스 로직을 분리하고 해당 관심사가 필요한 경우 aspect를 통해서 설정한 부분에서 실행되게 한다.

 

 

AOP 주요 개념

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

 

Spring AOP는 프록시 패턴이라는 디자인 패턴을 사용해서 AOP 효과를 낸다.

프록시 패턴을 사용하면 어떤 기능을 추가하려 할때 기존 코드를 변경하지 않고 기능을 추가할수 있다.

 

어떤 클래스가 Spring AOP의 대상이라면 그 기존 클래스의 빈이 만들어질때 Spring AOP가 프록시(기능이 추가된 클래스)를 자동으로 만들고 원본 클래스 대신 프록시를 빈으로 등록한다. 

그리고 원본 클래스가 사용되는 지점에서 프록시를 대신 사용한다.

 

Spring의 PetClinic 예제에서 OwnerRepository 인터페이스의 @Transactional 애노테이션이 이에 해당한다.

 

스프링 AOP 특징

  • 프록시 패턴 기반의 AOP 구현체, 프록시 객체를 쓰는 이유는 접근 제어 및 부가기능을 추가하기 위해서임
  • 스프링 빈에만 AOP를 적용 가능
  • 모든 AOP 기능을 제공하는 것이 아닌 스프링 IoC와 연동하여 엔터프라이즈 애플리케이션에서 가장 흔한 문제(중복코드, 프록시 클래스 작성의 번거로움, 객체들 간 관계 복잡도 증가 ...)에 대한 해결책을 지원하는 것이 목적

포인트컷을 이용하면 어드바이스 메소드가 적용될 비즈니스 메소드를 정확하게 필터링할 수 있다.

지시자(PCD, AspectJ pointcut designators)의 종류

몇가지들이 있다. 아래에선 execution 을 사용한 표현식에 대해 알아본다.

  1. execution : 가장 정교한 포인트컷을 만들수 있다. 리턴타입 패키지경로 클래스명 메소드명(매개변수)
  2. within : 타입패턴 내에 해당하는 모든 것들을 포인트컷
  3. bean : bean이름으로 포인트컷

1) 리턴타입 지정

표현식설명

* 모든 리턴타입 허용
void 리턴타입이 void인 메소드 선택
!void 리턴타입이 void가 아닌 메소드 선택

2) 패키지 지정

표현식설명

com.devljh.domain

정확하게 com.devljh.domain 패키지만 선택

com.devljh.domain..

com.devljh.domain 패키지로 시작하는 모든 패키지 선택

3) 클래스 지정

표현식설명

UserBO 정확하게 UserBO 클래스만 선택
*BO 이름이 BO로 끝나는 클래스만 선택
BaseObject+ 클래스 이름 뒤에 '+'가 붙으면 해당 클래스로부터 파생된 모든 자식 클래스 선택, 인터페이스 이름 뒤에 '+'가 붙으면 해당 인터페이스를 구현한 모든 클래스 선택

4) 메소드 지정

표현식설명

*(..) 모든 메소드 선택
update*(..) 메소드명이 update로 시작하는 모든 메소드 선택

5) 매개변수 지정

표현식설명

(..) 모든 매개변수
(*) 반드시 1개의 매개변수를 가지는 메소드만 선택

(com.devljh.domain.user.model.User)

매개변수로 User를 가지는 메소드만 선택. 꼭 풀패키지명이 있어야함

(!com.devljh.domain.user.model.User)

매개변수로 User를 가지지않는 메소드만 선택

(Integer, ..) 한 개 이상의 매개변수를 가지되, 첫 번째 매개변수의 타입이 Integer인 메소드만 선택
(Integer, *) 반드시 두 개의 매개변수를 가지되, 첫 번째 매개변수의 타입이 Integer인 메소드만 선택

JoinPoint 인터페이스

어드바이스 메소드를 의미있게 구현하려면 클라이언트가 호출한 비즈니스 메소드의 정보가 필요하다. 예를들면 예외가 터졌는데, 예외발생한 메소드의 이름이 뭔지 등을 기록할 필요가 있을 수 있다. 이럴때 JoinPoint 인터페이스가 제공하는 유용한 API들이 있다.

메소드설명

Signature getSignature() 클라이언트가 호출한 메소드의 시그니처(리턴타입, 이름, 매개변수) 정보가 저장된 Signature 객체 리턴
Object getTarget() 클라이언트가 호출한 비즈니스 메소드를 포함하는 비즈니스 객체 리턴
Object[] getArgs() 클라이언트가 메소드를 호출할 때 넘겨준 인자 목록을 Object 배열 로 리턴

Signature API

메소드설명

String getName() 클라이언트가 호출한 메소드 이름 리턴
String toLongString() 클라이언트가 호출한 메소드의 리턴타입, 이름, 매개변수(시그니처)를 패키지 경로까지 포함 하여 리턴
String toShortString() 클라이언트가 호출한 메소드 시그니처를 축약한 문자열로 리턴

 

Advice의 종류

Before Advice

 대상 객체의 메서드 호출 전에 공통 기능을 실행하는 데 사용됨.

 After Returning Advice

 비즈니스 메서드가 수행되고 나서 결과 데이터를 리턴할 때 동작하는 어드바이스로 매개변수로 리턴한 객체 값이 추가로 넘어온다.

 After Throwing Advice

 대상 객체의 메서드를 실행하는 도중 익셉션이 발생한 경우에 공통 기능을 실행하는 데 사용됨. 매개변수로 Exception이 넘아온다.

 After Advice

 종료 후에 무조건 실행됨.

 대상 객체의 메서드를 실행하는 도중에 익셉션이 발생했는지의 여부에 상관없이 메서드 실행 후 

공통 기능을 실행함. ( try-catch-finally의 finally블록과 비슷 )

 Around Advice 

 대상 객체의 메서드 실행 전, 후 또는 익셉션 발생 시점에 공통 기능을 실행하는 데 사용됨. 조인포인트가 아닌 ProceedingJoinPoint 객체가 매개변수로 넘어온다.

 

실습

 

1. 프로젝트 생성

저는 spring study 리포지토리를 만들려고 위처럼 다양한 의존성을 추가했지만

AOP만 테스트 할거라면 Spring Web만 추가해도 됩니다.

(횩여 전체 의존성을 가져왔다면 스프링 시큐리티 의존성을 주석처리해주세요. 안하면 login으로 이동하게 되서 ...)

 

spring aop가 starter-web에 webmvc 안에 있기 때문입니다.

 

2. 간단한 service를 만들어 줍니다.

 

3. 간단한 controller를 만들어 줍니다.

 

4. Aspect를 만들어줍니다.

코드에서 저는 Aspect의 종류 중 Around를 사용하였습니다. @Around 부분을 수정해서 다른 종류를 테스트 해볼 수 있습니다.

5. @EnableAspectJAutoProxy 설정

 

+ 어노테이션을 통해서 aop 설정하기

 

커스텀 어노테이션을 만들고

해당 어노테이션에 대한 aspect를 만들면 끝

다만 조심할 부분으로 어노테이션의 패키지가 aspect가 적힌 클래스 파일과 다른 패키지라면 패키지 전체를 적어줘야합니다.

'Spring' 카테고리의 다른 글

Spring Interceptor  (0) 2021.04.12
Spring Filter  (0) 2021.04.10
Spring Filter, Interceptor, AOP 비교  (0) 2021.04.09
Spring 요청의 흐름(Spring MVC 패턴)  (0) 2021.04.09
IOC & DI  (0) 2021.04.06