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

<img src="https://file.podo-dev.com/blogs/images/2019/07/10/origin/E0X8AG181224235505.PNG" style="width:400px">

기능을 핵심기능과 공통기능을 분리하고,

공통기능을 필요로하는 핵심기능들에서 사용할 수 있도록 하는 프로그래밍.

<br/>

AOP 관련 용어

  • Joinpoint [조인포인트] : 핵심기능들, 메소드들, 포인트 컷에 후보

  • Pointcut [포인트컷] : 공통기능을 사용하는 특정 조건의 Joinpoint를 선정하는 것.

  • Advice[어드바이스] : 공통기능 코드

  • Weaving[위빙] : Pointcut으로 지정된 핵심 메소드가 호출될때, Advice가 삽입되는 과정

  • Aspect[에스펙트] : Pointcut + Advice. 어떤 Pointcut에 어떤 Advice를 실행할지 결정한다.

<br/>

<br/>

<br/>

AOP 원리

<img src="https://file.podo-dev.com/blogs/images/2019/07/10/origin/BYVUUM181224235505.PNG" style="width:400px">

AOP는 Proxy를 사용하여 구현된다.

핵심기능을 호출하는 Caller의 요청을 Proxy가 가로챈다

Proxy는 핵심기능을 수행하기 전/후로 공통기능을 수행한다.

<br/>

AOP 구현

다음과 같은 클래스 구조의 프로그래밍을 설계하였다.

<img src="https://file.podo-dev.com/blogs/images/2019/07/10/origin/WSDNAL181224235506.PNG" style="width:300px">

sound() 메소드 호출이 있을때, AOP를 통하여 로그를 남겨주려고 한다.

<br/>

우선 Pom.xml에 다음 라이브러리를 추가한다.

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aspects</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>
<br/>

<br/>

AppContext.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"
	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.1.xsd">

	<bean id="Cat" class="com.changoos.study.aop.Cat"></bean>

	<bean id="MyAspect" class="com.changoos.study.aop.MyAspect"></bean>
	<aop:config>
		<aop:aspect ref="MyAspect">
			<aop:pointcut expression="execution(void com.changoos.study.aop.*.sound())"
				id="pointCut" />

			<aop:before method="before" pointcut-ref="pointCut" />
			<aop:after method="after" pointcut-ref="pointCut" />
			<aop:after-returning method="afterReturning" pointcut-ref="pointCut" />
			<aop:around method="around" pointcut-ref="pointCut" />
		</aop:aspect>
	</aop:config>
</beans>
<br/>
  • 11 : Aspect 빈 설정

  • 12 : Pointcut 설정, 표현식 하단에 정리

  • 15 : Pointcut 메소드 실행전 호출 메소드

  • 16 : Pointcut 메소드 실행후 호출 메소드

  • 17 : Pointcut 메소드 이상없이 실행 후 호출 메소드

  • 18 : Pointcut 메소드 실행전/후 호출 메소드

<br/>

<br/>

MyAspect 클래스를 살펴보면, 호출될 메소드가 정의되어있다.

public class MyAspect {
	public void before() {
		System.out.println("Before Advice");
	}

	public void after() {
		System.out.println("After Advice");
	}

	public void afterReturning() {
		System.out.println("After-Returning Advice");
	}

	public void around(ProceedingJoinPoint proceedingJoinPoint) {
		try {
			long start = System.currentTimeMillis();
			proceedingJoinPoint.proceed();
			long stop = System.currentTimeMillis();
			System.out.println("Around Advice : " + (stop - start));
		} catch (Throwable e) {
			e.printStackTrace();
		}

	}
}

<br/>

main 메소드 호출, 결과

public class Main {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/com/changoos/study/aop/AppContext.xml");
		
		AnimalType animal = (AnimalType)context.getBean("Cat");
		animal.sound();
	}
}

<br/>

출력값

12월 04, 2018 7:55:11 오후 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
정보: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@533ddba: startup date [Tue Dec 04 19:55:11 KST 2018]; root of context hierarchy
12월 04, 2018 7:55:12 오후 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
정보: Loading XML bean definitions from class path resource [com/changoos/study/aop/AppContext.xml]
Before Advice
냐옹~~
Around Advice : 0
After-Returning Advice
After Advice

<br/>

<br/>

Annotaion을 이용하여 구현해보자.

대량의 XML 파일을 작성하는 것 또한 단점이다.

외부설정 파일에 매번 일일이 다 작성하지말고, Annotaion을 통하여 구현해보자.

<br/>

AppContext.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"
	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.1.xsd">

	<bean id="Cat" class="com.changoos.study.aop.Cat"></bean>
	<bean id="Aspect" class="com.changoos.study.aop.MyAspect"></bean>
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
<br/>
  • 10 : Annotation을 통해서 auto로!

<br/>

<br/>

MyAspect 클래스는, Annotaion을 이용하여 다음과 같이 변경하였다.

@Aspect
public class MyAspect {

	@Pointcut("execution(void com.changoos.study.aop.*.sound())")
	public void myPointcut() {
	};

	@Before("myPointcut()")
	public void before() {
		System.out.println("Before Advice");
	}

	@After("myPointcut()")
	public void after() {
		System.out.println("After Advice");
	}

	@AfterReturning("myPointcut()")
	public void afterReturning() {
		System.out.println("After-Returning Advice");
	}

	@Around("myPointcut()")
	public void around(ProceedingJoinPoint proceedingJoinPoint) {

		try {
			long start = System.currentTimeMillis();
			proceedingJoinPoint.proceed();
			long stop = System.currentTimeMillis();
			System.out.println("Around Advice : " + (stop - start));
		} catch (Throwable e) {
			e.printStackTrace();
		}

	}
}

<br/>

출력값.

12월 04, 2018 8:20:44 오후 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
정보: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@533ddba: startup date [Tue Dec 04 20:20:44 KST 2018]; root of context hierarchy
12월 04, 2018 8:20:44 오후 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
정보: Loading XML bean definitions from class path resource [com/changoos/study/aop/AppContext.xml]
Before Advice
냐옹~~
Around Advice : 1
After Advice
After-Returning Advice

<br/>

<br/>

* Pointcut 표현식 정리

  • execution([수식어] [리턴타입] [클래스이름] 이름

  • execution(void sound*(..))<br/>
    리턴타입이 void이고, sound로 시작하는, 파라미터가 0개 이상인 메소드

  • execution(* com.changoos.study.di..())<br/>
    리턴타입에 관계없이, 메소드이름도 관계없이, com.changoos.study.di 패키지에있는, 파라미터가 0 개인 메소드

  • execution(* com.changoos.study.di..(..))<br/>
    리턴타입에 관계없이, 메소드이름도 관계없이, com.changoos.study.di 패키지에있는, 파라미터가 0 개이상인 메소드

  • execution(* sound*(*))<br/>
    리턴타입에 관계없이, 메소드이름이 sound로 시작하는, 매개변수가 1개인 메소드

  • execution(* sound*(,))<br/>
    리턴타입에 관계없이, 메소드이름이 sound로 시작하는, 매개변수가 2개인 메소드

  • execution(* sound*(Integer,*))<br/>
    리턴타입에 관계없이, 메소드이름이 sound로 시작하는, 매개변수가 2개인, 첫번째 파라미터 타입이 Integer인 메소드<br/>

  • within(패키지)

  • within(com.changoos.study.di)<br/>
    com.changoos.study.di 패키지의 모든 메소드

<br/>

<br/>

0
이전 댓글 보기
등록
TOP