웹사이트 검색

Spring AOP 예제 자습서 - Aspect, Advice, Pointcut, JoinPoint, 주석, XML 구성


Spring Framework는 Dependency Injection과 Aspect Oriented Programming(Spring AOP)이라는 두 가지 핵심 개념으로 개발되었습니다.

스프링 AOP

Spring Dependency Injection이 어떻게 작동하는지 이미 살펴봤습니다. 오늘은 Aspect-Oriented Programming의 핵심 개념과 Spring Framework를 사용하여 이를 구현하는 방법을 살펴보겠습니다.

스프링 AOP 개요

대부분의 엔터프라이즈 애플리케이션에는 다양한 유형의 개체 및 모듈에 적용할 수 있는 몇 가지 일반적인 교차 절단 문제가 있습니다. 일반적인 크로스커팅 문제 중 일부는 로깅, 트랜잭션 관리, 데이터 유효성 검사 등입니다. 객체 지향 프로그래밍에서 응용 프로그램의 모듈성은 클래스에 의해 달성되는 반면 Aspect Oriented Programming에서는 응용 프로그램 모듈성이 Aspect에 의해 달성되며 서로 다른 클래스를 가로지르도록 구성됩니다. Spring AOP는 일반적인 객체 지향 프로그래밍 모델을 통해 달성할 수 없는 클래스에서 크로스커팅 작업의 직접적인 종속성을 제거합니다. 예를 들어 로깅을 위한 별도의 클래스를 가질 수 있지만 기능 클래스는 다시 애플리케이션 전체에서 로깅을 달성하기 위해 이러한 메서드를 호출해야 합니다.

측면 지향 프로그래밍 핵심 개념

Spring AOP 구현에 대해 자세히 알아보기 전에 AOP의 핵심 개념을 이해해야 합니다.

  1. Aspect: Aspect는 트랜잭션 관리와 같이 여러 클래스에 걸친 엔터프라이즈 애플리케이션 문제를 구현하는 클래스입니다. Aspect는 Spring XML 구성을 통해 구성된 일반 클래스이거나 Spring AspectJ 통합을 사용하여 @Aspect 주석을 사용하여 클래스를 Aspect로 정의할 수 있습니다.
  2. 조인 포인트: 조인 포인트는 메소드 실행, 예외 처리, 객체 변수 값 변경 등과 같은 애플리케이션의 특정 포인트입니다. Spring AOP에서 조인 포인트는 항상 메소드 실행입니다.
  3. 어드바이스: 어드바이스는 특정 조인 포인트에 대해 취해진 조치입니다. 프로그래밍 측면에서 애플리케이션에서 일치하는 포인트컷이 있는 특정 조인포인트에 도달했을 때 실행되는 메서드입니다. 조언을 서블릿 필터로 생각할 수 있습니다.
  4. Pointcut: Pointcut은 어드바이스를 실행해야 하는지 여부를 결정하기 위해 조인 포인트와 일치하는 표현식입니다. Pointcut은 조인 포인트와 일치하는 다양한 종류의 표현식을 사용하고 Spring 프레임워크는 AspectJ pointcut 표현 언어를 사용합니다.
  5. Target Object: 어드바이스가 적용되는 대상입니다. Spring AOP는 런타임 프록시를 사용하여 구현되므로 이 객체는 항상 프록시된 객체입니다. 이는 대상 메서드가 재정의되고 해당 구성에 따라 조언이 포함되는 하위 클래스가 런타임에 생성된다는 것을 의미합니다.
  6. AOP 프록시: Spring AOP 구현은 JDK 동적 프록시를 사용하여 대상 클래스 및 어드바이스 호출이 있는 프록시 클래스를 생성하며 이를 AOP 프록시 클래스라고 합니다. 또한 Spring AOP 프로젝트에 의존성으로 추가하여 CGLIB 프록시를 사용할 수 있습니다.
  7. 위빙(Weaving): 어드바이스 프록시 객체를 생성하기 위해 애스펙트를 다른 객체와 연결하는 과정입니다. 이는 컴파일 시간, 로드 시간 또는 런타임에 수행할 수 있습니다. Spring AOP는 런타임에 weaving을 수행합니다.

