Back/Spring
Spring - AOP(Aspect Oriented Programming)
Hyeon_
2022. 1. 5. 17:26
AOP 개요
AOP (Aspects Oriented Programming)
- 관점 지향 프로그래밍
- 문제를 바라보는 관점을 기준으로 프로그래밍하는 기법
- 문제를 해결하기 위한 '핵심 관심 사항'과 전체에 적용되는 '공통 관심 사항'을 기준으로 프로그래밍
- 공통 모듈을 여러 코드에 쉽게 적용 가능
- AOP에서 가장 중요한 개념
- '횡단 관점의 분리(Separation of Cross-Cutting Concern)'
AOP와 횡단 관점
공통 기능
- 로그 처리
- 보안 처리
- 트랜잭션 처리
AOP 용어
- Aspect : 공통 기능(공통 코드)
- Advice
- Aspect의 주요 내용(공통 기능 안의 세부적인 내용들).
- 횡단 관심모듈 자체(What)
- 핵심 코드에 삽입되어 동작하는 코드와 시험
- Adivice 시점 : before/after/after-throwing/after-returning, around
- Joinpoint
- Advice 적용 시점(언제(When) 횡단 관심 모듈을 삽입할지를 정의)
- 스프링에서는 froxy 기반 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가 횡단 관심사 코드가 삽입된 바이너리 코드 제공
- 런타임 시 위빙 하기
- 소스 코드나 바이너리 파일의 변경 없이 froxy를 이용하여 AOP를 지원하는 방식
- Froxy를 통해 핵심 관심사를 구현한 객체에 접근하게 되는데, Froxy가 핵심 관심사 실행 전후에 횡단 관심사 실행
- 따라서 froxy 기반의 런타임 시 위빙 방법은 메소드 호출 시에만 AOP 적용 가능
스프링에서 AOP 구현 방식
- 라이브러리 의존성(dependency) 추가
- 공통 기능의 클래스 생성 - Advice 역할 클래스
- XML 설정 파일에 Aspect 설정
- application-config.xml
- 프로젝트 생성
- 클래스
- Gugudan
- Rect
- PerformanceAspect
- MainClass
Gugudan
// 핵심 기능 수행
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<10;i++) {
System.out.println(dan + " * " + i + " = " + (dan*i));
}
}
}
Rect
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)));
}
}
PerformanceAspect
// 공통 기능에 사용할 클래스 (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));
System.out.println("------------------------------------------------");
return result;
}
}
MainClass
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 gugudan = context.getBean("gugudan", Gugudan.class);
gugudan.showResult();
context.close();
}
}
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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
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="10" />
</bean>
<bean id="gugudan" class="com.aop.spring_aop_xml.Gugudan" >
<property name="dan" value="7" />
</bean>
</beans>