본문 바로가기
Back-end/Java 개념

AOP(Aspect Oriented Programming) 개념과 구현

by devraphy 2023. 7. 24.

0. 개요

- 토비의 스프링 6장을 읽다보면 AOP에 대한 이야기가 나온다.

- 해당 부분에서 Proxy가 왜 등장하는지, AOP와 proxy가 무슨 관계가 있는지 등 이해할 수 없는 부분이 너무 많았다.

- 즉, 내가 AOP에 대해 모르고 있기에 이해할 수 없다는 것을 깨닫고 AOP에 대해서 정리해보기로 했다. 

- 나와 같이 AOP 기본개념에 대해 모르는 분들에게 도움이 되기를 바란다.

 

1. AOP와 Proxy의 상관관계

- AOP 이야기를 할때면 프록시 이야기가 빠지지 않고 나온다. 

- 왜 그럴까? AOP와 프록시가 무슨 관계가 있는 것일까? 아니 그보다 프록시란 정확하게 무엇인가? 

- 이에 대해서 이해해보자. 

 

a) Proxy가 뭔데?

- 프록시는 어떤 역할을 대신 수행하는 대상을 가리킨다.

 

- 네트워크에서는 프록시 서버라는 개념으로 프록시가 등장하며, 이는 클라이언트와 서버 사이에서 중계자 역할을 하게된다.

- JPA에서도 프록시 객체라는 개념으로 프록시가 등장한다. 연관 관계가 있는 객체의 정보를 받아올 때, EAGER 방식으로 가져오는 것이 아니라 LAZY 방식으로 가져온다면 연관 관계를 갖는 객체의 값을 임의의 값으로 대체하는 역할을 수행한다.

 

- 이처럼 프록시는 무언가를 대신해주는 역할인 것이다.

- 그렇다면 AOP와 프록시는 무슨 관계가 있는 것일까? 

 

b) AOP에서 프록시가 등장하는 이유 

- 쉽게 설명하자면, AOP는 어떤 메서드(로직)의 실행에 있어서 전처리 또는 후처리를 담당하는 기능이다. 

- AOP는 모든 클래스나 메서드 또는 특정 클래스나 메서드에만 분리되어 적용될 수도 있는 공통적인 역할을 수행한다.

 

- 그렇다면 생각해보자. 어떤 메서드가 호출되면 메서드 실행 이전에 해당 메서드의 이름을 로그로 찍는 AOP를 작성한다고 했을때, 로그를 찍는 작업을 작업을 누군가 대신 수행해줘야 하지 않을까? 마치 스프링의 DI처럼 말이다. 

 

- 즉, 프록시가 AOP에 작성된 기능을 가지고 있는 것이고 프록시를 이용하여 메서드 실행 전 또는 실행 후에 특정한 기능이 작동하도록 하나의 새로운 흐름을 만드는 것이다.

 

- 결국 어떤 역할을 대신 수행하는 것으로 프록시에 필요할 수 밖에 없다. 

https://youtu.be/y2JkXjOocZ4

 

c) Core / Cross-cutting Concern

- Core concern은 비즈니스 로직을 의미한다.

- Cross-cutting Concern은 비즈니스 로직의 앞, 뒤에 삽입되는 AOP 로직을 의미한다.

- Cross-cutting이라고 표현하는 이유는 위에서 아래로 흐르는 비즈니스 로직의 흐름을 가로질러 AOP 로직이 추가되는 형태로 AOP가 작동하기 때문이다. 그래서 가로지른다는 의미의 Cross-cutting이라는 표현이 사용되는 것이다. 

 

d) Target

- AOP에서 target이라는 개념이 자주 등장하는데, 이는 AOP가 적용될 대상을 의미한다.

- AOP가 적용되는 대상은 어떤 클래스의 모든 메서드가 될 수도, 특정 메서드가 될 수도 있다. 

 

2. Java만으로 AOP 구현해보기

a) 예시코드

- 다음과 같은 인터페이스와 클래스가 있다. 해당 클래스는 AOP의 Target으로 사용될 예정이다. 

public interface BusinessInterface {
    String introduction();
}
public class BusinessClass implements BusinessInterface {