AOP 조언 유형

조언의 실행 전략에 따라 다음과 같은 유형이 있습니다.

  1. Before Advice: 이러한 조언은 조인 포인트 메서드를 실행하기 전에 실행됩니다. @Before 주석을 사용하여 어드바이스 유형을 어드바이스 이전으로 표시할 수 있습니다.
  2. After (finally) Advice: 조인 포인트 메서드가 정상적으로 실행되거나 예외가 발생하여 실행이 완료된 후 실행되는 조언입니다. @After 주석을 사용하여 어드바이스 후 생성할 수 있습니다.
  3. 어드바이스 반환 후: 조인 포인트 메서드가 정상적으로 실행되는 경우에만 어드바이스 메서드가 실행되기를 원할 때가 있습니다. @AfterReturning 주석을 사용하여 조언을 반환한 후 메서드를 표시할 수 있습니다.
  4. After Throwing Advice: 이 조언은 조인 포인트 메소드가 예외를 던질 때만 실행되며 트랜잭션을 선언적으로 롤백하는 데 사용할 수 있습니다. 이러한 유형의 조언을 위해 @AfterThrowing 주석을 사용합니다.
  5. Around Advice: 이것은 가장 중요하고 강력한 조언입니다. 이 조언은 조인 포인트 방법을 둘러싸고 있으며 조인 포인트 방법을 실행할지 여부도 선택할 수 있습니다. 조인 포인트 메서드 실행 전후에 실행되는 어드바이스 코드를 작성할 수 있습니다. 조인 포인트 메소드를 호출하고 메소드가 무언가를 리턴하는 경우 값을 리턴하는 것은 어드바이스 주변의 책임입니다. 우리는 @Around 주석을 사용하여 어드바이스 주변 방법을 생성합니다.

위에서 언급한 사항이 혼란스럽게 들릴 수 있지만 Spring AOP의 구현을 보면 상황이 더 명확해질 것입니다. AOP 구현으로 간단한 Spring 프로젝트 생성을 시작합시다. Spring은 관점을 생성하기 위해 AspectJ 주석을 사용하기 위한 지원을 제공하며 단순성을 위해 이를 사용할 것입니다. 위의 모든 AOP 주석은 org.aspectj.lang.annotation 패키지에 정의되어 있습니다. Spring Tool Suite는 측면에 대한 유용한 정보를 제공하므로 사용하는 것이 좋습니다. STS에 익숙하지 않다면 사용법을 설명한 Spring MVC Tutorial을 살펴보는 것이 좋습니다.

스프링 AOP 예제

Spring AOP AspectJ 종속성

Spring 프레임워크는 기본적으로 AOP 지원을 제공하지만 관점과 어드바이스를 구성하기 위해 AspectJ 주석을 사용하고 있으므로 pom.xml 파일에 포함해야 합니다.

<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.springframework.samples</groupId>
	<artifactId>SpringAOPExample</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>

		<!-- Generic properties -->
		<java.version>1.6</java.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

		<!-- Spring -->
		<spring-framework.version>4.0.2.RELEASE</spring-framework.version>

		<!-- Logging -->
		<logback.version>1.0.13</logback.version>
		<slf4j.version>1.7.5</slf4j.version>

		<!-- Test -->
		<junit.version>4.11</junit.version>

		<!-- AspectJ -->
		<aspectj.version>1.7.4</aspectj.version>

	</properties>

	<dependencies>
		<!-- Spring and Transactions -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>

		<!-- Logging with SLF4J & LogBack -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${slf4j.version}</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>${logback.version}</version>
			<scope>runtime</scope>
		</dependency>

		<!-- AspectJ dependencies -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>${aspectj.version}</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjtools</artifactId>
			<version>${aspectj.version}</version>
		</dependency>
	</dependencies>
</project>

프로젝트에 aspectjrtaspectjtools 종속성(버전 1.7.4)을 추가했습니다. 또한 Spring 프레임워크 버전을 최신 버전, 즉 4.0.2.RELEASE로 업데이트했습니다.

