본문 바로가기

Spring 3.0 - 4.3

Spring 3.0 - AOP 개념 및 예제 (미)

AOP란?

- Spring에서 처음 등장한 새로운 기술

- 공통의 관심사항을 적용해서 발생하는 의존관계의 복잡성과 코드 중복을 해소해 주는 프로그래밍 기법

AOP(Aspect Oriented Programming : 관점지향프로그래밍) 
OOP(Object Oriented Programming :객체지향프로그래밍)


AOP 예제

 

 

 

프로젝트명: SpringAOP  / 패키지명 : com.exe.aop

1. lib 받기 (maven)

- AOP 사용하려면 총 4개의 library 필요 - spring aop, AspectJ Runtime 1.8.13 , AspectJ Weaver 1.8.13, CGLib 3.2.5

2. Target 객체 생성

TargetA 클래스

package com.exe.aop;

import org.springframework.stereotype.Component;

@Component("targetA")// targetA란 이름으로 객체생성 
public class TargetA {

	public void doSomething1() {
		System.out.println("TargetA.doSomething1");
	}
	
	public void doSomething2() {
		System.out.println("TargetA.doSomething2");
	}
	
	public void doAnother1() {
		System.out.println("TargetA.doAnother1");
	}
	
	public void doAnother2() {
		System.out.println("TargetA.doAnother2");
	}
	
}

 

TargetB 클래스

package com.exe.aop;

import org.springframework.stereotype.Component;

@Component("targetB")
public class TargetB {

	public void doSomething1() {
		System.out.println("TargetB.doSomething1");
	}
	
	public void doSomething2() {
		System.out.println("TargetB.doSomething2");
	}
	
	public void doAnother1() {
		System.out.println("TargetB.doAnother1");
	}
	
	public void doAnother2() {
		System.out.println("TargetB.doAnother2");
	}
	
}

 

- 이 메소드들에 Advice를 적용하면 Target 이 되는 것 

 

3. app-context.xml 세팅

execution  (         public           void    com..aop *       .      *(..)) 

execution  (메소드의 접근지정자 반환값  패키지명  . 클래스 . 메소드명(매개변수)) 

-> 어떠한 패키지, 어떠한 메소드든지 상관 없다     

..은 생략이란 의미

* String 반환값만 받고 싶은 경우 : public String ~

* 특정 클래스의 특정 메소드만 하고 싶은 경우 : com..aop 부분만 건들면 됨 

<?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 
		https://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/context 
		https://www.springframework.org/schema/context/spring-context-3.0.xsd
		http://www.springframework.org/schema/aop 
		https://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
	
	<!-- aop 관련된 것 상단에 추가 -->

	<description>Example configuration to get you started.</description>

	<context:component-scan base-package="com.exe.aop" />
	
	<!-- execution(메소드 접근지정자,반환값,패키지명.클래스.메소드명(매개변수)) -->
	
	<bean id="beforeAdvice" class="com.exe.aop.MyBeforeAdvice"/>
	
	<aop:config>	
		<aop:aspect ref="beforeAdvice">
			<aop:pointcut id="beforePointcut" expression="execution(public void com..aop.*.*(..))"/>
			<aop:before method="beforeMethodCall" pointcut-ref="beforePointcut"/>
	    </aop:aspect>		
	</aop:config>

</beans>

beforeMethodCall은 내가 할 작업 즉, 공통으로 적용될 작업을 뜻한다.

그것을 beforePointcut에 넣어둔다 

 

<aop:before : 어디에 걸건지 위치를 정한것 - ex) 메소드 앞에? 뒤에? 
before을 썼다는건 메소드가 실행되기 전에 내가 지정한 작업을 실행하도록 설정한 것 

실행되는 어드바이스의 메소드는 beforeMethodCall 이다 

 

[Joinpoint]
애플리케이션 실행의 특정 지점을 의미
AOP에서 Aspect(횡단 관심사)를 적용하는 위치를 표시하는데 사용 (위치: 클래스안의 메소드를 뜻함)

[Pointcut]
여러 조인포인트의 집합. 언제 어드바이스를 실행하는 지 위치 표시

 

4. MAIN 절

package com.exe.aop;

import org.springframework.context.support.GenericXmlApplicationContext;

public class AopMain {

	public static void main(String[] args) {
		
		GenericXmlApplicationContext context =
				new GenericXmlApplicationContext("app-context.xml");
		
		TargetA ta = (TargetA)context.getBean("targetA");
		
		// 4개의 메소드가 실행되기 전에 내가 지정한 작업이 출력되는 것
		ta.doAnother1();
		ta.doAnother2();
		ta.doSomething1();
		ta.doSomething2(); 
	}
}

 

 

실행결과

 

 

 

Pointcut 종류에 따른 결과 보기

<>aop:after 이용 - Target 객체 실행 후 작동

MyAfterAdvice.java

@Component 
public class MyAfterAdvice {
	public void afterMethodCall() {	
		System.out.println("메소드 실행 후 (after)");		
	}
}

