스프링 프레임워크는 두 가지 핵심 개념, 즉 의존성 주입과 관점 지향 프로그래밍(스프링 AOP)에 기반을 두고 개발되었습니다.
스프링 AOP
우리는 이미 스프링 의존성 주입이 어떻게 작동하는지 살펴보았으며, 오늘은 관점 지향 프로그래밍의 핵심 개념과 그것을 스프링 프레임워크를 사용하여 어떻게 구현할 수 있는지 살펴보겠습니다.
스프링 AOP 개요
대부분의 기업 응용 프로그램은 다양한 종류의 객체 및 모듈에 적용 가능한 몇 가지 공통 교차 관심사를 가지고 있습니다. 로깅, 트랜잭션 관리, 데이터 유효성 검사 등이 일부 공통 교차 관심사입니다. 객체 지향 프로그래밍에서 응용 프로그램의 모듈성은 클래스에 의해 달성되지만 측면 지향 프로그래밍에서는 측면에 의해 응용 프로그램 모듈성이 달성되며 이는 다양한 클래스를 가로지르도록 구성됩니다. Spring AOP는 클래스에서 직접 교차 관심 작업에 대한 종속성을 제거하여 일반 객체 지향 프로그래밍 모델로는 달성할 수 없는 작업을 수행합니다. 예를 들어 로깅을 위한 별도의 클래스를 가질 수 있지만 기능적인 클래스는 여전히이러한 메서드를 호출하여 응용 프로그램 전체에 걸쳐 로깅을 달성해야 합니다.
측면 지향 프로그래밍 핵심 개념
Spring AOP 구현에 대한 구현에 들어가기 전에 AOP의 핵심 개념을 이해해야 합니다.
- 측면: 측면은 여러 클래스를 가로지르는 기업 응용 프로그램 관심을 구현하는 클래스입니다. 트랜잭션 관리와 같은 측면은 Spring XML 구성을 통해 정상적인 클래스로 구성될 수 있거나
@Aspect
주석을 사용하여 클래스를 측면으로 정의하기 위해 Spring AspectJ 통합을 사용할 수 있습니다. - 결합 지점: 결합 지점은 응용 프로그램의 특정 지점으로, 메소드 실행, 예외 처리, 객체 변수 값 변경 등이 있습니다. Spring AOP에서 결합 지점은 항상 메소드의 실행입니다.
- 조언: 조언은 특정 결합 지점에 대해 수행되는 작업입니다. 프로그래밍적으로, 애플리케이션에서 특정 결합 지점에 일치하는 포인트컷이 도달할 때 실행되는 메소드입니다. 조언은 Struts2 인터셉터 또는 Servlet 필터와 같은 것으로 생각할 수 있습니다.
- 포인트컷: 포인트컷은 조언이 실행되어야 하는지 여부를 결정하기 위해 결합 지점과 일치하는 표현식입니다. 포인트컷은 결합 지점과 일치하는 다양한 종류의 표현식을 사용하며, Spring 프레임워크는 AspectJ 포인트컷 표현 언어를 사용합니다.
- 대상 객체: 이들은 조언이 적용되는 객체입니다. Spring AOP는 런타임 프록시를 사용하여 구현되므로 이 객체는 항상 프록시된 객체입니다. 이것은 런타임에 하위 클래스가 생성되어 대상 메소드가 재정의되고 구성에 따라 조언이 포함된다는 것을 의미합니다.
- AOP 프록시: Spring AOP 구현은 JDK 동적 프록시를 사용하여 대상 클래스와 어드바이스 호출을 함께하여 프록시 클래스를 생성합니다. 이러한 것들은 AOP 프록시 클래스라고 합니다. 또한 Spring AOP 프로젝트에 종속성으로 추가하여 CGLIB 프록시를 사용할 수도 있습니다.
- 조각집: 이는 다른 객체들과 어드바이스를 링크하여 조언된 프록시 객체를 생성하는 프로세스입니다. 이것은 컴파일 시간, 로드 시간 또는 실행 시간에 수행될 수 있습니다. Spring AOP는 실행 시간에 조각집을 수행합니다.
AOP 어드바이스 유형
어드바이스의 실행 전략에 따라 다음과 같은 유형이 있습니다.
- 이전 어드바이스: 이러한 어드바이스는 조인 포인트 메서드의 실행 전에 실행됩니다. 어드바이스 유형을 이전 어드바이스로 표시하려면
@Before
주석을 사용할 수 있습니다. - 이후 (마지막) 어드바이스: 조인 포인트 메서드의 실행이 종료된 후에 실행되는 어드바이스로, 정상적으로 또는 예외를 throw하여 종료됩니다.
@After
주석을 사용하여 후에 어드바이스를 만들 수 있습니다. - 리턴 후 어드바이스: 때로는 조인 포인트 메서드가 정상적으로 실행될 경우에만 어드바이스 메서드를 실행하려고 합니다. 리턴 후 어드바이스로 메서드를 표시하려면
@AfterReturning
주석을 사용할 수 있습니다. - 조언을 던진 후: 이 조언은 조인 포인트 메소드가 예외를 던질 때에만 실행되며, 우리는 이를 선언적으로 트랜잭션 롤백에 사용할 수 있습니다. 이 유형의 조언에는
@AfterThrowing
주석을 사용합니다. - 주변 조언: 이것은 가장 중요하고 강력한 조언입니다. 이 조언은 조인 포인트 메소드를 둘러싸며, 우리는 또한 조인 포인트 메소드를 실행할지 여부를 선택할 수 있습니다. 우리는 조인 포인트 메소드 실행 전후에 실행될 조언 코드를 작성할 수 있습니다. 주변 조언은 조인 포인트 메소드를 호출하고, 메소드가 반환하는 경우 값을 반환하는 것이 주요 역할입니다. 주변 조언 메소드를 만들기 위해
@Around
주석을 사용합니다.
위에서 언급한 사항들은 혼란스러울 수 있지만, Spring AOP의 구현을 살펴보면 더욱 명확해질 것입니다. AOP 구현을 갖춘 간단한 Spring 프로젝트를 만들어 봅시다. Spring은 AspectJ 주석을 사용하여 어드바이스를 생성하는 것을 지원하며, 우리는 그것을 간단하게 사용할 것입니다. 위에서 언급한 모든 AOP 주석은 org.aspectj.lang.annotation
패키지에 정의되어 있습니다. Spring Tool Suite는 어드바이스에 대한 유용한 정보를 제공하므로 사용하는 것이 좋습니다. STS에 익숙하지 않다면, Spring MVC Tutorial을 살펴보는 것을 추천합니다. 거기에서 그것을 사용하는 방법을 설명했습니다.
Spring AOP 예제
새로운 간단한 Spring Maven 프로젝트를 만들어서 모든 Spring Core 라이브러리가 pom.xml 파일에 포함되어 있도록 하고, 명시적으로 포함할 필요가 없도록 합니다. 최종 프로젝트는 아래 이미지와 같이 구성됩니다. Spring 코어 컴포넌트와 Aspect 구현에 대해 자세히 살펴보겠습니다.
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>
aspectjrt 및 aspectjtools 종속성(버전 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;
}
}
I could have used Spring annotations to configure it as a Spring Component, but we will use XML based configuration in this project. EmployeeService class is very standard and just provides us an access point for Employee beans.
AOP와 함께의 스프링 빈 구성
STS를 사용하는 경우에는 “Spring 빈 구성 파일”을 생성하고 AOP 스키마 네임스페이스를 선택할 수 있지만 다른 IDE를 사용하는 경우에는 간단히 spring 빈 구성 파일에 추가할 수 있습니다. 내 프로젝트 빈 구성 파일은 다음과 같습니다. spring.xml:
<?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 빈에서 Spring AOP를 사용하려면 다음을 수행해야 합니다.
- AOP 네임스페이스를 다음과 같이 선언합니다. xmlns:aop=“https://www.springframework.org/schema/aop”
- aop:aspectj-autoproxy 요소를 추가하여 런타임에서 Spring AspectJ 지원을 자동으로 프록시화합니다.
- Aspect 클래스를 다른 Spring 빈으로 구성합니다.
당신은 나에게 스프링 빈 구성 파일에서 많은 측면을 정의했다는 것을 알 수 있습니다. 이제 하나씩 자세히 살펴보는 시간입니다.
스프링 AOP Before Aspect 예제
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 메서드에 대해 실행됩니다. 이는 기억해야 할 매우 중요한 포인트입니다. new 연산자를 사용하여 Employee 빈을 생성하면 어드바이스가 적용되지 않습니다. 빈을 가져오려면 ApplicationContext를 사용해야 어드바이스가 적용됩니다. - Pointcut 표현식에서 와일드카드로 별표 (*)를 사용할 수 있습니다. getAllAdvice()는
com.journaldev.spring.service
패키지에 속하며 이름이get
으로 시작하고 인수를 사용하지 않는 모든 클래스에 적용됩니다.
우리는 모든 다양한 어드바이스 유형을 살펴본 후에 테스트 클래스에서 어드바이스가 어떻게 작동하는지 살펴보겠습니다.
Spring AOP 포인트컷 메소드 및 재사용
가끔은 동일한 포인트컷 표현을 여러 곳에서 사용해야 할 때가 있습니다. 우리는 @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("within(com.journaldev.spring.service.*)")
public void allMethodsPointcut(){}
}
위의 예제는 매우 명확합니다. 표현식 대신 어드바이스 어노테이션 인자에서 메소드 이름을 사용하고 있습니다.
Spring AOP JoinPoint 및 Advice 인자
우리는 JoinPoint를 어드바이스 메소드의 매개변수로 사용하고, 이를 통해 메소드 서명 또는 대상 객체를 얻을 수 있습니다. 우리는 포인트컷에 args()
표현식을 사용하여 인자 패턴과 일치하는 모든 메소드에 적용할 수 있습니다. 이를 사용하면 어드바이스 메소드에서 인자 유형을 결정하는 곳과 같은 이름을 사용해야 합니다. 어드바이스 인자로는 일반 객체도 사용할 수 있습니다. 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()));
}
// 어드바이스 인자, 단일 문자열 인자를 갖는 빈 메소드에 적용될 것입니다
@Before("args(name)")
public void logStringArguments(String name){
System.out.println("String argument passed="+name);
}
}
Spring AOP 후 어드바이스 예시
간단한 측면 클래스와 후, 후에 발생하는 예와 후 반환 어드바이스의 예를 살펴보겠습니다. 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 어드바이스를 사용하여 조언된 메서드에서 반환된 객체를 얻을 수 있습니다. 우리는 Employee 빈에 throwException() 메서드가 있어 After Throwing 어드바이스의 사용을 쇼케이스할 수 있습니다.
Spring AOP Around 측면 예시
이전에 설명한 대로, 우리는 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를 인수로 사용하여 어라운드 어드바이스를 작성해야하며, 대상 객체를 호출하는데 proceed() 메서드를 사용해야합니다. 어드바이스된 메서드가 무언가를 반환하는 경우, 호출자 프로그램으로 반환하는 것은 어드바이스의 책임입니다. void 메서드의 경우, 어드바이스 메서드는 null을 반환할 수 있습니다. 어라운드 어드바이스가 어드바이스된 메서드를 중간에서 가로채기 때문에 메서드의 입력과 출력 및 실행 동작을 제어할 수 있습니다.
사용자 정의 어노테이션 포인트컷과 함께하는 스프링 어드바이스
위의 모든 조언 포인트컷 표현식을 살펴보면, 의도되지 않은 다른 빈에 적용될 수 있다는 가능성이 있습니다. 예를 들어, 누군가가 getName() 메서드를 가진 새로운 스프링 빈을 정의하면 의도하지 않은 상황에서도 어드바이스가 적용되기 시작할 수 있습니다. 그래서 포인트컷 표현식의 범위를 가능한 한 좁게 유지해야 합니다. 대안적인 접근 방식은 사용하려는 어드바이스를 적용하고자 하는 메서드에 사용자 정의 어노테이션을 생성하는 것입니다. 이것이 Employee의 setName() 메서드에 @Loggable 어노테이션을 붙이는 목적입니다. Spring Framework의 @Transactional 어노테이션은 이 접근 방식에 대한 좋은 예입니다. 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 어노테이션을 붙이면 됩니다.
Spring AOP XML 구성
I always prefer annotation but we also have the option to configure aspects in the spring configuration file. For example, let’s say we have a class as below. EmployeeXMLConfigAspect.java code:
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>
Here is your text translated into Korean with the custom delimiters preserved:
AOP XML 구성 요소의 목적은 이름에서 명확하기 때문에 그에 대해 자세히 다루지 않겠습니다.
Spring AOP 예제
간단한 Spring 프로그램을 만들어 모든 이러한 측면이 빈 메서드를 통과하는 방법을 살펴보겠습니다. 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)
포인트컷 구성에 기반하여 각각의 어드바이스가 순차적으로 실행되는 것을 볼 수 있습니다. 혼란을 피하려면 하나씩 구성해야 합니다. 이것으로 Spring AOP 예제 튜토리얼은 모두입니다. Spring과 함께 AOP의 기본을 배우고 예제에서 더 많이 배울 수 있기를 바랍니다. 아래 링크에서 샘플 프로젝트를 다운로드하여 실험해 보세요.