프로그래밍/Spring & Spring boot

[Spring/스프링] CGLib 방식으로 Proxy 구현하기

pupu91 2022. 9. 5. 16:13
반응형

 


 

프록시 생성 방식

 

프록시(Proxy) : 실제 Target의 기능을 수행하면서 기능을 확장시키거나 추가하는 실제 객체

 


 

1. JDK Dynamic Proxy 방식

    - 리플렉션을 이용해서 프록시 클래스를 동적으로 생성해주는 방식

    - 타켓의 "인터페이스"를 기준으로 프록시 생성

    - 사용자의 요청이 타겟을 바라보고 실행될 수 있도록 타겟 자체에 대한 코드 수정이 아닌 리플렉션을 이용한 방식으로 

      타겟의 위임 코드를 InvocationHandler를 이용하여 작성함

    - 사용자가 타겟에 대한 정보를 잘못 주입하는 경우가 발생할 수 있기 때문에 내부적으로 주입된 타겟에 대한 검증 코드        를 거친 후 invoke가 동작(느림)

 

 

 

 

2. CGLib 방식

    - 리플렉션 사용x

    - 동적으로 Proxy를 생성하지만, 바이트코드를 조작하여 프록시를 생성해주는 방식

    - 인터페이스 뿐 아니라 타겟의 클래스가 인터페이스를 구현하지 않아도 프록시를 생성 함.

    -  처음 메소드가 호출된 당시 동적으로 타겟 클래스의 바이트 코드를 조작하게 되고, 그 이후 호출 시부터는 변경 된 코         드를 재사용하여 빠름.

    - 리플렉션에 의한 것이 아닌 바이트 코드를 조작하는 방식이기 때문에 성능 면에서 우수함.

    

 

 


 

CGLib 방식으로 Proxy 구현하기

스프링에서 기본적으로 사용하는 프록시 방식은 CGLib 방식

 

 


 

 

인터페이스 없이 GreedyStudent 클래스만 작성

package com.greedy.section03.subsection02.cglib;

public class GreedyStudent{

	public void study(int hours) {
		System.out.println(hours + "시간 동안 열공~~ ");
		
	}

}

 

 

 

 

 

호출에 따른 프록시에서 처리할 메소드를 구현하기 위해 InvocationHandler를 구현한 클래스를 작성

package com.greedy.section03.subsection02.cglib;

import java.lang.reflect.Method;

public class Handler implements InvocationHandler{
	
    메소드 호출을 위해 타켓 인스턴스가 필요함.
	private final GreedyStudent student;
	
    public Handler(GreedyStudent student) {
		this.student = student;
	}
	
    @Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		
	System.out.println("========== 공부가 너무 하고 싶습니다. ==========");
	System.out.println("호출 대상 메소드 : " + method);
	for(Object arg : args) {
			System.out.println("전달 된 인자 : " + arg);
		}
		
	타겟 메소드를 호출하여 타겟 object와 인자를 매개변수로 전달
	method.invoke(student, args);
		
	System.out.println("========= 공부를 마치고 수면 학습을 시작합니다 =========");
		
	return proxy;
	
    }
}

 

 

 

 

코드 구현

package com.greedy.section03.subsection02.cglib;

import org.springframework.cglib.proxy.Enhancer;

public class Application {
	public static void main(String[] args) {
    	
		인터페이스 없이 GreedyStudent 클래스만 작성하여 cGLib를 이용한 프록시 생성 확인
		GreedyStudent student = new GreedyStudent();
		Handler handler = new Handler(student);
        
        Enhancer 클래스의 create static 메소드는 타겟 클래스의 메타 정보와 핸들러를 전달하면 proxy를 생성해서 반환
        GreedyStudent proxy = (GreedyStudent) Enhancer.create(GreedyStudent.class, handler);
        
        핸들러 코드 구현
        proxy.study(20);
   }
}   
출력 결과
========= 공부가 너무 하고 싶습니다. ============
호출 대상 메소드 : public void com.greedy.section03.subsection02.cglib.GreedyStudent.study(int)
전달 된 인자 : 20
20시간 동안 열공~~ 
========== 공부를 마치고 수면 학습을 시작합니다 =============

 

 

 

aspectj 의 autoProxy 사용에 관한 설정 시 true 면CGLib 방식으로 false면 JDKDynamic방식으로 동작한다.

ex) @EnableAspectJAutoProxy(proxyTargetClass=true)

 

 

 

반응형