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

[Spring] 스프링 AOP의 Around Advice이용하기 + Log4j 활용

by 파프리카_ 2020. 10. 29.
728x90
반응형

AOP Around Advice 연습예제

- Around Advice는 before, atfer, after-returning, after-throwing

네가지 advice를  모두 처리할 수 있다.


[ Spring, AOP, Log4j 환경 설정 준비 ]

1. convert to maven project 

 

2. 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>

 

3. src 내에 log4j.xml 생성 - scr에 만드는 이유 : class complie 시 컴파일 진행 대상이 되도록!


/log4j.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 설정 
             : spring-config.xml에서 다른 Service들이 loggingService를 aop하고 있으므로!
        	 - level은 info 이상 로깅되도록 하고
        	 - root level의 로깅 정책을 이어받지 않는다는 설정을 한다.
        	   (additivity = "false")
        -->	
        <Logger name="aop.common" level="info" additivity="false">
        	<AppenderRef ref="file"/>
        </Logger>
    </Loggers>
</Configuration>

Spring config 생성

/spring-config.xml (aop 설정 체크!)

<?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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
	
	<bean id="personSerivce" class="kosta.model.PersonService"></bean>
	<bean id="productService" class="kosta.model.ProductService"></bean>
	
	<!-- AOP 설정 -->
	<!-- 1. 횡단 관심사항 정의 bean -->
	<bean id="loggingSerivce" class="aop.common.AroundLoggingAspect"></bean>
	
 	<aop:config>
		<!-- 횡단 관심 사항 대상  -->
		<aop:aspect ref="loggingSerivce">
			<!-- 2. pointcut 지정 -->
			<aop:pointcut expression="within(kosta.model..*)" id="pt"/>
			<!-- 3. advice 지정 : around 로 설정-->
			<aop:around method="logging" pointcut-ref="pt"/>
		</aop:aspect>
	</aop:config> 
</beans>

/kosta.model

 

Exception 존재 이유 : 프로그램 수행 시, 예외가 발생했을 때 그에 맞는 적절한 대처를 하기 위함이다!

/InventoryException.java

package kosta.model;

public class InventoryException extends Exception{
	/**
	 * 객체 직렬화 : 메모리에 있는 object가 외부로 전송될 수 있는 상태로 만들기
	 * 기입 했을 경우 - 이후에 instacne varible or method 추가 시 계속 동작
	 * 기입 안하면 - 이후 코드 수정 시 자동으로 serial no가 생성되어, class 단계에서 문제가 발생한다.
	 */
	private static final long serialVersionUID = -5858290483290495200L;
	
	public InventoryException() { }
	
	public InventoryException(String message) {
		super(message);
	}
}

 

/PersonService.java

package kosta.model;

public class PersonService {
	
	public void registerPerson() {
		System.out.println("registerPerson");
	}
	
	//매개변수 있는 경우
	public void insertId(String id) {
		System.out.println("insertId: "+ id);
	}
	
	//리턴 값이 있는 경우
	public String getNick() {
		return "아이유";
	}
}

 

/ProductService.java

package kosta.model;

import java.sql.SQLException;

public class ProductService {
	
	public void registerProduct() {
		System.out.println("registerProduct");
	}
	//매개변수 있는 경우
	public void insertProduct(String id, String name) {
		System.out.println("insertProduct: "+ id + " " + name);
	}
	
	//리턴 값이 있는 경우
	public String getMaker() {
		return "오뚜기";
	}
	
	//Exception Throws
	public void sellProduct(int count) throws InventoryException{
		if (count < 1) 
			throw new InventoryException("잘못된 상품 수");
		System.out.println("sell " + count);
	}
	
	//SQL Exception
	public void buyProduct(int count) throws SQLException{
		if (count < 1)
			throw new SQLException();
		System.out.println("buy " + count);
	}
}

/aop.common

 

횡단 관심사항 정의 클래스 + Around Advice 적용

/AroundLoggingAspect.java

package aop.common;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;

import kosta.model.InventoryException;

//횡단 관심사항 정의 클래스
public class AroundLoggingAspect {
	Logger log = LogManager.getLogger(AroundLoggingAspect.class);
	
	//ProceedingJoinPoint : JoinPoint보다 세밀한 조정 가능
	public Object logging(ProceedingJoinPoint point) throws Throwable {
		
		// class의 method에 매개변수가 있는 경우 출력하여 확인할 수 있다.
		Object params[] = point.getArgs();
		for (int i = 0; i < params.length; i++) {
			System.out.println("* AOP parameter: "+ params[i]);
		}
		
		// core concern에 return 값이 있는 경우
		System.out.println("> AOP Before Advice ");
		Object retValue = null;
		
		try {
			//proceed() : 실제 대상(target method)을 실행
			// -> core concern 을 실행시킴
			retValue = point.proceed();
			
			// After-returning advice : 대상 메서드의 리턴값 제어
			// instanceof : 데이터 타입이 같은지 확인하는 keyword
			if(retValue != null && retValue instanceof String 
					&& retValue.equals("아이유")) {
				retValue += "> After-returning Advice ";
			}
			
		// After-throwing advice : 대상 메서드의 예외 및 에러를 제어
		} catch (InventoryException ie) {
			System.out.println("> After-throwing Advice: 재고부족으로 공급처에 메시지 전달");
			throw ie;
		} catch (Throwable e) {
			//System.out.println(e.getMessage());
			throw e; //기존 흐름 그대로 유지
			
		} finally {
			//After adivce : 예외발생 유무 관련없이 무조건 실행 
			String cn = point.getTarget().getClass().getName();
			String mn = point.getSignature().getName();
			System.out.println("> After Advice: " + cn + "|" + mn);
			//log.info("AOP : " + cn + " | " + mn);
		}
			
		return retValue;
	}
}

/test

 

/TestAOP.java

package test;

import java.sql.SQLException;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import kosta.model.InventoryException;
import kosta.model.PersonService;
import kosta.model.ProductService;

public class TestAOP {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext factory
			= new ClassPathXmlApplicationContext("spring-config.xml");
		
		PersonService person = (PersonService) factory.getBean("personSerivce");
		person.insertId("java");
		System.out.println(person.getNick());
		person.registerPerson();
		
		System.out.println("-----------------------------------------");
		
		ProductService product = (ProductService) factory.getBean("productService");
		product.registerProduct();
		product.insertProduct("java", "book");
		System.out.println(product.getMaker());
		
		//Exception 발생하는 메서드
		try {
			product.sellProduct(-3);
		} catch (InventoryException e) {
			System.out.println("Excpetion 전달받아 메인에서 예외처리");
			System.out.println("main: "+ e.getMessage());
		}
		
		try {
			product.buyProduct(-1);
		} catch (SQLException e) {
			System.out.println("buy에서 SQLException 발생해서 메인에서 예외처리");
		}
		
		factory.close();
	}
}

 


[ 결과 ]

728x90
반응형