모델 클래스

몇 가지 추가 메서드를 사용하여 예제에 사용할 간단한 자바 빈을 만들어 봅시다. Employee.java 코드:

package com.journaldev.spring.model;

import com.journaldev.spring.aspect.Loggable;

public class Employee {

	private String name;
	
	public String getName() {
		return name;
	}

	@Loggable
	public void setName(String nm) {
		this.name=nm;
	}
	
	public void throwException(){
		throw new RuntimeException("Dummy Exception");
	}	
}

setName() 메서드에 Loggable 주석이 달린 것을 보셨습니까? 프로젝트에서 우리가 정의한 사용자 지정 자바 주석입니다. 나중에 사용법을 살펴 보겠습니다.

서비스 등급

Employee 빈과 함께 작동하는 서비스 클래스를 생성해 봅시다. EmployeeService.java 코드:

package com.journaldev.spring.service;

import com.journaldev.spring.model.Employee;

public class EmployeeService {

	private Employee employee;
	
	public Employee getEmployee(){
		return this.employee;
	}
	
	public void setEmployee(Employee e){
		this.employee=e;
	}
}

Spring 주석을 사용하여 Spring 구성 요소로 구성할 수 있었지만 이 프로젝트에서는 XML 기반 구성을 사용합니다. EmployeeService 클래스는 매우 표준적이며 Employee 빈에 대한 액세스 포인트를 제공합니다.

AOP를 사용한 Spring Bean 구성

STS를 사용하는 경우 "Spring Bean 구성 파일\을 생성하고 AOP 스키마 네임스페이스를 선택하는 옵션이 있지만 다른 IDE를 사용하는 경우 간단히 Spring Bean 구성 파일에 추가할 수 있습니다. My project bean 구성 파일 아래와 같습니다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="https://www.springframework.org/schema/aop"
	xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-4.0.xsd
		https://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

<!-- Enable AspectJ style of Spring AOP -->
<aop:aspectj-autoproxy />

<!-- Configure Employee Bean and initialize it -->
<bean name="employee" class="com.journaldev.spring.model.Employee">
	<property name="name" value="Dummy Name"></property>
</bean>

<!-- Configure EmployeeService bean -->
<bean name="employeeService" class="com.journaldev.spring.service.EmployeeService">
	<property name="employee" ref="employee"></property>
</bean>

<!-- Configure Aspect Beans, without this Aspects advices wont execute -->
<bean name="employeeAspect" class="com.journaldev.spring.aspect.EmployeeAspect" />
<bean name="employeeAspectPointcut" class="com.journaldev.spring.aspect.EmployeeAspectPointcut" />
<bean name="employeeAspectJoinPoint" class="com.journaldev.spring.aspect.EmployeeAspectJoinPoint" />
<bean name="employeeAfterAspect" class="com.journaldev.spring.aspect.EmployeeAfterAspect" />
<bean name="employeeAroundAspect" class="com.journaldev.spring.aspect.EmployeeAroundAspect" />
<bean name="employeeAnnotationAspect" class="com.journaldev.spring.aspect.EmployeeAnnotationAspect" />

</beans>

Spring Bean에서 Spring AOP를 사용하려면 다음을 수행해야 합니다.

  1. xmlns:aop=\https://www.springframework.org/schema/aop”와 같은 AOP 네임스페이스 선언
  2. 런타임에 자동 프록시로 Spring AspectJ 지원을 활성화하기 위해 aop:aspectj-autoproxy 요소 추가
  3. Aspect 클래스를 다른 Spring bean으로 구성

Spring bean 설정 파일에 정의된 aspect가 많은 것을 볼 수 있는데, 이제 하나씩 살펴보자.

Aspect 이전의 Spring AOP 예제

EmployeeAspect.java 코드:

package com.journaldev.spring.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class EmployeeAspect {

	@Before("execution(public String getName())")
	public void getNameAdvice(){
		System.out.println("Executing Advice on getName()");
	}
	
	@Before("execution(* com.journaldev.spring.service.*.get*())")
	public void getAllAdvice(){
		System.out.println("Service method getter called");
	}
}

