biz
의 여러 부분의 공통적으로 사용되는 부가적인 횡단 관심사항들을 분리해서 Aspect
라는 별도의 모듈로 설계Target : AOP 적용 대상 biz 로직
Advice : AOP 기능 type & 실행 시점
before
: biz 실행 전after
: biz 실행 후after-throwing
: biz 예외처리 AOP after-returning
: biz 리턴값 반환 후Join Point : 적용할 위치
pointcut
: advice가 적용될 지점 execution
() , within()
함수로 지점 호출pom.xml
에 필요햔 dependencies
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<!--
AOP 적용시에 필요한 추가 library
- byte code를 실시간 동적으로 자동 생성해주는 기능의 library
-->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
<!--
AOP 기능의 framework
- spring이 AOP 기술을 활용해서 spring 스럽게 활용
-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.7.3</version>
</dependency>
NoticeAspect.java : common / AOP 부가기능 로직
package common;
import org.aspectj.lang.ProceedingJoinPoint;
public class NoticeAspect {
// buy*(..) 메소드 실행전 실행
public static void beforeNotice() {
System.out.println("common 1 - 구매를 하실건가요?" );
}
// buy*(..) 메소드 실행후 실행
public static void afterNotice() {
System.out.println("common 2 - 구매 완료 하셨습니다" );
}
// 예외 발생시 공통 : 예외가 발생되면 자동 실행되는 메소드
// 발생된 예외를 parameter로 받아서 메세지값 출력
// 현 ㅣ sell ~(int)에만 적용
public void noticeException(Exception e) {
System.out.println(e.getMessage());
}
// biz 메소드가 리턴시 적용할 공통 로직이라 간주
public void noticeReturnValue(Object v) {
System.out.println("biz가 리턴한 데이터 - " + v);
}
// import org.aspectj.lang.ProceedingJoinPoint;
public Object aroundCommon(ProceedingJoinPoint point) {
System.out.println("around common 1 - 구매를 하실건가요?" );
Object v = null;
try {
v = point.proceed(); // 실제 호출된 biz 메소드 실행
} catch (Throwable e) {
System.out.println(e.getMessage()); // throwing 처리 구간
}
System.out.println("around common 2 - 구매 완료 하셨습니다" );
return v; // 핵심 로직 실행 결과값 반환
}
}
biz 로직
package step02.aop.biz;
public class Car {
public String buy() {
System.out.println("자동차 구매 buy()");
return "return test를 위한 buy";
}
public void sell(int money) throws Exception {
if(money < 10000) {
throw new Exception("너무 저렴하니 판매 불가");
}else {
System.out.println("정상 판매");
}
}
public String playdata() {
return "encore playdata";
}
}
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">
<aop:aspectj-autoproxy />
<!-- biz spring bean -->
<bean id="car2" class="step02.aop.biz.Car"/>
<!-- common spring bean -->
<bean id="common" class="common.NoticeAspect"/>
<aop:config>
<!-- biz 로직의 적용 메소드 지정 -->
<aop:pointcut id="bizLogic"
expression="execution(* step02.aop.biz.Car.buy*(..))"/>
<aop:aspect ref="common">
<!-- Advice 작성 구간 -->
</aop:aspect>
</aop:config>
</beans>
🔵
<aop:aspectj-autoproxy />
: AOP 사용을 위한 필수 설정🔵 biz 와 common 을
bean
binding 필수🔵
<aop:config></aop:config>
: AOP 설정 구간🔵
<aop:aspect ref="common"></aop:aspect>
: Advice 작성 구간
ref
: AOP bean 호출
🔹
<aop:pointcut id="bizLogic" expression="..."/>
- pointcut의 expression, 즉 advice가 적용될 지점을
id
로 참조할 수 있게 생성
🔹
expression
작성 방법
execution()
:
"execution(* step02.aop.biz.Car.buy*(..))"
Car.java
에서buy~()
라는 이름을 가짐 함수 모두에 적용
(..)
: 모든 종류의 parameter일 때 사용되는 표현 (int)로 작성시 int 데이터타입의 parameter를 1개 받는 함수에만 적용
within()
"within(step02.aop.biz.Car)"
선택된 biz 객체에 대해 모두 적용
"within(step02.aop.biz.*)"
과 같은 방식으로 1개 이상에 bean 객체 선택 가능
📌
execution
&within()
차이점 :
within()
의 최대 조회 깊이는 .java- biz 클래스 내부 메소드에 따라 구분이 불가능
- 구체적인 구분이 필요하면
execution()
사용!
<aop:config>
<aop:pointcut id="bizLogic"
expression="execution(* step02.aop.biz.Car.buy*(..))"/>
<aop:aspect ref="common">
<!-- 호출된 biz 로직 실행전용 aop -->
<aop:before method="beforeNotice" pointcut-ref="bizLogic"/>
<!-- 호출된 biz 로직 실행 후용 aop -->
<aop:after method="afterNotice" pointcut-ref="bizLogic"/>
</aop:aspect>
</aop:config>
🔵
<aop:before method="beforeNotice" pointcut-ref="bizLogic"/>
bizLogic
pointcut 기준에 맞는 함수 실행 전에beforeNotice()
함수호출🔵
<aop:after method="afterNotice" pointcut-ref="bizLogic"/>
bizLogic
: pointcut 기준에 맞는 함수 실행 전에afterNotice()
함수호출
<aop:config>
<aop:pointcut id="bizLogic"
expression="execution(* step02.aop.biz.Car.buy*(..))"/>
<aop:aspect ref="common">
<aop:after-throwing method="noticeException"
throwing="e"
pointcut="execution(* step02.aop.biz.Car.sell*(int))"/>
</aop:aspect>
</aop:config>
biz logic:
// biz 내부 throws test method
public void sell(int money) throws Exception {
if(money < 10000) {
throw new Exception("너무 저렴하니 판매 불가");
}else {
System.out.println("정상 판매");
}
}
AOP logic:
public void noticeException(Exception e) {
System.out.println(e.getMessage());
}
🔵🚩
throwing
biz
로직이 실행될 때 try/catch문에서 던지는 Exception을 AOP 로직이 받는다- AOP 로직에서 받는 Exception 변수명과 동일하게 작성
- ❕
pointcut
: 개별로 지정하고 싶을 때 바로 tag 안에 작성- ❕
pointcut-ref
: 기존<aop:pointcut/>
을 참조하고 싶을 떄id
기준으로 binding
👩💻 biz 로직이 return 값을 반환할 때
<aop:config>
<aop:pointcut id="bizLogic"
expression="execution(* step02.aop.biz.Car.buy*(..))"/>
<aop:aspect ref="common">
<aop:after-returning method="noticeReturnValue"
pointcut-ref="bizLogic"
returning="v"/>
</aop:aspect>
</aop:config>
biz logic:
public String playdata() {
return "encore playdata";
}
AOP logic:
public void noticeReturnValue(Object v) {
System.out.println("biz가 리턴한 데이터 - " + v);
}
🔵🚩
returning
biz
로직이 return 하는 값을 AOP 로직이 parameter로 받음- AOP 로직에서 받는 parameter변수명과 동일하게 작성
- ❕ parameter의 Data-type은
Object
로, 유동적인 반환을 가능하게 설계 권장(? 아마도)
👀 before
,after
,after-throwing
,after-returning
하나의 tag로 처리가 가능
<aop:config>
<aop:pointcut id="bizLogic"
expression="execution(* step02.aop.biz.Car.buy*(..))"/>
<aop:around method="aroundCommon"
pointcut="within(step02.aop.biz.*)"/>
</aop:aspect>
</aop:config>
🔵 아래 코드는 위에 하나씩 실행 했던 모든 advice를 하나의 메소드로 처리한다
public Object aroundCommon(ProceedingJoinPoint point) {
System.out.println("around common 1 - 구매를 하실건가요?" );
Object v = null;
try {
v = point.proceed(); // 실제 호출된 biz 메소드 실행
} catch (Throwable e) {
System.out.println(e.getMessage()); // throwing 처리 구간
}
System.out.println("around common 2 - 구매 완료 하셨습니다" );
return v; // 핵심 로직 실행 결과값 반환
}
ProceedingJoinPoint
- Around Advice에서 사용할 공통 기능 메서드는 라미터로 전달 받은
ProceedingJoinPoint
의proceed()
메소드로 실행
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/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<aop:aspectj-autoproxy/>
<!-- annotation 사용하겠다는 설정 -->
<context:annotation-config />
<context:component-scan base-package="common"/>
<context:component-scan base-package="step02.aop.biz"/>
</beans>
AOP:
package common;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class NoticeAspect {
@Before("execution(* step02.aop.biz.Car.buy*(..))")
public static void beforeNotice() {
System.out.println("common 1 - 구매를 하실건가요?" );
}
@After("execution(* step02.aop.biz.Car.buy*(..))")
public static void afterNotice() {
System.out.println("common 2 - 구매 완료 하셨습니다" );
}
@AfterThrowing(pointcut="execution(* step02.aop.biz.Car.sell*(int))",
throwing="e")
public void noticeException(Exception e) {
System.out.println(e.getMessage());
}
@AfterReturning(pointcut = "execution(* step02.aop.biz.Car.buy*(..))",
returning = "v")
public void noticeReturnValue(Object v) {
System.out.println("biz가 리턴한 데이터 - " + v);
}
@Around("execution(* step02.aop.biz.Car.sell*(int))")
public Object aroundCommon(ProceedingJoinPoint point) {
System.out.println("around common 1 - 구매를 하실건가요?" );
Object v = null;
try {
v = point.proceed(); // 실제 호출된 biz 메소드 실행
} catch (Throwable e) {
System.out.println(e.getMessage()); // throwing 처리 구간
}
System.out.println("around common 2 - 구매 완료 하셨습니다" );
return v; // 핵심 로직 실행 결과값 반환
}
}