AOP 설정
: 관점 지향 프로그래밍
Aspect(애스팩트) : AOP(Aspect Oriented Programming)의 단위가 되는 횡단 관심사
- 횡단 관심사(Cross-Cutting Concern) : 핵심(core) 비즈니스 로직(삼겹살구어먹기, 빵또아의 아이스크림)과 다소 거리가 있지만, 여러 모듈에서 공통적이고 반복적인 처리를 요구하는 내용(불판닦기, 불판교체, 빵또아의 빵)
- 횡단 관심사 분리(Separation Of Cross-Cutting Concern) : 횡단 관심사에 해당하는 부분(불판닦기, 불판교체, 빵또아의 빵)을 분리해서 한 곳으로 모으는 것을 의미
- Component : 골뱅이Aspect와 짝궁. component-scan시 "여기 봐주세요"라는 의미
- JoinPoint : 어드바이스가 적용될 수 있는 위치
- Advice(로그 출력 한다) : 어떤 부가기능(불판닦기)을 언제(삼겹살을 굽기 전(Before)에) 사용할지 정의
* 언제?
- Before : 조인포인트(createPost()) 전에 실행. (삼겹살을 굽기 직전에)
- After : 조인 포인트(createPost())에서 처리가 완료된 후 실행(삽겹살을 굽고 먹은 직후 실행)
- Around : 조인 포인트(createPost()) 전후에 실행(삽겹살을 굽기 직전과 먹은 직후 실행)
- After Returning : 조인 포인트(createPost())가 정상적으로 종료 후 실행
- After Throwing : 조인 포인트(createPost())에서 예외 발생 시 실행. 예외가 발생안되면 실행 안함
pom.xml
dependencies 안에 추가
<!-- AOP(Aspect Oriented Programming : 관점 지향 프로그래밍) 시작
1) aspectjrt => 이미 있으므로 생략
2) aspectjweaver => 없으므로 의존 관계를 정의
-->
<!-- https://mvnrepository.com/artifact/aspectj/aspectjweaver
1.5.4 -->
<!-- AspectJ RunTime -->
<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>
<!-- AspectJ Tools -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<!-- AOP(Aspect Oriented Programming : 관점 지향 프로그래밍) 끝 -->
이미 있는 경우 아래 코드 지움
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
root-context.xml
상단 부분 수정
<?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:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
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.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">
하단에 추가
<!-- 스프링 AOP 활성화 -->
<aop:aspectj-autoproxy>
</aop:aspectj-autoproxy>
<!-- kr.or.ddit.aop 패키지를 컴포넌트 스캔 대상으로 등록 -->
<context:component-scan base-package="kr.or.ddit.aop">
</context:component-scan>
포인트컷 표현식
ServiceLoggerAdvice.java
로보트에 : AOP대상(로그, 보안, 트랜잭션, 에러)
포인트컷 표현식. 별쩜쩜별괄호쩜쩜괄호
excution : 포인트컷(대상(메소드)을 선별하는 것) 지정자
(* : 임의의 1개의 리턴타입
.. : 임의의 0개 이상
kr.or.ddit.*..*(..) : 패키지 밑의 각각의 패키지가 있고 그 하위에 모든 파일/패키지 각각의 메소드가 있고
(..) : 모든 파라미터
결론 : 포인트컷에 포함된 메서드를 대상으로 그 메서드가 실행되기 전에 로그를 출력해보자
Before어드바이스 : 조인 포인트 전에 실행됨. 예외가 발생하는 경우만 제외하고 항상 실행됨
- @Before 예시
ServiceLoggerAdvice.java
package kr.or.ddit.aop;
import java.util.Arrays;
import org.aopalliance.intercept.Joinpoint;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
@Aspect
public class ServiceLoggerAdvice {
@Before("execution(* kr.or.ddit.*..*(..))")
public void startLog(JoinPoint jp) {
log.info("startLog");
//.getSignature() : 어떤 클래스의 어떤 메서드가 실행되었는지 보여줌. 파라미터 타입은 무엇인지 보여줌
// kr.or.ddit.service.BoardService.register(BoardVO)
log.info("startLog : " + jp.getSignature());
//.getArgs() : 전달 된 파라미터 정보를 보여줌
// [BoardVO [boardNo=127,title=개똥이]]
log.info("startLog : " + Arrays.toString(jp.getArgs()));
}
}
=> Maven build 하기
+보이지 않을 시
log4j.xml
debug 로 수정해서 테스트 해보기 => 다 하면 원래대로 되돌려야 함
<!-- Application Loggers -->
<logger name="kr.or.ddit">
<level value="debug" />
</logger>
<root>
<priority value="debug" />
<appender-ref ref="console" />
</root>
- @AfterReturning 추가
ServiceLoggerAdvice.java
하단에 추가
package kr.or.ddit.aop;
import java.util.Arrays;
import org.aopalliance.intercept.Joinpoint;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
@Aspect
public class ServiceLoggerAdvice {
@Before("execution(* kr.or.ddit.*..*(..))")
public void startLog(JoinPoint jp) {
log.info("startLog");
//.getSignature() : 어떤 클래스의 어떤 메서드가 실행되었는지 보여줌. 파라미터 타입은 무엇인지 보여줌
// kr.or.ddit.service.BoardService.register(BoardVO)
log.info("startLog : " + jp.getSignature());
//.getArgs() : 전달 된 파라미터 정보를 보여줌
// [BoardVO [boardNo=127,title=개똥이]]
log.info("startLog : " + Arrays.toString(jp.getArgs()));
}
//어드바이스
//조인 포인트(메소드)가 정상적으로 종료한 후에 실행됨. 예외 발생 시 실행 안됨
@AfterReturning("execution(* kr.or.ddit.*..*(..))")
public void logReturning(JoinPoint jp) {
log.info("logReturning");
//.getSignature() : 어떤 클래스의 어떤 메서드가 실행되었는지 보여줌. 파라미터 타입은 무엇인지 보여줌
// kr.or.ddit.service.BoardService.register(BoardVO)
log.info("logReturning : " + jp.getSignature());
}
}
- @AfterThrowing
ServiceLoggerAdvice.java
하단에 추가
//어드바이스
//조인 포인트(메서드)에서 예외 발생 시 실행. 예외가 발생 안 되면 실행 안 됨
//throwing : 예외 발생 시 메시지가 들어감
@AfterThrowing(pointcut="excution(* kr.or.ddit.*..*(..))", throwing="e")
public void logException(JoinPoint jp, Exception e) {
log.info("logException");
//.getSignature() : 어떤 클래스의 어떤 메서드가 실행되었는지 보여줌. 파라미터 타입은 무엇인지 보여줌
// kr.or.ddit.service.BoardService.register(BoardVO)
log.info("logException : " + jp.getSignature());
//예외 메시지를 보여줌
log.info("logException : " + e);
}
- @After
ServiceLoggerAdvice.java
하단에 추가
//어드바이스
//조인 포인트(메서드)를 완료한 후에 실행함. 예외 발생이 되더라도 항상 실행 됨
@After("execution(* kr.or.ddit.*..*(..))")
public void endLog(JoinPoint jp) {
log.info("endLog");
//.getSignature() : 어떤 클래스의 어떤 메서드가 실행되었는지 보여줌. 파라미터 타입은 무엇인지 보여줌
// kr.or.ddit.service.BoardService.register(BoardVO)
log.info("endLog : " + jp.getSignature());
//.getArgs() : 전달 된 파라미터 정보를 보여줌
// [BoardVO [boardNo=127,title=개똥이]]
log.info("endLog : " + Arrays.toString(jp.getArgs()));
}
- @Around 예시
ServiceLoggerAdvice.java
하단에 추가
//ProceedingJoinPoint : around 어드바이스에서 사용함
// 횡단관심사 - 포인트컷 대상 core메소드 - 횡단관심사
// 스프링프레임워크가 컨트롤 하고 있는 비즈니스로직 호출을 가로챔. 책임이 around 어드바이스로 전가됨
// 그래서 비즈니스 메소드에 대한 정보를 around 어드바이스 메소드가 가지고 있어야 하고
// 그 정보를 스프링 컨테이너가 around 어드바이스 메소드로 넘겨주면
// ProceedingJoingPoint 객체로 받아서 around 어드바이스가 컨트롤 시 활용함
@Around("execution(* kr.or.ddit.*..*(..))")
public Object timeLog(ProceedingJoinPoint pjp) throws Throwable{
//메소드 실행 직전 시간 체킹
long startTime = System.currentTimeMillis();
log.info("pjpStart : " + Arrays.toString(pjp.getArgs()));
//메소드 실행
Object result = pjp.proceed();
//메소드 실행 직후 시간 체킹
long endTime = System.currentTimeMillis();
log.info("pjpEnd : " + Arrays.toString(pjp.getArgs()));
//직후 시간 - 직전 시간 => 메소드 실행 시간
log.info(pjp.getSignature().getName() + " : " + (endTime - startTime));
return result;
}
=> log.info 를 사용하지 않아도 편히 이용할 수 있음
'스프링 프레임워크' 카테고리의 다른 글
[스프링 프레임워크] 27장 스프링 시큐리티1 (0) | 2024.05.16 |
---|---|
[스프링 프레임워크] 26장 트랙잭션, 예외처리 (0) | 2024.05.16 |
[스프링 프레임워크] 24장 페이징 처리 (0) | 2024.05.14 |
[스프링 프레임워크] 23장 숨겨진 필드요소, 라벨요소, 입력값 검증 (0) | 2024.05.13 |
[스프링 프레임워크] 22장 공통 코드로 관리 (0) | 2024.05.10 |