위 aspect 클래스의 중요한 사항은 다음과 같습니다.

  • Aspect 클래스에는 @Aspect 주석이 있어야 합니다.
  • @Before 어드바이스를 만들기 위해 @Before 어노테이션을 사용함
  • @Before 주석에 전달된 문자열 매개변수는 Pointcut 표현식입니다.
  • getNameAdvice() 어드바이스는 서명 public String getName()이 있는 모든 Spring Bean 메소드에 대해 실행됩니다. 이것은 우리가 새 연산자를 사용하여 Employee bean을 만들면 어드바이스가 적용되지 않을 것이라는 점을 기억해야 할 매우 중요한 점입니다. Bean을 얻기 위해 ApplicationContext를 사용할 때만 어드바이스가 적용됩니다.
  • Pointcut 표현식에서 별표(*)를 와일드 카드로 사용할 수 있습니다. getAllAdvice()com.journaldev.spring.service 패키지의 모든 클래스에 적용됩니다. 이름이 get으로 시작하고 인수를 사용하지 않습니다.

모든 다른 유형의 어드바이스를 살펴본 후 테스트 클래스에서 실행 중인 어드바이스를 살펴보겠습니다.

Spring AOP Pointcut 메소드와 재사용

때때로 우리는 여러 곳에서 동일한 Pointcut 표현식을 사용해야 합니다. @Pointcut 주석이 있는 빈 메서드를 만든 다음 어드바이스에서 표현식으로 사용할 수 있습니다. EmployeeAspectPointcut.java 코드:

package com.journaldev.spring.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class EmployeeAspectPointcut {

	@Before("getNamePointcut()")
	public void loggingAdvice(){
		System.out.println("Executing loggingAdvice on getName()");
	}
	
	@Before("getNamePointcut()")
	public void secondAdvice(){
		System.out.println("Executing secondAdvice on getName()");
	}
	
	@Pointcut("execution(public String getName())")
	public void getNamePointcut(){}
	
	@Before("allMethodsPointcut()")
	public void allServiceMethodsAdvice(){
		System.out.println("Before executing service method");
	}
	
	//Pointcut to execute on all the methods of classes in a package
	@Pointcut("within(com.journaldev.spring.service.*)")
	public void allMethodsPointcut(){}
	
}

위의 예는 우리가 어드바이스 어노테이션 인수에서 메소드 이름을 사용하고 있는 표현식이 아니라 매우 명확합니다.

Spring AOP JoinPoint 및 Advice 인수

우리는 어드바이스 메소드에서 JoinPoint를 매개변수로 사용할 수 있고 이를 사용하여 메소드 서명이나 대상 객체를 얻을 수 있습니다. 인수 패턴과 일치하는 모든 메서드에 적용할 포인트컷의 args() 식을 사용할 수 있습니다. 이것을 사용한다면 인자 타입이 결정되는 어드바이스 메소드에서 같은 이름을 사용해야 합니다. 조언 인수에서도 Generic 개체를 사용할 수 있습니다. EmployeeAspectJoinPoint.java 코드:

package com.journaldev.spring.aspect;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class EmployeeAspectJoinPoint {
	
	@Before("execution(public void com.journaldev.spring.model..set*(*))")
	public void loggingAdvice(JoinPoint joinPoint){
		System.out.println("Before running loggingAdvice on method="+joinPoint.toString());
		
		System.out.println("Agruments Passed=" + Arrays.toString(joinPoint.getArgs()));

	}
	
	//Advice arguments, will be applied to bean methods with single String argument
	@Before("args(name)")
	public void logStringArguments(String name){
		System.out.println("String argument passed="+name);
	}
}

Spring AOP After Advice 예

After, After Throwing 및 After Returning 어드바이스의 예와 함께 간단한 aspect 클래스를 살펴보자. EmployeeAfterAspect.java 코드:

