local또는 remote 비상태유지(stateless) 세션빈의 메소드를 호출하기 위해, 클라이언트 코드는 (local또는 remote)EJB Home객체를 얻기위해 대개 JNDI룩업을 수행해야만 한다. 그 다음 실질적인 (local또는 remote)EJB객체를 얻기 위해 객체의 'create' 메소드 호출을 사용한다. 하나 이상의 메소드는 EJB에서 호출된다.
반복적인 하위레벨 코드를 파하기 위해 많은 EJB애플리케이션은 서비스 위치자(Locator)와 비지니스 위임 패턴을 사용한다. 클라이언트 코드 도처에 JDNI룩업을 하는것보다 더 좋다. 하지만 그들의 일반적인 구현물은 명백한 단점을 가진다. 예를 들면
EJB를 사용한 전형적인 코드는 서비스 위치자나 비지니스 위임 패턴에 의존한다. 하지만 이것은 테스트를 좀더 어렵게 한다.
비지니스 위임이 없이 사용되는 서비스 위치자 패턴의 경우, 애플리케이션 코드는 여전히 EJB Home의 create()메소드를 호출하는것으로 끝이 나고 결과 예외를 다룬다. 게다가 이것은 EJB API와 EJB프로그래밍 모델의 복잡함에 묶인다.
비지니스 위임 패턴을 구현하는 것은 일반적으로 우리가 EJB의 같은 메소드를 간단히 호출하는 다양한 메소드를 써야만 하는 위치의 명백한 코드 중복의 결과를 낳는다.
Spring접근법은 대개 코드가 없는 비지니스 위임처럼 작동하는 Spring ApplicationContext나 BeanFactory내 설정되는 프록시 객체의 생성과 사용을 허용한다. 당신은 실제값을 추가하지 않는다면 다른 서비스 위치자, 다른 JNDI 룩업 또는 손으로 작성된 비지니스 위임내 중복 메소드를 사용할 필요가 없다.
우리가 local EJB를 사용할 필요가 있는 웹 컨트롤러를 가지고 있다고 가정하자. 우리는 최상의 상황을 따를것이고 EJB 비지니스 메소드 인터페이스 패턴을 사용할것이다. 그래서 EJB의 local 인터페이스는 EJB 성격이 아닌 비지니스 메소드 인터페이스를 확장한다. 이 비지니스 메소드 인터페이스를 MyComponent라고 부르자.
public interface MyComponent {
...
}
(비지니스 메소드 인터페이스 패턴을 위한 중요한 이유중 하나는 local 인터페이스내 메소드 시그너처와 bean구현 클래스사이 동기화가 자동적이라는 것을 확인하는 것이다. 다른 이유는 이것이 나중에 우리는 위해 그렇게 하도록 만들때 서비스의 POJO로 교체하는것을 좀더 쉽게 만든다는 것이다. ) 물론 우리는 local home인터페이스를 구현할 필요가 필요가 있을것이다. 그리고 SessionBean과 MyComponent비지니스 메소드 인터페이스를 구현하는 bean구현 클래스를 제공한다. 지금 우리의 웹 티어 컨트롤러를 EJB구현물로 연결하는 필요가 있는 자바코드만이 컨트롤러의 MyComponent타입의 setter메소드를 드러낸다. 이것은 컨트롤러내 인스턴스 변수처럼 참조를 저장할것이다.
private MyComponent myComponent;
public void setMyComponent(MyComponent myComponent) {
this.myComponent = myComponent;
}
우리는 컨트롤러내 어떠한 비지니스 메소드내 인스턴스 변수를 순차적으로 사용할수 있다. 지금 우리가 Spring ApplicationContext 나 BeanFactory밖에서 컨트롤러 객체를 얻는다고 가정하자. 우리는 EJB프록시 객체가 될 LocalStatelessSessionProxyFactoryBean를 설정하는 같은 컨텍스트를 사용할수 있다. 프록시의 설정은 그리고 컨트롤러의 myComponent 프라퍼티의 셋팅은 다음처럼 설정 항목으로 한다.
<bean id="myComponent"
class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">
<property name="jndiName"><value>myComponent</value></property>
<property name="businessInterface"><value>com.mycom.MyComponent</value></property>
</bean>
<bean id="myController" class = "com.mycom.myController">
<property name="myComponent"><ref bean="myComponent"/></property>
</bean>
Spring AOP프레임워크의 도움으로 비록 당신이 이러한 결과를 즐기기 위해 AOP개념으로 작업을 강제로 하지 않더라도 이 상황뒤에는 마법같은 일이 많다. myComponent bean정의는 비지니스 메소드 인터페이스를 구현하는 EJB를 위한 프록시를 생성한다. EJBlocal home은 시작시 캐시된다. 그래서 하나의 JNDI룩업만이 있다. 각각의 EJB가 호출되는 시점에 프록시는 local EJB의 create()메소드를 호출하고 EJB의 관련된 비지니스 메소드를 호출한다.
myController bean정의는 프록시를 위한 컨트롤러의 myController 프라퍼티를 셋팅한다.
EJB접근 기법은 애플리케이션 코드의 굉장한 단순화를 초래한다. 웹 티어 코드(또는 다른 EJB클라이언트 코드)는 EJB사용의 의존성을 가지지 않는다. 만약 우리가 POJO나 모의(mock)객체 또는 다른 테스트 스텁(stub)을 가진 EJB참조를 교체하기를 원한다면 우리는 자바코드의 한줄의 변경도 없이 myComponent bean정의를 간단하게 변경할수 있다. 추가적으로 우리는 JNDI룩업을 한줄도 쓰지 않거나 우리의 애플리케이션의 일부처럼 다른 EJB 관련 코드를 쓰지 않는다.
실제 애플리케이션에서 벤치마크와 경험은 이 접근법(대상 EJB 반영적인 호출의 포함하는)의 의 성능 오버헤드가 죄소이고 일반적인 사용에서 측정불가능이라는 것을 표시힌다. 애플리케이션 서버내 EJB구조와 관련된 비용때문에 우리는 EJB를 위한 잘 정의된 호출을 만드는것을 원하지 않는다는것을 기억하라.
JNDI룩업에 관련되는 한가지 주의사항이 있다. bean컨테이너에서 이 클래스는 대개 싱글톤처럼(이것을 프로토타입으로 만들기 위한 이유는 없다) 사용되는것이 가장 좋다. 어쩄든 bean컨테이너가 싱글톤(XML ApplicationContext 변형을 하는것처럼) 을 먼저 인스턴스화 한다면 당신은 EJB컨테이너가 대상 EJB를 로드하기 전에 bean컨테이너가 로드된다면 문제를 가지게 된다. 그것은 JNDI룩업이 이 클래스의 init메소드내 수행될것이고 캐시되지만 EJB는 대상 위치에 여전히 바운드되지 않을것이기 때문이다. 해결법은 이 factory객체를 미리 인스턴스화하지 않지만 첫번째 사용시 이것이 생성되는것을 허용한다. XML컨테이너에서 이것은 lazy-init 속성을 통해 컨트롤된다.
비록 이것이 Spring사용자에게 중요 관심사가 되지는 않을것이지만 EJB를 사용한 프로그램에 따른 AOP작업을 하는것은 LocalSlsbInvokerInterceptor를 찾는것을 원할것이다.
remote EJB에 접근하는것은 local EJB에 접근하는것과 비교해서 SimpleRemoteStatelessSessionProxyFactoryBean를 사용하는것을 제외하고 기본적으로 같다. 물론 Spring을 사용하든 사용하지 않든 remote호출은 의미적으로 적용한다. 다른 컴퓨터내 다른 VM안에서 객체의 메소드를 호출하는것은 때때로 사용 시나리오와 실패(failure)핸들링의 개념으로 다르게 처리된다.
Spring의 EJB 클라이언트 지원은 Spring을 사용하지 않는 접근법에 비해 하나 이상의 장점을 추가한다. 대개 EJB를 local이나 remote로 호출하는 것들간에는 쉽게 진행하고 되돌리기 위한 EJB클라이언트 코드를 위해서는 문제가 있다. 이것은 local인터페이스 메소드가 호출되지 않는동안 remote인터페이스 메소드가 RemoteException를 던지는 것을 명시해야하고 클라이언트 코드는 이것을 다루어야 하기 때문이다. remote EJB로 옮겨질 필요가 있는 local EJB를 위해 쓰여진 클라이언트 코드는 일반적으로 remote 예외를 위한 핸들링을 추가하기 위해 변경되고 local EJB로 옮겨질 필요가 있는 remote EJB를 위해 쓰여진 클라이언트 코드는 remote 예외의 많은 필요없는 핸들링이 수행되지만 같은코드로 그대로 유지될수 있거나 그 코드를 제거하기 위해 변경될 필요가 있다. Spring remote EJB프록시를 사용하여 당신은 당신의 비지니스 메소드 인터페이스내 던져지는 RemoteException을 선언하는것을 대신할수 있고 EJB코드를 구현한다. RemoteException를 던지는 것을 제외하면 동일한 remote 인터페이스를 가지고 그들이 같다면 두개의 인터페이스를 자동적으로 처리하기 위한 프록시에 의존한다. 클라이언트 코드는 체크된 RemoteException을 다루지 않는다. 어떠한 실질적인 RemoteException은 EJB호출이 RuntimeException의 하위클래스인 체크되지 않은 RemoteAccessException 클래스처럼 다시 던져질것이다. 대상 서비스는 그 다음 클라이언트 코드의 인식및 처리가 없이 local EJB나 remote EJB(또는 POJO) 구현물 사이에서 교체될것이다. 물론 이것은 옵션적이다. 당신의 비지니스 인터페이스내 선언된 RemoteExceptions으로 부터 당신을 정지시키는것은 아무것도 없다.
Spring은 또한 당신이 EJB를 구현하도록 도와주는 편리한 클래스를 제공한다. EJB가 트랜잭션 설정과 원격작업을 책임지도록 놔둔체 POJO내 EJB뒤에 비지니스 로직을 두는 좋은 상황을 만들기 위해 디자인되었다.
비상태유지(stateless) 또는 상태유지(stateful) 세션빈, 또는 메시지빈을 구현하기 위해, 당신은 AbstractStatelessSessionBean, AbstractStatefulSessionBean, 그리고 AbstractMessageDrivenBean/AbstractJmsMessageDrivenBean으로 부터 반복적으로 당신의 구현 클래스를 끌어낸다.
구현물을 명확한 자바 서비스 객체로 실질적으로 위임하는 비상태유지(stateless) 세션빈을 시험하는것을 검토하라. 우리는 비지니스 인터페이스를 가진다.
public interface MyComponent {
public void myMethod(...);
...
}
We have the plain java implementation object:
public class MyComponentImpl implements MyComponent {
public String myMethod(...) {
...
}
...
}
그리고 마지막으로 비상태유지(stateless) 세션빈 자체:
public class MyComponentEJB extends AbstractStatelessSessionBean
implements MyComponent {
MyComponent _myComp;
/**
* Obtain our POJO service object from the BeanFactory/ApplicationContext
* @see org.springframework.ejb.support.AbstractStatelessSessionBean#onEjbCreate()
*/
protected void onEjbCreate() throws CreateException {
_myComp = (MyComponent) getBeanFactory().getBean(
ServicesConstants.CONTEXT_MYCOMP_ID);
}
// for business method, delegate to POJO service impl.
public String myMethod(...) {
return _myComp.myMethod(...);
}
...
}
Spring EJB지원 기초 클래스는 그들의 생명주기처럼 BeanFactory(또는 이 경우 ApplicationContext의 하위클래스)를 디폴트로 생성하고 로드함으로써 이루어진다. 그 다음 EJB(예를 들면 POJO서비스 객체를 얻기 위한 위의 코드내에서 사용된것처럼)에 사용가능하게 된다. 로딩은 BeanFactoryLocator의 하위클래스인 전략(strategy)객체를 통해 이루어진다. BeanFactoryLocator의 실질적인 구현은 디폴트인 JNDI환경 변수(EJB의 경우, java:comp/env/ejb/BeanFactoryPath)처럼 명시된 자원 위치로부터 ApplicationContext을 생성하는 ContextJndiBeanFactoryLocator에 의해 사용된다. 만약 BeanFactory/ApplicationContext 로딩 전략을 변경할 필요가 없다면 디폴트 BeanFactoryLocator구현물은 setBeanFactoryLocator()메소드를 호출하거나 setSessionContext()내, 또는 EJB의 실질적인 생성자내에서 오버라이드되어 사용된다. 좀더 다양한 정보를 위해서는 JavaDoc를 보라.
JavaDoc에서 언급된것처럼 상태유지(stateful) 세션빈은 그들의 생명주기의 일부처럼 수동적이고 재활성화되기 위해 기대되고 EJB컨테이너에 의해 저장되지 않을수 있기 때문에 수동적이고 활성화된 BeanFactory를 로드하지않고 다시 로드하기 위한 ejbPassivate 과 ejbActivate로 부터 unloadBeanFactory() 와 loadBeanFactory를 수동으로 호출할 non-serializable한 BeanFactory/ApplicationContext 인스턴스를 사용한다.
EJB의 사용을 위해 ApplicationContext를 로드하기 위한 ContextJndiBeanFactoryLocator의 디폴트 사용법은 몇몇 상황을 위해 적절하다. 어쟀든 모든 EJB가 자신이 복사본을 가진후 ApplicationContext가 많은 수의 bean을 로드하거나 그러한 bean의 초기화가 시간을 소비하거나 메모리 집중적일때 문제가 있다. 이 경우 사용자는 디폴트 ContextJndiBeanFactoryLocator사용법을 오버라이드하길 원할것이고 다중 EJB또는 다른 클라이언트에 의해 사용되기 위한 공유 BeanFactory나 ApplicationContext를 로드하고 사용할수 있는 ContextSingletonBeanFactoryLocatore와 같은 다른 BeanFactoryLocator을 사용한다. 이것을 하는것은 EJB를 위해 유사한 코드를 추가함으로써 비교적 간단하다.
/**
* Override default BeanFactoryLocator implementation
*
* @see javax.ejb.SessionBean#setSessionContext(javax.ejb.SessionContext)
*/
public void setSessionContext(SessionContext sessionContext) {
super.setSessionContext(sessionContext);
setBeanFactoryLocator(ContextSingletonBeanFactoryLocator.getInstance());
setBeanFactoryLocatorKey(ServicesConstants.PRIMARY_CONTEXT_ID);
}
그것들의 사용법에 대한 좀더 다양한 정보를 위해서 BeanFactoryLocator 와 ContextSingletonBeanFactoryLocatore를 위한 관련 JavaDoc를 보라.