[Spring] 스프링 AOP의 Around Advice를 통한 성능 확인 (+Log4j, StopWatch 라이브러리)
[ 요구사항 시나리오] 기존 시스템의 성능을 체크한다. 서비스하는 각 메서드의 소요 시간을 레벨 별로 체크해서, 리포트를 제출해야 한다. 리포트는 0.5초 ~ 1초 소요 시에는 log.warn(target class명,
creamilk88.tistory.com
> Spring AOP Annotation 종류
- @Component
- 컴포넌트 어노테이션을 명시해 스프링 컨테이너가 객체 생성하도록 한다. - @Aspect
- 스프링 컨테이너에 AOP 담당 객체임을 알린다. - @Around
- 횡단관심사항의 대상 지정과 적용 시점을 지정한다. (pointcut, advice)
> 어노테이션 방식의 Advice와 pointcut 설정
@Around("execution(public * kosta.. *Service.*(..))")
- Advice : 횡단 관심사항 적용 시점 ( ex. 메서드 실행 전, 후, 리 턴 시, 예외 발생 시)
- pointcut : 횡단 관심사항 적용 대상 지정 (execution, within, bean)
maven pom 설정
/pom.xml
<dependencies>
<!-- Spring Library -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.14.RELEASE</version>
</dependency>
<!-- Log4j 라이브러리 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.12.1</version>
</dependency>
<!-- AOP Library -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.1</version>
</dependency>
</dependencies>
Spring configure 설정
* namespace에서 'aop'와 'context'를 체크해준다!
<context:component-scan> : IOC, DI, DL에 대한 설정
1) 컴포넌트 계열의 어노테이션이 명시된 클래스에 대해 객체를 생성하고,
2) DI 계열 어노테이션이 명시된 대상에 대해 해당 객체를 주입하는 역할을 한다.
+ base-package="kosta, aop.common"
: 패키지가 여러 종류일 때는 컴마(,)로 구분하여 나열한다.
/spring-config.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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
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.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!-- IOC, DI, DL에 대한 설정
: 컴포넌트 계열의 어노테이션이 명시된 클래스에 대해
-> 객체를 생성하고,
DI 계열 어노테이션이 명시된 대상에 대해
-> 해당 객체를 주입하는 역할을 한다.
+ 패키지가 여러 종류일 때는 컴마(,)로 구분하여 나열한다.
-->
<context:component-scan base-package="kosta, aop.common"></context:component-scan>
<!-- 어노테이션 기반 AOP 설정 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
Log4j 설정
/log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout pattern="%d %-5p [%t] %C{10} (%F:%L) - %m%n" />
</Console>
<File name="file" fileName="report.log" append="true">
<PatternLayout pattern="%d %-5p [%t] %C{10} (%F:%L) - %m%n" />
</File>
</Appenders>
<Loggers>
<Root level="warn">
<AppenderRef ref="console" />
</Root>
<!-- aop.common 하위의 패키지 이하 클래스들만 별도로 Logging 설정
- level은 info 이상 로깅되도록 하고,
- root level의 로깅 정책을 이어받지 않는다는 설정을 한다.
(additivity = "false")
-->
<Logger name="aop.common" level="warn" additivity="false">
<AppenderRef ref="console" />
<AppenderRef ref="file"/>
</Logger>
</Loggers>
</Configuration>
Model
/aop.common
* AOP 횡단 관심사항을 정의한 클래스 *
@Component
- 컴포넌트 어노테이션을 명시해 스프링 컨테이너가 객체 생성하도록 한다.
@Aspect
- 스프링 컨테이너에 AOP 담당 객체임을 알린다.
@Around
- 횡단관심사항의 대상 지정과 적용 시점을 지정한다. (pointcut, advice)
/PerformanceReportService.java
package aop.common;
/* AOP 횡단 관심사항을 정의한 클래스 */
import org.apache.logging.log4j.LogManager;
//Around Advice를 이용
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
@Component //컴포넌트 어노테이션을 명시해 스프링 컨테이너가 객체 생성하도록 한다.
@Aspect //스프링 컨테이너에 AOP 담당 객체임을 알린다.
public class PerformanceReportService {
Logger log = LogManager.getLogger(PerformanceReportService.class);
// 어노테이션 방식의 Advice와 pointcut 설정
@Around("execution(public * kosta..*Service.*(..))")
public Object timeCheck(ProceedingJoinPoint point) throws Throwable {
String className = point.getTarget().getClass().getName();
String methodName = point.getSignature().getName();
Object retValue= null;
StopWatch watch = new StopWatch();
try{
watch.start();
//실제 대상(core) 메서드 호출
retValue = point.proceed();
} finally { //예외 발생 여부와 관계없이 항상 시간 체크
watch.stop();
Long time = watch.getTotalTimeMillis();
if (time >= 500 && time <= 1000) {
log.warn(className+ "(" + methodName + ") : " + time);
} else if (time > 1000) {
log.error(className+ "(" + methodName + ") : " + time);
}
}
return retValue;
}
}
/kosta.board
@Service
- 비즈니스 계층(서비스 계층)에 적용하는 어노테이션
/BoardService.java
package kosta.board;
import org.springframework.stereotype.Service;
@Service
public class BoardService {
public String find(){
try {
Thread.sleep(100); //0.1초
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("find board");
return "게시물정보";
}
public String findAllList(){
try {
Thread.sleep(700); //0.7초
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("findAllList board");
return "게시물 리스트";
}
}
/kosta.member
/MemberNotFoundException.java
package kosta.member;
public class MemberNotFoundException extends Exception {
private static final long serialVersionUID = 1L;
public MemberNotFoundException(String message){
super(message);
}
}
@Service
- 비즈니스 계층(서비스 계층)에 적용하는 어노테이션
/MemberService.java
package kosta.member;
import org.springframework.stereotype.Service;
@Service
public class MemberService {
public void findMember(){
try {
Thread.sleep(600); //0.6초
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("find member");
}
public void findAllMember(){
try {
Thread.sleep(1800); //1.8초
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("findAllMember");
}
public void deleteMember(String id) throws MemberNotFoundException{
try {
Thread.sleep(900); //0.9초
} catch (InterruptedException e) {
e.printStackTrace();
}
if(id==null)
throw new MemberNotFoundException("member not found!!!");
System.out.println("deleteMember");
}
}
Test
/TestPerfomanceCheck.java
package test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import kosta.board.BoardService;
import kosta.member.MemberNotFoundException;
import kosta.member.MemberService;
/*
* 기존 시스템의 성능을 체크한다.
- 서비스하는 각 메서드의 소요 시간을 레벨 별로 체크해서, 리포트를 제출해야 한다.
리포트는
0.5초 ~ 1초 소요 시에는 log.warn(target class명, 메서드 소요시간)
1초 초과 시에는 log.error(target class명, 메서드 소요시간)
* 유의사항 : Exception 발생 여부와 관계 없이, 모든 서비스 계열의 모든 메서드가 체크되어야 한다.
*/
public class TestPerfomanceCheck {
public static void main(String[] args) {
ClassPathXmlApplicationContext factory
= new ClassPathXmlApplicationContext("spring-config.xml");
BoardService bs = (BoardService) factory.getBean("boardService");
MemberService ms = (MemberService) factory.getBean("memberService");
System.out.println("> main: " + bs.find());
System.out.println("> main: " + bs.findAllList());
ms.findMember();
ms.findAllMember();
try {
ms.deleteMember(null); // 일부러 exception 내기
} catch (MemberNotFoundException e) {
System.out.println("> main: " + e.getMessage());
}
factory.close();
}
}
[ 결과 ]

'Java Web Programming > 6. Spring | MyBatis' 카테고리의 다른 글
[SpringMVC] has a 관계 - Annotation기반의 SpringMVC 연습 2! (0) | 2020.11.09 |
---|---|
[SpringMVC] Annotation 어노테이션 기반의 SpringMVC 연습! (2) | 2020.11.05 |
[Spring] 스프링 IOC/DI Annotation 어노테이션 (0) | 2020.11.05 |
[SpringMVC] xml 기반으로 SpringMVC 패턴 적용해보기 (0) | 2020.11.04 |
[Spring/MyBatis] Spring MVC Pattern 적용해보기 (0) | 2020.11.04 |