본문 바로가기

spring/AOP

AOP 구현하는 방법1 : Proxy를 통한 간접 접근

상황


핵심 로직 클래스인 MessageImpl class의 sayHi() 메소드가 실행되기 전과 이후에 어떤 모듈을 실행해야만 한다. AOP를 이용하여 처리한다.



Proxy를 통한 간접 접근


스프링 설정파일을 보면 bean태그가 id="hiAdvisor" 인 놈이 있다.


org.springframework.aop.support.DefaultPointcutAdvisor 클래스의 객체를 생성한 것인데 property로 advice와 pointcut을 가지고 있다.


예제에서 설정파일을 보면 property태그의 name이 advice와, pointcut 인 놈들을 property로 가지고 있는데


name이 advice인 놈에게는 실행할 모듈을 지정해 놓고(무엇을 할지 : 예제에서는 LoggingAdvice class가 되겠다.)


name이 pointcut인 놈에게는 어떤 메소드가 실행 될 때 실행할지 지정해 놓는다.(언제 : 예제에서는 sayHi 메소드가 되겠다.)




더 자세한 설명을 하자면 아래와 같다.


** spring aop 라이브러리가 필요하다.(아래는 maven 설정 할때)

<dependency>

<groupId>aspectj</groupId>

<artifactId>aspectjtools</artifactId>

<version>1.5.4</version>

</dependency>

<dependency>

<groupId>aspectj</groupId>

<artifactId>aspectjweaver</artifactId>

<version>1.5.4</version>

</dependency>


1.

스프링에서 제공하는 org.springframework.aop.framework.ProxyFactoryBean 클래스의 객체를 생성(예제에서는 proxy라는 id로 생성)하고 property로 target과 interceptorNames를 준다.

target은 sayHi 메소드가 담긴 클래스가 될것이고 interceptorNames는 Advisor가 된다. 

Advisor 는 아래의 설명을 따라가자.


  <!-- Proxy를 통한 간접 접근 -->

<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">

<property name="target">

<ref local="targetBean"/> <!-- local은 현재 설정파일, bean은 전체 설정파일 -->

</property>

<property name="interceptorNames">

<list>

<value>hiAdvisor</value> <!-- Advisor 기술 -->

</list>

</property>

</bean>


2.

Advisor(쉽게 말해 무엇을 언제 할지 정하기)를 사용하기 위해서는 스프링에서 제공하는 org.springframework.aop.support.DefaultPointcutAdvisor 클래스를 사용해야 한다.

(예제 에서는 <bean id="hiAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"> 로 객체를 생성함)


3.

pointcut property 태그 부분에 클래스를 아래와 같이 지정해 주고 패턴은 표현식을 이용하여 패턴을 정의한다.(패턴은 구글링하기를 권장)

     <bean class="org.springframework.aop.support.JdkRegexpMethodPointcut">

<property name="pattern">

<value>.*say*(..)</value> <!-- 정의된 패턴값과 일치되는 메소드가 실행될 경우 AOP 발동 -->

</property>

</bean>


4.

advice에서 지정한 클래스는 MethodInterceptor를 구현(Implement)하고 invoke 메소드를 재정의 하면 된다.

invoke 메소드는 MethodInvocation 타입의 변수를 인자로 가지고 있는데

그 변수로 아래와 같이 원래 실행하려 했던 메소드를 실행할 수 있다. 


Object object = invocation.proceed();


invocation이 MethodInvocation 타입의 변수라는 것을 참고하자.


위의 문장이 기준이 된다.


이 문장보다 먼저 실행되는 명령은 선처리가 될 것이고


이 문장보다 나중에 실행되는 명령은 후처리가 될 것이다.


아래의 에제에서 LoggingAdvice class를 보면 이해 할 수 있을 것이다.





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

스프링 설정 파일


<?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/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd

http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">


<!-- Target -->

<bean id="targetBean" class="pack.MessageImpl">

<property name="name" value="한국인"/>

</bean>

<!-- Proxy를 통한 간접 접근 -->

<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">

<property name="target">

<ref local="targetBean"/> <!-- local은 현재 설정파일, bean은 전체 설정파일 -->

</property>

<property name="interceptorNames">

<list>

<value>hiAdvisor</value> <!-- Advisor 기술 -->

</list>

</property>

</bean>

<!-- Advice(Aspect) : Target으로 weaving -->

<bean id="loggingAdvice" class="advice.LoggingAdvice"/>

<!-- Advisor(Advice + pointcut) -->

<bean id="hiAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">

<property name="advice"> 

<ref local="loggingAdvice"/>

</property>

<property name="pointcut">

<bean class="org.springframework.aop.support.JdkRegexpMethodPointcut"> 

<property name="pattern">

<value>.*say*(..)</value>

</property>

</bean>

</property>

</bean>

<!-- 

logginAdvice 안에는 MethodInterceptor 가 구현되어있고 invoke 메소드가 재정의 되어있다.

재정의된 메소드 invoke가 pointcut에 지정된 메소드가 실행됬을때 반응하여 실행되는 것이다.

-->

</beans>


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

MethodInterceptor 구현한 Advice class


package advice;


import org.aopalliance.intercept.MethodInterceptor;

import org.aopalliance.intercept.MethodInvocation;


public class LoggingAdvice implements MethodInterceptor{

//MethodInterceptor는 Around Advice를 지원

@Override

public Object invoke(MethodInvocation invocation) throws Throwable {

   //핵심 로직 전에 수행

String methodName = invocation.getMethod().getName(); // target 메소드 명 얻기

System.out.println("호출될 메소드 이름 : " + methodName);

Object object = invocation.proceed(); //핵심 로직 수행 - sayHi();

//핵심 로직 후에 수행

System.out.println(methodName + "핵심 로직 수행 후 마무리 작업 수행 ");

return object;

}

}


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

핵심 로직 클래스


package pack;


//핵심 로직 클래스 - Target Class

public class MessageImpl implements MessageInter {

private String name;

public void setName(String name) {

this.name = name;

}


@Override

public void sayHi() {

System.out.println(name + "님! 비즈니스 로직 처리 중");

//시간 끌기

int t = 0;

while(t < 5){

try {

Thread.sleep(1000);

System.out.print(".");

t++;

} catch (Exception e) {

e.printStackTrace();

}

}

System.out.println("sayHi 처리 완료");

}


}


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

Interface


package pack;


public interface MessageInter {

void sayHi(); // AOP가 적용될 메소드

}


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

메인 클래스


package pack;


import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;


public class Main {

public static void main(String[] args) {

ApplicationContext context = new ClassPathXmlApplicationContext("initaop.xml");

MessageInter inter = (MessageInter)context.getBean("proxy");

inter.sayHi();

}

}