지금 사용하는 기술은 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클래스 생성
/*===================================
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가 그거 가로채서 처리하는거
*/
/*
.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 객체를 생성해준것