ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 다이나믹 프록시 (2)
    Spring/Spring AOP 2020. 2. 19. 21:12
    반응형

    다이내믹 프록시 적용

    다이내믹 프록시의 동작 방식

    AOP - 다이나믹 프록시 (1)에 포함되어있는 HelloUppercase의 동작방식은 위와 같다.

     

    다이나믹 프록시는 프록시 팩토리에 의해 런타임 시 다이내믹하게 만들어지는 오브젝트다. 다이내믹 프록시 오브젝트는 타깃의 인터페이스와 같은 타입으로 만들어진다. 클라이언트는 다이내믹 프록시 오브젝트를 타깃 인터페이스를 통해 사용할 수 있다. 이렇게 됨으로써 프록시 적용 문제점 중 하나인 인터페이스의 모든 메소드를 구현해 위임하는 단점을 제고해 준다. 즉 프록시 팩토리에게 인터페이스 정보만 제공해주면 해당 인터페이스 구현한 클래스의 오브젝트를 자동으로 만들어 주기 때문이다.

     

    다이내믹 프록시가 인터페이스 구현클래스의 오브젝트는 만들어주지만 프록시로서 필요한 부가기능 제공 코드는 직접 작성해야 한다. 부가기능은 프록시 오브젝트와 독립적으로 InvocationHanlder를 구현한 오브젝트에 담는다. InvocationHandler 인터페이스는 아래와같이 한개만 갖는 메소드이다.

    public Objec Invoke(Object proxy, Method method, Object[] args)

    invoke() 메소드는 리픅랙션의 Method 인터페이스를 파라미터로 받는다. 매소드를 호출 하 ㄹ때 전다로디는 파라미터도 args로 받는다. 다이내믹 프록시 오브젝트는 클라이언트의 모든 요청을 리플렉션 정보로 변환해서 InvocationHandler 구현 오브젝트의 invoke() 메소드로 넘기는것. 즉 타깃 인터페이스의 모든 메소드 요청이 하나의 메소드로 집중되기 때문에 중복되는 기능을 효과적으로 제공 가능하다.(프록시 문제점 하나 해결)

     

    각 메소드의 요청은 리픅렉션으로 메소드와 파라미터 정보를 모두 갖고 있으므로 타깃 오브젝트의 메소드를 호출하게 할 수도 있따. 리플렉션 학습 테스트를 만들어 MEthod와 파라미터 정보가 있으면 특정 오브젝트의 메소드를 실행 할수 있다. InvocationHandler 구현 오브젝트가 타깃 오브젝트 레퍼런스를 갖고 있따면 리플렉션을 이용해 간단히 위임 코드를 만들 수가 있다.

     

    아리 그림은 Hello 인터페이스가 아무리 낳더라도 invoke() 메소드 하나로 처리가 가능하다는 것을 보여주는 그림이다.

    InvocationHanler를 통한 처리구조

     

     

    아래의 코드는다이내믹 프록시로부터 메소드 호출 정보를 받아 처리하는 InvocationHandler 소스이다. 기능은 모든 요청을 타깃에 위임하면서 리턴 값을 대문자로 바꿔주는 부가기능을 가진 InvocationHandler 구현 클래스이다.

    public class UppercaseHandler implements InvocationHandler {
    	Hello target;
    
    	public UppercaseHandler(Hello target) {
    		this.target = target;   //다이내믹 프록시로부터 전달받은 요청을 다시 타깃 오브젝트에
    					//위임해야 하기 때문에 타깃 오브젝트를 주입받아 둔다.
    	}
    	public Object invoke(Object proxy, Method method, Object[] args) 
    			throws Throwable {
    		String ret = (String) method.invoke(target, args); // 타깃으로 위임. 모든 인터페이스의 메소드 호출에 적용된다.
    		return ret.toUpperCase(); // 부가기능 제공
    	}
    }

    다이내믹 프록시로부터 요청을 전달받으려면 InvocationHandler를 구현해야 한다. 매소드는 invoke()하나뿐이다. 다이내믹 프록시가 클라이언트로부터 받는 요청은 invoke() 메소드로 전달된다. 다이내믹 프록시를 통해 요청이 전달되면 리플렉션 API를 이용해 타깃 오브젝트의 메소드를 호출한다. 타깃 오브젝트는 생성자를 통해 미리 전달 받아 둔다. 

     

    다이내믹 프록시의 생성은 Proxy 클래스의 newProxyInstance() 스태틱 팩토리 메소드를 아래와 같이 이용하면 된다.

     

    Hello proxiedHello = (Hello) Proxy.newProxyInstance(
          getClass().getClassLoader(), // 동적으로 생성되는 다이내믹 프록시 클래스의 로딩에 사용할 클래스 로더 
          new Class[] {Hello.class}, // 구현할 인터페이스 
          new UppercaseHandler(new HelloTarget())); // 부가기능과 위임 코드를 담은 InvocationHandler

     

    이렇게 다루기가 쉽지 않은 리플래견 API를 적용하고, 복잡한 다이내믹 프록시 생성방법을 적용했는데도 원래 만들었던 HelloUppercase 프록시 클래스보다 그다지 코드의 양도 줄지 않고, 코드의 작성은 까다로워졌다.

     

     

    다이내믹 프록시의 확장

    public class UppercaseHandler implements InvocationHandler {
    	Object target;
    
    	public UppercaseHandler(Object target) {
    		//어던 종류의 인터페이스를 구현한 타깃에도
    		//적용 가능하도록 Object 타입으로 수정
    		this.target = target;
    	}
    
    	public Object invoke(Object proxy, Method method, Object[] args) 
    			throws Throwable { 
    		//호출한 메소드의 리턴 타입이 STring인 경우에만
    		//대문자 변경기능을 적용하도록 수정
    		Object ret = method.invoke(target, args); 
    		if (ret instanceof String){
    			return ((String)ref).toUpperCase();
    		}else {
    			return ret;
    		}
    	}
    }
    public class UppercaseHandler implements InvocationHandler {
    	Object target;
    
    	public UppercaseHandler(Object target) {
    		this.target = target;
    	}
    
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		Object ret = method.invoke(target, args);
    		if (ret instanceof String && method.getName().startsWith("say")) { // 리턴 타입과 메소드 이름이 일치하는 경우에만 부가기능 적용
    			return ((String) ret).toUpperCase();
    		}
    		return ret; // 조건이 일치하지 않으면 타깃 오브젝트의 호출결과를 그대로 리턴
    	}
    }

     

     

     

    InvocationHandler 방식의 첫번째 장점은 다이내믹 프록시가 만들어질 때 추가된 메소드가 자동으로 포함이되고, 부가기능은 invocke() 메소드에서 처리되어 메소드가 늘어나도 상관이 없다. 두번째 장점은 타깃의 종류에 상관 없이도 적용이 가능하다는 것이다. 즉 리플렉션의 Method 인터페이스를 이용해 타깃의 메소드를 호출하기 때문에 Hello 타입의 타깃으로 제한할 필요도 없다.  어떤 종류의 인터페이스를 구현한 타깃이든 상관없이 재사용할 수 있다.

     

    두번째 코드를 보면 리턴 타입뿐 아니라 메소드의 이름도 조건도 걸 수 있다. 

     

    반응형

    'Spring > Spring AOP' 카테고리의 다른 글

    다이나믹 프록시 (4)  (0) 2020.03.02
    다이나믹 프록시 (3)  (0) 2020.02.26
    다이나믹 프록시 (1)  (0) 2020.02.18
    AOP - 트랜잭션  (0) 2020.02.12
    Spring AOP  (0) 2020.02.09
Designed by Tistory.