6.3.3 다이내믹 프록시를 이용한 트랙잭션 부가기능

왜? 다이내믹 프록시 방식으로 변경하는가?

→ 트랜잭션이 필요한 클래스와 메소드가 증가하면 프록시 클래스를 일일이 구현하는 것이 부담됨

// UserServiceTx를 다이내믹 프록시 방식으로 변경
public class TransactionHandler implements InvocationHandler {
	// 부가기능을 제공할 타깃 오브젝트. 어떤 타입의 오브젝트에도 적용 가능
	private Object target;
	// 트랜잭션 기능을 제공하는 데 필요한 트랜잭션 매니저
	private PlatformTransactionManager transactionManager;
	// 트랜잭션을 적용할 메소드 이름 패턴
	private String pattern;

	public void setTarget(Object target) {
		this.target = target;
	}

	public void setTransactionManager(PlatformTransactionManager transactionManager) {
		this.transactionManager = transactionManager;
	}

	public void setPattern(String pattern) {
		this.pattern = pattern;
	}

	//트랜잭션 적용 대상 메소드를 선별해서 트랜잭션 경계설정 기능 부여
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		if(method.getName().startsWith(pattern)){
			return invokeInTransaction(method, args);
		} else {
			return method.invoke(target, args);
		}
	}

	private Object invokeInTransaction(Method method, Object[] args) throws Throwable {
		TransactionStatus status = 
			this.transactionManager.getTransaction(new DefaultTransactionDefinition());
		try {
			// 트랜잭션을 시작하고 타깃 오브젝트의 메소드를 호출
			// 예외가 발생하지 않았다면 커밋
			Object ret = method.invoke(target, args);
			this.transactionManager.commit(status);
			return ret;
			// 예외가 발생하면 트랜잭션 롤백
		} catch (InvocationTargetException e) {
			this.transcationManager.rollback(status);
			throw e.getTargetException();
		}
	}
}
	

// 이후 트랜잭션 핸들러가 필요한 정보와 오브젝트를 DI하도록 
// (TransactionHandler를 이용하는 다이내믹 프록시를 사용하도록)
// 테스트 수정

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

DI의 대상이 되는 다이내믹 프록시 오브젝트는 일반적인 스프링의 빈으로는 등록할 방법이 없음

⇒ 팩토리 빈을 통한 생성

// FactoryBean 인터페이스

package org.spring.framework.beans.factory;

public interfave FactoryBean<T> {
	// 빈 오브젝트를 생성해서 돌려줌
	T getObject() throws Exception;
	// 생성되는 오브젝트의 타입을 알려줌
	Class<? extends T> getObjectType();
	// getObject()가 돌려주는 오브젝트가 항상 같은 싱글톤 오브젝트인지 알려줌
	boolean isSingleton();
// Message 클래스의 오브젝트를 생성해주는 팩토리 빈 클래스
public class MessageFactoryBean implements FactoryBean<Message> {
	String text;
	
// 오브젝트 생성 시 필요한 정보를 팩토리 빈의 프로퍼티로 설정
// 대신 DI받을 수 있게 함
// 주입된 정보는 오브젝트 생성 중에 사용
	public void setText(String text) {
		this.text = text;
	}

// 실제 빈으로 사용될 오브젝트 직접 생서
// 코드를 이용하기 때문에 복잡한 방식의 오브젝트 생성 및 초기화 작업도 가능
	public Message getObject() throws Exception {
		return Message.newMessage(this.text);
	}

	public Class<? extends Message> getObjectType() {
		return Message.class;
	}

// getObject() 메소드가 돌려주는 오브젝트가 싱글톤인지 알려줌
// 이 팩토리 빈은 매번 요청할 때마다 새로운 오브젝트를 만들므로 false로 설정
// 팩토리 빈의 동작방식에 관한 설정
// 만들어진 빈 오브젝트는 싱글톤으로 스프링이 관리해줄 수 있다
	public boolean isSingleton() {
		return false;
	}
}

팩토리 빈의 차이:

message 빈 오브젝트의 타입이 class애트리뷰트에 정의된 MessageFactoryBean이 아니라

Message 타입이다.

// 다이내믹 프록시를 생성하는 팩토리 빈
// 트랜잭션 프록시 빈

// 생성할 오브젝트 타입을 지정할 수도 있지만
// 범용적 사용을 위해 Object로 함
public class TxProxyFactoryBean implements FactoryBean<Object> {
	// TransactionHandler를 생성할 때 필요
	Object target;
	PlatformTransactionManager transactionManger;
	String pattern;
	// 다이내믹 프록시를 생성할 때 필요
	// UserService 외의 인터페이스를 가진 타깃에도 적용 가능
	Class<?> serviceInterface;
	
	public void setTarget(Object target) {
		this.target = target;
	}

	public void setTransactionManager(PlatformTransactionManager transactionManager) {
		this.transactionManager = transactionManager;
	}

	public void setPattern(String pattern) {
		this.pattern = pattern;
	}
	
	public void setServiceInterface(Class<?> serviceInterface) {
		this.serviceInterface = serviceInterface;
	}

	// FactoryBean 인터페이스 구현 메소드
	// DI 받은 정보를 이용해서 TransactionHandler를 사용하는 다이내믹 프록시 생성
	public Object getObject() throws Exception {
		TransactionHandler txHandler = new TransactionHandler();
		txHandler.setTarget(target);
		txHandler.setTransactionManager(transactionManager);
		txHandler.setPattern(pattern);
		return Proxy.newProxyInstance(
			getClass().getClassLoader(),new Class[] { serviceInterface }, txHandler);
		}

		// DI받은 정보를 이용해서 TransactionHandler를 사용하는 다이내믹 프록시 생성
		public Class<?> getObjectType() {
			return serviceInterface;
		}

		// 싱글톤 빈이 아니라는 뜻이 아니라,
		// getObject()가 매번 같은 오브젝트를 리턴하지 않는다는 의미 
		public boolean isSingleton() {
			return false;
		}
}

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

장점

한계