본문 바로가기
홈페이지 만들기/Spring

스프링 Aspect 정리

by 리틀홍콩 2015. 4. 14.
728x90

Aspect와 관련된 모든 세팅은

   나의 기준으로 resource -> applicationContext.xml

에서 담당한다.

 

 

=== applicationContext.xml ===========================

 

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:context="http://www.springframework.org/schema/context"
 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-3.0.xsd
                     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
                     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

 


 <context:component-scan base-package="com.packagenm1.packagenm2"/>

 <aop:aspectj-autoproxy /> <!-- aop관련설정 annotation 처럼 관리 가능 -->


 <!-- JdbcTemplate 클래스 등록 -->
 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
  <property name="dataSource" ref="dataSource" />
 </bean>

 

 <!-- DataSource클래스 등록 -->
 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
  <property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
  <property name="username" value="hr" />
  <property name="password" value="hr" />
 </bean>

 

</beans>

======================================================

** <aop:aspectj-autoproxy />

이거 하나만 있으면 component-scan 처럼 Log를 담당하게 될 클래스를 메모리상에 올릴 수 있다. 물론 이것만 설정한다고 되는게 아니라, 해당 클래스에서도 작업을 해주어야한다.

 

 

=== Log.java ==========================================

 

package com.packagenm1.packagenm2.common;

 

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Service;

 

@Service("log")
@Aspect
public class LogAdvice {
 
 @Before("PointcutCommon.allPointCut()") 
 public void printLog(JoinPoint jp) {
  String method = jp.getSignature().getName();
  Object[] args = jp.getArgs();
  
  System.out.println("[사전 처리] " + method + "() 매소드  ARGS 정보 : " + args[0].toString());
 }
}

======================================================

** @Aspect
위처럼 클래스상단에 선언을 해주어야 진짜 메모리상에 클래스가 올라간다. xml에서 작업해주었던건 이것을 위한 사전작업.

 

** @Before("PointcutCommon.allPointCut()") 

매소드 실행 전, 실행이 되는 매소드를 지정한다. 이것을 통해 Log를 찍어도 가능.

괄호안을 자세히 보면 PointcutCommon.allPointCut() 라고 작성이 되어 있는데 이것은,

PointcutCommon 클래스 안에 allPointCut 라고 작성되어있는 매소드의 @Pointcut 정보를 가져오기 위한 내용이다.

 왜 이렇게 클래스를 따로 두어 관리하는 이유는 각각의 Service클래스마다

 @Pointcut("execution(* com..package2..impl.*Impl.*(..))")
 public void allPointCut(){}

이런식으로 작성이 되어지며, 이렇게 되면 불필요한 소스가 증가하므로, 클래스에 모아서 관리한다.

 

PointcutCommon 의 소스는 하단에 있다.

 

=== PointcutCommon .java ==========================================

 

package com.packagenm1.packagenm2.common;

 

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

 

@Aspect
public class PointcutCommon {


 @Pointcut("execution(* com..packagenm2..impl.*Impl.*(..))")
 public void allPointCut(){}
 
 @Pointcut("execution(* com.packagenm1.packagenm2..impl.*Impl.get*(..))")
 public void getPointCut(){}
 
}

==================================================================

** @Pointcut("execution(* com..packagenm2..impl.*Impl.*(..))")
execution() 안의 파라메터 정리

 첫번째 인자 : 리턴타입(void, !void, int, String ....)

 두번째 인자 : 패키지명 (com..packagenm2 의 경우 안에 ..(점점)은  com 으로 시작하고 마지막으로 impl로 끝나는 모든 패키지를 가르킴

 세번째 인자 : 클래스( *impl의 경우, 다들 알다시피 impl로 끝나는 클래스 전부)

 네번째 인자 : *() 매소드명 (get*()의 경우 get으로 시작하는 모든 매소드)

   ※ *(..) 은 매개변수의 이름,개수,타입 인데... 보통은 무시하고 다들 .. 을 쓴다고 한다.

 

 

@AfterThrowing

@AfterReturning

@Around

은 Before와 조금 다르다.

 

먼저 AfterThrowing

매소드위에 다른건 before와 동일하고

 

==================================================================

 @AfterThrowing(pointcut="PointcutCommon.allPointCut()",  throwing="exceptObj")
 public void exceptionLog(JoinPoint jp, Exception exceptObj) {
  String method = jp.getSignature().getName();
  
  System.out.println("[예외 처리] " + method +
   "() 메소드 수행중 발생된 예외 메시지 : " + exceptObj.getMessage());
 }

==================================================================

** 우선 파라메터를 하나 더 받고 ( Exception exceptObj ) 그 파라메터의 값을 지정하기 위하여 @AfterThrowing 위에 throwing="exceptObj"을 지정해야지 오류메시지가 나오지 않는다.

 

다음 AfterReturning

 

==================================================================

@AfterReturning(pointcut="PointcutCommon.getPointCut()", returning="returnObj")

 Object returnObj에 맞는 returnObj을 설정해주어야 하므로 추가 작업이 들어간다.
 public void afterLog(JoinPoint jp, Object returnObj) {
  String method = jp.getSignature().getName();  
  
  System.out.println("[사후 처리] " + method +
   "() 메소드 리턴값 : " + returnObj.toString());
 }

==================================================================

** afterthrowing처럼 파라메터 및 상단에 파라메터의 값을 지정해주어야한다.

 

다음 Around

==================================================================

@Around("PointcutCommon.allPointCut()")
 public Object aroundLog(ProceedingJoinPoint pjp) throws Throwable

// return타입과 매개변수가 정해져있다. 유일하게 around에서만 ProceedingJoinPoint를 쓴다 
  Object obj = null;
  
  System.out.println("---< before logic >---");
  
  long startTime = System.currentTimeMillis();
  obj = pjp.proceed();
  long endTime = System.currentTimeMillis();
  
  System.out.println("---< after logic >---");
  System.out.println("실행시간 : " + (endTime-startTime));
  
  return obj;
 }

==================================================================

** ProceedingJoinPoint 은 상단에 적힌대로 return타입이 정해져있고, JoinPoint를 상속받는 클래스가 ProceedingJoinPoint 인데 상속받은 이유는 기존 JoinPoint의 매소드는 그대로 사용하되 proceed()를 사용하기 위해서다.

 

 

 

댓글