본문 바로가기

Backend/Spring | Spring Boot

AOP (Aspects Oriented Programming)

AOP (Aspects Oriented Programming)

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

AOP와 횡단 관점

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

 

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

 

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

 


AOP 용어

Advice 종류


Weaving  방법

컴파일 시 위빙하기

  • 별도 컴파일러를 통해 핵심 관심 사항 모듈 사이에 관점(Aspect) 형태로 만들어진 횡단 관심사 코드들이 삽입되어 관점(Aspect)이 적용된 최종 바이너리가 만들어 지는 방식

클래스 로딩 시 위빙하기

  • 별도의 Agent를 이용하여, JVM이 클래스를 로드할 때 해당 클래스의 바이너리 정보 변경. 즉, Agent가 횡단 관심사 코드가 삽입된 바이너리 코드 제공

런타임 시 위빙하기

  • 소스 코드나 바이너리 파일의 변경 없이 프록시를 이용하여 AOP를 지원하는 방식
  • 프록시를 통해 핵심 관심사를 구현한 객체에 접근하게 되는데,
  • 프록시가 핵심 관심사 실행 전후에 횡단 관심사 실행
  • 따라서 프록시 기반의 런타임 시 위빙 방법은 메소드 호출 시에만 AOP 적용 가능
  • Spring AOP 프레임워크에서 사용하는 방법

스프링에서 AOP 구현 방식

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

XML을 이용한 AOP 구현 예제

프로젝트 : Group Id : com.aop_project.ex / Artifact Id : aop_01
패키지 : com.aop.spring_aop_xml
pom.xml : Spring 라이브러리 추가, aspectjweaver 추가
application-config.xml : aop / context 네임스페이스 추가
클래스 : Gugudan (핵심 기능), Rect 클래스 (핵심 기능), MainClass, PerformanceAspect (공통 기능)
Gugudan 클래스와  Rect 클래스의 핵심 기능 수행 전/후에 시간 출력

 

Gugudan

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));
		}
	}
}

 

Rect

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 * height));
		System.out.println("둘레 : " + (2 * (width+height)));
	}
	
}

 

MainClass

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();
	}

}

 

PerformanceAspect

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 (Exception 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;
	}
}

 

application-config.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"
	xmlns:context="http://www.springframework.org/schema/context"
	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/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.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 expression="within(com.aop.spring_aop_xml.*)" id="timeElapse"/>
			
			<!-- 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>

'Backend > Spring | Spring Boot' 카테고리의 다른 글

Spring MVC 웹 프로젝트  (0) 2022.01.06
JSP 모델1/2 구조와 MVC 패턴  (0) 2022.01.06
Singleton  (0) 2022.01.05
DI (Dependency Injection : 의존성 주입)  (0) 2022.01.04
스프링 프레임워크 기본 개념  (0) 2022.01.04