06 - 1. Aspect Oriented Programming
Business Component 개발에서 가장 중요한 두가지 원칙
- 낮은 결합도 : Dependency Injection
- 높은 응집도 : AOP
1. 관점 지향 프로그래밍 (AOP)
- 메소드마다 공통으로 등장하는 코드 (로깅, 예외, 트랜잭션 처리 등) : 횡단 관심
- 사용자 요청에 따라 실제 수행되는 핵심 로직 : 핵심 관심
핵심 관심과 횡단 관심을 분리하는 것이 더욱 간결하고 응집도가 높은 코드를 유지할 수 있는 방법이다.
기존의 OOP 언어에서는 횡단 관심에 해당하는 공통 코드를 완벽하게 독립적인 모듈로 분리해내기 어렵다.
Spring의 AOP는 이러한 한계를 극복할 수 있도록 도와준다.
2. AOP 설정
- build.gradle의 dependencies에 다음 문장을 추가
implementation 'org.springframework.boot:spring-boot-starter-aop' // AspectJ 라이브러리 추가
- applicationContext.xml에 루트 엘리먼트 <beans>에 aop에 대한 네임스페이스와 스키마주소 추가
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd"
....
>
- applicationContext.xml에 AOP 관련 설정을 추가하고 실행할 횡단 관심 클래스를 bean 등록 & 테스트
<bean id="log" class="com.springbook.biz.common.Log4jAdvice"></bean>
<aop:config>
<!-- expression 형식: 리턴 타입 - 패키지 경로 - 클래스명 - 메소드명 및 매개변수 !-->
<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="printLogging"/>
<!-- <aop:before pointcut-ref="allPointcut" method="printLogging"/> !-->
</aop:aspect>
</aop:config>
- 실행 결과
스프링의 AOP는 클라이언트가 핵심 관심에 해당하는 비즈니스 메소드를 호출할 때, 횡단 관심에 해당하는 메소드를 적절하게 실행해준다. 이때, 핵심 관심 메소드와 횡단 관심 메소드 사이에서 소스상 결합이 발생하지 않으며, 이것이 AOP를 사용하는 주된 목적이다.
3. AOP 용어 정리
- 조인포인트(Joinpoint)
클라이언트가 호출하는 모든 비즈니스 메소드 (*ServiceImpl 클래스의 모든 메소드가 해당)
- 포인트컷(Pointcut)
필터링된 조인포인트, 수많은 비즈니스 메소드 중에서 원하는 특정 메소드에서만 횡단 관심에 해당하는 공통 기능을 수행시키기 위해 포인트컷이 필요하다.
- 어드바이스(Advice)
횡단 관심에 해당하는 공통 기능의 코드를 의미 (ex. 트랜잭션 관리 기능의 어드바이스 메소드)
- 애스팩트(Aspect)
포인트컷으로 지정한 메소드가 호출될 때, 어드바이스 메소드를 삽입하도록 하는 설정
애스팩트 설정에 따라 어떤 포인트컷 메소드에 어떤 어드바이스 메소드를 실행할지가 결정된다.
특별히 트랜잭션 설정에서는 <aop:aspect> 엘리먼트 대신 <aop:advisor>를 사용한다.
- 위빙(Weaving)
포인트컷으로 지정된 핵심 관심 메소드가 호출될 때, 어드바이스에 해당되는 횡단 관심 메소드가 삽입되는 과정.
위빙을 통해서 비즈니스 메소드를 수정하지 않고도 횡단 관심에 해당하는 기능을 추가, 변경할 수 있다.
(Spring에서는 여러 위빙 방식 중, 런타임 위빙 방식만 지원한다.)
<bean id="log" class="com.springbook.biz.common.Log4jAdvice"></bean>
<aop:config>
<aop:pointcut id="getPointcut"
expression="execution(* com.springbook.biz..*Impl.get*(..))"/>
<aop:aspect ref="log">
<aop:before pointcut-ref="getPointcut" method="printLogging"/>
</aop:aspect>
</aop:config>
- 위 설정 코드의 동작 과정은 다음과 같다.
1. id를 getPointcut으로 설정한 포인트컷 메소드가 호출
2. <aop:aspect>에 ref="log"로 등록된 <bean> 어드바이스 객체의 printLogging 메소드가 실행
3. 메소드 동작 시점은 <aop:before>
4. AOP 엘리먼트
스프링에서는 AOP 관련 설정을 XML 방식과 어노테이션 방식으로 지원한다.
XML 방식은 다음과 같다.
- <aop:config>
AOP 설정의 루트 엘리먼트, 하위에는 <aop:pointcut>, <aop:aspect> 엘리먼트가 위치할 수 있다.
- <aop:pointcut>
포인트컷을 지정하기 위해 사용, <aop:config>의 자식이나 <aop:aspect>의 자식 엘리먼트로 사용할 수 있다.
<aop:aspect>의 자식인 경우에는 해당 <aop:aspect>에서만 사용할 수 있다.
- <aop:aspect>
핵심 관심에 해당하는 포인트컷 메소드와 횡단 관심에 해당하는 어드바이스 메소드를 결합하기 위해 사용
이 애스팩트를 어떻게 설정하느냐에 따라 위빙 결과가 달라진다.
- <aop:advisor>
포인트컷과 어드바이스를 결합한다는 점에서 애스팩트와 같은 기능
만약 어드바이스 객체의 아이디를 모르거나 메소드 이름을 확인할 수 없는 경우에는 <aop:aspect>를 사용할 수 없다.
이런 경우 advisor를 사용한다.
<aop:advisor pointcut-ref="getPointcut" advice-ref="txAdvice"/>
위 XML 설정에서 id가 getPointcut으로 지정된 포인트컷은 com.springbook.biz 패키지로 시작하는 클래스 중에서 이름이 Impl로 끝나는 클래스의 get으로 시작하는 메소드를 포인트컷으로 지정하고 있다.
그리고 애스팩트 설정에서 <aop:before> 엘리먼트의 pointcut-ref 속성으로 이 포인트컷을 참조하고 있다.