본문 바로가기
Java Web Programming/6. Spring | MyBatis

[Spring] 스프링 AOP Annotation 어노테이션

by 파프리카_ 2020. 11. 5.
728x90
반응형

기존 XML 방식으로 AOP 적용한 예시

 

[Spring] 스프링 AOP의 Around Advice를 통한 성능 확인 (+Log4j, StopWatch 라이브러리)

[ 요구사항 시나리오] 기존 시스템의 성능을 체크한다. 서비스하는 각 메서드의 소요 시간을 레벨 별로 체크해서, 리포트를 제출해야 한다. 리포트는 0.5초 ~ 1초 소요 시에는 log.warn(target class명,

creamilk88.tistory.com


> Spring AOP Annotation 종류

  1. @Component
    - 컴포넌트 어노테이션을 명시해 스프링 컨테이너가 객체 생성하도록 한다.
  2. @Aspect
    - 스프링 컨테이너에 AOP 담당 객체임을 알린다.
  3. @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();
	}
}

[ 결과 ]

728x90
반응형