Spring/Spring AOP

다이나믹 프록시 (4)

레알윙 2020. 3. 2. 20:36
반응형

다이나믹 프록시를 위한 팩토리 빈

위의 글까지 일게 되면 이제 RansactionHandler와 다이내믹 프록시를 스프링의 DI를 통해 사용하 ㄹ수 있도록 만들어야 할 차례이다.

 

스프링의 빈은 기본적으로 클래스 이름과 프로퍼티로 정의된다. 스프링은 지정되는 클래스의 이름을 가지고 리플랙션을 이용해서 해당 클래스의 오브젝트를 만든다. 클래스의 이름을 갖고 있다면 다음과 같은 방법으로 새로운 오브젝트를 생성할 수 있다. Class의 newInstance()메소드는 해당 클래스의 파라미터가 없는 생성자를 호출하고, 그 결과 생성되는 오브젝트를 돌려주는 리프렉션 API다.

 

Date now = (Date) Class.forName("java.util.Date").newInstance();

 

프링은 내부적으로 리플랙션 API를 이용해서 빈 정의에 나오는 클래스 이름을 가지고 빈 오브젝트를 생성한다. 문제는 다이내믹 프록시 오브젝트는 이런 식으로 프록시 오브젝트가 생성되지 않는다는 점이다. 사실 다이내믹 프록시 오브젝트의 클래스가 어떤 것인지 알 수도 없다. 클래스 자체도 내부적으로 다이내믹하게 새로 정의해서 사용하기 때문이다.따라서 사전에 프록시 오브젝트의 클래스 정보를 미리 알아내서 스프링의 빈에 정의할 방법이 없다. 다이내믹 프록시는 Proxy클래스의 newProxyInstance()라는 스태틱 팩토리 메소드를 통해서만 만들 수 있다.

 

팩토리 빈

스프링은 클래스 정보를 가지고 디폴트 생성자를 통해 오브젝트를 만드는 방법 외에도 빈을 만들 수 있는 여러가지 방법을 제공한다. 대표적으로 팩토리 빈을 이용한 빈 생성 방법을 들 수 있다. 팩토리 빈이란 스프링을 대신해서 오브젝트의 생성로직을 담당하도록 만들어진 특별한 빈을 말한다.

 

팩토리빈을 만드는 방법에는 여러 가지가 있는데, 가장 간단한 방법은 스프링의 FactoryBean이라는 인터페이스를 구현하는 것이다.

package org.springframework.beans.factory;

public interface FactoryBean<T> {
    T getObject() throws Exception; // 빈 오브젝트를 생성해서 돌려준다.
    Class<? extends T> getOBjectType(); // 생성되는 오브젝트의 타입을 알려준다.
    boolean isSingleton(); // getObject()가 돌려주는 오브젝트가 항상 같은 싱글톤 오브젝트인지 알려준다.
}

스프링은 아래와 같이 private 생성자를 가진 클래스도 빈으로 등록해주면 리플랙션을 이용해 오브젝트를 만들어 준다.리플랙션은 private으로 선언된 접근 규약을 위반할 수 있는 강력한 기능이 있기 때문이다. 하지만 생성자를 private으로 만들었다는 것은 스태틱 메소드를 통해 오브젝트가 만들어져야 하는 중요한 이유가 있기 때문이므로 이를 무시하고 오브젝트를 강제로 생성하면 위험하다. 

package kr.co.stusy;

public class Message {
	String text;
    
	// 시작
    private Message(String text){
    	this.text = text;
    }
    // 끝
    // 생성자가 private으로 선언되어 있어서 외부에서 생성자를 통해 오브젝트를 만들 수 있다.
    
    public String getText() {
    	return text;
    }
    
    // 시작
    public static Message newMessage(String text) {
    	return new Message(text);
    }
    // 끝
    // 생성자 대신 사용할 수 있는 스태틱 팩토리 메소드를 제공한다.

}

 

다이내믹 프록시를 만들어주는 팩토리 빈

Proxy의 newProxyInstance() 메소드를 통해서만 생성이 가능한 다이내믹 프록시 오브젝트는 일반적인 방법으로는 스프링의 빈으로 등록 할 수 없다. 대신 팩토리 빈을 사용하면 다이내믹 프록시 오브젝트를 스프링의 빈으로 만들어줄 수가 있다.  팩토리 빈의 getObject() 메소드에 다이내믹 프록시 오브젝트를 만들어주는 코드를 넣으면 되기 때문이다.

 

 

스프링 빈에는 팩토리빈과 UserServiceImpl만 빈으로 등록한다. 팩토리 빈은 다이내믹 프록시가 위임할 타깃 오브젝트인 UserServiceIMpl에 대한 래퍼런스를 프로퍼티를 통해 DI를 받아둬야 한다. 다이내믹 프록시와 함께 생성할 TransactionHandler에게 타깃 오브젝트를 전달해줘야 하기 때문이다. 그 외에도 다이내믹 프록시나 TransactionHandler를 만들 때 필요한 정보는 팩토리 빈의 프로퍼티로 설정해뒀다가 다이내믹 프록시를 만들면서 전달해줘야 한다.

 

 

프록시 팩토리 빈 방식의 장점과 한계

다이내믹 프록시를 생성해주는 팩토리 빈을 사용하는 방법은 여러 가지 장점이 있다. 한번 부가기능을 가진 프록시를 생성하는 팩토리 빈을 만들어두면 타깃의 타입에 상관없이 재사용할 수 있기 때문이다.

 

프록시 팩토리 빈 방식의 장점

다이내믹 프록시를 이용하면 타깃 인터페이스를 구현하는클래스를 일일이 만드는 번거로움을 제거할 수 있다. 하나의 핸들러 메소드를 구현하는 것만으로도 수많은 메소드에 부가기능을 부여해줄 수 있으니 부가기능 코드의 중복 문제도 사라진다. 다이내믹 프록시에 팩토리 빈을 이용한 DI까지 더해주면 번거로운 다이내믹 프록시 생성 코드도 제거할 수 있다. DI 설정만으로 다양한 타깃 오브젝트에 적용도 가능하다.

 

프록시 팩토리 빈 방식의 한계

프록시를 통해 타깃에 부가기능을 제공하는 것은 메소드 단위로 일어나는 일이다. 하나의 클래스 안에 존재하는 여러 개의 메소드에 부가기능을 한 번에 제공하는 건 어렵지 않게 가능했다. 하지만 한 번에 여러 개의 클래스에 공통적인 부가기능을 제공하는 일은 지금까지 살펴본 방법으로는 불가능하다. 하나의 타깃 오브젝트에만 부여되는 부가기능이라면 상관없겠지만, 트랜잭션과 같이 비즈니스 로직을 담은 많은 클래스의 메소드에 적용할 필요가 있따면 거의 비슷한 프록시 팩토리 빈의 설정이 중복되는 것을 막을 수 없다.

 

하나의 타깃에 여러 개의 부가기능을 적용하려고 할 때도 문제다. 같은 타깃 오브젝트에 대해 트랜잭션 프록시뿐 아니라 보안 기능을 제공하는 프록시도 적용한 갯수 만큼 프록시 팩토리 빈 설정이 붙어야 한다.

 

 

 

 

 

출처

토비의 스프링

반응형