AOP를 이용한 Spring 실습-2

유동현·2022년 11월 12일
0
post-thumbnail

MethodInterceptor를 통한 프록시 생성

① 주 업무가 implements할 인터페이스 생성

/*======================

 Calculator.java
 - 인터페이스
 ===========================*/

// ※ 스프링에서 AOP 기법을 적용하기 위해서는
//  대상 객체가 인터페이스를 구현하고 있어야 한다.
package com.test.spr;

public interface Calculator
{
	
	//주 업무(core concern) 진행을 위한 메소드 선언
	// --- 덧셈 , 뺄셈, 곱셈 나눗셈 연산 수행
	
	public int add(int x,int y);
	public int sub(int x,int y);
	public int mul(int x,int y);
	public int div(int x,int y);
	

}



② 주 업무 클래스 생성

package com.test.spr;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.StopWatch;

public class CalculatorImpl implements Calculator
{

	// 인터페이스로부터 상속받은 메소드 재정의를 통해
	// 주 업무(core concern) 진행(수행)을 위한 메소드 구현

	@Override
	public int add(int x, int y)
	{
		// 반환할 결과값
		int result = 0;
		// 주 엄무(core concern) 실행 내용
		result = x + y;
		System.out.printf("%d + %d = %d\n", x, y, result);

		// 최종 결과값 반환
		return result;
	}



③ 프록시 클래스 생성

/*
 CalculatorAspect.java
 -보조 엄무 수행 클래스.
 - 보조 업무 적용 및 주 업무 호출 구조
 */

package com.test.spr;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.StopWatch;

// ※ Spring AOP Proxy 클래스를 설계하기 위해
// MethodInterceptor 인터페이스를 구현하는 클래스로 만든다.


public class CalculatorAspect implements MethodInterceptor
{

	//MethodInterceptor 인터페이스의 invoke() 메소드 재정의
	@Override
	public Object invoke(MethodInvocation method) throws Throwable
	{
		
		// 보조 업무 적용 및 주 업무 호출 과정 추가
		// 어제 쓴 invoke() 와 크게 다르지 않은데, 매개변수가 다름
		// 아래 invoke()에서 매개변수는 InvocationHandler의 invoke() 에서의 method 변수역할 수행함
		// 우리가 정의한 메소드 아님 → return 값은 우리가 얻으려는 값이 아님
		
		Object result = null;
		
		//보조 업무(cross-cutting-concern)설정
		Log log= LogFactory.getLog(this.getClass());
		StopWatch sw = new StopWatch();
		
		sw.start();
		log.info("처리시간 측정 시간 ============================");
		
		//주 업무 실행 내용 호출~!
		result = method.proceed();
		
		//보조 업무(cross-cutting-concern)처리
		sw.stop();
		log.info("처리시간 측정 종료=============================");
		log.info(String.format("경과시간 : %s/1000초", sw.getTotalTimeMillis()));
		
		return result;

	}
}



④ 설정정보(XML)

	<!-- ① 먼저 우리가 작성한 클래스 중에 뭘 bean으로 등록할 건지 생각 -->
	<!-- CalculatorImpl, CalculatorAspect 객체 생성해야함 -->
	
	
	<!-- CalculatorImpl 클래스의 객체 생성 및 관리를 위한 정보 전달 -->
	<bean id="cal" class="com.test.spr.CalculatorImpl"></bean>
	<!-- → 주 업무 수행하는 클래스 -->
	
	<!-- CalculatorAspect 클래스의 객체 생성 및 관리를 위한 정보 전달 -->
	<bean id="aspect" class="com.test.spr.CalculatorAspect"></bean>
	<!-- → 보조 업무 수행하는 클래스 -->
	
	
	<!-- check~!!! -->
	<!-- 스프링이 제공하는 가짜 객체(Proxy) 클래스의 객체 생성 및 관리를 위한 정보 전달 -->
	<!-- → 『ProxyFactoryBean』 → 이름만 기억해두기! 
	        ====================
	         └→ proxyFactory를 찍어내는 bean 
	         +) beanFactory : bean 찍어내는 factory
	            proxyFactory: proxy 찍어내는 factory -->
	            
	<!-- 아무 클래스나 가서 ProxyFactoryBean 입력했다가 지우면, import 구문 작성됨
		 그 import 구문 복사해서 class="" 에 작성함 -->
	<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
	<!-- └→ 너가 가진 설계도 사용해서 proxy 만들어줘 -->
	
		<!-- 주 업무 클래스의 인터페이스 정보 제공 -->
		<!-- → 『proxyInterfaces』 -->
		<!-- 단일값 넘겨주는 애가 아니니까 → list로 구성 -->
		<!-- 그런데 여기서 넘기는건 사실상 한 개 넘김 (여러개일 때는 쭉 나열하면 됨)-->
		<property name="proxyInterfaces">
			<list>
				<value>com.test.spr.Calculator</value>
			</list>
		</property>
		
		<!-- 주 업무 클래스의 객체 정보 제공 -->
		<!-- → 『target』 -->
		<!-- 주업무 수행해야 하는 거 → CalculatorImpl  
		     → 단수로 제공되는거니까 그냥 ref="cal" 참조하라고 하면 끝! 
		     그런데 다른 복수로 제공되는 것들은 list 만들어서 넘겨줘야 한다. -->
		<property name="target" ref="cal"></property>
		
		<!-- 보조 업무 클래스의 객체 정보 제공 -->
		<!-- → 『interceptorNames』 -->
	 	<!-- 보조 업무도 before/after/aroundAdvice 여러개가 있을 수 있으니 복수형으로 names! -->
	 	<!-- 그런데 여기서 넘기는건 사실상 한 개 넘김 -->
	 	<property name="interceptorNames">
	 		<list>
	 			<!-- 보조 업무는 우리가 위에서 aspect로 만들어놓았음 -->
	 			<value>aspect</value>
	 		</list>
	 	</property>
	</bean>
	<!-- 
		proxy 쓸 때, property name으로
		proxyInterfaces, target, interceptorNames 는 고정!
		proxy가 그 값들을 필요로 함
	-->

즉 이전에 개발자가 Main 에서 사용하던 proxy.newproxyInstance(); 가 하던일을 설정 정보로써 넘겨 Spring이 자체적으로 객체를 관리하게 만드는것



⑤ Main에서의 사용

	//주 업무 실행에 대한 테스트(Spring AOP 기법 적용 후)
		ApplicationContext context = new ClassPathXmlApplicationContext("config.xml");
		
		//Spring 에서 제공하는 proxy를 활용하여
		//보조 업무와 결합된 형태로 업무 처리~~!!!
		//인터페이스 형태의 변수명에 = new 인터페이스를 구현한 객체();
		Calculator cal = context.getBean("proxy", Calculator.class);
		
		int add = cal.add(10, 20);
		System.out.println(add);

결과

0개의 댓글