Spring/4. 스프링 AOP

Day 68 : AOP 용어 및 기본 설정

pancakemaker 2022. 1. 20. 18:46

1. 조인포인트(JoinPoint)

: 클라이언트가 호출하는 모든 비즈니스 메소드 (ex: BoardServiceImpl, UserServiceImpl 클래스의 모든 메소드)


2. 포인트컷(Poingcut)

: 필터링 된 조인포인트

(ex: 트랜잭션을 처리하는 공통기능을 만들었다고 가정. 이 횡단 관심은 등록, 수정, 삭제 기능의 비즈니스 메소드에 대해서는 당연히 동작해야 하지만, 검색 기능의 메소드에 대해서는 트랜잭션과 무관하므로 동작할 필요가 없음)

: 원하는 특정 메소드만 횡단 관심에 해당하는 공통 기능을 수행시키기 위해 필요

 

※ 포인트컷 표현식

expression = "execution(* com.multicampus.biz..*Impl.get*(..))"
		   		    (1)	 (2)			(3)  	 (4)
               
(1) : 리턴 타입
(2) : 패키지 경로
(3) : 클래스명
(4) : 메소드명 매개 변수

 

① 리턴타입 지정

표현식 설명
* 모든 리턴타입 허용
void 리턴타입이 void인 메소드 선택
!void 리턴타입이 void가 아닌 메소드 선택

② 패키지 지정

표현식 설명
com.springbook.biz 정학하게 com.springbook.biz 패키지만 선택
com.springbook.biz.. com.springbook.biz로 시작하는 모든 패키지 선택
com.springbook..impl com.springbook으로 시작하면서 마지막 패키지 이름이 impl로 끝나는 패키지 선택

③ 클래스 지정

표현식 설명
BoardServiceImpl 정확하게 BoardServiceImpl 클래스만 선택
*Impl 클래스 이름이 Impl로 끝나는 클래스만 선택
BoardService+ 클래스 이름 뒤에 '+'가 붙으면 해당 클래스로부터 파생된 모든 자식 클래스 선택
인터페이스 뒤에 '+'가 붙으면 해당 인터페이스를 구현한 모든 클래스 선택

④ 메소드 지정

표현식 설명
*(..) 가장 기본 설정으로 모든 메소드 선택
get*(..) 메소드 이름이 get으로 시작하는 모든 메소드 선택

⑤ 매개변수 지정

표현식 설명
(..) 가장 기본 설정으로써 '..'은 매개변수와 개수와 타입에 제약이 없음을 의미
(*) 반드시 1개의 매개변수를 가지는 메소드만 선택
(com.springbook.user.UserVO) 매개변수로 userVO를 가지는 메소드만 선택
이 때 클래스의 패키지 경로가 반드시 포함되어야 함
(!com.springbook.user.UserVO) 매개변수로 userVO를 가지지 않는 메소드만 선택
(Integer, ..) 한 개 이상의 매개변수를 가지되, 첫 번째 매개변수의 타입이 Integer인 메소드만 선택
(Integer, *) 반드시 두 개의 매개변수를 가지되. 첫 번째 매개변수의 타입이 Integer인 메소드만 선택

 

- 스프링 설정 파일에 포인트컷 추가 및 포인트컷 참조(pointcut-ref) 수정

<?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"
	xmlns:p="http://www.springframework.org/schema/p"
	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.2.xsd
		http://www.springframework.org/schema/aop 
		http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">

	<context:component-scan base-package="com.springbook.biz">
	</context:component-scan>
	
	<bean id="log" class="com.springbook.biz.common.LogAdvice"></bean>
	
	<aop:config>
		<aop:pointcut id="allPointcut"
					expression="execution(* com.springbook.biz..*Impl.*(..))"/>
		<aop:pointcut id="getPointcut"
					expression="execution(* com.springbook.biz..*Impl.get*(..))"/>
					
		<aop:aspect ref="log">
			<aop:before pointcut-ref="getPointcut" method="printLog"/>
		</aop:aspect>
	</aop:config>	
</beans>

 

- 클라이언트 클래스 실행 결과 → [공통 로그]가 get으로 시작하는 메소드 호출에 대해서만 반응

INFO : org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [applicationContext.xml]
INFO : org.springframework.context.support.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@442d9b6e: startup date [Thu Jan 20 18:26:58 KST 2022]; root of context hierarchy
INFO : org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor - JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
===> JDBC로 insertBoard() 기능 처리
[공통 로그] 비즈니스 로직 수행 전 동작
===> JDBC로 getBoardList() 기능 처리
--->BoardVO [seq = 7, title = 임시 제목, writer = 홍길동, content = 임시 내용............, regDate = 2022-01-20, cnt = 0]
--->BoardVO [seq = 6, title = 임시 제목, writer = 홍길동, content = 임시 내용............, regDate = 2022-01-20, cnt = 0]
--->BoardVO [seq = 5, title = 임시 제목, writer = 홍길동, content = 임시 내용............, regDate = 2022-01-20, cnt = 0]
--->BoardVO [seq = 4, title = 임시 제목, writer = 홍길동, content = 임시 내용............, regDate = 2022-01-20, cnt = 0]
--->BoardVO [seq = 3, title = 임시 제목, writer = 홍길동, content = 임시 내용............, regDate = 2022-01-20, cnt = 0]
--->BoardVO [seq = 2, title = 임시 제목, writer = 홍길동, content = 임시 내용............, regDate = 2022-01-20, cnt = 0]
--->BoardVO [seq = 1, title = 가입인사, writer = 관리자, content = 게시판 등록 테스트입니다.테스트 1, regDate = 2022-01-18, cnt = 0]
INFO : org.springframework.context.support.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@442d9b6e: startup date [Thu Jan 20 18:26:58 KST 2022]; root of context hierarchy

