AOP란? -> 관점 지향 프로그래밍의 약자

=공통되는 부분을 따로 빼내어 필요한 시점에 해당코드를 추가해주는 기술

ex)컨트롤러 -> 서비스 이동시마다 println 을 실행한다면?

 

1. Advice : 공통되는 부분을 따로 빼내어 작성하는 메소드

2. JoinPoint : Advice가 적용될 수 있는 모든 관점(시점) -> 매우많음

3. Pointcut : JoinPoint 중 실제 Advice를 적용할 부분

4. Weaving : 그 시점에 공통 코드를 끼워넣는 작업

(컴파일 시 , 클래스 로딩 시 , 런타임 시 ) 가능

 

*Aspect = Advice + Pointcut : 실제동작코드 + 실제 적용된 부분 을 작성한클래스

=> 부가기능(로깅, 보안, 트랜잭션 등)을 나타내는 공통 관심사에 대한 추상적인 명칭)

=> 여러 객체에 공통으로 적용되는 부가기능을 작성한 클래스를 나타냄

=> 독립적인 요소로 구분해 내서 부가기능을 런타임 시 필요한 위치에 동적으로 참여하게 할 수 있다

 

Advice 시점에 따라

Before Advice JoinPoint 앞에서 실행
Around Advice JoinPoint  앞과 뒤에서 실행
After Advice JoinPoint  호출이 리턴되기 직전에 실행
After Returning Advice JoinPoint  메소드 호출이 정상적으로 종료된 후에 실행
After Throwing Advice 예외가 발생했을 때 실행

 

*Proxy : 대상 객체에 Advice가 적용된 후 생성되는 객체

*Target Object : Advice를 삽입할 대상 객체

 

스프링은 대상 객체에 대한 프록시를 만들어 감싸서 대상 객체를 호출할 때 먼저 호출되서

Advice 로직을 처리한 후 대상 객체를 다시 호출한다.

 

[ AOP 구현 ] - *애너테이션 방식*

Spring pom.xml에 이미 aspectj 가 포함되어있음

+ 좀 더 쉬운 구현을 위한 Weaver 추가

***MVN Repository => Aspectj Weaver 추가

 

1. 해당 클래스에 @Aspect -> 공통 관심사가 작성된 클래스임을 명시

+(Advice, Pointcut 작성되어 있어야 함)

+@Conmonent 추가해 런타임 중 Advice코드가 동적으로 추가될 수 있도록 bean 등록

 

***servlet-context에서 namespace - aop 체크 + <aop:aspectj-autoproxy/> 작성 필요 ***

 

2. 수행할 메소드 + @Before / @After ...

*@Before("Pointcut")  :  Pointcut으로 지정된 메소드가 수행되기 전에 Advice 수행

**Pointcut 작성법 3가지

1) 직접

@Before("execution( * a.b.c..*Controller.*(..))")

작성식 : execution [접근제한자] +리턴타입 + 패키지경로명 + 클래스(*C : 모든 컨트롤러) + 메서드( *(..) : 모든 메서드)

-접근제한자는 생략가능/ *: 모든 리턴타입 의미 / .. :  하위 모든 패키지 or 0개 이상의 매개변수-

 

2) Pointcut이 작성된 메소드명을 작성

 

 

 

3) 타 클래스에 작성된 Pointcut 메소드를 작성

 

3. 메소드 작성

//@Before(직접작성)
@Before("PointcutCollection.controllerPointcut()")
public void controllerLog(JoinPoint jp) {

	logger.info("테스트");
    
    jp.getArgs() : 수행되는 메소드의 모든 매개변수를 배열로 얻어옴
    jp.getTarget() : 타겟이 된 객체를 얻어온다
    jp.getSignature() : 수행되려는 메소드 선언부를 얻어옴
{

-매개변수로 입력한 JoinPoint : 부가 기능 메소드를 제공하는 인터페이스

 

3-1 Pointcut을 모아둘 클래스

public class PointcutCollection {

	@Pointcut("execution(  식 작성 )
    public void controllerPointcut(){}
    
    @Pointcut("execution(  식 작성2 )
    public void servicePointcut(){}
}

 

 

 

 

++ Around 사용 -- 앞뒤 모두 아우르는 관점 ex) 러닝타임계산

@Aspect // Advice + Pointcut 공통 관심사가 작성된 클래스임을 명시
@Component
public class AroundAspect {
	
	//slf4j import
	private Logger logger = LoggerFactory.getLogger(AroundAspect.class);
	
	
	//Around : 전처리, 후처리를 모두 해결하고자 할 때 
	//ProceedingJoinPoint를 매개변수 필수 사용 
	//(Proceeding: 진행)
	//-> proceed() 메소드 사용 가능: 해당 메소드를 기준으로 Before, After 나뉨
	
	
	@Around("PointcutCollection.serviceImplPointcut()")
	public Object aroundLog(ProceedingJoinPoint pp) throws Throwable {
		
		  // 클래스명
		  String className = pp.getTarget().getClass().getSimpleName(); // 대상 객체의 간단한 클래스명(패키지명 제외)
		  
		  // 메소드명
		  String methodName = pp.getSignature().getName(); // 대상 객체 메소드의 정보 중 메소드명을 반환. 
		  String str = "";
		  
		  str += "[Service]" +  className + "-" + methodName + "()";
		  
		  long startMs = System.currentTimeMillis(); // 서비스 시작 시의 ms 값
		  
		  //구분기준
		  Object obj = pp.proceed();
		  long endMs = System.currentTimeMillis(); // 서비스 종료 시의 ms 값
		  
		  //걸린시간
		  str+= "[Running Time]" +(endMs-startMs) + "ms";
		  
		  //서비스 수행 시 사용된 파라미터
		  str+= "[Param]" + Arrays.toString(pp.getArgs());
		  
		  logger.debug(str);
		  
		  //proceed 반환값을 무조건 리턴
		  return obj;
	}
}

 

 

+ Recent posts