Day 68 : AOP 용어 및 기본 설정
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>라는(④) 내용의 설정