오봉이와 함께하는 개발 블로그

Spring - AOP 간단한 기초 설명 본문

BE/Spring

Spring - AOP 간단한 기초 설명

오봉봉이 2022. 1. 5. 22:32
728x90

AOP(Aspect Oriented Programming)

  • 관점 지향 프로그래밍
  • 문제를 바라보는 관점을 기준으로 프로그래밍 하는 기법
  • 문제를 해결하기 위한 ‘핵심 관심 사항’과 전체에 적용되는 ‘공통 관심 사항’을 기준으로 프로그래밍
  • 공통 모듈을 여러 코드에 쉽게 적용 가능
  • AOP에서 가장 중요한 개념
    • ‘횡단 관점의 분리’
    • Separation of Cross-Cutting Concern

AOP와 횡단 관점

  • 공통 기능
    • 로그 처리
    • 보안 처리
    • 트랜잭션 처리

공통 기능을 핵심 기능(비즈니스 로직)에서 분리

스프링에서 AOP 구현 방법 (Proxy 사용)

AOP 용어

  • Aspect
    • 공통 기능(공통 코드)
  • Advice
    • Aspect의 주요 내용(공통 기능 안의 세부적인 내용들)
    • 횡단 관심 모듈 자체(What)
    • 핵심 코드에 삽입되어 동작하는 코드와 시점
    • Advice 시점 : before/after/after-returning/after-throwing, around
  • JoinPoint
    • Advice 적용 시점(언제(When) 횡단 관심 모듈을 삽입할지를 정의)
    • 스프링에서는 프록시 기반 AOP를 지원하기 때문에 메소드 호출
    • JoinPoint만 지원
  • Pointcut
    • 실제 Advice가 적용되는 곳(메소드에 적용)
    • 어느 부분(Where)에 횡단 관심 모듈을 삽입 할 것인지 정의
  • Weaving
    • Advice를 핵심 코드에 삽입하는 행위

Advice 종류

  • <aop:before>
    • 핵심 기능 메소드 실행 전에 advice 실행
  • <aop:after>
    • 메소드 실행 후에 advice 실행(exception 발생해도 실행)
  • <aop:after-returning>
    • 정상적으로 메소드 실행 후에 advice 실행(exception 발생 시 실행 안됨)
  • <aop:after-throwing>
    • 메소드 실행 중 exception 발생 시 advice 실행
  • <aop:around>
    • 메소드 실행 전에/후 및 exception 발생 시 advice 실행

Weaving 방법

  • 컴파일 시 위빙하기
    • 별도 컴파일러를 통해 핵심 관심 사항 모듈 사이에 관점(Aspect) 형태로 만들어진 횡단 관심사 코드들이 삽입되어 관점(Aspect)이 적용된 최종 바이너리가 만들어 지는 방식
  • 클래스 로딩 시 위빙하기
    • 별도의 Agent를 이용하여 JVM이 클래스를 로드할 때 해당 클래스의 바이너리 정보 변경. 즉, Agent가 횡단 관심사 코드가 삽입된 바이너리 코드 제공
  • 런타임 시 위빙하기
    • 소스 코드나 바이너리 파일의 변경 없이 프록시를 이용하여 AOP를 지원하는 방식
    • 프록시를 통해 핵심 관심사를 구현한 객체에 접근하게 되는데,
    • 프록시가 핵심 관심사 실행 전후에 횡단 관심사 실행
      • 따라서 프록시 기반의 런타임 시 위빙 방법은 메소드 호출 시에만 AOP 적용 가능
    • Spring AOP 프레임워크에서 사용하는 방법

스프링에서 AOP 구현 방식

  • XML을 이용한 AOP 구현
    • 라이브러리 의존성 <dependency> 추가 : pom.xml
    • 공통 기능의 클래스 생성 - Advice 역할 클래스
  • 어노테이션을 이용한 AOP 구현
    • @Aspect
      • @Aspect 어노테이션을 이용한 AOP 구현 작업
        • 라이브러리 의존성 추가 : pom.xml <aop:aspectj-autoproxy />
    • @Pointcut
    • @Around / @Before / @After / @AfterReturning / @AfterThrowing

XML을 이용한 AOP 구현 예제 - pom.xml, application-config.xml, Gugudan.java, Rect.java, PerformanceAspect.java, MainClass.java

    <dependencies>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>
    </dependencies>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <bean id="performanceAspect" class="com.aop.spring_aop_xml.PerformanceAspect" />

    <aop:config>
        <aop:aspect id="performanceAspect" ref="performanceAspect">
            <!-- where : com.aop.spring_aop_xml 패키지 내 모든 메소드에 적용 -->
            <aop:pointcut id="timeElapse" expression="within(com.aop.spring_aop_xml.*)"/>
            <!-- when : 전 / 후, what : trace() 메소드 -->
            <aop:around method="trace" pointcut-ref="timeElapse" />
        </aop:aspect>
    </aop:config>
    <bean id="rect" class="com.aop.spring_aop_xml.Rect" >
        <property name="width" value="10" />
        <property name="height" value="20" />
    </bean>
    <bean id="gugudan" class="com.aop.spring_aop_xml.Gugudan" >
        <property name="dan" value="7" />
    </bean>
</beans>
package com.aop.spring_aop_xml;

public class Gugudan {
    private int dan;
    public int getDan() {
        return dan;
    }
    public void setDan(int dan) {
        this.dan = dan;
    }
    public void showResult() {
        for(int i = 1; i <= 9; i++) {
            System.out.println(dan + " * " + i + " = " + (dan*i));
        }
    }
}
package com.aop.spring_aop_xml;

public class Rect {
    private int width;
    private int height;

    public int getWidth() {
        return width;
    }
    public void setWidth(int width) {
        this.width = width;
    }
    public int getHeight() {
        return height;
    }
    public void setHeight(int height) {
        this.height = height;
    }
    public void showResult() {
        System.out.println("넓이 : " + (width * width));
        System.out.println("둘레 : " + (2 * (width + height)));
    }
}
package com.aop.spring_aop_xml;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;

// 공통 기능에 사용될 클래스(Proxy에 해당)
public class PerformanceAspect {
    public Object trace(ProceedingJoinPoint joinPoint) throws Throwable {
        Signature s = joinPoint.getSignature();
        String methodName = s.getName();

        System.out.println("---------------------------------------------");
        System.out.println("[Log]Before : " + methodName + "() : 실행 시작");
        System.out.println("---------------------------------------------");
        long startTime = System.nanoTime();

        Object result = null;
        try {
            result = joinPoint.proceed(); // 핵심 기능 수행
        } catch (Throwable e) {
            System.out.println("[Log]Exception : " + methodName);
        }
        long endTime = System.nanoTime();
        System.out.println("---------------------------------------------");
        System.out.println("[Log]after : " + methodName + "() : 실행 종료");
        System.out.println("[Log] : " + methodName + "() 실행 시간 : " + (endTime - startTime) + "ns");
        System.out.println("---------------------------------------------");
        return result;
    }
}
package com.aop.spring_aop_xml;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class MainClass {
    public static void main(String[] args) {
        AbstractApplicationContext context = new GenericXmlApplicationContext("application-config.xml");
        Rect rect = context.getBean("rect", Rect.class);
        rect.showResult();
        Gugudan gg = context.getBean("gugudan", Gugudan.class);
        gg.showResult();
        context.close();
    }
}
728x90
Comments