원래 객체의 기능을 다른 객체가 대신 처리하도록 설계하는 패턴

AOP 등에 사용되는 패턴

 

 

Proxy 기본 패턴

[Subject interface]

1
2
3
public interface Subject {
    public String action();
}
cs

 

[RealSubject class]

실제 사용하는 객체

1
2
3
4
5
6
7
8
public class RealSubject implements Subject{
 
    @Override
    public String action() {
        return "Real Subject action()";
    }
}
 
cs

 

[Proxy class]

프록시 객체.

내부적으로 RealSubject 객체를 생성하여 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Proxy implements Subject {
    
    RealSubject realSubject;
    
    @Override
    public String action() {
        if(realSubject == null) {
            realSubject = new RealSubject();
        }
        return realSubject.action();
    }
}
 
cs

 

[Client class]

1
2
3
4
5
6
7
8
9
public class Client {
 
    public static void main(String[] args) {
        Subject subject = new Proxy();
        System.out.println(subject.action());
    }
 
}
 
cs

 

 

Proxy의 실제 사용

[AOP 흉내내기]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Proxy implements Subject {
    
    RealSubject realSubject;
    
    @Override
    public Object action() {
        if(realSubject == null) {
            realSubject = new RealSubject();
        }
        
        preProcess();
        Object result = realSubject.action();
        postProcess();
        
        return result;
    }
    
    private void preProcess() {
        System.out.println("선행작업");
    }
    
    private void postProcess() {
        System.out.println("사후작업");
    }
}
 
cs

 

[실제 AOP 소스]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@Component
@Aspect
public class ControllerAOP {
 
    protected static final Logger logger = LoggerFactory.getLogger(ControllerAOP.class);
    private final String CANNOT_PRINT_INPARAM = "{IN_PARAMS LENGTH IS TOO LONG TO PRINT}";
    private final String CANNOT_PRINT_OUTPARAM = "{OUT_PARAMS LENGTH IS TOO LONG TO PRINT}";
    private final String CANNOT_PRINT_VALUE = "VALUE LENGTH IS TOO LONG TO PRINT";
    private final String NOTHING_TO_PRINT = "RETURN TYPE IS VOID";
    
    @Around("execution(public void com.sample..*Controller.*(..))")
    public void voidAround(ProceedingJoinPoint pjp) {
        
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
 
        String inputParam = null;
        
        for (Object obj : pjp.getArgs()) {
            if (obj instanceof Map) {
                inputParam = obj.toString();
            } else if (obj instanceof String){
                inputParam = (String)obj;
            }
        }
 
        long start = System.currentTimeMillis();
 
        String controller = (String) pjp.getTarget().getClass().getSimpleName();
 
        String path = request.getRequestURI();
        String addr = request.getRemoteAddr();
        int port = request.getRemotePort();
 
        logger.info("##########################################################################");
        logger.info("# REQUEST | CONTROLLER = {} | METHOD = {} | REMOTEADDR = {} | PORT = {} | IN_PARAMS = {}",
                controller, path, addr, port, inputParam==null?"":(inputParam.length()>=1500?this.CANNOT_PRINT_INPARAM:inputParam));
        logger.info("##########################################################################");
        
        String result_msg = "success";
        
        try {
            pjp.proceed();
            result_msg = "success";
        } catch (APIException e) {
            result_msg = "fail";
        } catch (Throwable e) {
            result_msg = "fail";
        } finally {
            long end = System.currentTimeMillis();
            logger.info("##########################################################################");
            logger.info("# RESPONSE | CONTROLLER = {} | METHOD = {} | RESULT = {} | REMOTEADDR = {} | PORT = {} | TIME = {} ms | IN_PARAMS = {} | OUT_PARAMS = {}"
                    controller, path, result_msg, addr, port, end-start, inputParam==null?"":(inputParam.length()>=1500?this.CANNOT_PRINT_INPARAM:inputParam), 
                    this.NOTHING_TO_PRINT);
            logger.info("##########################################################################");
        }
 
 
    }
cs

위 AOP 코드 요약 : joinpoint(클라이언트에서 호출되는 모든 메소드) 중에서 pointcut으로(execution) 잡은 특정 메소드만 잡아 공통 관심사로 (advice(@Around)) 로 처리.

 

실제 호출되는 메소드를 인자(ProceedingJoinPoint)로 받아 프록시 패턴과 같이 처리한다.

pjp.proceed() 가 실제 메소드 실행부이며,

pjp.proceed() 위 아래로 로그를 처리하는 코드가 있다.

 

 

참고 :

도서 Head first design patterns,

https://effectiveprogramming.tistory.com/entry/Proxy-%ED%8C%A8%ED%84%B4%EA%B3%BC-%EA%B7%B8-%ED%99%9C%EC%9A%A9

 

반응형

+ Recent posts