java-school logo

Spring AOP

자바 은행에서 로깅을 스프링 AOP로 구현해 보자. 아래 의존성을 pom.xml에 추가한다.

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-aspects</artifactId>
	<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjweaver</artifactId>
	<version>1.8.10</version>
</dependency>

TestLogger.java를 아래와 같이 작성한다.

TestLogger.java
package net.java_school.commons;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Aspect
public class TestLogger {
	Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@AfterReturning("execution(* net.java_school.bank.BankDao.deposit(..))")
	public void depositLog(JoinPoint point) {
		Object[] a = point.getArgs();
		String accountNo = (String) a[0];
		Long amount = (Long) a[1];
		String methodName = point.getSignature().getName();
		logger.debug("{}|{}|{}", methodName, accountNo, amount);
	}
	
	@AfterReturning("execution(* withdraw(..)) && args(accountNo, amount)")
	public void withdrawLog(String accountNo, long amount) {
		logger.debug("WITHDRAW|{}|{}", accountNo, amount);
	}
	
}

자바 소스에서 로그 관련 코드를 제거한다.

ShinhanBankDao.java
package net.java_school.bank;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class ShinhanBankDao implements BankDao {
	static final String URL = "jdbc:oracle:thin:@127.0.0.1:1521:XE";
	static final String USER = "scott";
	static final String PASSWORD = "tiger";
	
	//중간 생략..
	
	@Override
	public void deposit(String accountNo, long amount) {
		Connection con = null;
		PreparedStatement pstmt = null;
		
		String sql = "UPDATE bankaccount " +
				"SET balance = balance + ? " +
				"WHERE accountNo = ?";
		
		try {
			con = getConnection();
			pstmt = con.prepareStatement(sql);
			pstmt.setLong(1, amount);
			pstmt.setString(2, accountNo);
			pstmt.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			close(null, pstmt, con);
		}
		
	}

	@Override
	public void withdraw(String accountNo, long amount) {
		Connection con = null;
		PreparedStatement pstmt = null;
		
		String sql = "UPDATE bankaccount " +
				"SET balance = balance - ? " +
				"WHERE accountNo = ?";
		
		try {
			con = getConnection();
			pstmt = con.prepareStatement(sql);
			pstmt.setLong(1, amount);
			pstmt.setString(2, accountNo);
			pstmt.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			close(null, pstmt, con);
		}
		
	}
	
	//중간 생략..
	
}	

XML 설정

applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:aop="http://www.springframework.org/schema/aop" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	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.xsd">
	
	<aop:aspectj-autoproxy />

	<bean id="testLogger" class="net.java_school.commons.TestLogger" />
    
	
    <bean id="bankUi" class="net.java_school.bank.BankUi">
	    <property name="stream" value="#{T(System).out}" />
	    <property name="bank" ref="shinhanBank" />
    </bean>

    <bean id="shinhanBank" class="net.java_school.bank.ShinhanBank">
	    <property name="dao" ref="shinhanBankDao" />
    </bean>

    <bean id="shinhanBankDao" class="net.java_school.bank.ShinhanBankDao">
    </bean>
		
</beans>

테스트하기 전에 BankUi.java의 메인 메서드를 아래를 참조해 수정한다.

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); //XML
//AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(BankConfig.class); //JavaConfig

JavaConfig 설정

BankConfig.java
package net.java_school.bank;

import net.java_school.commons.TestLogger;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy
public class BankConfig {
	
	@Bean
	public TestLogger testLogger() {
		return new TestLogger();
	}

	@Bean
	public BankDao shinhanBankDao() {
		return new ShinhanBankDao();
	}

	@Bean
	public Bank shinhanBank() {
		Bank bank = new ShinhanBank();
		bank.setDao(shinhanBankDao());
		return bank;
	}

	@Bean
	public BankUi bankUi() {
		BankUi ui = new BankUi();
		ui.setBank(shinhanBank());
		ui.setStream(System.out);
		return ui;
	}
	
}	

테스트하기 전에, applicationContext.xml 설정을 모두 주석 처리한다. BankUi.java의 메인 메서드를 아래를 참조해 수정한다.

//ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); //XML
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(BankConfig.class); //JavaConfig

최종 소스

예제 다운로드

참고