    @Override
    public String introduction() {
        return "비즈니스 로직입니다.";
    }

    public BusinessClass() {}
}

 

- AOP를 이용하여 다음과 같은 로직의 흐름을 만들어보고자 한다.

public class BusinessClass implements BusinessInterface {

    // AOP를 이용하여 "AOP 입니다"를 출력하고 아래의 메서드가 출력된다. 
    
    @Override
    public String introduction() {
        return "비즈니스 로직입니다.";
    }

    public BusinessClass() {}
}

 

b) 로직 설계 및 작성

- 이제 Proxy를 만들어서 "AOP 입니다"를 출력하는 메서드를 작성하고, Proxy에서 해당 메서드를 실행한 뒤에 businessLogic() 메서드를 실행하도록 설계하면 되는 것이다. 

 

- 이는 다음과 같이 작성할 수 있다. (Main 메서드에 작성하였다.)

import org.example.aop.BusinessClass;
import org.example.aop.BusinessInterface;

import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Main {

    public static void main(String[] args) throws IOException {

        BusinessInterface business = new BusinessClass();

        BusinessInterface proxy = (BusinessInterface) Proxy
                .newProxyInstance(BusinessClass.class.getClassLoader(), 
                        new Class[]{BusinessInterface.class}, 
                        new InvocationHandler() {
                    
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        System.out.println("AOP 입니다.");

                        // invoke에서 AOP가 대신 비즈니스 메서드를 호출한다.
                        return method.invoke(business, args);
                    }
                }
        );
        System.out.println(proxy.introduction());
    }
}

 

- newProxyInstance()메서드는 다음과 같은 매개변수를 받는다. 

- 여기서 interface를 이용하여 Proxy가 생성되는 것은 OCP를 지키기 위한 것입니다. 

 

c) 실행 결과

- 실행결과는 다음과 같다.

 

3. Spring을 이용하여 AOP 만들기

- 위에서는 Java만을 이용하여 AOP를 만들어 적용해보았다.

- Spring에서 어떻게 AOP를 구현하는지 알아보자! 

 

a) XML을 이용한 AOP 설정

- 우선 XML코드는 다음과 같이 작성됩니다. 

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

	<!-- target 등록 -->
    <bean id="target" class="com.example.aop.entity.BusinessClass"/> 
    
    <!-- Handler 등록부 -->
    <bean id="logAroundAdvice" class="com.example.aop.advice.LogAroundAdvice"/> 
    
    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    	<!-- name = setter의 이름, ref = 참조 객체명-->
        <property name="target" ref="target"/>  
        
        <property name="interceptorNames">
            <list>
            	<!-- AOP에서 실행될 handler의 이름-->
                <value>logAroundAdvice</value> 
            </list>
        </property>
    </bean>

</beans>

 

- target: AOP의 적용 대상을 Bean으로 등록합니다.

- logAroundAdvice: AOP가 실행할 메서드를 가지고 있는 Handler를 Bean으로 등록합니다. 참고로 이 이름은 proxy에서 객체의 이름으로 사용될 것이기에 클래스 이름처럼 대문자로 시작하는 것이 아니라 객체 이름으로 소문자로 등록합니다. 

- proxy: Advice와 target의 메서드를 대신 수행할 proxy를 등록하고, 해당 proxy가 실행할 handler의 이름을 등록합니다. 

 

b) Handler 예시코드

- 위에서 핸들러의 역할을 하는 LogAroundAdvice 클래스의 코드입니다. 

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class LogAroundAdvice implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {

        // AOP 코드를 작성부
        long start = System.currentTimeMillis();

        // 비즈니스 로직이 실행부
        Object result = invocation.proceed(); // invoke 역할을 하는 메서드

        long end = System.currentTimeMillis();

        String message = (end - start) + "ms초가 걸렸습니다.";
        System.out.println("Around AOP 로직입니다. " + message);

        return result;
    }
}

- 위의 코드는 비즈니스 로직이 실행되는 시간을 측정하는 기능을 AOP로 적용해볼 것입니다. 

 

c) Target 클래스 예시코드

- AOP가 실행할 Core-concern(비즈니스 로직)을 담고있는 클래스의 코드입니다.