3. 어드바이스(Advice)

: 포인터컷으로 지정한 메소드가 호출될 때 어드바이스 객체의 어드바이스 메소드가 실행됨

: 횡단 관심에 해당하는 공통 기능의 코드로 독립된 클래스의 메소드로 작성

: 어드바이스로 구현된 메소드가 언제 동작할지 스프링 설정 파일을 통해 지정 가능 (before, after, after-running, after-throwing, 'around)

 

- 스프링 설정 파일에 어드바이스 동작 시점 변경 (before → after)

<?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"
	xmlns:p="http://www.springframework.org/schema/p"
	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.2.xsd
		http://www.springframework.org/schema/aop 
		http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">

	<context:component-scan base-package="com.springbook.biz">
	</context:component-scan>
	
	<bean id="log" class="com.springbook.biz.common.LogAdvice"></bean>
	
	<aop:config>
		<aop:pointcut id="allPointcut"
					expression="execution(* com.springbook.biz..*Impl.*(..))"/>
		<aop:pointcut id="getPointcut"
					expression="execution(* com.springbook.biz..*Impl.get*(..))"/>
					
		<aop:aspect ref="log">
			<aop:after pointcut-ref="getPointcut" method="printLog"/>
		</aop:aspect>
	</aop:config>	
</beans>

 

- 클라이언트 클래스 실행 → 비즈니스 로직 수행 후 동작하는 것 확인 (출력 메시지는 관련 X)

INFO : org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [applicationContext.xml]
INFO : org.springframework.context.support.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@442d9b6e: startup date [Thu Jan 20 18:32:19 KST 2022]; root of context hierarchy
INFO : org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor - JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
===> JDBC로 insertBoard() 기능 처리
===> JDBC로 getBoardList() 기능 처리
[공통 로그] 비즈니스 로직 수행 전 동작
--->BoardVO [seq = 8, title = 임시 제목, writer = 홍길동, content = 임시 내용............, regDate = 2022-01-20, cnt = 0]
--->BoardVO [seq = 7, title = 임시 제목, writer = 홍길동, content = 임시 내용............, regDate = 2022-01-20, cnt = 0]
--->BoardVO [seq = 6, title = 임시 제목, writer = 홍길동, content = 임시 내용............, regDate = 2022-01-20, cnt = 0]
--->BoardVO [seq = 5, title = 임시 제목, writer = 홍길동, content = 임시 내용............, regDate = 2022-01-20, cnt = 0]
--->BoardVO [seq = 4, title = 임시 제목, writer = 홍길동, content = 임시 내용............, regDate = 2022-01-20, cnt = 0]
--->BoardVO [seq = 3, title = 임시 제목, writer = 홍길동, content = 임시 내용............, regDate = 2022-01-20, cnt = 0]
--->BoardVO [seq = 2, title = 임시 제목, writer = 홍길동, content = 임시 내용............, regDate = 2022-01-20, cnt = 0]
--->BoardVO [seq = 1, title = 가입인사, writer = 관리자, content = 게시판 등록 테스트입니다.테스트 1, regDate = 2022-01-18, cnt = 0]
INFO : org.springframework.context.support.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@442d9b6e: startup date [Thu Jan 20 18:32:19 KST 2022]; root of context hierarchy

4. 위빙(Weaving)

: 포인트컷으로 지정한 핵심 관심 메소드(ex: get메소드)가 호출될 때, 어드바이스에 해당하는 횡단 관심 메소드가 삽입되는 과정

: 위빙을 통해 메소드를 수정하지 않고도 횡단 관심에 해당하는 기능을 추가하거나 변경할 수 있음


5. 애스팩트(Aspect) 또는 어드바이저(Advisor)

- 애스팩트 <aop:aspect> : 포인트컷 + 어드바이스 : 어떤 포인트컷 메소드에 대해서 어떤 어드바이스 메소드를 실행할지 결정

: 애스팩트와 어드바이저는 같은 의미이지만, 어드바이저는 트랜잭션 설정에서 사용 <aop:advisor>

getPointercut으로 설정한 포인트컷 메소드가 호출될 때(), "log"라는 어드바이스 객체()의 printLog() 메소드가 실행되고(), 이 때 printLog() 메소드 동작 시점이 <aop:before>라는() 내용의 설정