중복되어 흩어져 있는 코드들을 한 곳으로 모으기
class A{
method a(){
중복 코드1
a
중복 코드2
}
method b(){
중복 코드1
b
중복 코드2
}
method c(){
중복 코드1
c
중복 코드2
}
}
class A{
method a(){
a
}
method b(){
b
}
method c(){
c
}
method p(JoinPoint point){
중복코드1
point.execute();
중복코드2
}
}
Pointcut
관심사와 비즈니스 로직이 결합되는 지점을 결정하는 것
구분 | Pointcut 설정 |
---|---|
@execution | 메소드를 기준 |
@within | 특정한 타입(클래스) 기준 |
this | 주어진 인터페이스를 구현한 객체 대상 |
@args | 특정 파라미터를 가지는 대상들만 설정 |
@annotation | 특정 어노테이션이 적용된 대상들만 |
Advice
Aspect는 관심사를 의미
Advice는 Aspect를 구현한 코드
구분 | 설명 |
---|---|
Before Advice | Target의 JoinPoint를 호출하기 전에 실행되는 코드 |
After Returning Advice | 모든 실행이 정상적으로 이루어진 후에 동작하는 코드 |
After Throwing Advice | 예외가 발생한 뒤에 동작하는 코드 |
After Advice | 정상적으로 실행되거나 예외가 발생했을 때 구분 없이 실행되는 코드 |
Around Advice | 메소드의 실행 자체를 제어할 수 있는 가장 강력한 코드(직접 호출, 결과/예외 처리 가능) |
소스코드 -> 컴파일 클래스 파일 -> 클래스 로딩 -> 메모리
(클래스 로딩할 때 메모리상 클래스의 메소드에 특정 코드 들어가는 것)
Real-World Analogy 핵심
기존의 코드 다루지 않고, 그 객체를 다른 객체로 바꾸는 방법
- AspectJ 설정
<properties>
<java-version>1.8</java-version>
<org.springframework-version>5.0.7.RELEASE</org.springframework-version>
<org.aspectj-version>1.9.4</org.aspectj-version>
<org.slf4j-version>1.6.6</org.slf4j-version>
</properties>
- lombok 디펜던시 추가
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
- AspectJ Weaver 디펜던시 추가
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<!-- AspectJ weaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<context:component-scan base-package="com.wipia.study"></context:component-scan>
<!-- @Aspect 어노테이션을 통해 어드바이스가 동작 하도록 -->
<aop:aspectj-autoproxy />
package com.wipia.study.aop;
import java.util.Arrays;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class Advice {
/*
* Before : 클래스의 메소드 실행 전
* within : BoardController 클래스를 지정
*/
@Before("within (com.wipia.study.controller.BoardController)")
public void beforeAdvice() {
System.out.println("BoardController Before");
}
/*
* After : 메소드 실행 후
* execution : getBoardList 메소드 지정 * 로 모든 메소드 지정 가능
* 접근지정자 : 생략 가능 ex) public, private
* * : 변환 타입
*
*/
@After("execution(* com.wipia.study.controller.BoardController.getBoardList(..))")
public void afterAdvice() {
System.out.println("after getBoardList");
}
/*
* AfterThrowing : 예외 발생 시
* 모든 클래스에서 메소드 호출 에러가 발생했을 때 동작
*/
@AfterThrowing(pointcut="execution(* com.wipia*..*.*(..))", throwing="e")
public void afterThrowingAdvice(Exception e) {
System.out.println("에러다 : "+e);
}
/*
* 모든 메소드 실행시 얼마나 걸리는지 시간 출력
*/
@Around("execution (* com.wipia..*.*(..))")
public Object time(ProceedingJoinPoint pjp) {
long start = System.currentTimeMillis();
System.out.println("--- Target : "+pjp.getTarget());
System.out.println("--- Parameter : "+Arrays.toString(pjp.getArgs()));
Object result = null;
try {
result=pjp.proceed();
}catch (Throwable e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("--- Time : "+(end-start));
return result;
}
}
@Aspect : 해당 클래스 객체가 Aspect를 구현한 것이라는 것을 나타내기 위해
@Component : AOP와 관계가 없지만 스프링에서 bean으로 인식하기 위해(전에 bean에서 설명함)