public class BusinessClass implements BusinessInterface {

    @Override
    public String introduction() {

        for(int i = 0; i < 1000000000; i++) {
            // 시간 측정을 위한 for문
        }
        return "비즈니스 로직입니다.";
    }

    public BusinessClass() { /* TODO document why this constructor is empty */ }
}

 

d) main() 예시코드

- 아래는 main 함수에서 proxy Bean을 불러와 실행시키는 코드입니다. 

public class AopApplication {

	public static void main(String[] args) {

	   ApplicationContext context
				= new FileSystemXmlApplicationContext("src/main/java/com/example/aop/setting.xml");

		BusinessInterface proxy = (BusinessInterface)context.getBean("proxy");
		System.out.println(proxy.introduction());

		SpringApplication.run(AopApplication.class, args);
	}

}

 

e) 실행 결과

- Java로 AOP를 만들었을때에 비해서 main() 함수의 코드가 훨씬 깔끔해졌죠? 

- 이렇게 작성된 코드를 실행하면 다음과 같이 결과가 출력되는 것을 확인할 수 있습니다.

 

4. 다양한 AOP 적용시점 설정하기

- 현재까지 작성한 AOP코드는 모두 비즈니스 로직 이전에 AOP에 작성된 로직이 실행되는 흐름이었습니다.

- 그렇다면 비즈니스 로직 이후에 작성해야한다면 어떻게 해야할까요? 

- 이를 위해 AOP는 다음과 같은 다양한 적용 시점을 설정할 수 있습니다.

  • Around - 메소드 호출 자체를 가로채서 타겟 메소드 실행 전&후 모두에 처리할 로직을 삽입할 수 있음(설정하기 나름)
  • Before - 타겟 메서드 실행 이전 실행된다.
  • After returning - 타겟 메서드가 실행된 이후에 실행된다.
  • After throwing - 타겟 메서드가 예외를 던지는 경우 실행된다.

 

a) Before Advice

- 다음과 같은 클래스를 생성해보자.

import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;

public class BeforeAdvice implements MethodBeforeAdvice {

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {

        // method - 현재 호출되고 있는 함수의 정보를 얻고 싶은 경우 사용한다.(이름, 파라미터 등)
        // target - target에 대한 객체를 이용하고 싶은 경우 사용한다. 
        System.out.print("Before Advice 입니다.");
    }
}

 

- 이전과 동일하게 setting.xml에 등록해준다. 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	<!-- target 등록부 -->
    <bean id="target" class="com.example.aop.entity.BusinessClass"/> 
    
    <!-- Handler 등록부 -->
    <bean id="logAroundAdvice" class="com.example.aop.advice.LogAroundAdvice"/> 
    <bean id="beforeAdvice" class="com.example.aop.advice.BeforeAdvice"/> 
    
    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    	<!-- name = setter의 이름, ref = 참조 객체명-->
        <property name="target" ref="target"/>  
        <property name="interceptorNames">
            <list>
            	<!-- AOP에서 실행될 handler의 이름-->
                <value>logAroundAdvice</value> 
                <value>beforeAdvice</value>
            </list>
        </property>
    </bean>

</beans>

 

- 이제 실행결과를 확인해보자. 

 

b) After-Returning

- AfterReturning은 비즈니스 로직이 반환 값을 성공적으로 반환 했을때 동작하는 AOP이다.

- 이 반환된 값은 AOP 프록시에게 전달된다.

 

- AfterReturning 클래스의 코드는 다음과 같다. 

import java.lang.reflect.Method;

public class AfterReturningAdvice implements org.springframework.aop.AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        // returnValue - 비즈니스 로직에서의 반환 값
        System.out.print("After Returning 입니다. ==> ");
        System.out.println("returnValue: " + returnValue + ", method.getName(): " + method.getName());
    }
}

 

- 실행 결과는 다음과 같다. 

- 비즈니스 로직이 우선적으로 실행됐기 때문에 AfterReturning 위처럼 반환 값음 출력할 수 있었겠죠?

- 다만 return의 순서로 인해 비즈니스 로직보다 After Returning AOP가 더 빠르게 실행된 것처럼 보일 뿐입니다.  

 

c) After throwing

