AOP => 관점 지향 프로그래밍
AOP의 핵심개념 : 관심분리(Separation of Concerns)
: 어떤 로직을 기준으로 핵심적인 관점, 부과적인 관점으로 나누어서 보고 그 관점을 기준으로 각각 모듈화 하겠다는 것.
모듈화란 어떤 공통된 로직이나 기능을 하나의 단위로 묶는 것
예로 핵심적인 관점은 우리가 적용하고자 하는 핵심 비즈니스 로직이 된다. 또한 부가적인 관점은 로직을 실행하기 위해서 행해지는 데이터베이스 연결 로깅, 파일 입출력등이 있다.
=> 횡단 관심(Cross Cutting Concerns)
** IoC가 결합도와 관련이 있다면, AOP는 응집도와 관련이 있다.
AOP사용
1. xml파일을 사용
2. 어노테이션을 사용
1) XML문서 사용
먼저 pom.xml에서 해당 dependency 확인 및 추가
<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>
해당 라이브러리가 있는지 확인
설정파일로 설정한 xml파일로 가서 Namespace -> aop추가
xmlns:aop="http://www.springframework.org/schema/aop"
사용준비 끝
조인 포인트(Joinpoint)
: 클라이언트가 호출하는 모든 비즈니스 메소드 -> 포인트컷 대상, 포인트컷 후보
포인트컷(Pointcut)
: 특정 조건에 의해 필터링된 조인포인트, 수많은 조인포인트 중에 특정 메소드에서만 횡단 공통기능을 수행시키기 위해서 사용한다.
어드바이스(Advice)
: 횡단 관심에 해당하는 공통 기능의 코드, 독립된 클래스의 메소드로 작성
위빙(Weaving)
: 포인트컷으로 지정한 핵심 관심 메소드가 호출될 때, 어드바이스에 해당하는 횡단 관심 메소드가 삽입되는 과정을 의미한다.
이를 통해 비즈니스 메소드를 수정하지 않고도 횡단 관심에 해당하는 기능을 추가하거나 변경이 가능해진다.
애스팩트(Aspect)
: 포인트컷과 어드바이스의 결합이다. 어떤 포인트컷 메소드에 대해 어떤 어드바이스 메소드를 실행할지 결정한다.
예시)
<aop:config>
<aop:pointcut expression="execution(* com.jdh.board..*Impl.*(..))" id="allPointcut"/>
<aop:pointcut expression="execution(* com.jdh.board..*Impl.get*(..))" id="getPointcut"/>
<!-- 실질적으로 값을 설정할때는 aspect 사용 -->
<aop:aspect ref="log">
<!-- <aop:before method="printLogging" pointcut-ref="allPointcut"/>
<aop:before method="printLogging" pointcut-ref="getPointcut"/> -->
<aop:after method="printLogging" pointcut-ref="allPointcut"/> 어드바이스
</aop:aspect>
<aop:advisor >
어드바이저는 주로 트랜젝션을 설정할때 사용된다.
</aop:advisor >
</aop:config>
aop를 설정하기 위해서는 <aop:config> 태그 내부에 설정
포인트컷(PointCut)
: <aop:pointcut expression="execution( ~~~~~ )함수" id="유일한식식별값" />
expression 내부의 execution() 명시자 설정
ex). execution(* com.jdh.board..*Impl.*(..)
-> execution( 리턴값 패키지경로 클래스명 메서드명(..))
리턴 타입
표현식 | 설명 |
* | 모든 리턴타입 허용 |
void | 리턴타입이 void인 메서드 선택 |
!void | 리턴타입이 void가 아닌 메서드 선택 |
패키지 지정
표현식 | 설명 |
com.jdh.domain | 정확하게 com.jdh.domain 패키지만 선택 |
com.jdh.domain.. | com.jdh.domain 패키지로 시작하는 모든 패키지 선택 |
메서드 지정
표현식 | 설명 |
*(..) | 모든 메서드 선택 |
test*(..) | test메서드 선택 |
매개변수 지정
표현식 | 설명 |
(..) | 모든 매개변수 |
(*) | 반드시 1개의 매개변수를 가지는 메소드만 선택 |
(com.jdh.domain.user.model.User) | 매개변수로 User를 가지는 메소드만 선택. 꼭 풀패키지명이 있어야함 |
(!com.jdh.domain.user.model.User) | 매개변수로 User를 가지지않는 메소드만 선택 |
(Integer, ..) | 한 개 이상의 매개변수를 가지되, 첫 번째 매개변수의 타입이 Integer인 메소드만 선택 |
(Integer, *) | 반드시 두 개의 매개변수를 가지되, 첫 번째 매개변수의 타입이 Integer인 메소드만 선택 |
메서드 동작 시점
동작 시점 | 설명 |
Before | 메서드 실행 전에 동작 |
After | 메서드 실행 후에 동작 |
After-returning | 메서드가 정상적으로 실행된 후에 동작 |
After-throwing | 예외가 발생한 후에 동작 |
Around | 메서드 호출 이전, 이후, 예외발생 등 모든 시점에서 동작 |
XML -> 어노테이션 형식으로
<bean id="log" class="com.jdh.spring.common.LogAdvice" />
<aop:config>
<aop:pointcut expression="execution(* com.jdh.spring..*Impl.*(..))" id="allPointcut"/>
<aop:pointcut expression="execution(* com.jdh.spring..*Impl.get*(..))" id="getPointcut"/>
<aop:aspect ref="log">
<aop:after method="printLog" pointcut-ref="getPointcut"/>
</aop:aspect>
</aop:config>
<bean id="before" class="com.jdh.spring.common.BeforeAdvice" />
<aop:config>
<aop:pointcut expression="execution(* com.jdh.spring..*Impl.*(..))" id="allPointcut"/>
<aop:aspect ref="before">
<aop:before method="beforeLog" pointcut-ref="allPointcut"/>
</aop:aspect>
</aop:config>
<bean id="after" class="com.jdh.spring.common.AfterReturningAdvice" />
<aop:config>
<aop:pointcut expression="execution(* com.jdh.spring..*Impl.*(..))" id="allPointcut"/>
<aop:aspect ref="after">
<aop:after-returning method="afterLog" pointcut-ref="allPointcut"/>
</aop:aspect>
</aop:config>
<bean id="exeption" class="com.jdh.spring.common.AfterThrowingAdvice" />
<aop:config>
<aop:pointcut expression="execution(* com.jdh.spring..*Impl.*(..))" id="allPointcut"/>
<aop:aspect ref="exeption">
<aop:after-throwing method="exceptionLog" pointcut-ref="allPointcut"/>
</aop:aspect>
</aop:config>
<bean id="around" class="com.jdh.spring.common.AroundAdvice" />
<aop:config>
<aop:pointcut expression="execution(* com.jdh.spring..*Impl.*(..))" id="allPointcut"/>
<aop:aspect ref="around">
<aop:around method="aroungLog" pointcut-ref="allPointcut"/>
</aop:aspect>
</aop:config>
예제에서 사용됐던 xml형식의 AOP
각각의 특징에 맞게 xml에 등록시켜 사용했다. 하지만 어노테이션 형식으로 바뀌게 되면 필요없는 코드이다.
xml파일에서 기존의 AOP관련 코드를 지우고 새로운 코드 추가
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
스프링 컨테이너에서 AOP관련 어노테이션을 인식하고 용도에 맞게 처리해준다.
** AOP는 스프링 컨테이너가 처리하게 하려면 반드시 <bean>등록을 하거나 @Service 사용하여 컴포넌트가 검색될 수 있도록 해야한다.!!
<aop:pointcut ~~~> => @Pointcut으로 변경
** 이때 @Pointcut 참조 하기 위해서는 메서드을 통하여 식별한다.
참조 메서드는 반드시 비어있는 형태의 메서드!!
ex)
@Pointcut("execution(* com.jdh.spring..*Impl.*(..)")
public void allPointcut() { }
@Pointcut("execution(* com.jdh.spring..*Impl.get*(..)")
public void getPointcut() { }
어드바이스 설정
@Service
public class LogAdvice {
@Pointcut("execution(* com.jdh.spring..*Impl.*(..)")
public void allPointcut() { }
@Pointcut("execution(* com.jdh.spring..*Impl.get*(..)")
public void getPointcut() { }
@Before("allPointcut()") // 포인트컷 참조 메서드 지정
public void printLog(JoinPoint jp){
System.out.println("[공통 로그] : 비즈니스 수행전 동작(LogAdvice)");
}
}
어노테이션 | 설명 |
@Before | 비즈니스 메서드 실행 전 동작 |
@AfterReturning | 비즈니스 메서드가 성공적으로 리턴되면 동작 |
@AfterThrowing | 비즈니스 메서드 실행 중 예외가 발생하면 동작 |
@After | 비즈니스 메서드가 실행된 후, 무조건 실행 |
@Arount | 호출 자체를 가로채 비즈니스 메서드 실행 전후에 처리할 로직을 삽입 할 수 있다. |
@Aspect
: 포인트 컷과 어드바이스의 결합
@Service
@Aspect
public class LogAdvice {
@Pointcut("execution(* com.jdh.spring..*Impl.*(..)")
public void allPointcut() { } // 포인트컷
+
@Before("allPointcut()") //어드바이스
public void printLog(JoinPoint jp){
System.out.println("[공통 로그] : 비즈니스 수행전 동작(LogAdvice)");
}
}
설정된 어노테이션에 의해 위빙(weaving)처리 된다.
'개발노트 > Spring' 카테고리의 다른 글
[Spring] JDBC (0) | 2021.06.18 |
---|---|
[Spring] IoC(inversion of Control) (0) | 2021.06.18 |
[Spring] src/main/resources 구조 (0) | 2021.06.15 |
[Spring] lombok.jar 설정 (0) | 2021.06.14 |
[Spring] 입문 (0) | 2021.06.09 |