Move reference docs => src/reference
This change eliminates the spring-framework-reference subproject, moving these sources into the root project's own src directory. This makes sense because the reference docs span all submodules, and also because api Javadoc is created at the root project level as well. This means that both api and reference documentation output will now reside in the root project's 'build' directory. This is more consistent and easy to discover.
This commit is contained in:
439
src/reference/docbook/ejb.xml
Normal file
439
src/reference/docbook/ejb.xml
Normal file
@@ -0,0 +1,439 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
||||
|
||||
<chapter id="ejb">
|
||||
<title>Enterprise JavaBeans (EJB) integration</title>
|
||||
|
||||
<section id="ejb-introduction">
|
||||
<title>Introduction</title>
|
||||
<para>
|
||||
As a lightweight container, Spring is often considered an EJB
|
||||
replacement. We do believe that for many if not most applications and use
|
||||
cases, Spring as a container, combined with its rich supporting
|
||||
functionality in the area of transactions, ORM and JDBC access, is a better
|
||||
choice than implementing equivalent functionality via an EJB container and
|
||||
EJBs.
|
||||
</para>
|
||||
<para>
|
||||
However, it is important to note that using Spring does not prevent
|
||||
you from using EJBs. In fact, Spring makes it much easier to access EJBs and
|
||||
implement EJBs and functionality within them. Additionally, using Spring to
|
||||
access services provided by EJBs allows the implementation of those services
|
||||
to later transparently be switched between local EJB, remote EJB, or POJO
|
||||
(plain old Java object) variants, without the client code having to
|
||||
be changed.
|
||||
</para>
|
||||
<para>
|
||||
In this chapter, we look at how Spring can help you access and
|
||||
implement EJBs. Spring provides particular value when accessing stateless
|
||||
session beans (SLSBs), so we'll begin by discussing this.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="ejb-access">
|
||||
<title>Accessing EJBs</title>
|
||||
|
||||
<section id="ejb-access-concepts">
|
||||
<title>Concepts</title>
|
||||
<para>
|
||||
To invoke a method on a local or remote stateless session bean,
|
||||
client code must normally perform a JNDI lookup to obtain the (local or
|
||||
remote) EJB Home object, then use a 'create' method call on that object
|
||||
to obtain the actual (local or remote) EJB object. One or more methods
|
||||
are then invoked on the EJB.
|
||||
</para>
|
||||
<para>
|
||||
To avoid repeated low-level code, many EJB applications use the
|
||||
Service Locator and Business Delegate patterns. These are better than
|
||||
spraying JNDI lookups throughout client code, but their usual
|
||||
implementations have significant disadvantages. For example:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
Typically code using EJBs depends on Service Locator or
|
||||
Business Delegate singletons, making it hard to test.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
In the case of the Service Locator pattern used without a
|
||||
Business Delegate, application code still ends up having to invoke
|
||||
the create() method on an EJB home, and deal with the resulting
|
||||
exceptions. Thus it remains tied to the EJB API and the complexity
|
||||
of the EJB programming model.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Implementing the Business Delegate pattern typically results
|
||||
in significant code duplication, where we have to write numerous
|
||||
methods that simply call the same method on the EJB.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
The Spring approach is to allow the creation and use of proxy objects,
|
||||
normally configured inside a Spring container, which act as codeless
|
||||
business delegates. You do not need to write another Service Locator, another
|
||||
JNDI lookup, or duplicate methods in a hand-coded Business Delegate unless
|
||||
you are actually adding real value in such code.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="ejb-access-local">
|
||||
<title>Accessing local SLSBs</title>
|
||||
<para>
|
||||
Assume that we have a web controller that needs to use a local
|
||||
EJB. We’ll follow best practice and use the EJB Business Methods
|
||||
Interface pattern, so that the EJB’s local interface extends a non
|
||||
EJB-specific business methods interface. Let’s call this business
|
||||
methods interface <classname>MyComponent</classname>.
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[public interface MyComponent {
|
||||
...
|
||||
}]]></programlisting>
|
||||
<para>
|
||||
One of the main reasons to use the Business Methods Interface pattern
|
||||
is to ensure that synchronization between method signatures in local
|
||||
interface and bean implementation class is automatic. Another reason is
|
||||
that it later makes it much easier for us to switch to a POJO (plain old
|
||||
Java object) implementation of the service if it makes sense to do so.
|
||||
Of course we’ll also need to implement the local home interface and
|
||||
provide an implementation class that implements <classname>SessionBean</classname>
|
||||
and the <classname>MyComponent</classname> business methods interface. Now the
|
||||
only Java coding we’ll need to do to hook up our web tier controller to the
|
||||
EJB implementation is to expose a setter method of type <classname>MyComponent</classname>
|
||||
on the controller. This will save the reference as an instance variable in the
|
||||
controller:
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[private MyComponent myComponent;
|
||||
|
||||
public void setMyComponent(MyComponent myComponent) {
|
||||
this.myComponent = myComponent;
|
||||
}]]></programlisting>
|
||||
<para>
|
||||
We can subsequently use this instance variable in any business
|
||||
method in the controller. Now assuming we are obtaining our controller
|
||||
object out of a Spring container, we can (in the same context) configure a
|
||||
<classname>LocalStatelessSessionProxyFactoryBean</classname> instance, which
|
||||
will be the EJB proxy object. The configuration of the proxy, and setting of
|
||||
the <literal>myComponent</literal> property of the controller is done
|
||||
with a configuration entry such as:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<bean id="myComponent"
|
||||
class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">
|
||||
<property name="jndiName" value="ejb/myBean"/>
|
||||
<property name="businessInterface" value="com.mycom.MyComponent"/>
|
||||
</bean>
|
||||
|
||||
<bean id="myController" class="com.mycom.myController">
|
||||
<property name="myComponent" ref="myComponent"/>
|
||||
</bean>]]></programlisting>
|
||||
<para>
|
||||
There’s a lot of work happening behind the scenes, courtesy of
|
||||
the Spring AOP framework, although you aren’t forced to work with AOP
|
||||
concepts to enjoy the results. The <literal>myComponent</literal> bean
|
||||
definition creates a proxy for the EJB, which implements the business
|
||||
method interface. The EJB local home is cached on startup, so there’s
|
||||
only a single JNDI lookup. Each time the EJB is invoked, the proxy
|
||||
invokes the <literal>classname</literal> method on the local EJB and
|
||||
invokes the corresponding business method on the EJB.
|
||||
</para>
|
||||
<para>
|
||||
The <literal>myController</literal> bean definition sets the
|
||||
<literal>myComponent</literal> property of the controller class to the
|
||||
EJB proxy.
|
||||
</para>
|
||||
<para>
|
||||
Alternatively (and preferably in case of many such proxy definitions),
|
||||
consider using the <literal><jee:local-slsb></literal>
|
||||
configuration element in Spring's "jee" namespace:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<jee:local-slsb id="myComponent" jndi-name="ejb/myBean"
|
||||
business-interface="com.mycom.MyComponent"/>
|
||||
|
||||
<bean id="myController" class="com.mycom.myController">
|
||||
<property name="myComponent" ref="myComponent"/>
|
||||
</bean>]]></programlisting>
|
||||
<para>
|
||||
This EJB access mechanism delivers huge simplification of
|
||||
application code: the web tier code (or other EJB client code) has no
|
||||
dependence on the use of EJB. If we want to replace this EJB reference
|
||||
with a POJO or a mock object or other test stub, we could simply change
|
||||
the <literal>myComponent</literal> bean definition without changing a
|
||||
line of Java code. Additionally, we haven’t had to write a single line of
|
||||
JNDI lookup or other EJB plumbing code as part of our application.
|
||||
</para>
|
||||
<para>
|
||||
Benchmarks and experience in real applications indicate that the
|
||||
performance overhead of this approach (which involves reflective
|
||||
invocation of the target EJB) is minimal, and is typically undetectable
|
||||
in typical use. Remember that we don’t want to make fine-grained calls
|
||||
to EJBs anyway, as there’s a cost associated with the EJB infrastructure
|
||||
in the application server.
|
||||
</para>
|
||||
<para>
|
||||
There is one caveat with regards to the JNDI lookup. In a bean
|
||||
container, this class is normally best used as a singleton (there simply
|
||||
is no reason to make it a prototype). However, if that bean container
|
||||
pre-instantiates singletons (as do the various XML
|
||||
<classname>ApplicationContext</classname> variants)
|
||||
you may have a problem if the bean container is loaded before the EJB
|
||||
container loads the target EJB. That is because the JNDI lookup will be
|
||||
performed in the <literal>init()</literal> method of this class and then
|
||||
cached, but the EJB will not have been bound at the target location yet.
|
||||
The solution is to not pre-instantiate this factory object, but allow it
|
||||
to be created on first use. In the XML containers, this is controlled via
|
||||
the <literal>lazy-init</literal> attribute.
|
||||
</para>
|
||||
<para>
|
||||
Although this will not be of interest to the majority of Spring
|
||||
users, those doing programmatic AOP work with EJBs may want to look at
|
||||
<classname>LocalSlsbInvokerInterceptor</classname>.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="ejb-access-remote">
|
||||
<title>Accessing remote SLSBs</title>
|
||||
<para>
|
||||
Accessing remote EJBs is essentially identical to accessing local
|
||||
EJBs, except that the
|
||||
<classname>SimpleRemoteStatelessSessionProxyFactoryBean</classname> or
|
||||
<literal><jee:remote-slsb></literal> configuration element is used.
|
||||
Of course, with or without Spring, remote invocation semantics apply; a
|
||||
call to a method on an object in another VM in another computer does
|
||||
sometimes have to be treated differently in terms of usage scenarios and
|
||||
failure handling.
|
||||
</para>
|
||||
<para>
|
||||
Spring's EJB client support adds one more advantage over the
|
||||
non-Spring approach. Normally it is problematic for EJB client code to
|
||||
be easily switched back and forth between calling EJBs locally or
|
||||
remotely. This is because the remote interface methods must declare that
|
||||
they throw <classname>RemoteException</classname>, and client code must deal
|
||||
with this, while the local interface methods don't. Client code
|
||||
written for local EJBs which needs to be moved to remote EJBs
|
||||
typically has to be modified to add handling for the remote exceptions,
|
||||
and client code written for remote EJBs which needs to be moved to local
|
||||
EJBs, can either stay the same but do a lot of unnecessary handling of
|
||||
remote exceptions, or needs to be modified to remove that code. With the
|
||||
Spring remote EJB proxy, you can instead not declare any thrown
|
||||
<classname>RemoteException</classname> in your Business Method Interface and
|
||||
implementing EJB code, have a remote interface which is identical except
|
||||
that it does throw <classname>RemoteException</classname>, and rely on the
|
||||
proxy to dynamically treat the two interfaces as if they were the same.
|
||||
That is, client code does not have to deal with the checked
|
||||
<classname>RemoteException</classname> class. Any actual
|
||||
<classname>RemoteException</classname> that is thrown during the EJB
|
||||
invocation will be re-thrown as the non-checked
|
||||
<classname>RemoteAccessException</classname> class, which is a subclass of
|
||||
<classname>RuntimeException</classname>. The target service can then be
|
||||
switched at will between a local EJB or remote EJB (or even plain Java
|
||||
object) implementation, without the client code knowing or caring. Of
|
||||
course, this is optional; there is nothing stopping you from declaring
|
||||
<classname>RemoteExceptions</classname> in your business interface.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="ejb-access-ejb2-ejb3">
|
||||
<title>Accessing EJB 2.x SLSBs versus EJB 3 SLSBs</title>
|
||||
<para>
|
||||
Accessing EJB 2.x Session Beans and EJB 3 Session Beans via Spring
|
||||
is largely transparent. Spring's EJB accessors, including the
|
||||
<literal><jee:local-slsb></literal> and <literal><jee:remote-slsb></literal>
|
||||
facilities, transparently adapt to the actual component at runtime.
|
||||
They handle a home interface if found (EJB 2.x style), or perform straight
|
||||
component invocations if no home interface is available (EJB 3 style).
|
||||
</para>
|
||||
<para>
|
||||
Note: For EJB 3 Session Beans, you could effectively use a
|
||||
<classname>JndiObjectFactoryBean</classname> / <literal><jee:jndi-lookup></literal>
|
||||
as well, since fully usable component references are exposed for plain
|
||||
JNDI lookups there. Defining explicit <literal><jee:local-slsb></literal>
|
||||
/ <literal><jee:remote-slsb></literal> lookups simply provides
|
||||
consistent and more explicit EJB access configuration.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id="ejb-implementation">
|
||||
<title>Using Spring's EJB implementation support classes</title>
|
||||
|
||||
<section id="ejb-implementation-ejb2">
|
||||
<title>EJB 2.x base classes</title>
|
||||
<para>
|
||||
Spring provides convenience classes to help you implement EJBs.
|
||||
These are designed to encourage the good practice of putting business
|
||||
logic behind EJBs in POJOs, leaving EJBs responsible for transaction
|
||||
demarcation and (optionally) remoting.
|
||||
</para>
|
||||
<para>
|
||||
To implement a Stateless or Stateful session bean, or a Message Driven
|
||||
bean, you need only derive your implementation class from
|
||||
<classname>AbstractStatelessSessionBean</classname>,
|
||||
<classname>AbstractStatefulSessionBean</classname>, and
|
||||
<classname>AbstractMessageDrivenBean</classname>/<classname>AbstractJmsMessageDrivenBean</classname>,
|
||||
respectively.
|
||||
</para>
|
||||
<para>
|
||||
Consider an example Stateless Session bean which actually delegates
|
||||
the implementation to a plain java service object. We have the business
|
||||
interface:
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[public interface MyComponent {
|
||||
public void myMethod(...);
|
||||
...
|
||||
}]]></programlisting>
|
||||
<para>We also have the plain Java implementation object:</para>
|
||||
<programlisting language="java"><![CDATA[public class MyComponentImpl implements MyComponent {
|
||||
public String myMethod(...) {
|
||||
...
|
||||
}
|
||||
...
|
||||
}]]></programlisting>
|
||||
<para>And finally the Stateless Session Bean itself:</para>
|
||||
<programlisting language="java"><![CDATA[public class MyFacadeEJB extends AbstractStatelessSessionBean
|
||||
implements MyFacadeLocal {
|
||||
|
||||
private 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 myFacadeMethod(...) {
|
||||
return myComp.myMethod(...);
|
||||
}
|
||||
...
|
||||
}]]></programlisting>
|
||||
<para>
|
||||
The Spring EJB support base classes will by default create and load
|
||||
a Spring IoC container as part of their lifecycle, which is then available
|
||||
to the EJB (for example, as used in the code above to obtain the POJO
|
||||
service object). The loading is done via a strategy object which is a subclass of
|
||||
<classname>BeanFactoryLocator</classname>. The actual implementation of
|
||||
<classname>BeanFactoryLocator</classname> used by default is
|
||||
<classname>ContextJndiBeanFactoryLocator</classname>, which creates the
|
||||
ApplicationContext from a resource locations specified as a JNDI
|
||||
environment variable (in the case of the EJB classes, at
|
||||
<literal>java:comp/env/ejb/BeanFactoryPath</literal>). If there is a need
|
||||
to change the BeanFactory/ApplicationContext loading strategy, the default
|
||||
<classname>BeanFactoryLocator</classname> implementation used may be overridden
|
||||
by calling the <literal>setBeanFactoryLocator()</literal> method, either
|
||||
in <literal>setSessionContext()</literal>, or in the actual constructor of
|
||||
the EJB. Please see the Javadocs for more details.
|
||||
</para>
|
||||
<para>
|
||||
As described in the Javadocs, Stateful Session beans expecting to be
|
||||
passivated and reactivated as part of their lifecycle, and which use a
|
||||
non-serializable container instance (which is the normal case) will have
|
||||
to manually call <literal>unloadBeanFactory()</literal> and
|
||||
<literal>loadBeanFactory()</literal> from <literal>ejbPassivate()</literal>
|
||||
and <literal>ejbActivate()</literal>, respectively, to unload and reload the
|
||||
BeanFactory on passivation and activation, since it can not be saved by
|
||||
the EJB container.
|
||||
</para>
|
||||
<para>
|
||||
The default behavior of the
|
||||
<classname>ContextJndiBeanFactoryLocator</classname> class is to load an
|
||||
<classname>ApplicationContext</classname> for use by an EJB, and is
|
||||
adequate for some situations. However, it is problematic when the
|
||||
<classname>ApplicationContext</classname> is loading a number of beans,
|
||||
or the initialization of those beans is time consuming or memory
|
||||
intensive (such as a Hibernate <classname>SessionFactory</classname>
|
||||
initialization, for example), since every EJB will have their own copy.
|
||||
In this case, the user may want to override the default
|
||||
<classname>ContextJndiBeanFactoryLocator</classname> usage and use
|
||||
another <classname>BeanFactoryLocator</classname> variant, such as the
|
||||
<classname>ContextSingletonBeanFactoryLocator</classname> which can load
|
||||
and use a shared container to be used by multiple EJBs or other clients.
|
||||
Doing this is relatively simple, by adding code similar to this to the
|
||||
EJB:
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[ /**
|
||||
* 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);
|
||||
}]]></programlisting>
|
||||
<para>
|
||||
You would then need to create a bean definition file named <literal>beanRefContext.xml</literal>.
|
||||
This file defines all bean factories (usually in the form of application contexts) that may be used
|
||||
in the EJB. In many cases, this file will only contain a single bean definition such as this (where
|
||||
<literal>businessApplicationContext.xml</literal> contains the bean definitions for all business
|
||||
service POJOs):
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<beans>
|
||||
<bean id="businessBeanFactory" class="org.springframework.context.support.ClassPathXmlApplicationContext">
|
||||
<constructor-arg value="businessApplicationContext.xml" />
|
||||
</bean>
|
||||
</beans>]]></programlisting>
|
||||
<para>
|
||||
In the above example, the <literal>ServicesConstants.PRIMARY_CONTEXT_ID</literal> constant
|
||||
would be defined as follows:
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[public static final String ServicesConstants.PRIMARY_CONTEXT_ID = "businessBeanFactory";]]></programlisting>
|
||||
<para>
|
||||
Please see the respective Javadocs for the <classname>BeanFactoryLocator</classname> and
|
||||
<classname>ContextSingletonBeanFactoryLocator</classname> classes for more information on
|
||||
their usage.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="ejb-implementation-ejb3">
|
||||
<title>EJB 3 injection interceptor</title>
|
||||
<para>
|
||||
For EJB 3 Session Beans and Message-Driven Beans, Spring provides a convenient
|
||||
interceptor that resolves Spring 2.5's <literal>@Autowired</literal> annotation
|
||||
in the EJB component class:
|
||||
<classname>org.springframework.ejb.interceptor.SpringBeanAutowiringInterceptor</classname>.
|
||||
This interceptor can be applied through an <code>@Interceptors</code> annotation
|
||||
in the EJB component class, or through an <literal>interceptor-binding</literal>
|
||||
XML element in the EJB deployment descriptor.
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[@Stateless
|
||||
@Interceptors(SpringBeanAutowiringInterceptor.class)
|
||||
public class MyFacadeEJB implements MyFacadeLocal {
|
||||
|
||||
// automatically injected with a matching Spring bean
|
||||
@Autowired
|
||||
private MyComponent myComp;
|
||||
|
||||
// for business method, delegate to POJO service impl.
|
||||
public String myFacadeMethod(...) {
|
||||
return myComp.myMethod(...);
|
||||
}
|
||||
...
|
||||
}]]></programlisting>
|
||||
<para>
|
||||
<classname>SpringBeanAutowiringInterceptor</classname> by default obtains target
|
||||
beans from a <classname>ContextSingletonBeanFactoryLocator</classname>, with the
|
||||
context defined in a bean definition file named <literal>beanRefContext.xml</literal>.
|
||||
By default, a single context definition is expected, which is obtained by type rather
|
||||
than by name. However, if you need to choose between multiple context definitions,
|
||||
a specific locator key is required. The locator key (i.e. the name of the context
|
||||
definition in <literal>beanRefContext.xml</literal>) can be explicitly specified
|
||||
either through overriding the <literal>getBeanFactoryLocatorKey</literal> method
|
||||
in a custom <classname>SpringBeanAutowiringInterceptor</classname> subclass.
|
||||
</para>
|
||||
<para>
|
||||
Alternatively, consider overriding <classname>SpringBeanAutowiringInterceptor</classname>'s
|
||||
<literal>getBeanFactory</literal> method, e.g. obtaining a shared
|
||||
<interfacename>ApplicationContext</interfacename> from a custom holder class.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
</chapter>
|
||||
Reference in New Issue
Block a user