-AfterThrowing 클래스의 코드는 다음과 같다.

import org.springframework.aop.ThrowsAdvice;

public class AfterThrowingAdvice implements ThrowsAdvice {

    // Override 메서드가 없는 이유는 예외에 따라 실행되는 메서드가 달라질 수 있기 때문이다.
    public void afterThrowing(IllegalArgumentException e) throws Throwable {
        System.out.println("AfterThrowing Advice::: IllegalArgumentException");
    }

    // 이런식으로 예외 케이스를 여러개 등록하여 사용할 수 있다.
    public void afterThrowing(NullPointerException e) throws Throwable {
        System.out.println("AfterThrowing Advice::: NullPointerException");
    }
}

 

- main에서 다음과 같이 의도적으로 Exception을 발생시켜보자. 

public class BusinessClass implements BusinessInterface {

    @Override
    public String introduction() {

        for(int i = 0; i < 1000000000; i++) {
            // 시간 측정을 위한 for문

            if(i == 500000000) {
                throw new IllegalArgumentException();
            }
        }
        return "비즈니스 로직입니다.";
    }

    public BusinessClass() { /* TODO document why this constructor is empty */ }
}

 

- 실행 결과는 다음과 같다.

 

5. Point Cut 개념

- Spring AOP를 이야기할때는 Point cut, Join point, Weaving이라는 용어가 자주 등장한다.

- 우선은 단어의 의미에 대해서 하나씩 알아보도록 하자. 

 

a) Weaving

- Weaving이란 AOP가 동작하는 전체 흐름의 과정을 의미한다.

- Target을 설정하고 Proxy를 연동하여 AOP가 실행된 후 Business 로직이 실행되거나, Business 로직이 실행된 후 AOP가 실행되는 일련의 과정을 Weaving이라고 한다. 

- 이 과정을 보면 Proxy와 Target이 서로 얽히고 설켜서 동작하는 것을 알 수 있는데, 이러한 모습을 보고 Weaving(뜨개질)이라고 표현하는 것이다. 

 

b) Join Point

- Join Point는 Proxy에 의해 실행되는, AOP 안에 끼워지는 Target의 메서드를 의미한다.

- 즉 AOP에서 실행되는 Business 로직을 의미하는 것이다. 

 

c) Point cut

- Target의 모든 메서드를 대상으로 AOP를 실행하는 것이 아니라, 특정 AOP가 특정 Business 로직에만 적용되도록 하고 싶을 때가 있다.

- 즉 특정 Buisness 로직(메서드)에만 Weaving이 되도록 설정하는데 필요한 정보를 의미한다. 

- Join Point를 잘라낸다, 경계를 둔다는 의미에서 Cut이라는 단어로 표현된다. 

- 이처럼 특정 Business 로직에만 AOP가 적용될 수 있도록 설정하는 설정 정보를 Point cut이라고 부른다. 

 

6. Point Cut 구현

a) Point cut 기본 설정

- 아래 코드는 setting.xml 파일이다.

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

	<!-- target 등록부 -->
    <bean id="target" class="com.example.aop.entity.BusinessClass"/>
    
    <!-- Handler 등록부 -->
    <bean id="logAroundAdvice" class="com.example.aop.advice.LogAroundAdvice"/>
    <bean id="beforeAdvice" class="com.example.aop.advice.BeforeAdvice"/>
    <bean id="afterReturning" class="com.example.aop.advice.AfterReturningAdvice"/>
    <bean id="afterThrowing" class="com.example.aop.advice.AfterThrowingAdvice"/>

    <!-- 포인트컷 설정 부-->
    <bean id="basicPointCut" class="org.springframework.aop.support.NameMatchMethodPointcut">
        <property name="mappedName" value="basicPointCutMethod"/>
    </bean>
    <bean id="classBeforeAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
        <property name="advice" ref="beforeAdvice"/>
        <property name="pointcut" ref="basicPointCut"/>
    </bean>

    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    	<!-- name = setter의 이름, ref = 참조 객체명-->
        <property name="target" ref="target"/>  
        <property name="interceptorNames">
            <list>
                <!-- AOP에서 실행될 handler의 이름-->
                <value>logAroundAdvice</value>
                <value>classBeforeAdvisor</value>
                <value>afterReturning</value>
                <value>afterThrowing</value>
            </list>
        </property>
    </bean>
