(책) 자바 웹을 다루는 기술

Chap 20 스프링 AOP 기능

EunaSon 2023. 9. 15. 11:42

20.1 관점 지향 프로그래밍의 등장 792p    

20.2 스프링에서 AOP 기능 사용하기 794p


20.1 관점 지향 프로그래밍의 등장

792p

보안기능, 로깅, 트랜잭션, 예외처리, 이메일 통보 기능 등은 모든 웹 애플리케이션에서 공통으로 사용하는 기능임. 따라서 웹 애플리케이션에 주 기능을 추가할 때마다 이러한 공통 기능도 일일이 구현해주어야 하는데, 이는 배(주 기능)보다 배꼽(보조 기능)이 더 커지는 결과가 되기도 함

=> 스프링에서는 이런 문제를 관점 지향 프로그래밍으로 해결함

 

관점 지향 프로그래밍(AOP, Aspect Oriented Programming)

: 메서드 안의 주 기능과 보조 기능을 분리한 후 선택적으로 메서드에 적용해서 사용한다는 개념

전체 코드에 흩어져있는 보조 기능을 하나의 장소에 모아서 관리할 수 있음 & 보조 기능을 원하는 주 기능에 선택적으로 적용할 수 있음

 

20.2 스프링에서 AOP 기능 사용하기

794p

▼AOP 관련 용어

용어 설명
aspect 구현하고자 하는 보조 기능을 의미
advice aspect의 실제 구현체(클래스)를 의미함. 메서드 호출을 기준으로 여러 지점에서 실행됨.
joinpoint advice를 적용하는 지점을 의미함. 스프링은 method 결합점만 제공함
pointcut advice가 적용되는 대상을 지정함. 패키지이름/클래스이름/메서드이름을 정규식으로 지정하여 사용함
target advice가 적용되는 클래스를 의미함
weaving advice를 주 기능에 적용하는 것을 의미함

 

스프링 프레임워크게서 AOP 기능을 구현하는 방법

- 스프링 프레임워크에서 제공하는 API를 이용하는 방법

- @Aspect 애너테이션을 이용하는 방법

 

1. 스프링 API를 이용한 AOP 기능 구현 과정

▼스프링 API에서 제공하는 여러 가지 Advice 인터페이스

인터페이스 추상 메서드 설명
MethodBeforeAdvice void before(
  Method method,
  Object[] args,
  Object target) throws Throwable
해당 메서드가 실행되기 전 실행
- Method method : 대상 객체(target)에서 실행된 메서드를 나타내는 메서드 객체
- Object[] args : 메서드 인자 목록
- Object target : 대상 객체
AfterReturningAdvice void afterReturning(
  Object returnValue,
  Method method,
  Object[] args,
  Object target) throws Throwable)
해당 메서드가 실행된 후 실행
- Object returnValue : 대상 객체(target)의 메서드가 반환하는 값
- Method method : 대상 객체에서 실행된 메서드를 나타내는 메서드 객체
- Object[] args : 메서드 인자 목록
- Object target : 대상 객체
ThrowsAdvice void afterThrowing(
  Method method,
  Object[] args,
  Object target,
  Exception ex)
해당 메서드에서 예외 발생 시 실행
- Method method : 대상 객체(target)에서 실행된 메서드를 나타내는 메서드 객체
- Object[] args : 메서드 인자 목록
- Object target : 대상 객체
- Exception ex : 발생한 예외 타입
MethodInterceptor Object invoke(
  MethodInvocation invocation)
  throws Throwable
해당 메서드의 실행 전/후와
예외 발생 시 실행
MethodInvocation invocation : 대상 객체의 모든 정보를 담고 있는 객체(호출된 메서드, 인자 등)

이 중 인터페이스 MethodInterceptor는 invoke() 메서드를 이용해 다른 세 가지 인터페이스들의 기능을 동시에 수행할 수 있음

 

2. 스프링 API를 이용한 AOP 기능 실습

새 프로젝트 pro20을 만들고, lib 폴더 만들어서 라이브러리 클래스 패스를 설정(19장 참고)

src 패키지 아래에

com.spring.ex01 패키지 생성 후 CalcTest, Calculator, LoggingAdvice 클래스 생성,

AOP 설정 파일인 AOQTest.xml을 생성

 

[ AOPTest.xml ]

1. <bean> 태그로 타겟 빈과 어드바이스 빈을 생성

2. ProxyFactoryBean 클래스 빈 생성, <property> 태그를 이용해서 타깃 빈과 어드바이스 빈을 엮어줌

 * 사용할 어드바이스가 여러 개이면 <value> 태그로 추가하면 됨

<beans>
    <bean id="calcTarget" class="com.spring.ex01.Calculator" /> //target
    <bean id="logAdvice" class="com.spring.ex01.LoggingAdvice" /> // advice
    
    <bean id="proxyCal" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target" ref="calcTarget"/> // 타겟 빈 설정
        <property name="interceptorNames"> // 어드바이스 빈 추가
            <list>
                <value>logAdvice</value>
            </list>
        </property>
    </bean>
</beans>

 

[ Calculator 클래스] - target, 주 기능 : 계산기

add(), subtract(), multiply(), divide() 등의 메서드를 가지는 평범한 클래스

 

 

[ LoggingAdvice 클래스 ] - advice, 보조 기능 : 로깅(메서드 실행 전후 설정한 로그 출력됨)

1. 인터페이스 MethodInterceptor 를 구현함

2. invocation.proceed() 메서드를 기준으로 target의 메서드 호출 전/후를 분리하여 로그 메시지 출력함 

...
public class LoggingAdvice implements MethodInterceptor {
    public Object invoke(MethodInvocation invocation) throws Throwable {
        // invocation : target의 모든 정보를 담고 있는 객체
        System.out.println("[메서드 호출 전 : LoggingAdvice]");
        System.out.println(invocation.getMethod() + "메서드 호출 전");
        // invocation.getMethod() : target에서 호출된 메서드
        
        Object object = invocation.proceed();
        
        System.out.println("[메서드 호출 후 : LoggingAdvice]");
        System.out.println(invocation.getMethod() + "메서드 호출 후");
        
        return object;
    }
}

target에서 메서드 호출 시, 메서드 호출 전 후에 지정해둔대로 로그 메시지를 출력함.

메서드 호출 전 출력할 로그메시지는 proceed() 메서드 앞에 써주고,

호출 후 출력할 로그메시지는 proceed() 메서드 뒤에 써줌

 

[ CalcTest 클래스] - 실행 클래스

1. AOPTest.xml을 읽어들임 -> 빈을 생성(calcTarget, logAdvice, proxyCal)

2. 타깃 클래스의 메서드를 호출하면 결과 출력 전후에 어드바이스에서 설정한 로그가 출력됨

...
public class CalcTest{
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("AOPTest.xml");
        Calculator cal = (Calculator) context.getBean("proxyCal");
        // proxyCal 빈을 가져옴, 'target' 속성에 지정된 'calcTarget'으로 가져옴
        cal.add(100, 20); // target의 메서드 호출됨, 결과 출력 전후로 로그메시지 출력됨
    }
]