package com.journaldev.spring.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class EmployeeAfterAspect {

	@After("args(name)")
	public void logStringArguments(String name){
		System.out.println("Running After Advice. String argument passed="+name);
	}
	
	@AfterThrowing("within(com.journaldev.spring.model.Employee)")
	public void logExceptions(JoinPoint joinPoint){
		System.out.println("Exception thrown in Employee Method="+joinPoint.toString());
	}
	
	@AfterReturning(pointcut="execution(* getName())", returning="returnString")
	public void getNameReturningAdvice(String returnString){
		System.out.println("getNameReturningAdvice executed. Returned String="+returnString);
	}
	
}

클래스의 모든 메서드에 조언을 적용하기 위해 포인트컷 표현식에서 within을 사용할 수 있습니다. @AfterReturning 어드바이스를 사용하여 어드바이스된 메소드에 의해 반환된 객체를 얻을 수 있습니다. After Throwing 어드바이스의 사용을 보여주기 위해 Employee 빈에 throwException() 메소드가 있습니다.

Aspect 주변의 Spring AOP 예제

앞에서 설명한 것처럼 메서드 실행 전후를 잘라내기 위해 Around 측면을 사용할 수 있습니다. 이를 사용하여 조언된 메서드가 실행되는지 여부를 제어할 수 있습니다. 반환된 값을 검사하고 변경할 수도 있습니다. 이것은 가장 강력한 조언이며 적절하게 적용해야 합니다. EmployeeAroundAspect.java 코드:

package com.journaldev.spring.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class EmployeeAroundAspect {

	@Around("execution(* com.journaldev.spring.model.Employee.getName())")
	public Object employeeAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
		System.out.println("Before invoking getName() method");
		Object value = null;
		try {
			value = proceedingJoinPoint.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		System.out.println("After invoking getName() method. Return value="+value);
		return value;
	}
}

주변 어드바이스는 항상 ProceedingJoinPoint를 인수로 가져야 하며 대상 객체 어드바이드 메소드를 호출하기 위해 프로시저() 메소드를 사용해야 합니다. 어드바이스된 메서드가 무언가를 반환하는 경우 호출자 프로그램에 반환하는 것은 어드바이스 책임입니다. void 메서드의 경우 어드바이스 메서드는 null을 반환할 수 있습니다. 어드바이스 주변은 어드바이스된 메소드를 잘라내기 때문에 메소드의 입력과 출력은 물론 실행 동작을 제어할 수 있습니다.

사용자 지정 주석 Pointcut을 사용한 Spring 조언

위의 모든 어드바이스 포인트컷 표현식을 보면 의도하지 않은 다른 빈에 적용될 가능성이 있습니다. 예를 들어, 누군가가 getName() 메소드로 새로운 스프링 빈을 정의할 수 있고 어드바이스가 의도하지 않았음에도 적용되기 시작할 것입니다. 이것이 우리가 포인트컷 표현의 범위를 가능한 한 좁게 유지해야 하는 이유입니다. 다른 접근 방식은 사용자 지정 주석을 만들고 조언을 적용하려는 메서드에 주석을 추가하는 것입니다. 이것은 Employee setName() 메서드에 Spring Transaction Management 주석을 추가하는 목적입니다. Loggable.java 코드:

package com.journaldev.spring.aspect;

public @interface Loggable {

}

EmployeeAnnotationAspect.java 코드:

package com.journaldev.spring.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class EmployeeAnnotationAspect {

	@Before("@annotation(com.journaldev.spring.aspect.Loggable)")
	public void myAdvice(){
		System.out.println("Executing myAdvice!!");
	}
}

myAdvice() 메서드는 setName() 메서드만 어드바이스합니다. 이것은 매우 안전한 접근 방식이며 어떤 방법에든 어드바이스를 적용하고 싶을 때마다 Loggable 주석으로 주석을 추가하기만 하면 됩니다.

스프링 AOP XML 구성

나는 항상 주석을 선호하지만 스프링 구성 파일에서 측면을 구성하는 옵션도 있습니다. 예를 들어 아래와 같은 클래스가 있다고 합시다. EmployeeXMLConfigAspect.java 코드:

package com.journaldev.spring.aspect;