</beans>

- 위의 설정정보에서 주목해야하는 부분은 basicPointCut 그리고 classBeforeAdvisor 이다.

 

  • basicPointCut
    - basicPointCut은 포인트 컷이 사용될 타겟 메서드를 지정하는 역할을 한다.
    - basicPointCut에는 basicPointCutMethod() 라는 이름의 메서드가 연결된 것을 확인할 수 있다. 

  • classBeforeAdvisor
    - 이름 그대로 어드바이저의 역할을 한다.
    - Point cut을 사용하기 위해 해당 Point cut이 어떤 AOP와 연결될 것인지를 입력한다.
    - 위의 설정정보를 보면 Before 시점에 basicPointCut이라는 타겟이 실행된다는 것을 확인할 수 있다.

  • Proxy 설정 부분
    - 가장 중요한 것은 Point cut으로 사용하기위해 만든 classBeforeAdvisor를 AOP의 handler로 등록하는 것이다.
    - proxy 설정정보 내부에 AOP에서 실행될 handler를 등록해놓은 부분을 보면 기존의 beforeAdvice 대신 classBeforeAdvisor가 들어간 것을 확인할 수 있다.  

 

b) Point cut 메서드 구현

- 그렇다면 basicPointCutMethod() 라는 메서드가 타겟에 있어야한다.

- 이를위해 아래와 같이 타겟에 메서드를 추가해주었다. 

public interface BusinessInterface {
    String introduction();
    String basicPointCutMethod();
}
public class BusinessClass implements BusinessInterface {

    @Override
    public String introduction() {
        for(int i = 0; i < 1000000000; i++) {
            // 시간 측정을 위한 for문
        }
        return "비즈니스 로직입니다.";
    }

    @Override
    public String basicPointCutMethod() {
        return "BASIC 포인트컷 입니다.";
    }

    public BusinessClass() {}
}

 

c) main() 코드

- main() 메서드에서 다음과 같이 추가적으로 메서드를 호출해준다. 

public static void main(String[] args) {

   ApplicationContext context
        = new FileSystemXmlApplicationContext("src/main/java/com/example/aop/setting.xml");

   BusinessInterface proxy = (BusinessInterface)context.getBean("proxy");
   System.out.println(proxy.introduction());
   System.out.println(proxy.basicPointCutMethod());

   SpringApplication.run(AopApplication.class, args);
}

 

d) 실행결과

- 실행 결과는 다음과 같다.

 

e) 개선사항

- 이처럼 Point cut을 만드는 방법을 알아보았다.

- 다만, 이 방식에는 중복코드가 작성된다는 단점이 있다.

- 새로운 Point cut을 적용할 때마다 Advisor를 계속 작성해야한다는 불편함이 있는 것이다.

- 이 포스팅의 마지막으로 이를 어떻게 개선시킬 수 있는지 확인해보자. 

 

7. 개선된 Advisor

- 위에서 Point cut을 여러개 만들기 위해서는 Advisor를 여러개 만들어야했다.

- 이번에는 하나의 Advisor를 이용해서 여러개의 Point cut을 관리할 수 있는 방법에 대해 알아보자. 

 

a) NameMatchMethodPointcutAdvisor

- NameMatchMethodPointcutAdvisor는 Point cut과 Advisor 매핑을 동시에 할 수 있도록 만들어졌다.

- 이는 다음과 같이 setting.xml 파일에서 설정할 수 있다. 

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

	<!-- target 등록부 -->
    <bean id="target" class="com.example.aop.entity.BusinessClass"/>
    
    <!-- Handler 등록부 -->
    <bean id="logAroundAdvice" class="com.example.aop.advice.LogAroundAdvice"/>
    <bean id="beforeAdvice" class="com.example.aop.advice.BeforeAdvice"/>
    <bean id="afterReturning" class="com.example.aop.advice.AfterReturningAdvice"/>
    <bean id="afterThrowing" class="com.example.aop.advice.AfterThrowingAdvice"/>

    <!-- 포인트컷 설정 부-->
