AOP--프록시 모드, 차단기의 간편한 실현 및 원리

위에서 언급한 에이전트는 정적 에이전트와 동적 에이전트로 나뉘는데 에이전트를 사용하는 것은 원본 코드를 수정하지 않은 상황에서 프로그램의 동적 통일에 기능을 추가하기 위해서이다. 에이전트 기술을 이용하여 업무 논리에서 비업무 논리적인 코드를 분리하여 그들을 업무 논리 클래스에 독립시킬 수 있다. 예를 들어 로그 기록, 성능 통계, 안전 제어, 사무 처리, 이상 처리 등이다.이렇게 하면 업무 논리와 비업무 논리의 결합성을 낮출 뿐만 아니라 프로그램의 중용성을 높일 뿐만 아니라 개발의 효율도 높일 수 있다.
다음은 로그 기록을 추가하는 것을 예로 삼아 정적 에이전트의 사용을 분석합니다.사용자 관리 클래스 UserManagerImpl을 생성하고 사용자 추가 방법addUser를 생성하여 확장성을 극대화하고 다음과 같은 공통 인터페이스 UserManager를 생성합니다.
인터페이스 코드:
package com.snail.pattern;

public interface UserManager {

	public void addUser(String userId,String userName);
}

클래스 코드 구현:
package com.snail.pattern;

public class UserManagerImpl implements UserManager {

	public void addUser(String userId, String userName) {
		
		try {
			//System.out.println("    ");
			System.out.println("HelloWorld!");
			//System.out.println("    !");
		}catch(Exception e) {
			e.printStackTrace();
			//System.out.println("    !");
			throw new RuntimeException();
		}	
	}
}

코드를 통해 알 수 있듯이 주석 안의 로그 내용은 업무 논리와 아무런 관계가 없고 어느새 결합성을 증가시킨다. 만약에 많은 종류에 이런 로그 코드를 추가해야 한다면 작업량은 말하지 않아도 알 수 있고 수정하기도 매우 번거롭다.정적 에이전트를 사용하여 인쇄 로그의 코드를 에이전트 클래스에 추출하면 에이전트 클래스와 업무 논리 클래스를 통해 같은 부류를 계승하고 클라이언트는 에이전트 클래스를 직접 호출하여 수요를 완성한다. 그러면 클라이언트와 업무 논리 클래스의 결합을 해결할 수 있다.예제 코드는 다음과 같습니다.
package com.snail.pattern;


public class UserManagerImplProxy implements UserManager{


	private UserManager userManager;
	
	public UserManagerImplProxy(UserManager userManager){
		this.userManager = userManager;
	}
	
	@Override
	public void addUser(String userId, String userName) {
		try {
			System.out.println("    ");
			userManager.addUser(userId, userName);
			System.out.println("    !");
		}catch(Exception e) {
			e.printStackTrace();
			System.out.println("    !");
		}			
	}
}

클라이언트 호출 코드는 다음과 같습니다.
package com.snail.pattern;

public class Client {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		UserManager userManager = new UserManagerImplProxy(new UserManagerImpl());
		userManager.addUser("0111", "  ");
	}

}

정적 에이전트는 업무 논리와 무관한 코드를 격리하고 결합을 낮추며 업무 논리류를 업무 논리에 더욱 집중하게 하지만 코드량을 줄일 수 없고 시스템의 중복 코드가 너무 많아 프로그래머의 작업량을 증가시켰다.따라서 JDK 동적 에이전트는 이 문제를 완벽하게 해결했다. 동적 에이전트는 시스템 운행 기간에 클래스에 동적 에이전트를 추가한 다음에 에이전트 클래스를 조종하여 목표 클래스에 대한 호출을 완성할 수 있다.
계속해서 상기 예를 들어 정적 에이전트를 동적 에이전트로 바꾸고 추상 클래스 UserManager와 목표 클래스 UserManager Impl의 코드는 변하지 않으며 정적 에이전트 클래스 UserManager ImplProxy를 삭제하고 LoadHandler 클래스를 추가하여 InvocationHandler 인터페이스의 invoke 방법을 실현하도록 한다. 코드는 다음과 같다.
package com.snail.pattern;


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


public class LogHandler implements InvocationHandler {
	
	//    targetObject     
	private Object targetObject;
	
	//Proxy            
	public Object newProxyInstance(Object targetObject){
		this.targetObject = targetObject;
		return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);	
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		System.out.println("    !");
		
		for(int i=0;i<args.length;i++){
			System.out.println(args[i]);
		}
		Object ret = null;
		
		try{
			//      
			ret = method.invoke(targetObject, args);
			System.out.println("    !");
		}catch(Exception e){
			e.printStackTrace();
			System.out.println("    !");
			throw e;
		}
		return ret;
	}
}

Proxy 클래스가 만든 목표 클래스는 최소한의 인터페이스를 실현해야 하며, newProxyInstance 방법을 호출할 때 목표 클래스의 클래스 마운트와 인터페이스와 일치해야 한다.invoke 방법은 Filter의doFilter 방법과 매우 유사합니다. 목표 클래스의 모든 방법을 UserManagerImpl에 도착하기 전에 캡처하고, 우리의 요구에 따라 미리 처리한 후에 UserManagerImpl을 계속 호출합니다.
invoke 방법의 통용성을 유지하기 위해 목표 방법의 매개 변수는 수조args 형식으로 전달되며, 방법에 반환 값이 있으면 반환하고 반환 값이 없으면 반환null로 되돌아옵니다.이렇게 되면 프로그래머는 모든 목표 클래스에 프록시 클래스를 설계할 필요가 없다. 로그를 출력해야 하는 모든 클래스가 이 LogHandler를 공용할 수 있다. 로그 기능을 사용하지 않으려면 LogHandler 클래스를 직접 삭제할 수 있고 원래 기능에 아무런 영향을 주지 않는다. 마치 모니터의 보호막을 벗기는 것처럼 모니터의 사용에 영향을 주지 않는다.
클라이언트 호출 코드는 다음과 같습니다.
package com.snail.pattern;

public class Client {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		LogHandler logHandler = new LogHandler();
		
		UserManager userManager = (UserManager)logHandler.newProxyInstance(new UserManagerImpl());
		
		userManager.addUser("id", "name");
	}

}

상기 두 편의 블로그를 통해 저는 여러분들이 AOP의 원리와 응용 장소에 대해 반은 알고 있고 문장에 오류가 발생할 수 있다고 믿습니다. 여러분의 아낌없는 가르침을 바랍니다.

좋은 웹페이지 즐겨찾기