AOP를 이용한 Spring 실습

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

JDK가 제공하는 동적 프록시 기술 (InvocationHandler)

지금 사용하는 기술은 JDK가 제공하는 동적 프록시 기술 InvocationHandler 인터페이스를 이용하는 기술이다.

①주 업무 객체가 가질 추상 메소드 구현

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

 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);
	

}

주업무(core concern)를 진행할 객체가 가질 메소드 선언


② 주 업무 클래스 생성

/*
 CalculatorImpl.java
 - 클래스
 - Calculator 인터페이스를 구현하는 클래스
 - 주 엄무, 보조 업무가 결합된 형태로 함께 처리되는 구조로 구성
  (AOP) 기법 적용 이전1
 */

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;
	}

③ Proxy 클래스 생성

사용자가 주업무에 직접 도달하지 않도록 주업무와 같은 형상을 가진 Proxy클래스 생성

/*===================================
 CalculatorProxy.java
 -프록시 클래스
 - 보조업무 적용 및 주 업무 호출
 =====================================*/
package com.test.spr;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

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

// ※ Proxy 클래스를 설계하는 방법들 중
//  비교적 쉽고 직관적인 방법은
// InvocationHandler 인터페이스를 구현하는 클래스를 만드는 것이다.
//----------------------------
//└→ like 실리콘 가면 만들어주는 거

public class CalculatorProxy implements InvocationHandler
{ 

// 주요 속성 구성

// target → 행세를 하게 될 대상
	private Object target;

// 생성자 정의(얻어낸 주 업무객체를 받아 대신 행세할수 있도록 정의)
	public CalculatorProxy(Object target)
	{
		this.target = target;
	}

// 보조 업무 적용 및 주 업무 호출 과정 추가
// 실제 invoke() 어떻게 활용하는지 우리가 알 필요 없음
//-- method 파라미터는 
//   우리가 별로 알 필요 없는 정보
//-- 우리는 invoke() 라는 메소드 호출하지 않는다.
//   또한 파라미터 값들도 우리가 넘기는게 아님
//-- invoke() : 프록시에 의해서 움직이는 메소드. 주 업무 삽입하는 형태로 작업
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
	{            // ----- ------------- -------------
                       //   		 	내부적으로 			└→ Object가 가지고 있는 프로퍼티들 다 넘기는거
                      // 				어떻게 되어있다는 
                      //				설정값    

// 보조 업무(cross-cutting concern) 설정
//-- 연산 과정에서 소요된 시간 측정 및 로그 기록
		Log log = LogFactory.getLog(this.getClass());// 추가한 org.apache.logging에 있는함수 LogFactory 즉 로그 구성 객체
		StopWatch sw = new StopWatch();// 스톱워치 객체
		sw.start();// -- 스톱워치의 시작버튼 클릭
		log.info("처리 시간 측정 시작 -----------------");// --로그 기록

// 주 업무(core concern) 실행 내용
		Object result = method.invoke(target, args);
//-- CalculatorProxy()의 반환 타입이 Object 니까 result type을 Object로
// ex. target: 태형 , args: 태형이의 유전자,혈액,아빠사진 같은 거

		sw.stop();// 스톱워치 종료버튼 클릭
		log.info("처리 시간 측정 종료 -----------------");// 로그 기록
		log.info(String.format("경과시간 : %s/1000초", sw.getTotalTimeMillis()));

		return result;
	}
}
// +,-,x,/ 무슨 요청이 오던 얘를 만나는거임

/*
 * 특정 업무 처리해주는 A기계 있는데 그거랑 똑같이 만든 proxy 인 B 기계가 있다. A 기계는 1000원 내면 100원 갖고 900원
 * 돌려주는건데, B 기계를 만든 이유는 거기서 20원의 수수료 떼기 위해서!
 * 
 * A 기계(서버)에 (클라이언트의)요청 들어오면 B가 그거 가로채서 처리하는거
 */



③ Main 에서의 사용

/*
		 
		 .newProxyInstance는 실리콘 마스크를 위한 준비물 3개고 이를 .Invoke에 전달하고 
		 .Invoke가 실리콘마스크를 제작하는것과 같다.
		  
		Proxy.newProxyInstance(주 업무 실행 클래스에 대한 정보 전달
								, 주 업무 실행 클래스의 인터페이스들에 대한 정보 전달
								, 보조 업무 실행 클래스에 대한 정보 전달);
		*/
		
		//주 업무를 실행할 수 있는 객체 준비
		Calculator cal = new CalculatorImpl();
		Calculator cal2 = (Calculator)Proxy.newProxyInstance(cal.getClass().getClassLoader() 
				              , cal.getClass().getInterfaces()
				              , new CalculatorProxy(cal));

		
//		cal : 객체  : 바이든
//		getClass() : 객체의 클래스를 가져온다 :바이든의 주민등록본
//		getClassLoader() : 클래스의 정보를 가져온다. : 주민등록본을 읽는 장치
//		
		
		int add = cal2.add(10, 20);
		System.out.println(add);		
	}



Calculator cal2 = (Calculator)Proxy.newProxyInstance(cal.getClass().getClassLoader() 
				              , cal.getClass().getInterfaces()
				              , new CalculatorProxy(cal));

즉 위 부분에서 주 업무 객체의 클래스정보, 주 업무객체가 implements한 인터페이스들의 정보, 보조 업무 실행 클래스에 관한 정보(주업무 객체 포함)을 넘기며 가짜 프록시 객체 cal2를 생성하고 사용할수 있게되는것

이의 결과는

위와 같다 즉 보조업무정보,주업무 정보를 줌으로써 보조업무 규칙에 따라 proxy 객체를 생성해준것

0개의 댓글