import org.aspectj.lang.ProceedingJoinPoint;

public class EmployeeXMLConfigAspect {

	public Object employeeAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
		System.out.println("EmployeeXMLConfigAspect:: Before invoking getName() method");
		Object value = null;
		try {
			value = proceedingJoinPoint.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		System.out.println("EmployeeXMLConfigAspect:: After invoking getName() method. Return value="+value);
		return value;
	}
}

Spring Bean 구성 파일에 다음 구성을 포함하여 구성할 수 있습니다.

<bean name="employeeXMLConfigAspect" class="com.journaldev.spring.aspect.EmployeeXMLConfigAspect" />

<!-- Spring AOP XML Configuration -->
<aop:config>
	<aop:aspect ref="employeeXMLConfigAspect" id="employeeXMLConfigAspectID" order="1">
		<aop:pointcut expression="execution(* com.journaldev.spring.model.Employee.getName())" id="getNamePointcut"/>
		<aop:around method="employeeAroundAdvice" pointcut-ref="getNamePointcut" arg-names="proceedingJoinPoint"/>
	</aop:aspect>
</aop:config>

AOP xml 구성 요소의 목적은 이름에서 명확하므로 자세한 내용은 다루지 않겠습니다.

스프링 AOP 예제

간단한 Spring 프로그램을 가지고 이 모든 측면이 bean 메소드를 통해 어떻게 잘리는지 봅시다. SpringMain.java 코드:

package com.journaldev.spring.main;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.journaldev.spring.service.EmployeeService;

public class SpringMain {

	public static void main(String[] args) {
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
		EmployeeService employeeService = ctx.getBean("employeeService", EmployeeService.class);
		
		System.out.println(employeeService.getEmployee().getName());
		
		employeeService.getEmployee().setName("Pankaj");
		
		employeeService.getEmployee().throwException();
		
		ctx.close();
	}
}

이제 위의 프로그램을 실행하면 다음과 같은 결과가 나타납니다.

Mar 20, 2014 8:50:09 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4b9af9a9: startup date [Thu Mar 20 20:50:09 PDT 2014]; root of context hierarchy
Mar 20, 2014 8:50:09 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring.xml]
Service method getter called
Before executing service method
EmployeeXMLConfigAspect:: Before invoking getName() method
Executing Advice on getName()
Executing loggingAdvice on getName()
Executing secondAdvice on getName()
Before invoking getName() method
After invoking getName() method. Return value=Dummy Name
getNameReturningAdvice executed. Returned String=Dummy Name
EmployeeXMLConfigAspect:: After invoking getName() method. Return value=Dummy Name
Dummy Name
Service method getter called
Before executing service method
String argument passed=Pankaj
Before running loggingAdvice on method=execution(void com.journaldev.spring.model.Employee.setName(String))
Agruments Passed=[Pankaj]
Executing myAdvice!!
Running After Advice. String argument passed=Pankaj
Service method getter called
Before executing service method
Exception thrown in Employee Method=execution(void com.journaldev.spring.model.Employee.throwException())
Exception in thread "main" java.lang.RuntimeException: Dummy Exception
	at com.journaldev.spring.model.Employee.throwException(Employee.java:19)
	at com.journaldev.spring.model.Employee$$FastClassBySpringCGLIB$$da2dc051.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:711)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:58)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644)
	at com.journaldev.spring.model.Employee$$EnhancerBySpringCGLIB$$3f881964.throwException(<generated>)
	at com.journaldev.spring.main.SpringMain.main(SpringMain.java:17)

pointcut 구성에 따라 Advice가 하나씩 실행되는 것을 볼 수 있습니다. 혼동을 피하기 위해 하나씩 구성해야 합니다. 여기까지 Spring AOP 예제 자습서의 내용입니다. Spring을 사용하여 AOP의 기본 사항을 배우고 예제를 통해 더 많은 정보를 얻을 수 있기를 바랍니다. 아래 링크에서 샘플 프로젝트를 다운로드하고 가지고 놀아보세요.

Spring AOP 프로젝트 다운로드