app-context.xml

	<bean id="afterAdvice" class="com.exe.aop.MyAfterAdvice"/>
	
	<aop:config>		
		<aop:aspect ref="afterAdvice">
			<aop:pointcut id="afterPointcut" expression="execution(public void com..aop.*.*(..))"/>
			<aop:after method="afterMethodCall" pointcut-ref="afterPointcut"/>
		</aop:aspect>			
	</aop:config>

 

 

<>aop:after, <>aop:before 동시에 사용 가능

		<aop:aspect ref="beforeAdvice">
			<aop:pointcut id="beforePointcut"
				expression="execution(public void com..aop.*.*(..))" />
			<aop:before method="beforeMethodCall"
				pointcut-ref="beforePointcut" />
		</aop:aspect>
		
		<aop:aspect ref="afterAdvice">
			<aop:pointcut id="afterPointcut"
				expression="execution(public void com..aop.*.*(..))" />
			<aop:after method="afterMethodCall"
				pointcut-ref="afterPointcut" />
		</aop:aspect>

 

 

 

 

<>aop:around - Target 메소드 실행 전,후 적용

내가 Advice를 적용할 위치(메소드)를 기준으로 before과 after가 실행된다.
method가 리턴값이 있을 때는 그 값을 받아놔야만,  after한테 값을 넘겨줄 수 있다. (result = return값 받는 변수)
(before + after 합친게 MyAround라고 생각하면 됨)

MyAroundAdvice.java

@Component
public class MyAroundAdvice {	

	public Object aroundMethodCall(
			ProceedingJoinPoint joinPoint) {
		
		Object result = null;
		// result = return값을 받는 변수
		
		try {
			
			System.out.println("메소드 실행 전(round)");
			
			result = joinPoint.proceed(); 
			// 메소드를 실행 시키는 것 => 반환값이 있다면 반환값 받아놓고
			
			System.out.println("메소드 실행후(round)");
			// 그 받은 리턴값을 원하는 곳으로 되돌려줌			
			
		} catch (Throwable e) {
			// exception 가지고는 AOP에 대한건 예외처리가 안됨 
			// Throwable 이용
		}		
		return result;		
	}
}

app-context.xml

	<aop:config>

	 	<aop:aspect ref="aroundAdvice">
			<aop:pointcut id="aroundPointcut" expression="execution(* com..aop.*.*(..))"/> 
			<aop:around method="aroundMethodCall" pointcut-ref="aroundPointcut"/>
		</aop:aspect> 		

	</aop:config>

 


Annotation을 적용한 AOP 예제

1. app-context.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 
		https://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/context 
		https://www.springframework.org/schema/context/spring-context-3.0.xsd
		http://www.springframework.org/schema/aop 
		https://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

	<description>Example configuration to get you started.</description>

	<context:component-scan base-package="com.exe.aop" />

	<aop:aspectj-autoproxy/> <!-- AOP를 annotation으로 사용하겠다 -->

</beans>

<>aop:after-returning - 메소드 정상 실행 후

MyAfterReturningAdvice.java

package com.exe.aop;

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class MyAfterReturningAdvice {
	
	@AfterReturning("execution(* com..aop.*.*(..))")
	public void afterReturningMethodCall() {
		
		System.out.println("메소드 실행 완료 (return) ");		
		//리턴값이 돌려졌따는건 메소드가 에러없이 잘 실행됐다는 말
		//그걸 체크하라 		
	}
}

@Aspect는  <aop:aspect ref="afterReturningAdvice"> 와 같은말

 

@AfterReturning("execution(* com..aop.*.*(..))") 는 아래와 같은말

<aop:pointcut id="afterThrowingPointcut" expression="execution(* com..aop.*.*(..))"/> 
<aop:after-throwing method="afterThrowingMethodCall" pointcut-ref="afterThrowingPointcut"/>

 

 

 

<>aop:after-throwing - 메소드 실행 에러가 났을 때 적용 (예외 처리 작동) 

MyAfterThrowingAdvice.java

package com.exe.aop;

import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class MyAfterThrowingAdvice {
	
	@AfterThrowing("execution(* com..aop.*.*(..))")
	public void afterThrowingMethodCall() {
		
		System.out.println("메소드 실행 에러가 났을 때만 뜨는 메세지");
		
	}

}

 

TargetA.java - TargetA 객체에 일부로 에러내보았다. 

package com.exe.aop;

import org.springframework.stereotype.Component;

@Component("targetA")// targetA란 이름으로 객체생성 
public class TargetA {

	public void doSomething1() {
		System.out.println("TargetA.doSomething1");
	}
	
	public void doSomething2() {
		System.out.printlns("TargetA.doSomething2");
	} // 일부로 lns라고 적어서 에러를 냄
	
	public void doAnother1() {
		System.out.println("TargetA.doAnother1");
	}
	
	public void doAnother2() {
		System.out.println("TargetA.doAnother2");
	}
	
}

-> TargetA.doSomething2만 실행 안됨