Spring AOP는 핵심 비즈니스 로직과 보조적인 관심사를 분리하여 코드의 모듈성과 유지보수성을 높이기 위한 프로그래밍 패러다임이다. Spring AOP를 통해 핵심코드와 횡단 관심사를 분리함으로써 더 깨끗하고 이해하기 쉬운 코드를 작성할 수 있다.
예시: 주문을 처리하는 비즈니스 로직은 핵심 관심 코드이다.
public class OrderService {
public void processOrder() {
// 주문 처리 로직
System.out.println("Processing order...");
}
}
횡단 관심 코드는 여러 모듈에서 공통으로 적용되는 부가적인 기능을 의미한다. 예를 들어 로깅, 보안 검사, 트랜잭션 관리 등이 해당된다.
횡단 관심사는 여러 핵심 관심 코드에 걸쳐서 반복적으로 나타날 수 있고, 코드 중복과 복잡성을 증가시킨다.
예시: 주문 처리 전후로 로깅을 하는 기능은 횡단 관심 코드입니다. 이 코드가 주문 처리뿐 아니라 다른 비즈니스 로직에도 필요할 수 있습니다.
@Aspect
@Component
public class LoggingAspect {
// OrderService의 모든 메서드 실행 전에 로깅
@Before("execution(* com.example.OrderService.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Logging before: " + joinPoint.getSignature().getName());
}
// OrderService의 모든 메서드 실행 후에 로깅
@After("execution(* com.example.OrderService.*(..))")
public void logAfter(JoinPoint joinPoint) {
System.out.println("Logging after: " + joinPoint.getSignature().getName());
}
}
코드 중복 감소: 공통적으로 적용되는 횡단 관심사를 한 곳에 모듈화하여 코드 중복을 줄일 수 있다.
유지보수성 향상: 핵심 비즈니스 로직과 부가적인 기능을 분리되어 있어, 코드 수정이 더 쉬워진다.
모듈화: 비즈니스 로직과 횡단 관심사가 분리되어 있어 각각을 독립적으로 관리하고 테스트할 수 있다.
Spring AOP 기능을 사용하기 위해서는 AspectJ Runtime, AspectJ Weaver 라이브러리가 프로젝트에 빌드 처리되어 있어야된다. 메이븐 - pom.xml
Spring AOP 기능을 구현하기 위해 Spring Bean Configuration File(XML) 파일의 엘리먼트를 사용하거나 AOP 관련 어노테이션을 사용해 환경 설정 해줘야한다.
사용하기 위해서는 spring-aop.xsd 파일을 제공받도록 설정해야한다.

