[Spring] AOP 관점 지향 프로그래밍, Advice 동작시점
AOP (Aspect Oriented Programming) - 관점 지향 프로그래밍
관점 지향은 쉽게 말하면 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고
그 관점을 기준으로 각각 모듈화 하겠다는 것
장점 : 중복코드 제거, 효율적인 유지보수, 높은 생산성, 재활용성 극대화, 변화 수용 용이
AOP 언제 쓰는데?
핵심로직(비즈니스 메서드, CRUD)에는 공통적으로 들어가는 로직이 존재함!
== 공통로직(횡단관심)
ex) 로깅, 예외처리, 트랜잭션, 보안(인증, 인가, 허가)...
어떤 횡단관심들을
어떤 핵심로직에 연결할지 "설정"
=> AOP설정
< AOP 용어 정리 >
Aspect
"결합"
횡단관심+포인트컷(핵심로직)
위에서 설명한 흩어진 관심사를 모듈화 한 것. 주로 부가기능을 모듈화함.
Target
Aspect를 적용하는 곳 (클래스, 메서드 .. )
Advice
횡단관심 (트랜잭션, 로그, 보안, 인증 등)
실질적으로 어떤 일을 해야할 지에 대한 것, 실질적인 부가기능을 담은 구현체
JointPoint
Advice가 적용될 위치, 끼어들 수 있는 지점.
메서드 진입 지점, 생성자 호출 시점, 필드에서 값을 꺼내올 때 등 다양한 시점에 적용가능
PointCut
회단관심을 적용할 핵심 로직을 의미
JointPoint의 상세한 스펙을 정의한 것.
'A란 메서드의 진입 시점에 호출할 것'과 같이 더욱 구체적으로 Advice가 실행될 지점을 정할 수 있음
Advice 동작시점
before, after, after-returning, after-throwing, around 5가지의 동작 시점이 존재
어드바이스는 횡단 관심에 해당하는 공통 기능의 코드를 의미하며, 독립된 클래스의 메소드로 작성
그리고 어드바이스로 구현된 메소드가 언제 동작할지를 스프링설정파일을 통해 지정할 수 있음
Before
- 비즈니스 메소드 실행 전 동작
After Returning
- 비즈니스 메소드가 성공적으로 리턴되면 동작
(단, 메소드 내부에 리턴값이 존재하는 경우)
After Throwing
- 비즈니스 메소드 실행 중, 예외가 발생하면 동작
(예를들면 try~catch 블록의 catch 부분에 해당!)
After
- 비즈니스 메소드가 실행된 후, !무조건! 실행
(예를들면 try~catch 블록에서 finally 부분에 해당!)
Around
- 메소드 호출 자체를 가로채서 비즈니스 메소드 실행 전&후 모두에 처리할 로직을 삽입할 수 있음
AOP 라이브러리 추가
AOP를 적용하기 위해서 poem.xml 파일에 라이브러리 추가
<!-- AOP -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.8</version>
</dependency>
저장 후 "Maven Dependencies" 에 설정한대로 라이브러리가 변경되었는지 확인
aop 네임스페이스를 추가
스프링 설정파일 (applicationContext.xml)에서 AOP태그를 사용하려면
aop 네임스페이스를 추가 해야함
LogAdvice.java
package com.kim.biz.common;
public class LogAdvice {
public void printLog() {
System.out.println(">>> 공통로직 <<< 핵심로직 수행전에 호출");
}
}
※ 포인트컷 표현식
execution() : 가장 대표적이고 강력한 지시자로, 접근제어자, 리턴 타입, 타입 패턴, 메서드, 파라미터 타입, 예외 타입 등을 조합해서 메서드까지 선택가능한 가장 정교한 포인트컷을 만들수 있다.
execution([수식어] 리턴타입 [클래스이름].이름(파라미터)
execution(public Integer com.edu.aop.*.*(*))
- com.edu.aop 패키지에 속해있고, 파라미터가 1개인 모든 메서드
execution(* com.edu..*.get*(..))
- com.edu 패키지 및 하위 패키지에 속해있고, 이름이 get으로 시작하는 파라미터가 0개 이상인 모든 메서드
execution(* com.edu.aop..*Service.*(..))
- com.edu.aop 패키지 및 하위 패키지에 속해있고, 이름이 Service르 끝나는 인터페이스의 파라미터가 0개 이상인 모든 메서드
execution(* com.edu.aop.BoardService.*(..))
- com.edu.aop.BoardService 인터페이스에 속한 파마리터가 0개 이상인 모든 메서드
execution(* some*(*, *))
- 메서드 이름이 some으로 시작하고 파라미터가 2개인 모든 메서드
설정파일
<?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 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.kim.biz" />
<bean id="LogAdvice" class="com.kim.biz.common.LogAdvice" />
<aop:config>
<aop:pointcut expression="execution(* com.kim.biz..*Impl.*(..))" id="aPointcut"/>
<aop:aspect ref="LogAdvice">
<aop:before method="printLog" pointcut-ref="aPointcut"/>
</aop:aspect>
</aop:config>
</beans>
실행화면
실습
select류를 수행하면 "데이터를 탐색합니다..."라는 로그를 남길 수 있게 aspect 처리하라.
(applicationContext.xml(스프링설정파일)에 aop설정하라는 뜻)
LogSelectAdvice
package com.kim.biz.common;
public class LogSelectAdvice {
public void printLogSelect() {
System.out.println("데이터를 탐색합니다....");
}
}
표현식 부분 수정
execution(* com.kim.biz..*Impl.select*(..))
<?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 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.kim.biz"/>
<bean id="LogSelectAdvice" class="com.kim.biz.common.LogSelectAdvice"/>
<aop:config>
<aop:pointcut expression="execution(* com.kim.biz..*Impl.*(..))" id="aPointcut"/>
<aop:pointcut expression="execution(* com.kim.biz..*Impl.select*(..))" id="bPointcut"/>
<aop:aspect ref="LogSelectAdvice">
<aop:before method="printLogSelect" pointcut-ref="bPointcut"/>
</aop:aspect>
</aop:config>
</beans>