How to Spring » History » Revision 43
« Previous |
Revision 43/62
(diff)
| Next »
Henning Blohm, 14.09.2012 12:08
How to use the Spring framework in Z2¶
There is actually nothing really particular about using Spring in Z2. But when knowing how Z2 modularity works, there is much to gain by spending a few minutes reading this Howto. We assume however that you are familiar with the Spring framework as such.
It may be best to inspect the the basic sample-spring-basic sample in parallel to reading this page, as that makes use of most of what you will find here and is still really simple. Plus, running the sample requires no more than Eclipse and five minutes of your time.
Pre-requisites¶
In order to have the Spring libraries available you need to add the add-on Spring add-on via the repository z2-addons.spring to your environment (the samples do so in their environment). To use the master branch version, add a springRepository.properties component descriptor to your environment module saying
com.zfabrik.systemStates.participation=com.zfabrik.boot.main/sysrepo_up
# git stored component repository
com.zfabrik.component.type=com.zfabrik.gitcr
# true <=> optional repository. If gitcr.uri is invalid, then this gitcr will be ignore silently
gitcr.optional=true
# this can also point to a remote repository like
# ssh://myserver/some/git/repo
gitcr.uri=http://git.z2-environment.net/z2-addons.spring
# the git branch to use (e.g. 'master')
gitcr.branch=master
For your own system, you may need to adapt the repository URL and the branch selection accordingly.
If that sounds like meaningless gibberish to you - sorry! Please consult the documentation at http://www.z2-environment.eu/v20doc and go back to First_steps_with_z2.
When you added that repository the following modules are available:
- org.springframework.orm
- org.springframework.security
- org.springframework.foundation
- org.springframework.transaction
- org.springframework.jdbc
- org.springframework.web
- com.zfabrik.springframework
- com.zfabrik.springframework.web
With the exception of those starting with "com.zfabrik", these do, more or less, correspond to the typical Spring modules found out there.
Before you get frustrated by the amount of details in this Howto, please remember that there is practical samples available to help you check how things can really be assembled to work nicely:
Using Spring in Web applications¶
This is the simplest and really just the standard case. If you do not strive for re-use across modules that use Spring, then there is not much to worry about.
As usual, you define an application context in the WEB-INF folder of the Web application and set up a context listener in WEB-INF/web.xml to have the application
context initialized as the Web app is started.
In order to have the minimal set of dependencies satisfied - i.e. not assuming you want to use the (very cool) AspectJ based Spring configuration, you should add (i.e. augment whatever refs you already have) the following references to java/z.properties:
java.privateReferences=\
com.zfabrik.springframework.web,\
org.springframework.foundation,\
org.springframework.web
The reference to com.zfabrik.springframework.web is not strictly needed but adds the following capabilities:
- You implicitly get com.zfabrik.springframework, i.e. the integration features described below
- You can use a parent application context to your Web application application context as easily as you would hope (see below).
Using Spring Context Support¶
The module org.springframework.foundation holds a Java component org.springframework.foundation/context.support that is not meant to be referenced but to be included.
Including a Java component means that it's resources (class files, jars,...) will be copied into the including scope rather than referenced via class loader delegation.
The difference is that in that case, all the includer sees is also be seen by the types in the included component. And that is exactly what context.support is about: It holds adapter classes that effectively have dangling dependencies on other prominent libraries. Say for example we are talking about the Lucene search engine library. If your Java component references the Lucene libs (which are not included with Z2 by default), and say you want to use the Spring adapter types, then include context.support by adding (i.e. customizing correspondingly) the private include:
java.privateIncludes=\
org.springframework.foundation/context.support
Typically it makes most sense to only use private includes. If you include into the API, which is possible, other modules that reference your API will find the included types
of your API with preference of their includes (as part of the parent-first delegation principle). So, they may not be able to repeat the same include pattern described above.
Using Spring in re-use modules¶
When going modular, you will leave the pure "Spring in a Web Application" path. That is, you may want to use Spring in modules that expose components and APIs to other modules. For example to implement shared services that are used in more than one Web Application, or for example to implement scheduled job execution.
Define an Application Context in a Java Module
In that case, you will not define an application context in a WEB-INF folder (as there most likely is none) but instead you will define a class path defined application context, or more specifically you will put your application context (most likely) in java/src.impl/META-INF/applicationContext.xml of your re-use module.
Starting an Application Context
Now that you have that application context, you need to make sure it is started. If you went this far, there is good reasons you use Z2 runtime dependencies to start it. Pre-requisite for that is to have the application context declared as a Z2 component. So you create a file applicationContext.properties in your module saying
com.zfabrik.component.type=org.springframework.context
#
# context config location is where the context is
# actually defined.
#
context.contextConfigLocation=classpath:META-INF/applicationContext.xml
Once it is defined as a component, you can have it started via a system state dependency, via a generic component depencency.
Another way is to have it started implicitly from a bean component declaration. In that case, before providing the bean instance, the application context will be initialized.
Exposing a bean from an application context
Now that you have declared an application context component, you can actually expose beans from it as Z2 components that may be looked up from other modules. To do so, create a component like this:
com.zfabrik.component.type=org.springframework.bean
#
# the context that defines the bean (more than one
# bean can be exposed like this)
#
bean.context=<module name>/applicationContext
#
# the bean name
#
bean.name=<bean name>
Now that a bean has been made available to other modules, the next natural question is how to actually refer to a Z2 component from a Spring application context.
Importing a bean or just any component
The module com.zfabrik.springframework contains a factory bean that just does that. Assuming you want to use component <module>/<name> which is of type (or has super type) my.Class, you would add to your importing application context:
<bean id="beanId" class="com.zfabrik.springframework.ComponentFactoryBean">
<property name="componentName" value="<module>/<name>" />
<property name="className" value="my.Class" />
</bean>
Ok, so far so good. Next subject is how to add more configuration magic to all of this.
Using Spring's AspectJ configuration¶
By default, Spring's annotation support reaches as far as Spring "can see". That is, objects instantiated outside of Spring's control will not be subject to Spring configuration - simply because Spring had no chance to look at it.
When using Spring's AspectJ feature, Spring's configuration reach can be extended to virtually anything within the implementation scope. Normally, configuring your build to include the AspectJ compilation can be tricky and tiring. The alternative of using load-time-weaving is rather error-prone as types may not have been loaded before the aspect has been registered - soemthing where you can so easily fool yourself.
Fortunately for you, the Spring integration features of Z2 also provide a compiler extension for Spring with AspectJ. To use it, you need to modify your java/z.properties to reflect the following includes and refs and in parcitular the compiler settings:
java.privateReferences=\
com.zfabrik.springframework,\
org.springframework.foundation,\
org.springframework.orm,\
org.springframework.transaction
java.privateIncludes=\
org.springframework.foundation/aspects
java.compile.order=java,spring_aspectj
The refs to spring transaction and spring ORM look unintuitive. And they are. At the time of this writing the Spring aspect unfortunately still required those.
When you have applied the configuration above, you can make any type Spring configurable by annotating it with @Configurable
. For example the ControllerServlet in the sample z2-samples.spring-basic has the following skeleton:
@Configurable
public class ControllerServlet extends HttpServlet {
@Autowired
private IComputationService computations;
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//...
}
}
Important things to know about Spring with AspectJ
As always, there is some important pitfalls to watch out for. The Spring AspectJ integration roughly works as follows:
- When an application context is loaded that has <context:spring-configured /> and the Spring aspect classes are on the classpath (i.e. can be found by the class loader of the application context), then the current application context will be set as a static class variable of one Spring aspect implementation class.
- Later, when a configurable object is being instantiated, the woven-in code will look for that static class variable to determine the application context to take configuration from.
This has a few dramatic consequences:
- If you would have two application contexts seeing the same static class variable they would override each other and hence you essentially lose control over what configuration is effective.
- Hence you should not using Spring AspectJ configuration in API types, as anyone referring to your API would not be able to repeat the trick.
That's why: Only use Spring AspectJ in implementations, i.e. under src.impl or src.test
Using a parent application context in a Web application application context¶
Sometimes, later, when you find that your module has a web app but in addition you want to expose services, from the very same module, i.e. when you have spring configured objects in the web app but also a classpath defined application context that should serve as a parent application context to the one of the web app (admittedly an advanced case), then you will find that that is slightly tricky to achieve (see e.g. http://blog.springsource.org/2007/06/11/using-a-shared-parent-application-context-in-a-multi-war-spring-application/).
The class com.zfabrik.springframework.web.ComponentParentContextContextLoaderListener is a drop-in replacement for Spring's ContextLoaderListener implementation that simplifies that use-case as explained in http://z2spring.z2-environment.net/javadoc/com.zfabrik.springframework.web!2Fjava/api/com/zfabrik/springframework/web/ComponentParentContextContextLoaderListener.html.
Advanced notes¶
As may have leaked through above already, some features of the Spring framework have slightly non-trivial prerequisites. In general these require that Spring provided libraries are used within the context of other Java components - as for context.support above - and that requires some explanation:
At runtime, Java components in Z2 have two class loader instances. One for the API definitions of the component, the other for the implementation definitions of the component. The latter is not visible to any referencing component, while the former is. Visibility is achieved by class loader delegation. That is, the class loaders of a referencing Java component will first delegate to the API class loader of the referenced component when looking for a type before checking their own resources.
The delegation sequence is always strictly along the references.
When we use the term shared library or shared Java component, we are referring to a Java component that can be referenced by others to provide some functionality. At runtime, the types of that Java component are loaded exactly once into the VM's memory. As a side-effect, static class members for example will be shared amongst all usages.
The alternative to sharing a Java component is to “include” a Java component. When a Java component references a component of type com.zfabrik.files or com.zfabrik.java, libraries and class files of the referenced component will be copied into the referencing component (depending on the reference either into the API or the implementation part). In that case, the types provided by the files component are used in the context of there referencing component and may be loaded several times.
Considering Spring there are two cases that mandate a non-shared use of libraries.
Dangling Imports¶
Some Spring libraries provide integration of Spring with a variety of third-party libraries. When those libraries were compiled, all those third-party libraries were present on the compilation class path. Java's late linking paradigm allows to use those libraries without the presence of the third-party libraries as long as visibility of those types is not necessary yet.
One example of such a library is the context support Spring module. See above for more details.
Static Members Holding Singletons¶
Another reason that mandates in context use is module specific static initialization. In other words there is a class that holds static, class-level data that is specific to the using context.
One example of such is the Spring AspectJ context that points to its underlying application context by a class variable. As a consequence, sharing these classes between modules with different application context would lead to confusions about what application context is used during the application of the Spring aspect. That is why the Spring AspectJ library should always be included by a private reference, and the implementation should use only one application context as far as AspectJ configuration is concerned.
Updated by Henning Blohm over 12 years ago · 43 revisions