config: Spring AOP 기능을 구현하기 위한 정보를 제공하기 위한 엘리먼트
advisor 엘리먼트, aspect 엘리먼트, pointcut 엘리먼트<aop:config>
<하위 엘리먼트>
</aop:config>
aspect: 핵심관심코드에 횡단관심코드를 원하는 위치에 삽입하여 실행되도록 설정하기 위한 엘리먼트
before 엘리먼트, after-returning 엘리먼트, after-throwing 엘리먼트, after 엘리먼트, around 엘리먼트<aop:config>
<aop:aspect ref="hewonAdvice">
<하위 엘리먼트>
</aop:aspect>
</aop:config>
before: 타겟 메소드의 명령(핵심관심코드) 실행 전 횡단관심코드를 삽입해 실행되도록 설정하기 위한 엘리먼트
method 속성: 횡단관심모듈(Advice 클래스)의 메소드을 속성값으로 설정
pointcut 속성: 핵심관심모듈로 등록된 Spring Bean의 메소드 중 횡단관심코드가 삽입될 타겟메소드를 지정하기 위한 Pointcut 표현식을 속성값으로 설정
execution 제한자, within 제한자, bean 제한자 및 연산자를 사용해 타겟메소드가 지정되도록 설정 - 제한자에서는 패턴문자를 사용해 타겟메소드 설정 가능..(0개 이상), *(1개 이상), ?(0개 또는 1개)execution 제한자를 사용해 타겟 메소드를 지정하는 방법=> execution 제한자는 메소드의 머릿부를 사용해 타겟메소드 지정
형식) execution([접근제한자] 반환형 [패키지명.클래스명.]메소드명(자료형,자료형,...))
<!-- 반환형 또는 매개변수의 자료형이 클래스(인터페이스)인 경우 패키지를 포함하여 표현 -->
<aop:before method="beforeLog" pointcut="execution(void addHewon(xyz.itwill06.aop.Hewon))"/>
<!-- Spring Bean으로 등록된 모든 클래스의 메소드를 타겟메소드로 지정 -->
<aop:before method="beforeLog" pointcut="execution(* *(..))"/>
<!-- Spring Bean으로 등록된 모든 클래스에서 매개변수가 1개 이상 작성된 메소드를 타겟메소드로 지정 -->
<aop:before method="beforeLog" pointcut="execution(* *(*))"/>
<!-- Spring Bean으로 등록된 클래스 중 특정 패키지(하위 패키지 미포함)에 작성된 클래스의 메소드를 대상으로 타겟메소드 지정 -->
<aop:before method="beforeLog" pointcut="execution(* xyz.itwill06.aop.*(..))"/>
<!-- Spring Bean으로 등록된 클래스 중 특정 패키지(하위 패키지 포함)에 작성된 클래스의 메소드를 대상으로 타겟메소드 지정 -->
<aop:before method="beforeLog" pointcut="execution(* xyz.itwill06.aop..*(..))"/>
<!-- Spring Bean으로 등록된 클래스 중 특정 클래스의 메소드를 대상으로 타겟메소드 지정 -->
<aop:before method="beforeLog" pointcut="execution(* xyz.itwill06.aop.HewonDAOImpl.*(..))"/>
<!-- Spring Bean으로 등록된 클래스 중 인터페이스를 상속받은 자식클래스의 메소드를 대상으로 타겟메소드 지정 -->
<aop:before method="beforeLog" pointcut="execution(* xyz.itwill06.aop.HewonDAO.*(..))"/>
<aop:before method="beforeLog" pointcut="execution(* xyz.itwill06.aop.Hewon*.*(..))"/>
<aop:before method="beforeLog" pointcut="execution(* get*(..))"/>
<aop:before method="beforeLog" pointcut="!execution(* get*(..))"/>
<aop:before method="beforeLog" pointcut="execution(* *(int)) or execution(int *(..))"/>
within 제한자를 사용해 타겟 메소드를 지정하는 방법=> Spring Bean으로 등록된 클래스의 모든 메소드를 타겟메소드 지정
형식) within(패키지명.클래스명)
=> within 제한자에 패턴문자를 사용할 수 있지만 클래스 대신 인터페이스 사용 불가능
<aop:before method="beforeLog" pointcut="within(xyz.itwill06.aop.HewonDAOImpl)"/>
<aop:before method="beforeLog" pointcut="within(xyz.itwill06.aop.*)"/>
bean 제한자를 사용해 타겟 메소드를 지정하는 방법Spring Bean으로 등록된 클래스의 모든 메소드를 타겟메소드 지정
within 제한자는 클래스명으로 타겟메소드를 지정하지만 bean 제한자는 클래스의 식별자(beanName)를 사용해 타겟메소드 지정
형식) bean(beanName)
<aop:before method="beforeLog" pointcut="bean(hewonDAO)"/>
<aop:before method="beforeLog" pointcut="bean(hewonService*)"/>
pointcut: Spring Bean으로 등록된 클래스(핵심관심모듈)의 메소드 중 회단관심코드가 삽입될 타겟메소드 정보를 설정하기 위한 엘리먼트
=> 자주 사용되는 Pointcut 표현식을 저장하여 aspect 엘리먼트의 하위 엘리먼트에게 제공하기 위해 사용
<aop:pointcut expression="execution(* xyz.itwill06.aop.HewonDAO.*(..))" id="hewonDAOPointcut"/>
<aop:pointcut expression="execution(* xyz.itwill06.aop.HewonService.*(..))" id="hewonServicePointcut"/>
<aop:before method="beforeLog" pointcut-ref="hewonDAOPointcut"/>
<aop:before method="beforeLog" pointcut-ref="hewonServicePointcut"/>
<aop:after-returning method="afterReturningLog" pointcut-ref="hewonServicePointcut"/>
<aop:after-throwing method="afterThrowingLog" pointcut-ref="hewonServicePointcut"/>
<aop:after method="afterLog" pointcut-ref="hewonServicePointcut"/>
<aop:around method="aroundLog" pointcut-ref="hewonServicePointcut"/>

→ Hewon(클래스)
→ HewonDAO(인터페이스)
→ HewonDAOImpl(클래스)
→ HewonService(인터페이스)
→ HewonServiceImpl(클래스)
→ HewonApp(클래스)
JoinPoint 인터페이스는 Spring AOP에서 매우 중요하며, 주로 횡단 관심 코드를 구현할 때 사용된다.
JoinPoint는 Aspect가 적용될 수 있는 지점(예: 메서드 호출, 객체 생성 등)을 나타내며, 현재 실행 중인 메서드, 대상 객체, 인수 등을 제공하는 메서드들을 포함하고 있다.
JoinPoint 메서드JoinPoint 인터페이스는 AOP Advice에서 실행 지점에 대한 다양한 정보를 제공하는 메서드를 포함하고 있다.
getArgs()
getTarget()
getSignature()
- 현재 실행 중인 조인포인트의 메서드 시그니처(Signature) 정보를 반환한다. 시그니처는 메서드의 이름, 반환 타입, 파라미터 타입 등을 포함한다.
→ JoinPointBean(클래스)
→ JoinPointApp(클래스)
→ JoinPointAdvice(클래스)