<!--    <bean id="basicPointCut" class="org.springframework.aop.support.NameMatchMethodPointcut">-->
<!--        <property name="mappedName" value="basicPointCutMethod"/>-->
<!--    </bean>-->
<!--    <bean id="classBeforeAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">-->
<!--        <property name="advice" ref="beforeAdvice"/>-->
<!--        <property name="pointcut" ref="basicPointCut"/>-->
<!--    </bean>-->

    <!-- 개선된 포인트컷 설정 부-->
    <bean id="classBeforeAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
        <property name="advice" ref="beforeAdvice"/>
        <property name="mappedNames">
            <list>
                <value>basicPointCutMethod</value> <!-- 메서드 이름 -->
            </list>
        </property>
    </bean>
    
    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    	<!-- name = setter의 이름, ref = 참조 객체명-->
        <property name="target" ref="target"/>  
        <property name="interceptorNames">
            <list>
                <!-- AOP에서 실행될 handler의 이름-->
                <value>logAroundAdvice</value>
                <value>classBeforeAdvisor</value>
                <value>afterReturning</value>
                <value>afterThrowing</value>
            </list>
        </property>
    </bean>
</beans>

 

b) 실행 결과

- 아래와 같이 실행결과가 잘 나오는 것을 확인할 수 있다.

 

c) RegexpMethodPointcutAdvisor

- 이 뿐만 아니라 정규식을 이용하여 다수의 method를 point cut에 등록하는 방법도 있다.  

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

	<!-- target 등록부 -->
    <bean id="target" class="com.example.aop.entity.BusinessClass"/>
    
    <!-- Handler 등록부 -->
    <bean id="logAroundAdvice" class="com.example.aop.advice.LogAroundAdvice"/>
    <bean id="beforeAdvice" class="com.example.aop.advice.BeforeAdvice"/>
    <bean id="afterReturning" class="com.example.aop.advice.AfterReturningAdvice"/>
    <bean id="afterThrowing" class="com.example.aop.advice.AfterThrowingAdvice"/>

    <!-- 포인트컷 설정 부-->
<!--    <bean id="basicPointCut" class="org.springframework.aop.support.NameMatchMethodPointcut">-->
<!--        <property name="mappedName" value="basicPointCutMethod"/>-->
<!--    </bean>-->
<!--    <bean id="classBeforeAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">-->
<!--        <property name="advice" ref="beforeAdvice"/>-->
<!--        <property name="pointcut" ref="basicPointCut"/>-->
<!--    </bean>-->

    <!-- 개선된 포인트컷 설정 부-->
<!--    <bean id="classBeforeAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">-->
<!--        <property name="advice" ref="beforeAdvice"/>-->
<!--        <property name="mappedNames">-->
<!--            <list>-->
<!--                <value>basicPointCutMethod</value> &lt;!&ndash; 메서드 이름 &ndash;&gt;-->
<!--            </list>-->
<!--        </property>-->
<!--    </bean>-->

    <!-- Regex패턴을 이용한 포인트컷 설정 부-->
    <bean id="classBeforeAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="advice" ref="beforeAdvice"/>
        <property name="patterns">
            <list>
                <value>.*basic.*</value> <!-- 정규식(basic 이름이 들어가는 메서드)-->
            </list>
        </property>
    </bean>

    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    	<!-- name = setter의 이름, ref = 참조 객체명-->
        <property name="target" ref="target"/>  
        <property name="interceptorNames">
            <list>
                <!-- AOP에서 실행될 handler의 이름-->
                <value>logAroundAdvice</value>
                <value>classBeforeAdvisor</value>
                <value>afterReturning</value>
                <value>afterThrowing</value>
            </list>
        </property>
    </bean>
</beans>

 

8. 마무리하며

- 지금까지 설명한 방법 보다는 Annotation을 이용하여 AOP를 쉽게 구현할 수 있다.

- 다만, AOP의 근본적인 동작 방식을 이해한다면 AOP를 더욱 잘 활용할 수 있을 것이다.

- Annotation을 사용한 AOP 구현이 궁금하다면 직접 찾아서 공부해보자. 

- 여기까지 AOP의 기본 동작과정에 대해서 알아보았다.

댓글