You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
50254 lines
1.9 MiB
50254 lines
1.9 MiB
= Spring Framework Reference Documentation |
|
Rod Johnson; Juergen Hoeller; Keith Donald; Colin Sampaleanu; Rob Harrop; Thomas Risberg; Alef Arendsen; Darren Davison; Dmitriy Kopylenko; Mark Pollack; Thierry Templier; Erwin Vervaet; Portia Tung; Ben Hale; Adrian Colyer; John Lewis; Costin Leau; Mark Fisher; Sam Brannen; Ramnivas Laddad; Arjen Poutsma; Chris Beams; Tareq Abedrabbo; Andy Clement; Dave Syer; Oliver Gierke; Rossen Stoyanchev; Phillip Webb; Rob Winch; Brian Clozel; Stephane Nicoll; Sebastien Deleuze |
|
|
|
|
|
:javadoc-baseurl: http://docs.spring.io/spring/docs/current/javadoc-api |
|
|
|
[[spring-introduction]] |
|
= Overview of Spring Framework |
|
|
|
[partintro] |
|
-- |
|
The Spring Framework is a lightweight solution and a potential one-stop-shop for |
|
building your enterprise-ready applications. However, Spring is modular, allowing you to |
|
use only those parts that you need, without having to bring in the rest. You can use the |
|
IoC container, with any web framework on top, but you can also use only the |
|
<<orm-hibernate,Hibernate integration code>> or the <<jdbc-introduction,JDBC abstraction |
|
layer>>. The Spring Framework supports declarative transaction management, remote access |
|
to your logic through RMI or web services, and various options for persisting your data. |
|
It offers a full-featured <<mvc-introduction,MVC framework>>, and enables you to |
|
integrate <<aop-introduction,AOP>> transparently into your software. |
|
|
|
Spring is designed to be non-intrusive, meaning that your domain logic code generally |
|
has no dependencies on the framework itself. In your integration layer (such as the data |
|
access layer), some dependencies on the data access technology and the Spring libraries |
|
will exist. However, it should be easy to isolate these dependencies from the rest of |
|
your code base. |
|
|
|
This document is a reference guide to Spring Framework features. If you have any |
|
requests, comments, or questions on this document, please post them on the |
|
https://groups.google.com/forum/#!forum/spring-framework-contrib[user mailing |
|
list]. Questions on the Framework itself should be asked on StackOverflow |
|
(see https://spring.io/questions[]). |
|
-- |
|
|
|
|
|
|
|
|
|
|
|
[[overview-getting-started-with-spring]] |
|
== Getting Started with Spring |
|
This reference guide provides detailed information about the Spring Framework. |
|
It provides comprehensive documentation for all features, as well as some background |
|
about the underlying concepts (such as __"Dependency Injection"__) that Spring has |
|
embraced. |
|
|
|
If you are just getting started with Spring, you may want to begin using the Spring Framework |
|
by creating a http://projects.spring.io/spring-boot/[Spring Boot] based application. |
|
Spring Boot provides a quick (and opinionated) way to create a production-ready Spring based |
|
application. It is based on the Spring Framework, favors convention over configuration, and is |
|
designed to get you up and running as quickly as possible. |
|
|
|
You can use http://start.spring.io[start.spring.io] to generate a basic project or follow |
|
one of the https://spring.io/guides["Getting Started" guides] like the |
|
https://spring.io/guides/gs/rest-service/[Getting Started Building a RESTful Web Service] |
|
one. As well as being easier to digest, these guides are very __task focused__, and most of |
|
them are based on Spring Boot. They also cover other projects from the Spring portfolio |
|
that you might want to consider when solving a particular problem. |
|
|
|
|
|
|
|
[[overview]] |
|
== Introduction to the Spring Framework |
|
The Spring Framework is a Java platform that provides comprehensive infrastructure support |
|
for developing Java applications. Spring handles the infrastructure so you can focus on |
|
your application. |
|
|
|
Spring enables you to build applications from "plain old Java objects" (POJOs) and to |
|
apply enterprise services non-invasively to POJOs. This capability applies to the Java |
|
SE programming model and to full and partial Java EE. |
|
|
|
Examples of how you, as an application developer, can benefit from the Spring platform: |
|
|
|
* Make a Java method execute in a database transaction without having to deal with |
|
transaction APIs. |
|
* Make a local Java method a remote procedure without having to deal with remote APIs. |
|
* Make a local Java method a management operation without having to deal with JMX APIs. |
|
* Make a local Java method a message handler without having to deal with JMS APIs. |
|
|
|
|
|
|
|
|
|
[[overview-dependency-injection]] |
|
=== Dependency Injection and Inversion of Control |
|
|
|
A Java application -- a loose term that runs the gamut from constrained, embedded |
|
applications to n-tier, server-side enterprise applications -- typically consists of |
|
objects that collaborate to form the application proper. Thus the objects in an |
|
application have __dependencies__ on each other. |
|
|
|
Although the Java platform provides a wealth of application development functionality, |
|
it lacks the means to organize the basic building blocks into a coherent whole, leaving |
|
that task to architects and developers. Although you can use design patterns such |
|
as __Factory__, __Abstract Factory__, __Builder__, __Decorator__, and __Service Locator__ |
|
to compose the various classes and object instances that make up an application, |
|
these patterns are simply that: best practices given a name, with a description |
|
of what the pattern does, where to apply it, the problems it addresses, and so forth. |
|
Patterns are formalized best practices that __you must implement yourself__ in your |
|
application. |
|
|
|
The Spring Framework __Inversion of Control__ (IoC) component addresses this concern by |
|
providing a formalized means of composing disparate components into a fully working |
|
application ready for use. The Spring Framework codifies formalized design patterns as |
|
first-class objects that you can integrate into your own application(s). Numerous |
|
organizations and institutions use the Spring Framework in this manner to engineer |
|
robust, __maintainable__ applications. |
|
|
|
[[background-ioc]] |
|
.Background |
|
**** |
|
"__The question is, what aspect of control are [they] inverting?__" Martin Fowler posed |
|
this question about Inversion of Control (IoC) |
|
http://martinfowler.com/articles/injection.html[on his site] in 2004. Fowler suggested |
|
renaming the principle to make it more self-explanatory and came up with __Dependency |
|
Injection__. |
|
**** |
|
|
|
|
|
|
|
|
|
[[overview-modules]] |
|
=== Modules |
|
The Spring Framework consists of features organized into about 20 modules. These modules |
|
are grouped into Core Container, Data Access/Integration, Web, AOP (Aspect Oriented |
|
Programming), Instrumentation, Messaging, and Test, as shown in the following diagram. |
|
|
|
.Overview of the Spring Framework |
|
image::images/spring-overview.png[width=400] |
|
|
|
The following sections list the available modules for each feature along with their |
|
artifact names and the topics they cover. Artifact names correlate to _artifact IDs_ used |
|
in <<dependency-management,Dependency Management tools>>. |
|
|
|
|
|
[[overview-core-container]] |
|
==== Core Container |
|
The <<beans-introduction,__Core Container__>> consists of the `spring-core`, |
|
`spring-beans`, `spring-context`, `spring-context-support`, and `spring-expression` |
|
(Spring Expression Language) modules. |
|
|
|
The `spring-core` and `spring-beans` modules <<beans-introduction,provide the fundamental |
|
parts of the framework>>, including the IoC and Dependency Injection features. The |
|
`BeanFactory` is a sophisticated implementation of the factory pattern. It removes the |
|
need for programmatic singletons and allows you to decouple the configuration and |
|
specification of dependencies from your actual program logic. |
|
|
|
The <<context-introduction,__Context__>> (`spring-context`) module builds on the solid |
|
base provided by the <<beans-introduction,__Core and Beans__>> modules: it is a means to |
|
access objects in a framework-style manner that is similar to a JNDI registry. The |
|
Context module inherits its features from the Beans module and adds support for |
|
internationalization (using, for example, resource bundles), event propagation, resource |
|
loading, and the transparent creation of contexts by, for example, a Servlet container. |
|
The Context module also supports Java EE features such as EJB, JMX, and basic remoting. |
|
The `ApplicationContext` interface is the focal point of the Context module. |
|
`spring-context-support` provides support for integrating common third-party libraries |
|
into a Spring application context for caching (EhCache, Guava, JCache), mailing |
|
(JavaMail), scheduling (CommonJ, Quartz) and template engines (FreeMarker, JasperReports, |
|
Velocity). |
|
|
|
|
|
The `spring-expression` module provides a powerful <<expressions,__Expression |
|
Language__>> for querying and manipulating an object graph at runtime. It is an extension |
|
of the unified expression language (unified EL) as specified in the JSP 2.1 |
|
specification. The language supports setting and getting property values, property |
|
assignment, method invocation, accessing the content of arrays, collections and indexers, |
|
logical and arithmetic operators, named variables, and retrieval of objects by name from |
|
Spring's IoC container. It also supports list projection and selection as well as common |
|
list aggregations. |
|
|
|
|
|
[[overview-aop-instrumentation]] |
|
==== AOP and Instrumentation |
|
The `spring-aop` module provides an <<aop-introduction,__AOP__>> Alliance-compliant |
|
aspect-oriented programming implementation allowing you to define, for example, |
|
method interceptors and pointcuts to cleanly decouple code that implements functionality |
|
that should be separated. Using source-level metadata functionality, you can also |
|
incorporate behavioral information into your code, in a manner similar to that of .NET |
|
attributes. |
|
|
|
The separate `spring-aspects` module provides integration with AspectJ. |
|
|
|
The `spring-instrument` module provides class instrumentation support and classloader |
|
implementations to be used in certain application servers. The `spring-instrument-tomcat` |
|
module contains Spring's instrumentation agent for Tomcat. |
|
|
|
|
|
[[overview-messaging]] |
|
==== Messaging |
|
Spring Framework 4 includes a `spring-messaging` module with key abstractions from the |
|
_Spring Integration_ project such as `Message`, `MessageChannel`, `MessageHandler`, and |
|
others to serve as a foundation for messaging-based applications. The module also |
|
includes a set of annotations for mapping messages to methods, similar to the Spring MVC |
|
annotation based programming model. |
|
|
|
|
|
[[overview-data-access]] |
|
==== Data Access/Integration |
|
The __Data Access/Integration__ layer consists of the JDBC, ORM, OXM, JMS, and |
|
Transaction modules. |
|
|
|
The `spring-jdbc` module provides a <<jdbc-introduction,JDBC>>-abstraction layer that |
|
removes the need to do tedious JDBC coding and parsing of database-vendor specific error |
|
codes. |
|
|
|
The `spring-tx` module supports <<transaction,programmatic and declarative transaction>> |
|
management for classes that implement special interfaces and for __all your POJOs (Plain |
|
Old Java Objects)__. |
|
|
|
The `spring-orm` module provides integration layers for popular |
|
<<orm-introduction,object-relational mapping>> APIs, including <<orm-jpa,JPA>>, |
|
<<orm-jdo,JDO>>, and <<orm-hibernate,Hibernate>>. Using the `spring-orm` module you can |
|
use all of these O/R-mapping frameworks in combination with all of the other features |
|
Spring offers, such as the simple declarative transaction management feature mentioned |
|
previously. |
|
|
|
The `spring-oxm` module provides an abstraction layer that supports <<oxm,Object/XML |
|
mapping>> implementations such as JAXB, Castor, XMLBeans, JiBX and XStream. |
|
|
|
The `spring-jms` module (<<jms,Java Messaging Service>>) contains features for producing and |
|
consuming messages. Since Spring Framework 4.1, it provides integration with the |
|
`spring-messaging` module. |
|
|
|
|
|
[[overview-web]] |
|
==== Web |
|
The __Web__ layer consists of the `spring-web`, `spring-webmvc`, `spring-websocket`, and |
|
`spring-webmvc-portlet` modules. |
|
|
|
The `spring-web` module provides basic web-oriented integration features such as |
|
multipart file upload functionality and the initialization of the IoC container using |
|
Servlet listeners and a web-oriented application context. It also contains an HTTP client |
|
and the web-related parts of Spring's remoting support. |
|
|
|
The `spring-webmvc` module (also known as the __Web-Servlet__ module) contains Spring's |
|
model-view-controller (<<mvc-introduction,__MVC__>>) and REST Web Services implementation |
|
for web applications. Spring's MVC framework provides a clean separation between domain |
|
model code and web forms and integrates with all of the other features of the Spring |
|
Framework. |
|
|
|
The `spring-webmvc-portlet` module (also known as the __Web-Portlet__ module) provides |
|
the MVC implementation to be used in a Portlet environment and mirrors the functionality |
|
of the `spring-webmvc` module. |
|
|
|
|
|
[[overview-testing]] |
|
==== Test |
|
The `spring-test` module supports the <<unit-testing,unit testing>> and |
|
<<integration-testing,integration testing>> of Spring components with JUnit or TestNG. It |
|
provides consistent <<testcontext-ctx-management,loading>> of Spring |
|
++ApplicationContext++s and <<testcontext-ctx-management-caching,caching>> of those |
|
contexts. It also provides <<mock-objects,mock objects>> that you can use to test your |
|
code in isolation. |
|
|
|
|
|
|
|
[[overview-usagescenarios]] |
|
=== Usage scenarios |
|
The building blocks described previously make Spring a logical choice in many scenarios, |
|
from embedded applications that run on resource-constrained devices to full-fledged |
|
enterprise applications that use Spring's transaction management functionality and web |
|
framework integration. |
|
|
|
.Typical full-fledged Spring web application |
|
image::images/overview-full.png[width=400] |
|
|
|
Spring's <<transaction-declarative,declarative transaction management features>> make |
|
the web application fully transactional, just as it would be if you used EJB |
|
container-managed transactions. All your custom business logic can be implemented with |
|
simple POJOs and managed by Spring's IoC container. Additional services include support |
|
for sending email and validation that is independent of the web layer, which lets you |
|
choose where to execute validation rules. Spring's ORM support is integrated with JPA, |
|
Hibernate and and JDO; for example, when using Hibernate, you can continue to use |
|
your existing mapping files and standard Hibernate `SessionFactory` configuration. Form |
|
controllers seamlessly integrate the web-layer with the domain model, removing the need |
|
for `ActionForms` or other classes that transform HTTP parameters to values for your |
|
domain model. |
|
|
|
.Spring middle-tier using a third-party web framework |
|
image::images/overview-thirdparty-web.png[width=400] |
|
|
|
Sometimes circumstances do not allow you to completely switch to a different framework. |
|
The Spring Framework does __not__ force you to use everything within it; it is not an |
|
__all-or-nothing__ solution. Existing front-ends built with Struts, Tapestry, JSF |
|
or other UI frameworks can be integrated with a Spring-based middle-tier, which allows |
|
you to use Spring transaction features. You simply need to wire up your business logic |
|
using an `ApplicationContext` and use a `WebApplicationContext` to integrate your web |
|
layer. |
|
|
|
.Remoting usage scenario |
|
image::images/overview-remoting.png[width=400] |
|
|
|
When you need to access existing code through web services, you can use Spring's |
|
`Hessian-`, `Burlap-`, `Rmi-` or `JaxRpcProxyFactory` classes. Enabling remote access to |
|
existing applications is not difficult. |
|
|
|
.EJBs - Wrapping existing POJOs |
|
image::images/overview-ejb.png[width=400] |
|
|
|
The Spring Framework also provides an <<ejb,access and abstraction layer>> for |
|
Enterprise JavaBeans, enabling you to reuse your existing POJOs and wrap them in |
|
stateless session beans for use in scalable, fail-safe web applications that might need |
|
declarative security. |
|
|
|
|
|
|
|
[[dependency-management]] |
|
==== Dependency Management and Naming Conventions |
|
Dependency management and dependency injection are different things. To get those nice |
|
features of Spring into your application (like dependency injection) you need to |
|
assemble all the libraries needed (jar files) and get them onto your classpath at |
|
runtime, and possibly at compile time. These dependencies are not virtual components |
|
that are injected, but physical resources in a file system (typically). The process of |
|
dependency management involves locating those resources, storing them and adding them to |
|
classpaths. Dependencies can be direct (e.g. my application depends on Spring at |
|
runtime), or indirect (e.g. my application depends on `commons-dbcp` which depends on |
|
`commons-pool`). The indirect dependencies are also known as "transitive" and it is |
|
those dependencies that are hardest to identify and manage. |
|
|
|
If you are going to use Spring you need to get a copy of the jar libraries that comprise |
|
the pieces of Spring that you need. To make this easier Spring is packaged as a set of |
|
modules that separate the dependencies as much as possible, so for example if you don't |
|
want to write a web application you don't need the spring-web modules. To refer to |
|
Spring library modules in this guide we use a shorthand naming convention `spring-*` or |
|
`spring-*.jar,` where `*` represents the short name for the module (e.g. `spring-core`, |
|
`spring-webmvc`, `spring-jms`, etc.). The actual jar file name that you use is normally |
|
the module name concatenated with the version number |
|
(e.g. __spring-core-{spring-version}.jar__). |
|
|
|
Each release of the Spring Framework will publish artifacts to the following places: |
|
|
|
* Maven Central, which is the default repository that Maven queries, and does not |
|
require any special configuration to use. Many of the common libraries that Spring |
|
depends on also are available from Maven Central and a large section of the Spring |
|
community uses Maven for dependency management, so this is convenient for them. The |
|
names of the jars here are in the form `spring-*-<version>.jar` and the Maven groupId |
|
is `org.springframework`. |
|
* In a public Maven repository hosted specifically for Spring. In addition to the final |
|
GA releases, this repository also hosts development snapshots and milestones. The jar |
|
file names are in the same form as Maven Central, so this is a useful place to get |
|
development versions of Spring to use with other libraries deployed in Maven Central. |
|
This repository also contains a bundle distribution zip file that contains all Spring |
|
jars bundled together for easy download. |
|
|
|
So the first thing you need to decide is how to manage your dependencies: we generally |
|
recommend the use of an automated system like Maven, Gradle or Ivy, but you can also do |
|
it manually by downloading all the jars yourself. |
|
|
|
You will find bellow the list of Spring artifacts. For a more complete description of each |
|
modules, see <<overview-modules>>. |
|
|
|
|
|
.Spring Framework Artifacts |
|
|=== |
|
|GroupId |ArtifactId |Description |
|
|
|
|org.springframework |
|
|spring-aop |
|
|Proxy-based AOP support |
|
|
|
|org.springframework |
|
|spring-aspects |
|
|AspectJ based aspects |
|
|
|
|org.springframework |
|
|spring-beans |
|
|Beans support, including Groovy |
|
|
|
|org.springframework |
|
|spring-context |
|
|Application context runtime, including scheduling and remoting abstractions |
|
|
|
|org.springframework |
|
|spring-context-support |
|
|Support classes for integrating common third-party libraries into a Spring application context |
|
|
|
|org.springframework |
|
|spring-core |
|
|Core utilities, used by many other Spring modules |
|
|
|
|org.springframework |
|
|spring-expression |
|
|Spring Expression Language (SpEL) |
|
|
|
|org.springframework |
|
|spring-instrument |
|
|Instrumentation agent for JVM bootstrapping |
|
|
|
|org.springframework |
|
|spring-instrument-tomcat |
|
|Instrumentation agent for Tomcat |
|
|
|
|org.springframework |
|
|spring-jdbc |
|
|JDBC support package, including DataSource setup and JDBC access support |
|
|
|
|org.springframework |
|
|spring-jms |
|
|JMS support package, including helper classes to send and receive JMS messages |
|
|
|
|org.springframework |
|
|spring-messaging |
|
|Support for messaging architectures and protocols |
|
|
|
|org.springframework |
|
|spring-orm |
|
|Object/Relational Mapping, including JPA and Hibernate support |
|
|
|
|org.springframework |
|
|spring-oxm |
|
|Object/XML Mapping |
|
|
|
|org.springframework |
|
|spring-test |
|
|Support for unit testing and integration testing Spring components |
|
|
|
|org.springframework |
|
|spring-tx |
|
|Transaction infrastructure, including DAO support and JCA integration |
|
|
|
|org.springframework |
|
|spring-web |
|
|Web support packages, including client and web remoting |
|
|
|
|org.springframework |
|
|spring-webmvc |
|
|REST Web Services and model-view-controller implementation for web applications |
|
|
|
|org.springframework |
|
|spring-webmvc-portlet |
|
|MVC implementation to be used in a Portlet environment |
|
|
|
|org.springframework |
|
|spring-websocket |
|
|WebSocket and SockJS implementations, including STOMP support |
|
|=== |
|
|
|
|
|
|
|
[[overview-spring-dependencies]] |
|
===== Spring Dependencies and Depending on Spring |
|
Although Spring provides integration and support for a huge range of enterprise and |
|
other external tools, it intentionally keeps its mandatory dependencies to an absolute |
|
minimum: you shouldn't have to locate and download (even automatically) a large number |
|
of jar libraries in order to use Spring for simple use cases. For basic dependency |
|
injection there is only one mandatory external dependency, and that is for logging (see |
|
below for a more detailed description of logging options). |
|
|
|
Next we outline the basic steps needed to configure an application that depends on |
|
Spring, first with Maven and then with Gradle and finally using Ivy. In all cases, if |
|
anything is unclear, refer to the documentation of your dependency management system, or |
|
look at some sample code - Spring itself uses Gradle to manage dependencies when it is |
|
building, and our samples mostly use Gradle or Maven. |
|
|
|
|
|
[[overview-maven-dependency-management]] |
|
===== Maven Dependency Management |
|
If you are using http://maven.apache.org/[Maven] for dependency management you don't even |
|
need to supply the logging dependency explicitly. For example, to create an application |
|
context and use dependency injection to configure an application, your Maven dependencies |
|
will look like this: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes,attributes"] |
|
---- |
|
<dependencies> |
|
<dependency> |
|
<groupId>org.springframework</groupId> |
|
<artifactId>spring-context</artifactId> |
|
<version>{spring-version}</version> |
|
<scope>runtime</scope> |
|
</dependency> |
|
</dependencies> |
|
---- |
|
|
|
That's it. Note the scope can be declared as runtime if you don't need to compile |
|
against Spring APIs, which is typically the case for basic dependency injection use |
|
cases. |
|
|
|
The example above works with the Maven Central repository. To use the Spring Maven |
|
repository (e.g. for milestones or developer snapshots), you need to specify the |
|
repository location in your Maven configuration. For full releases: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<repositories> |
|
<repository> |
|
<id>io.spring.repo.maven.release</id> |
|
<url>http://repo.spring.io/release/</url> |
|
<snapshots><enabled>false</enabled></snapshots> |
|
</repository> |
|
</repositories> |
|
---- |
|
|
|
For milestones: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<repositories> |
|
<repository> |
|
<id>io.spring.repo.maven.milestone</id> |
|
<url>http://repo.spring.io/milestone/</url> |
|
<snapshots><enabled>false</enabled></snapshots> |
|
</repository> |
|
</repositories> |
|
---- |
|
|
|
And for snapshots: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<repositories> |
|
<repository> |
|
<id>io.spring.repo.maven.snapshot</id> |
|
<url>http://repo.spring.io/snapshot/</url> |
|
<snapshots><enabled>true</enabled></snapshots> |
|
</repository> |
|
</repositories> |
|
---- |
|
|
|
|
|
[[overview-maven-bom]] |
|
===== Maven "Bill Of Materials" Dependency ===== |
|
It is possible to accidentally mix different versions of Spring JARs when using Maven. |
|
For example, you may find that a third-party library, or another Spring project, |
|
pulls in a transitive dependency to an older release. If you forget to explicitly declare |
|
a direct dependency yourself, all sorts of unexpected issues can arise. |
|
|
|
To overcome such problems Maven supports the concept of a "bill of materials" (BOM) |
|
dependency. You can import the `spring-framework-bom` in your `dependencyManagement` |
|
section to ensure that all spring dependencies (both direct and transitive) are at |
|
the same version. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes,attributes"] |
|
---- |
|
<dependencyManagement> |
|
<dependencies> |
|
<dependency> |
|
<groupId>org.springframework</groupId> |
|
<artifactId>spring-framework-bom</artifactId> |
|
<version>{spring-version}</version> |
|
<type>pom</type> |
|
<scope>import</scope> |
|
</dependency> |
|
</dependencies> |
|
</dependencyManagement> |
|
---- |
|
|
|
An added benefit of using the BOM is that you no longer need to specify the `<version>` |
|
attribute when depending on Spring Framework artifacts: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes,attributes"] |
|
---- |
|
<dependencies> |
|
<dependency> |
|
<groupId>org.springframework</groupId> |
|
<artifactId>spring-context</artifactId> |
|
</dependency> |
|
<dependency> |
|
<groupId>org.springframework</groupId> |
|
<artifactId>spring-web</artifactId> |
|
</dependency> |
|
<dependencies> |
|
---- |
|
|
|
|
|
[[overview-gradle-dependency-management]] |
|
===== Gradle Dependency Management |
|
To use the Spring repository with the http://www.gradle.org/[Gradle] build system, |
|
include the appropriate URL in the `repositories` section: |
|
|
|
[source,groovy,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
repositories { |
|
mavenCentral() |
|
// and optionally... |
|
maven { url "http://repo.spring.io/release" } |
|
} |
|
---- |
|
|
|
You can change the `repositories` URL from `/release` to `/milestone` or `/snapshot` as |
|
appropriate. Once a repository has been configured, you can declare dependencies in the |
|
usual Gradle way: |
|
|
|
[source,groovy,indent=0] |
|
[subs="verbatim,quotes,attributes"] |
|
---- |
|
dependencies { |
|
compile("org.springframework:spring-context:{spring-version}") |
|
testCompile("org.springframework:spring-test:{spring-version}") |
|
} |
|
---- |
|
|
|
|
|
[[overview-ivy-dependency-management]] |
|
===== Ivy Dependency Management |
|
If you prefer to use http://ant.apache.org/ivy[Ivy] to manage dependencies then there |
|
are similar configuration options. |
|
|
|
To configure Ivy to point to the Spring repository add the following resolver to your |
|
`ivysettings.xml`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<resolvers> |
|
<ibiblio name="io.spring.repo.maven.release" |
|
m2compatible="true" |
|
root="http://repo.spring.io/release/"/> |
|
</resolvers> |
|
---- |
|
|
|
You can change the `root` URL from `/release/` to `/milestone/` or `/snapshot/` as |
|
appropriate. |
|
|
|
Once configured, you can add dependencies in the usual way. For example (in `ivy.xml`): |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes,attributes"] |
|
---- |
|
<dependency org="org.springframework" |
|
name="spring-core" rev="{spring-version}" conf="compile->runtime"/> |
|
---- |
|
|
|
|
|
[[overview-distribution-zip]] |
|
===== Distribution Zip Files |
|
Although using a build system that supports dependency management is the recommended |
|
way to obtain the Spring Framework, it is still possible to download a distribution |
|
zip file. |
|
|
|
Distribution zips are published to the Spring Maven Repository (this is just for our |
|
convenience, you don't need Maven or any other build system in order to download them). |
|
|
|
To download a distribution zip open a web browser to |
|
http://repo.spring.io/release/org/springframework/spring and select the appropriate |
|
subfolder for the version that you want. Distribution files end `-dist.zip`, for example |
|
+spring-framework-{spring-version}-RELEASE-dist.zip+. Distributions are also published |
|
for http://repo.spring.io/milestone/org/springframework/spring[milestones] and |
|
http://repo.spring.io/snapshot/org/springframework/spring[snapshots]. |
|
|
|
|
|
|
|
[[overview-logging]] |
|
==== Logging |
|
Logging is a very important dependency for Spring because __a)__ it is the only mandatory |
|
external dependency, __b)__ everyone likes to see some output from the tools they are |
|
using, and __c)__ Spring integrates with lots of other tools all of which have also made |
|
a choice of logging dependency. One of the goals of an application developer is often to |
|
have unified logging configured in a central place for the whole application, including |
|
all external components. This is more difficult than it might have been since there are so |
|
many choices of logging framework. |
|
|
|
The mandatory logging dependency in Spring is the Jakarta Commons Logging API (JCL). We |
|
compile against JCL and we also make JCL `Log` objects visible for classes that extend |
|
the Spring Framework. It's important to users that all versions of Spring use the same |
|
logging library: migration is easy because backwards compatibility is preserved even |
|
with applications that extend Spring. The way we do this is to make one of the modules |
|
in Spring depend explicitly on `commons-logging` (the canonical implementation of JCL), |
|
and then make all the other modules depend on that at compile time. If you are using |
|
Maven for example, and wondering where you picked up the dependency on |
|
`commons-logging`, then it is from Spring and specifically from the central module |
|
called `spring-core`. |
|
|
|
The nice thing about `commons-logging` is that you don't need anything else to make your |
|
application work. It has a runtime discovery algorithm that looks for other logging |
|
frameworks in well known places on the classpath and uses one that it thinks is |
|
appropriate (or you can tell it which one if you need to). If nothing else is available |
|
you get pretty nice looking logs just from the JDK (java.util.logging or JUL for short). |
|
You should find that your Spring application works and logs happily to the console out |
|
of the box in most situations, and that's important. |
|
|
|
|
|
[[overview-not-using-commons-logging]] |
|
===== Not Using Commons Logging |
|
Unfortunately, the runtime discovery algorithm in `commons-logging`, while convenient |
|
for the end-user, is problematic. If we could turn back the clock and start Spring now |
|
as a new project it would use a different logging dependency. The first choice would |
|
probably be the Simple Logging Facade for Java ( http://www.slf4j.org[SLF4J]), which is |
|
also used by a lot of other tools that people use with Spring inside their applications. |
|
|
|
There are basically two ways to switch off `commons-logging`: |
|
|
|
. Exclude the dependency from the `spring-core` module (as it is the only module that |
|
explicitly depends on `commons-logging`) |
|
. Depend on a special `commons-logging` dependency that replaces the library with |
|
an empty jar (more details can be found in the |
|
http://slf4j.org/faq.html#excludingJCL[SLF4J FAQ]) |
|
|
|
To exclude commons-logging, add the following to your `dependencyManagement` section: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes,attributes"] |
|
---- |
|
<dependencies> |
|
<dependency> |
|
<groupId>org.springframework</groupId> |
|
<artifactId>spring-core</artifactId> |
|
<version>{spring-version}</version> |
|
<exclusions> |
|
<exclusion> |
|
<groupId>commons-logging</groupId> |
|
<artifactId>commons-logging</artifactId> |
|
</exclusion> |
|
</exclusions> |
|
</dependency> |
|
</dependencies> |
|
---- |
|
|
|
Now this application is probably broken because there is no implementation of the JCL |
|
API on the classpath, so to fix it a new one has to be provided. In the next section we |
|
show you how to provide an alternative implementation of JCL using SLF4J as an example. |
|
|
|
|
|
[[overview-logging-slf4j]] |
|
===== Using SLF4J |
|
SLF4J is a cleaner dependency and more efficient at runtime than `commons-logging` |
|
because it uses compile-time bindings instead of runtime discovery of the other logging |
|
frameworks it integrates. This also means that you have to be more explicit about what |
|
you want to happen at runtime, and declare it or configure it accordingly. SLF4J |
|
provides bindings to many common logging frameworks, so you can usually choose one that |
|
you already use, and bind to that for configuration and management. |
|
|
|
SLF4J provides bindings to many common logging frameworks, including JCL, and it also |
|
does the reverse: bridges between other logging frameworks and itself. So to use SLF4J |
|
with Spring you need to replace the `commons-logging` dependency with the SLF4J-JCL |
|
bridge. Once you have done that then logging calls from within Spring will be translated |
|
into logging calls to the SLF4J API, so if other libraries in your application use that |
|
API, then you have a single place to configure and manage logging. |
|
|
|
A common choice might be to bridge Spring to SLF4J, and then provide explicit binding |
|
from SLF4J to Log4J. You need to supply 4 dependencies (and exclude the existing |
|
`commons-logging`): the bridge, the SLF4J API, the binding to Log4J, and the Log4J |
|
implementation itself. In Maven you would do that like this |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes,attributes"] |
|
---- |
|
<dependencies> |
|
<dependency> |
|
<groupId>org.springframework</groupId> |
|
<artifactId>spring-core</artifactId> |
|
<version>{spring-version}</version> |
|
<exclusions> |
|
<exclusion> |
|
<groupId>commons-logging</groupId> |
|
<artifactId>commons-logging</artifactId> |
|
</exclusion> |
|
</exclusions> |
|
</dependency> |
|
<dependency> |
|
<groupId>org.slf4j</groupId> |
|
<artifactId>jcl-over-slf4j</artifactId> |
|
<version>1.5.8</version> |
|
</dependency> |
|
<dependency> |
|
<groupId>org.slf4j</groupId> |
|
<artifactId>slf4j-api</artifactId> |
|
<version>1.5.8</version> |
|
</dependency> |
|
<dependency> |
|
<groupId>org.slf4j</groupId> |
|
<artifactId>slf4j-log4j12</artifactId> |
|
<version>1.5.8</version> |
|
</dependency> |
|
<dependency> |
|
<groupId>log4j</groupId> |
|
<artifactId>log4j</artifactId> |
|
<version>1.2.14</version> |
|
</dependency> |
|
</dependencies> |
|
---- |
|
|
|
That might seem like a lot of dependencies just to get some logging. Well it is, but it |
|
__is__ optional, and it should behave better than the vanilla `commons-logging` with |
|
respect to classloader issues, notably if you are in a strict container like an OSGi |
|
platform. Allegedly there is also a performance benefit because the bindings are at |
|
compile-time not runtime. |
|
|
|
A more common choice amongst SLF4J users, which uses fewer steps and generates fewer |
|
dependencies, is to bind directly to http://logback.qos.ch[Logback]. This removes the |
|
extra binding step because Logback implements SLF4J directly, so you only need to depend |
|
on two libraries not four ( `jcl-over-slf4j` and `logback`). If you do that you might |
|
also need to exclude the slf4j-api dependency from other external dependencies (not |
|
Spring), because you only want one version of that API on the classpath. |
|
|
|
|
|
[[overview-logging-log4j]] |
|
===== Using Log4J |
|
Many people use http://logging.apache.org/log4j[Log4j] as a logging framework for |
|
configuration and management purposes. It's efficient and well-established, and in fact |
|
it's what we use at runtime when we build and test Spring. Spring also provides some |
|
utilities for configuring and initializing Log4j, so it has an optional compile-time |
|
dependency on Log4j in some modules. |
|
|
|
To make Log4j work with the default JCL dependency ( `commons-logging`) all you need to |
|
do is put Log4j on the classpath, and provide it with a configuration file ( |
|
`log4j.properties` or `log4j.xml` in the root of the classpath). So for Maven users this |
|
is your dependency declaration: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes,attributes"] |
|
---- |
|
<dependencies> |
|
<dependency> |
|
<groupId>org.springframework</groupId> |
|
<artifactId>spring-core</artifactId> |
|
<version>{spring-version}</version> |
|
</dependency> |
|
<dependency> |
|
<groupId>log4j</groupId> |
|
<artifactId>log4j</artifactId> |
|
<version>1.2.14</version> |
|
</dependency> |
|
</dependencies> |
|
---- |
|
|
|
And here's a sample log4j.properties for logging to the console: |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
log4j.rootCategory=INFO, stdout |
|
|
|
log4j.appender.stdout=org.apache.log4j.ConsoleAppender |
|
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout |
|
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n |
|
|
|
log4j.category.org.springframework.beans.factory=DEBUG |
|
---- |
|
|
|
[[overview-native-jcl]] |
|
====== Runtime Containers with Native JCL |
|
Many people run their Spring applications in a container that itself provides an |
|
implementation of JCL. IBM Websphere Application Server (WAS) is the archetype. This |
|
often causes problems, and unfortunately there is no silver bullet solution; simply |
|
excluding `commons-logging` from your application is not enough in most situations. |
|
|
|
To be clear about this: the problems reported are usually not with JCL per se, or even |
|
with `commons-logging`: rather they are to do with binding `commons-logging` to another |
|
framework (often Log4J). This can fail because `commons-logging` changed the way they do |
|
the runtime discovery in between the older versions (1.0) found in some containers and |
|
the modern versions that most people use now (1.1). Spring does not use any unusual |
|
parts of the JCL API, so nothing breaks there, but as soon as Spring or your application |
|
tries to do any logging you can find that the bindings to Log4J are not working. |
|
|
|
In such cases with WAS the easiest thing to do is to invert the class loader hierarchy |
|
(IBM calls it "parent last") so that the application controls the JCL dependency, not |
|
the container. That option isn't always open, but there are plenty of other suggestions |
|
in the public domain for alternative approaches, and your mileage may vary depending on |
|
the exact version and feature set of the container. |
|
|
|
|
|
|
|
|
|
|
|
[[spring-whats-new]] |
|
= What's New in Spring Framework 4.x |
|
|
|
|
|
|
|
|
|
[[new-in-4.0]] |
|
== New Features and Enhancements in Spring Framework 4.0 |
|
The Spring Framework was first released in 2004; since then there have been significant |
|
major revisions: Spring 2.0 provided XML namespaces and AspectJ support; Spring 2.5 |
|
embraced annotation-driven configuration; Spring 3.0 introduced a strong Java 5+ foundation |
|
across the framework codebase, and features such as the Java-based `@Configuration` model. |
|
|
|
Version 4.0 is the latest major release of the Spring Framework and the first to fully |
|
support Java 8 features. You can still use Spring with older versions of Java, however, |
|
the minimum requirement has now been raised to Java SE 6. We have also taken the |
|
opportunity of a major release to remove many deprecated classes and methods. |
|
|
|
A https://github.com/spring-projects/spring-framework/wiki/Migrating-from-earlier-versions-of-the-spring-framework[migration guide for upgrading to Spring 4.0] |
|
is available on the https://github.com/spring-projects/spring-framework/wiki[Spring Framework GitHub Wiki]. |
|
|
|
|
|
|
|
|
|
=== Improved Getting Started Experience |
|
The new https://spring.io[spring.io] website provides a whole series of |
|
https://spring.io/guides["Getting Started"] guides to help you learn Spring. You |
|
can read more about the guides in the <<overview-getting-started-with-spring>> section |
|
in this document. The new website also provides a comprehensive overview of the many |
|
additional projects that are released under the Spring umbrella. |
|
|
|
If you are a Maven user you may also be interested in the helpful |
|
<<overview-maven-bom,bill of materials>> POM file that is now published with each Spring |
|
Framework release. |
|
|
|
|
|
|
|
|
|
=== Removed Deprecated Packages and Methods |
|
All deprecated packages, and many deprecated classes and methods have been removed with |
|
version 4.0. If you are upgrading from a previous release of Spring, you should ensure |
|
that you have fixed any deprecated calls that you were making to outdated APIs. |
|
|
|
For a complete set of changes, check out the |
|
http://docs.spring.io/spring-framework/docs/3.2.4.RELEASE_to_4.0.0.RELEASE/[API |
|
Differences Report]. |
|
|
|
Note that optional third-party dependencies have been raised to a 2010/2011 minimum |
|
(i.e. Spring 4 generally only supports versions released in late 2010 or later now): |
|
notably, Hibernate 3.6+, EhCache 2.1+, Quartz 1.8+, Groovy 1.8+, and Joda-Time 2.0+. |
|
As an exception to the rule, Spring 4 requires the recent Hibernate Validator 4.3+, |
|
and support for Jackson has been focused on 2.0+ now (with Jackson 1.8/1.9 support |
|
retained for the time being where Spring 3.2 had it; now just in deprecated form). |
|
|
|
|
|
|
|
|
|
=== Java 8 (as well as 6 and 7) |
|
Spring Framework 4.0 provides support for several Java 8 features. You can make use of |
|
__lambda expressions__ and __method references__ with Spring's callback interfaces. There |
|
is first-class support for `java.time` (http://jcp.org/en/jsr/detail?id=310[JSR-310]), |
|
and several existing annotations have been retrofitted as `@Repeatable`. You can also |
|
use Java 8's parameter name discovery (based on the `-parameters` compiler flag) as an |
|
alternative to compiling your code with debug information enabled. |
|
|
|
Spring remains compatible with older versions of Java and the JDK: concretely, Java SE 6 |
|
(specifically, a minimum level equivalent to JDK 6 update 18, as released in January 2010) |
|
and above are still fully supported. However, for newly started development projects |
|
based on Spring 4, we recommend the use of Java 7 or 8. |
|
|
|
|
|
|
|
|
|
=== Java EE 6 and 7 |
|
Java EE version 6 or above is now considered the baseline for Spring Framework 4, with |
|
the JPA 2.0 and Servlet 3.0 specifications being of particular relevance. In order to |
|
remain compatible with Google App Engine and older application servers, it is possible |
|
to deploy a Spring 4 application into a Servlet 2.5 environment. However, Servlet 3.0+ |
|
is strongly recommended and a prerequisite in Spring's test and mock packages for test |
|
setups in development environments. |
|
|
|
[NOTE] |
|
==== |
|
If you are a WebSphere 7 user, be sure to install the JPA 2.0 feature pack. On |
|
WebLogic 10.3.4 or higher, install the JPA 2.0 patch that comes with it. This turns |
|
both of those server generations into Spring 4 compatible deployment environments. |
|
==== |
|
|
|
On a more forward-looking note, Spring Framework 4.0 supports the Java EE 7 level of |
|
applicable specifications now: in particular, JMS 2.0, JTA 1.2, JPA 2.1, Bean Validation |
|
1.1, and JSR-236 Concurrency Utilities. As usual, this support focuses on individual |
|
use of those specifications, e.g. on Tomcat or in standalone environments. However, |
|
it works equally well when a Spring application is deployed to a Java EE 7 server. |
|
|
|
Note that Hibernate 4.3 is a JPA 2.1 provider and therefore only supported as of |
|
Spring Framework 4.0. The same applies to Hibernate Validator 5.0 as a Bean Validation |
|
1.1 provider. Neither of the two are officially supported with Spring Framework 3.2. |
|
|
|
|
|
|
|
[[groovy-bean-definition-dsl]] |
|
=== Groovy Bean Definition DSL |
|
Beginning with Spring Framework 4.0, it is possible to define external bean configuration |
|
using a Groovy DSL. This is similar in concept to using XML bean definitions but allows |
|
for a more concise syntax. Using Groovy also allows you to easily embed bean definitions |
|
directly in your bootstrap code. For example: |
|
|
|
[source,groovy,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
def reader = new GroovyBeanDefinitionReader(myApplicationContext) |
|
reader.beans { |
|
dataSource(BasicDataSource) { |
|
driverClassName = "org.hsqldb.jdbcDriver" |
|
url = "jdbc:hsqldb:mem:grailsDB" |
|
username = "sa" |
|
password = "" |
|
settings = [mynew:"setting"] |
|
} |
|
sessionFactory(SessionFactory) { |
|
dataSource = dataSource |
|
} |
|
myService(MyService) { |
|
nestedBean = { AnotherBean bean -> |
|
dataSource = dataSource |
|
} |
|
} |
|
} |
|
---- |
|
|
|
For more information consult the `GroovyBeanDefinitionReader` |
|
{javadoc-baseurl}/org/springframework/beans/factory/groovy/GroovyBeanDefinitionReader.html[javadocs]. |
|
|
|
|
|
|
|
|
|
=== Core Container Improvements |
|
There have been several general improvements to the core container: |
|
|
|
* Spring now treats <<beans-generics-as-qualifiers,__generic types__ as a form of |
|
__qualifier__>> when injecting Beans. For example, if you are using a Spring Data |
|
`Repository` you can now easily inject a specific implementation: |
|
`@Autowired Repository<Customer> customerRepository`. |
|
* If you use Spring's meta-annotation support, you can now develop custom annotations that |
|
<<beans-meta-annotations,expose specific attributes from the source annotation>>. |
|
* Beans can now be __ordered__ when they are <<beans-autowired-annotation,autowired into |
|
lists and arrays>>. Both the `@Order` annotation and `Ordered` interface are |
|
supported. |
|
* The `@Lazy` annotation can now be used on injection points, as well as on `@Bean` |
|
definitions. |
|
* The <<beans-java-bean-description,`@Description` annotation has been introduced>> for |
|
developers using Java-based configuration. |
|
* A generalized model for <<beans-java-conditional,conditionally filtering beans>> has |
|
been added via the `@Conditional` annotation. This is similar to `@Profile` support but |
|
allows for user-defined strategies to be developed programmatically. |
|
* <<aop-pfb-proxy-types,CGLIB-based proxy classes>> no longer require a default |
|
constructor. Support is provided via the http://code.google.com/p/objenesis/[objenesis] |
|
library which is repackaged __inline__ and distributed as part of the Spring Framework. |
|
With this strategy, no constructor at all is being invoked for proxy instances anymore. |
|
* There is managed time zone support across the framework now, e.g. on `LocaleContext`. |
|
|
|
|
|
|
|
|
|
=== General Web Improvements |
|
Deployment to Servlet 2.5 servers remains an option, but Spring Framework 4.0 is now |
|
focused primarily on Servlet 3.0+ environments. If you are using the |
|
<<spring-mvc-test-framework,Spring MVC Test Framework>> you |
|
will need to ensure that a Servlet 3.0 compatible JAR is in your __test classpath__. |
|
|
|
In addition to the WebSocket support mentioned later, the following general improvements |
|
have been made to Spring's Web modules: |
|
|
|
* You can use the <<mvc-ann-restcontroller,new `@RestController` annotation>> with Spring |
|
MVC applications, removing the need to add `@ResponseBody` to each of your |
|
`@RequestMapping` methods. |
|
* The `AsyncRestTemplate` class has been added, <<rest-async-resttemplate,allowing |
|
non-blocking asynchronous support>> when developing REST clients. |
|
* Spring now offers <<mvc-timezone,comprehensive timezone support>> when developing |
|
Spring MVC applications. |
|
|
|
|
|
|
|
|
|
=== WebSocket, SockJS, and STOMP Messaging |
|
A new `spring-websocket` module provides comprehensive support for WebSocket-based, |
|
two-way communication between client and server in web applications. It is compatible with |
|
http://jcp.org/en/jsr/detail?id=356[JSR-356], the Java WebSocket API, and in addition |
|
provides SockJS-based fallback options (i.e. WebSocket emulation) for use in browsers |
|
that don't yet support the WebSocket protocol (e.g. Internet Explorer < 10). |
|
|
|
A new `spring-messaging` module adds support for STOMP as the WebSocket sub-protocol |
|
to use in applications along with an annotation programming model for routing and |
|
processing STOMP messages from WebSocket clients. As a result an `@Controller` |
|
can now contain both `@RequestMapping` and `@MessageMapping` methods for handling |
|
HTTP requests and messages from WebSocket-connected clients. The new `spring-messaging` |
|
module also contains key abstractions formerly from the |
|
http://projects.spring.io/spring-integration/[Spring Integration] project such as |
|
`Message`, `MessageChannel`, `MessageHandler`, and others to serve as a foundation |
|
for messaging-based applications. |
|
|
|
For further details, including a more thorough introduction, see the <<websocket>> section. |
|
|
|
|
|
|
|
|
|
=== Testing Improvements |
|
In addition to pruning of deprecated code within the `spring-test` module, Spring |
|
Framework 4.0 introduces several new features for use in unit and integration testing. |
|
|
|
* Almost all annotations in the `spring-test` module (e.g., `@ContextConfiguration`, |
|
`@WebAppConfiguration`, `@ContextHierarchy`, `@ActiveProfiles`, etc.) can now be used |
|
as <<integration-testing-annotations-meta,meta-annotations>> to create custom |
|
_composed annotations_ and reduce configuration duplication across a test suite. |
|
* Active bean definition profiles can now be resolved programmatically, simply by |
|
implementing a custom <<testcontext-ctx-management-env-profiles-ActiveProfilesResolver,`ActiveProfilesResolver`>> |
|
and registering it via the `resolver` attribute of `@ActiveProfiles`. |
|
* A new `SocketUtils` class has been introduced in the `spring-core` module |
|
which enables you to scan for free TCP and UDP server ports on localhost. This |
|
functionality is not specific to testing but can prove very useful when writing |
|
integration tests that require the use of sockets, for example tests that start |
|
an in-memory SMTP server, FTP server, Servlet container, etc. |
|
* As of Spring 4.0, the set of mocks in the `org.springframework.mock.web` package is |
|
now based on the Servlet 3.0 API. Furthermore, several of the Servlet API mocks |
|
(e.g., `MockHttpServletRequest`, `MockServletContext`, etc.) have been updated with |
|
minor enhancements and improved configurability. |
|
|
|
|
|
|
|
|
|
[[new-in-4.1]] |
|
== New Features and Enhancements in Spring Framework 4.1 |
|
|
|
=== JMS Improvements |
|
Spring 4.1 introduces a much simpler infrastructure <<jms-annotated,to register JMS |
|
listener endpoints>> by annotating bean methods with |
|
{javadoc-baseurl}/org/springframework/jms/annotation/JmsListener.html[`@JmsListener`]. |
|
The XML namespace has been enhanced to support this new style (`jms:annotation-driven`), |
|
and it is also possible to fully configure the infrastructure using Java config |
|
({javadoc-baseurl}/org/springframework/jms/annotation/EnableJms.html[`@EnableJms`], |
|
`JmsListenerContainerFactory`). It is also possible to register listener endpoints |
|
programmatically using |
|
{javadoc-baseurl}/org/springframework/jms/annotation/JmsListenerConfigurer.html[`JmsListenerConfigurer`]. |
|
|
|
Spring 4.1 also aligns its JMS support to allow you to benefit from the `spring-messaging` |
|
abstraction introduced in 4.0, that is: |
|
|
|
* Message listener endpoints can have a more flexible signature and benefit from |
|
standard messaging annotations such as `@Payload`, `@Header`, `@Headers`, and `@SendTo`. It |
|
is also possible to use a standard `Message` in lieu of `javax.jms.Message` as method |
|
argument. |
|
* A new {javadoc-baseurl}/org/springframework/jms/core/JmsMessageOperations.html[`JmsMessageOperations`] |
|
interface is available and permits `JmsTemplate` like operations using the `Message` |
|
abstraction. |
|
|
|
Finally, Spring 4.1 provides additional miscellaneous improvements: |
|
|
|
* Synchronous request-reply operations support in `JmsTemplate` |
|
* Listener priority can be specified per `<jms:listener/>` element |
|
* Recovery options for the message listener container are configurable using a |
|
{javadoc-baseurl}/org/springframework/util/backoff/BackOff.html[`BackOff`] implementation |
|
* JMS 2.0 shared consumers are supported |
|
|
|
=== Caching Improvements |
|
|
|
Spring 4.1 supports <<cache-jsr-107,JCache (JSR-107) annotations>> using Spring's |
|
existing cache configuration and infrastructure abstraction; no changes are required |
|
to use the standard annotations. |
|
|
|
Spring 4.1 also improves its own caching abstraction significantly: |
|
|
|
* Caches can be resolved at runtime using a |
|
<<cache-annotations-cacheable-cache-resolver,`CacheResolver`>>. As a result the |
|
`value` argument defining the cache name(s) to use is no longer mandatory. |
|
* More operation-level customizations: cache resolver, cache manager, key |
|
generator |
|
* A new <<cache-annotations-config,`@CacheConfig` class-level annotation>> allows |
|
common settings to be shared at the class level **without** enabling any cache operation. |
|
* Better exception handling of cached methods using `CacheErrorHandler` |
|
|
|
Spring 4.1 also has a breaking change in the `CacheInterface` as a new `putIfAbsent` |
|
method has been added. |
|
|
|
=== Web Improvements |
|
|
|
* The existing support for resource handling based on the `ResourceHttpRequestHandler` |
|
has been expanded with new abstractions `ResourceResolver`, `ResourceTransformer`, |
|
and `ResourceUrlProvider`. A number of built-in implementations provide support |
|
for versioned resource URLs (for effective HTTP caching), locating gzipped resources, |
|
generating an HTML 5 AppCache manifests, and more. See <<mvc-config-static-resources>>. |
|
* JDK 1.8's `java.util.Optional` is now supported for `@RequestParam`, `@RequestHeader`, |
|
and `@MatrixVariable` controller method arguments. |
|
* `ListenableFuture` is supported as a return value alternative to `DeferredResult` |
|
where an underlying service (or perhaps a call to `AsyncRestTemplate`) already |
|
returns `ListenableFuture`. |
|
* `@ModelAttribute` methods are now invoked in an order that respects inter-dependencies. |
|
See https://jira.spring.io/browse/SPR-6299[SPR-6299]. |
|
* Jackson's `@JsonView` is supported directly on `@ResponseBody` and `ResponseEntity` |
|
controller methods for serializing different amounts of detail for the same POJO (e.g. |
|
summary vs. detail page). This is also supported with View-based rendering by |
|
adding the serialization view type as a model attribute under a special key. |
|
See <<mvc-ann-jsonview>> for details. |
|
* JSONP is now supported with Jackson. See <<mvc-ann-jsonp>>. |
|
* A new lifecycle option is available for intercepting `@ResponseBody` and `ResponseEntity` |
|
methods just after the controller method returns and before the response is written. |
|
To take advantage declare an `@ControllerAdvice` bean that implements `ResponseBodyAdvice`. |
|
The built-in support for `@JsonView` and JSONP take advantage of this. |
|
See <<mvc-handlermapping-interceptor>>. |
|
* There are three new `HttpMessageConverter` options: |
|
** Gson -- lighter footprint than Jackson; has already been in use in Spring Android. |
|
** Google Protocol Buffers -- efficient and effective as an inter-service communication |
|
data protocol within an enterprise but can also be exposed as JSON and XML for browsers. |
|
** Jackson based XML serialization is now supported through the |
|
https://github.com/FasterXML/jackson-dataformat-xml[jackson-dataformat-xml] extension. |
|
When using `@EnableWebMvc` or `<mvc:annotation-driven/>`, this is used by default |
|
instead of JAXB2 if `jackson-dataformat-xml` is in the classpath. |
|
* Views such as JSPs can now build links to controllers by referring to controller mappings |
|
by name. A default name is assigned to every `@RequestMapping`. For example `FooController` |
|
with method `handleFoo` is named "FC#handleFoo". The naming strategy is pluggable. |
|
It is also possible to name an `@RequestMapping` explicitly through its name attribute. |
|
A new `mvcUrl` function in the Spring JSP tag library makes this easy to use in JSP pages. |
|
See <<mvc-links-to-controllers-from-views>>. |
|
* `ResponseEntity` provides a builder-style API to guide controller methods |
|
towards the preparation of server-side responses, e.g. `ResponseEntity.ok()`. |
|
* `RequestEntity` is a new type that provides a builder-style API to guide client-side REST |
|
code towards the preparation of HTTP requests. |
|
* MVC Java config and XML namespace: |
|
** View resolvers can now be configured including support for content |
|
negotiation, see <<mvc-config-view-resolvers>>. |
|
** View controllers now have built-in support for redirects and for setting the response |
|
status. An application can use this to configure redirect URLs, render 404 responses |
|
with a view, send "no content" responses, etc. |
|
Some use cases are |
|
https://jira.spring.io/browse/SPR-11543?focusedCommentId=100308&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-100308[listed here]. |
|
** Path matching customizations are frequently used and now built-in. |
|
See <<mvc-config-path-matching>>. |
|
* http://groovy-lang.org/docs/groovy-2.3.6/html/documentation/markup-template-engine.html[Groovy markup template] |
|
support (based on Groovy 2.3). See the `GroovyMarkupConfigurer` and respecitve |
|
`ViewResolver` and `View' implementations. |
|
|
|
=== WebSocket STOMP Messaging Improvements |
|
|
|
* SockJS (Java) client-side support. See `SockJsClient` and classes in same package. |
|
* New application context events `SessionSubscribeEvent` and `SessionUnsubscribeEvent` published |
|
when STOMP clients subscribe and unsubscribe. |
|
* New "websocket" scope. See <<websocket-stomp-websocket-scope>>. |
|
* `@SendToUser` can target only a single session and does not require an authenticated user. |
|
* `@MessageMapping` methods can use dot "." instead of slash "/" as path separator. |
|
See https://jira.spring.io/browse/SPR-11660[SPR-11660]. |
|
* STOMP/WebSocket monitoring info collected and logged. See <<websocket-stomp-stats>>. |
|
* Significantly optimized and improved logging that should remain very readable |
|
and compact even at DEBUG level. |
|
* Optimized message creation including support for temporary message mutability |
|
and avoiding automatic message id and timestamp creation. See Javadoc of |
|
`MessageHeaderAccessor`. |
|
* Close STOMP/WebSocket connections that have no activity within 60 seconds after the |
|
WebSocket session is established. See https://jira.spring.io/browse/SPR-11884[SPR-11884]. |
|
|
|
=== Testing Improvements |
|
|
|
* Groovy scripts can now be used to configure the `ApplicationContext` loaded for |
|
integration tests in the TestContext framework. |
|
** See <<testcontext-ctx-management-groovy>> for details. |
|
* Test-managed transactions can now be programmatically started and ended within |
|
transactional test methods via the new `TestTransaction` API. |
|
** See <<testcontext-tx-programmatic-tx-mgt>> for details. |
|
* SQL script execution can now be configured declaratively via the new `@Sql` and |
|
`@SqlConfig` annotations on a per-class or per-method basis. |
|
** See <<testcontext-executing-sql>> for details. |
|
* Test property sources which automatically override system and application property |
|
sources can be configured via the new `@TestPropertySource` annotation. |
|
** See <<testcontext-ctx-management-property-sources>> for details. |
|
* Default ++TestExecutionListener++s can now be automatically discovered. |
|
** See <<testcontext-tel-config-automatic-discovery>> for details. |
|
* Custom ++TestExecutionListener++s can now be automatically merged with the default |
|
listeners. |
|
** See <<testcontext-tel-config-merging>> for details. |
|
* The documentation for transactional testing support in the TestContext framework has |
|
been improved with more thorough explanations and additional examples. |
|
** See <<testcontext-tx>> for details. |
|
* Various improvements to `MockServletContext`, `MockHttpServletRequest`, and other |
|
Servlet API mocks. |
|
* `AssertThrows` has been refactored to support `Throwable` instead of `Exception`. |
|
* In Spring MVC Test, JSON responses can be asserted with https://github.com/skyscreamer/JSONassert[JSON Assert] |
|
as an extra option to using JSONPath much like it has been possible to do for XML with |
|
XMLUnit. |
|
* `MockMvcBuilder` _recipes_ can now be created with the help of `MockMvcConfigurer`. This |
|
was added to make it easy to apply Spring Security setup but can be used to encapsulate |
|
common setup for any 3rd party framework or within a project. |
|
* `MockRestServiceServer` now supports the `AsyncRestTemplate` for client-side testing. |
|
|
|
|
|
|
|
|
|
[[spring-core]] |
|
= Core Technologies |
|
[partintro] |
|
-- |
|
This part of the reference documentation covers all of those technologies that are |
|
absolutely integral to the Spring Framework. |
|
|
|
Foremost amongst these is the Spring Framework's Inversion of Control (IoC) container. A |
|
thorough treatment of the Spring Framework's IoC container is closely followed by |
|
comprehensive coverage of Spring's Aspect-Oriented Programming (AOP) technologies. The |
|
Spring Framework has its own AOP framework, which is conceptually easy to understand, |
|
and which successfully addresses the 80% sweet spot of AOP requirements in Java |
|
enterprise programming. |
|
|
|
Coverage of Spring's integration with AspectJ (currently the richest - in terms of |
|
features - and certainly most mature AOP implementation in the Java enterprise space) is |
|
also provided. |
|
|
|
Finally, the adoption of the test-driven-development (TDD) approach to software |
|
development is certainly advocated by the Spring team, and so coverage of Spring's |
|
support for integration testing is covered (alongside best practices for unit testing). |
|
The Spring team has found that the correct use of IoC certainly does make both unit and |
|
integration testing easier (in that the presence of setter methods and appropriate |
|
constructors on classes makes them easier to wire together in a test without having to |
|
set up service locator registries and suchlike)... the chapter dedicated solely to |
|
testing will hopefully convince you of this as well. |
|
|
|
* <<beans>> |
|
* <<resources>> |
|
* <<validation>> |
|
* <<expressions>> |
|
* <<aop>> |
|
* <<aop-api>> |
|
* <<testing>> |
|
-- |
|
|
|
|
|
|
|
|
|
[[beans]] |
|
== The IoC container |
|
|
|
|
|
|
|
|
|
[[beans-introduction]] |
|
=== Introduction to the Spring IoC container and beans |
|
This chapter covers the Spring Framework implementation of the Inversion of Control |
|
(IoC) footnote:[See pass:specialcharacters,macros[<<background-ioc>>] ] principle. IoC |
|
is also known as __dependency injection__ (DI). It is a process whereby objects define |
|
their dependencies, that is, the other objects they work with, only through constructor |
|
arguments, arguments to a factory method, or properties that are set on the object |
|
instance after it is constructed or returned from a factory method. The container then |
|
__injects__ those dependencies when it creates the bean. This process is fundamentally |
|
the inverse, hence the name __Inversion of Control__ (IoC), of the bean itself |
|
controlling the instantiation or location of its dependencies by using direct |
|
construction of classes, or a mechanism such as the __Service Locator__ pattern. |
|
|
|
The `org.springframework.beans` and `org.springframework.context` packages are the basis |
|
for Spring Framework's IoC container. The |
|
{javadoc-baseurl}/org/springframework/beans/factory/BeanFactory.html[`BeanFactory`] |
|
interface provides an advanced configuration mechanism capable of managing any type of |
|
object. |
|
{javadoc-baseurl}/org/springframework/context/ApplicationContext.html[`ApplicationContext`] |
|
is a sub-interface of `BeanFactory`. It adds easier integration with Spring's AOP |
|
features; message resource handling (for use in internationalization), event |
|
publication; and application-layer specific contexts such as the `WebApplicationContext` |
|
for use in web applications. |
|
|
|
In short, the `BeanFactory` provides the configuration framework and basic |
|
functionality, and the `ApplicationContext` adds more enterprise-specific functionality. |
|
The `ApplicationContext` is a complete superset of the `BeanFactory`, and is used |
|
exclusively in this chapter in descriptions of Spring's IoC container. For more |
|
information on using the `BeanFactory` instead of the `ApplicationContext,` refer to |
|
<<beans-beanfactory>>. |
|
|
|
In Spring, the objects that form the backbone of your application and that are managed |
|
by the Spring IoC __container__ are called __beans__. A bean is an object that is |
|
instantiated, assembled, and otherwise managed by a Spring IoC container. Otherwise, a |
|
bean is simply one of many objects in your application. Beans, and the __dependencies__ |
|
among them, are reflected in the __configuration metadata__ used by a container. |
|
|
|
|
|
|
|
|
|
[[beans-basics]] |
|
=== Container overview |
|
The interface `org.springframework.context.ApplicationContext` represents the Spring IoC |
|
container and is responsible for instantiating, configuring, and assembling the |
|
aforementioned beans. The container gets its instructions on what objects to |
|
instantiate, configure, and assemble by reading configuration metadata. The |
|
configuration metadata is represented in XML, Java annotations, or Java code. It allows |
|
you to express the objects that compose your application and the rich interdependencies |
|
between such objects. |
|
|
|
Several implementations of the `ApplicationContext` interface are supplied |
|
out-of-the-box with Spring. In standalone applications it is common to create an |
|
instance of |
|
{javadoc-baseurl}/org/springframework/context/support/ClassPathXmlApplicationContext.html[`ClassPathXmlApplicationContext`] |
|
or {javadoc-baseurl}/org/springframework/context/support/FileSystemXmlApplicationContext.html[`FileSystemXmlApplicationContext`]. |
|
While XML has been the traditional format for defining configuration metadata you can |
|
instruct the container to use Java annotations or code as the metadata format by |
|
providing a small amount of XML configuration to declaratively enable support for these |
|
additional metadata formats. |
|
|
|
In most application scenarios, explicit user code is not required to instantiate one or |
|
more instances of a Spring IoC container. For example, in a web application scenario, a |
|
simple eight (or so) lines of boilerplate web descriptor XML in the `web.xml` file |
|
of the application will typically suffice (see <<context-create>>). If you are using the |
|
https://spring.io/tools/sts[Spring Tool Suite] Eclipse-powered development |
|
environment this boilerplate configuration can be easily created with few mouse clicks or |
|
keystrokes. |
|
|
|
The following diagram is a high-level view of how Spring works. Your application classes |
|
are combined with configuration metadata so that after the `ApplicationContext` is |
|
created and initialized, you have a fully configured and executable system or |
|
application. |
|
|
|
.The Spring IoC container |
|
image::images/container-magic.png[width=250] |
|
|
|
|
|
|
|
[[beans-factory-metadata]] |
|
==== Configuration metadata |
|
As the preceding diagram shows, the Spring IoC container consumes a form of |
|
__configuration metadata__; this configuration metadata represents how you as an |
|
application developer tell the Spring container to instantiate, configure, and assemble |
|
the objects in your application. |
|
|
|
Configuration metadata is traditionally supplied in a simple and intuitive XML format, |
|
which is what most of this chapter uses to convey key concepts and features of the |
|
Spring IoC container. |
|
|
|
[NOTE] |
|
==== |
|
XML-based metadata is __not__ the only allowed form of configuration metadata. The |
|
Spring IoC container itself is __totally__ decoupled from the format in which this |
|
configuration metadata is actually written. These days many developers choose |
|
<<beans-java,Java-based configuration>> for their Spring applications. |
|
==== |
|
|
|
For information about using other forms of metadata with the Spring container, see: |
|
|
|
* <<beans-annotation-config,Annotation-based configuration>>: Spring 2.5 introduced |
|
support for annotation-based configuration metadata. |
|
* <<beans-java,Java-based configuration>>: Starting with Spring 3.0, many features |
|
provided by the Spring JavaConfig project became part of the core Spring Framework. |
|
Thus you can define beans external to your application classes by using Java rather |
|
than XML files. To use these new features, see the `@Configuration`, `@Bean`, `@Import` |
|
and `@DependsOn` annotations. |
|
|
|
Spring configuration consists of at least one and typically more than one bean |
|
definition that the container must manage. XML-based configuration metadata shows these |
|
beans configured as `<bean/>` elements inside a top-level `<beans/>` element. Java |
|
configuration typically uses `@Bean` annotated methods within a `@Configuration` class. |
|
|
|
These bean definitions correspond to the actual objects that make up your application. |
|
Typically you define service layer objects, data access objects (DAOs), presentation |
|
objects such as Struts `Action` instances, infrastructure objects such as Hibernate |
|
`SessionFactories`, JMS `Queues`, and so forth. Typically one does not configure |
|
fine-grained domain objects in the container, because it is usually the responsibility |
|
of DAOs and business logic to create and load domain objects. However, you can use |
|
Spring's integration with AspectJ to configure objects that have been created outside |
|
the control of an IoC container. See <<aop-atconfigurable,Using AspectJ to |
|
dependency-inject domain objects with Spring>>. |
|
|
|
The following example shows the basic structure of XML-based configuration metadata: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xsi:schemaLocation="http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd"> |
|
|
|
<bean id="..." class="..."> |
|
<!-- collaborators and configuration for this bean go here --> |
|
</bean> |
|
|
|
<bean id="..." class="..."> |
|
<!-- collaborators and configuration for this bean go here --> |
|
</bean> |
|
|
|
<!-- more bean definitions go here --> |
|
|
|
</beans> |
|
---- |
|
|
|
The `id` attribute is a string that you use to identify the individual bean definition. |
|
The `class` attribute defines the type of the bean and uses the fully qualified |
|
classname. The value of the id attribute refers to collaborating objects. The XML for |
|
referring to collaborating objects is not shown in this example; see |
|
<<beans-dependencies,Dependencies>> for more information. |
|
|
|
|
|
|
|
[[beans-factory-instantiation]] |
|
==== Instantiating a container |
|
Instantiating a Spring IoC container is straightforward. The location path or paths |
|
supplied to an `ApplicationContext` constructor are actually resource strings that allow |
|
the container to load configuration metadata from a variety of external resources such |
|
as the local file system, from the Java `CLASSPATH`, and so on. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ApplicationContext context = |
|
new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"}); |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
After you learn about Spring's IoC container, you may want to know more about Spring's |
|
`Resource` abstraction, as described in <<resources>>, which provides a convenient |
|
mechanism for reading an InputStream from locations defined in a URI syntax. In |
|
particular, `Resource` paths are used to construct applications contexts as described in |
|
<<resources-app-ctx>>. |
|
==== |
|
|
|
The following example shows the service layer objects `(services.xml)` configuration file: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xsi:schemaLocation="http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd"> |
|
|
|
<!-- services --> |
|
|
|
<bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl"> |
|
<property name="accountDao" ref="accountDao"/> |
|
<property name="itemDao" ref="itemDao"/> |
|
<!-- additional collaborators and configuration for this bean go here --> |
|
</bean> |
|
|
|
<!-- more bean definitions for services go here --> |
|
|
|
</beans> |
|
---- |
|
|
|
The following example shows the data access objects `daos.xml` file: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xsi:schemaLocation="http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd"> |
|
|
|
<bean id="accountDao" |
|
class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao"> |
|
<!-- additional collaborators and configuration for this bean go here --> |
|
</bean> |
|
|
|
<bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao"> |
|
<!-- additional collaborators and configuration for this bean go here --> |
|
</bean> |
|
|
|
<!-- more bean definitions for data access objects go here --> |
|
|
|
</beans> |
|
---- |
|
|
|
In the preceding example, the service layer consists of the class `PetStoreServiceImpl`, |
|
and two data access objects of the type `JpaAccountDao` and `JpaItemDao` (based |
|
on the JPA Object/Relational mapping standard). The `property name` element refers to the |
|
name of the JavaBean property, and the `ref` element refers to the name of another bean |
|
definition. This linkage between `id` and `ref` elements expresses the dependency between |
|
collaborating objects. For details of configuring an object's dependencies, see |
|
<<beans-dependencies,Dependencies>>. |
|
|
|
|
|
[[beans-factory-xml-import]] |
|
===== Composing XML-based configuration metadata |
|
It can be useful to have bean definitions span multiple XML files. Often each individual |
|
XML configuration file represents a logical layer or module in your architecture. |
|
|
|
You can use the application context constructor to load bean definitions from all these |
|
XML fragments. This constructor takes multiple `Resource` locations, as was shown in the |
|
previous section. Alternatively, use one or more occurrences of the `<import/>` element |
|
to load bean definitions from another file or files. For example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<import resource="services.xml"/> |
|
<import resource="resources/messageSource.xml"/> |
|
<import resource="/resources/themeSource.xml"/> |
|
|
|
<bean id="bean1" class="..."/> |
|
<bean id="bean2" class="..."/> |
|
</beans> |
|
---- |
|
|
|
In the preceding example, external bean definitions are loaded from three files: |
|
`services.xml`, `messageSource.xml`, and `themeSource.xml`. All location paths are |
|
relative to the definition file doing the importing, so `services.xml` must be in the |
|
same directory or classpath location as the file doing the importing, while |
|
`messageSource.xml` and `themeSource.xml` must be in a `resources` location below the |
|
location of the importing file. As you can see, a leading slash is ignored, but given |
|
that these paths are relative, it is better form not to use the slash at all. The |
|
contents of the files being imported, including the top level `<beans/>` element, must |
|
be valid XML bean definitions according to the Spring Schema. |
|
|
|
[NOTE] |
|
==== |
|
It is possible, but not recommended, to reference files in parent directories using a |
|
relative "../" path. Doing so creates a dependency on a file that is outside the current |
|
application. In particular, this reference is not recommended for "classpath:" URLs (for |
|
example, "classpath:../services.xml"), where the runtime resolution process chooses the |
|
"nearest" classpath root and then looks into its parent directory. Classpath |
|
configuration changes may lead to the choice of a different, incorrect directory. |
|
|
|
You can always use fully qualified resource locations instead of relative paths: for |
|
example, "file:C:/config/services.xml" or "classpath:/config/services.xml". However, be |
|
aware that you are coupling your application's configuration to specific absolute |
|
locations. It is generally preferable to keep an indirection for such absolute |
|
locations, for example, through "${...}" placeholders that are resolved against JVM |
|
system properties at runtime. |
|
==== |
|
|
|
|
|
|
|
[[beans-factory-client]] |
|
==== Using the container |
|
The `ApplicationContext` is the interface for an advanced factory capable of maintaining |
|
a registry of different beans and their dependencies. Using the method `T getBean(String |
|
name, Class<T> requiredType)` you can retrieve instances of your beans. |
|
|
|
The `ApplicationContext` enables you to read bean definitions and access them as follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// create and configure beans |
|
ApplicationContext context = |
|
new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"}); |
|
|
|
// retrieve configured instance |
|
PetStoreService service = context.getBean("petStore", PetStoreService.class); |
|
|
|
// use configured instance |
|
List<String> userList = service.getUsernameList(); |
|
---- |
|
|
|
You use `getBean()` to retrieve instances of your beans. The `ApplicationContext` |
|
interface has a few other methods for retrieving beans, but ideally your application |
|
code should never use them. Indeed, your application code should have no calls to the |
|
`getBean()` method at all, and thus no dependency on Spring APIs at all. For example, |
|
Spring's integration with web frameworks provides for dependency injection for various |
|
web framework classes such as controllers and JSF-managed beans. |
|
|
|
|
|
|
|
|
|
[[beans-definition]] |
|
=== Bean overview |
|
A Spring IoC container manages one or more __beans__. These beans are created with the |
|
configuration metadata that you supply to the container, for example, in the form of XML |
|
`<bean/>` definitions. |
|
|
|
Within the container itself, these bean definitions are represented as `BeanDefinition` |
|
objects, which contain (among other information) the following metadata: |
|
|
|
* __A package-qualified class name:__ typically the actual implementation class of the |
|
bean being defined. |
|
* Bean behavioral configuration elements, which state how the bean should behave in the |
|
container (scope, lifecycle callbacks, and so forth). |
|
* References to other beans that are needed for the bean to do its work; these |
|
references are also called __collaborators__ or __dependencies__. |
|
* Other configuration settings to set in the newly created object, for example, the |
|
number of connections to use in a bean that manages a connection pool, or the size |
|
limit of the pool. |
|
|
|
This metadata translates to a set of properties that make up each bean definition. |
|
|
|
[[beans-factory-bean-definition-tbl]] |
|
.The bean definition |
|
|=== |
|
| Property| Explained in... |
|
|
|
| class |
|
| <<beans-factory-class>> |
|
|
|
| name |
|
| <<beans-beanname>> |
|
|
|
| scope |
|
| <<beans-factory-scopes>> |
|
|
|
| constructor arguments |
|
| <<beans-factory-collaborators>> |
|
|
|
| properties |
|
| <<beans-factory-collaborators>> |
|
|
|
| autowiring mode |
|
| <<beans-factory-autowire>> |
|
|
|
| lazy-initialization mode |
|
| <<beans-factory-lazy-init>> |
|
|
|
| initialization method |
|
| <<beans-factory-lifecycle-initializingbean>> |
|
|
|
| destruction method |
|
| <<beans-factory-lifecycle-disposablebean>> |
|
|=== |
|
|
|
In addition to bean definitions that contain information on how to create a specific |
|
bean, the `ApplicationContext` implementations also permit the registration of existing |
|
objects that are created outside the container, by users. This is done by accessing the |
|
ApplicationContext's BeanFactory via the method `getBeanFactory()` which returns the |
|
BeanFactory implementation `DefaultListableBeanFactory`. `DefaultListableBeanFactory` |
|
supports this registration through the methods `registerSingleton(..)` and |
|
`registerBeanDefinition(..)`. However, typical applications work solely with beans |
|
defined through metadata bean definitions. |
|
|
|
|
|
|
|
[[beans-beanname]] |
|
==== Naming beans |
|
Every bean has one or more identifiers. These identifiers must be unique within the |
|
container that hosts the bean. A bean usually has only one identifier, but if it |
|
requires more than one, the extra ones can be considered aliases. |
|
|
|
In XML-based configuration metadata, you use the `id` and/or `name` attributes |
|
to specify the bean identifier(s). The `id` attribute allows you to specify |
|
exactly one id. Conventionally these names are alphanumeric ('myBean', |
|
'fooService', etc.), but may contain special characters as well. If you want to |
|
introduce other aliases to the bean, you can also specify them in the `name` |
|
attribute, separated by a comma (`,`), semicolon (`;`), or white space. As a |
|
historical note, in versions prior to Spring 3.1, the `id` attribute was |
|
defined as an `xsd:ID` type, which constrained possible characters. As of 3.1, |
|
it is defined as an `xsd:string` type. Note that bean `id` uniqueness is still |
|
enforced by the container, though no longer by XML parsers. |
|
|
|
You are not required to supply a name or id for a bean. If no name or id is supplied |
|
explicitly, the container generates a unique name for that bean. However, if you want to |
|
refer to that bean by name, through the use of the `ref` element or |
|
<<beans-servicelocator,Service Locator>> style lookup, you must provide a name. |
|
Motivations for not supplying a name are related to using <<beans-inner-beans,inner |
|
beans>> and <<beans-factory-autowire,autowiring collaborators>>. |
|
|
|
.Bean Naming Conventions |
|
**** |
|
The convention is to use the standard Java convention for instance field names when |
|
naming beans. That is, bean names start with a lowercase letter, and are camel-cased |
|
from then on. Examples of such names would be (without quotes) `'accountManager'`, |
|
`'accountService'`, `'userDao'`, `'loginController'`, and so forth. |
|
|
|
Naming beans consistently makes your configuration easier to read and understand, and if |
|
you are using Spring AOP it helps a lot when applying advice to a set of beans related |
|
by name. |
|
**** |
|
|
|
|
|
[[beans-beanname-alias]] |
|
===== Aliasing a bean outside the bean definition |
|
In a bean definition itself, you can supply more than one name for the bean, by using a |
|
combination of up to one name specified by the `id` attribute, and any number of other |
|
names in the `name` attribute. These names can be equivalent aliases to the same bean, |
|
and are useful for some situations, such as allowing each component in an application to |
|
refer to a common dependency by using a bean name that is specific to that component |
|
itself. |
|
|
|
Specifying all aliases where the bean is actually defined is not always adequate, |
|
however. It is sometimes desirable to introduce an alias for a bean that is defined |
|
elsewhere. This is commonly the case in large systems where configuration is split |
|
amongst each subsystem, each subsystem having its own set of object definitions. In |
|
XML-based configuration metadata, you can use the `<alias/>` element to accomplish this. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<alias name="fromName" alias="toName"/> |
|
---- |
|
|
|
In this case, a bean in the same container which is named `fromName`, may also, |
|
after the use of this alias definition, be referred to as `toName`. |
|
|
|
For example, the configuration metadata for subsystem A may refer to a DataSource via |
|
the name `subsystemA-dataSource`. The configuration metadata for subsystem B may refer to |
|
a DataSource via the name `subsystemB-dataSource`. When composing the main application |
|
that uses both these subsystems the main application refers to the DataSource via the |
|
name `myApp-dataSource`. To have all three names refer to the same object you add to the |
|
MyApp configuration metadata the following aliases definitions: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<alias name="subsystemA-dataSource" alias="subsystemB-dataSource"/> |
|
<alias name="subsystemA-dataSource" alias="myApp-dataSource" /> |
|
---- |
|
|
|
Now each component and the main application can refer to the dataSource through a name |
|
that is unique and guaranteed not to clash with any other definition (effectively |
|
creating a namespace), yet they refer to the same bean. |
|
|
|
.Java-configuration |
|
**** |
|
If you are using Java-configuration, the `@Bean` annotation can be used to provide aliases |
|
see <<beans-java-bean-annotation>> for details. |
|
**** |
|
|
|
[[beans-factory-class]] |
|
==== Instantiating beans |
|
A bean definition essentially is a recipe for creating one or more objects. The |
|
container looks at the recipe for a named bean when asked, and uses the configuration |
|
metadata encapsulated by that bean definition to create (or acquire) an actual object. |
|
|
|
If you use XML-based configuration metadata, you specify the type (or class) of object |
|
that is to be instantiated in the `class` attribute of the `<bean/>` element. This |
|
`class` attribute, which internally is a `Class` property on a `BeanDefinition` |
|
instance, is usually mandatory. (For exceptions, see |
|
<<beans-factory-class-instance-factory-method>> and <<beans-child-bean-definitions>>.) |
|
You use the `Class` property in one of two ways: |
|
|
|
* Typically, to specify the bean class to be constructed in the case where the container |
|
itself directly creates the bean by calling its constructor reflectively, somewhat |
|
equivalent to Java code using the `new` operator. |
|
* To specify the actual class containing the `static` factory method that will be |
|
invoked to create the object, in the less common case where the container invokes a |
|
`static` __factory__ method on a class to create the bean. The object type returned |
|
from the invocation of the `static` factory method may be the same class or another |
|
class entirely. |
|
|
|
**** |
|
.Inner class names |
|
If you want to configure a bean definition for a `static` nested class, you have to use |
|
the __binary__ name of the inner class. |
|
|
|
For example, if you have a class called `Foo` in the `com.example` package, and this |
|
`Foo` class has a `static` inner class called `Bar`, the value of the `'class'` |
|
attribute on a bean definition would be... |
|
|
|
`com.example.Foo$Bar` |
|
|
|
Notice the use of the `$` character in the name to separate the inner class name from |
|
the outer class name. |
|
**** |
|
|
|
|
|
[[beans-factory-class-ctor]] |
|
===== Instantiation with a constructor |
|
When you create a bean by the constructor approach, all normal classes are usable by and |
|
compatible with Spring. That is, the class being developed does not need to implement |
|
any specific interfaces or to be coded in a specific fashion. Simply specifying the bean |
|
class should suffice. However, depending on what type of IoC you use for that specific |
|
bean, you may need a default (empty) constructor. |
|
|
|
The Spring IoC container can manage virtually __any__ class you want it to manage; it is |
|
not limited to managing true JavaBeans. Most Spring users prefer actual JavaBeans with |
|
only a default (no-argument) constructor and appropriate setters and getters modeled |
|
after the properties in the container. You can also have more exotic non-bean-style |
|
classes in your container. If, for example, you need to use a legacy connection pool |
|
that absolutely does not adhere to the JavaBean specification, Spring can manage it as |
|
well. |
|
|
|
With XML-based configuration metadata you can specify your bean class as follows: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="exampleBean" class="examples.ExampleBean"/> |
|
|
|
<bean name="anotherExample" class="examples.ExampleBeanTwo"/> |
|
---- |
|
|
|
For details about the mechanism for supplying arguments to the constructor (if required) |
|
and setting object instance properties after the object is constructed, see |
|
<<beans-factory-collaborators,Injecting Dependencies>>. |
|
|
|
|
|
[[beans-factory-class-static-factory-method]] |
|
===== Instantiation with a static factory method |
|
When defining a bean that you create with a static factory method, you use the `class` |
|
attribute to specify the class containing the `static` factory method and an attribute |
|
named `factory-method` to specify the name of the factory method itself. You should be |
|
able to call this method (with optional arguments as described later) and return a live |
|
object, which subsequently is treated as if it had been created through a constructor. |
|
One use for such a bean definition is to call `static` factories in legacy code. |
|
|
|
The following bean definition specifies that the bean will be created by calling a |
|
factory-method. The definition does not specify the type (class) of the returned object, |
|
only the class containing the factory method. In this example, the `createInstance()` |
|
method must be a __static__ method. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="clientService" |
|
class="examples.ClientService" |
|
factory-method="createInstance"/> |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class ClientService { |
|
private static ClientService clientService = new ClientService(); |
|
private ClientService() {} |
|
|
|
public static ClientService createInstance() { |
|
return clientService; |
|
} |
|
} |
|
---- |
|
|
|
For details about the mechanism for supplying (optional) arguments to the factory method |
|
and setting object instance properties after the object is returned from the factory, |
|
see <<beans-factory-properties-detailed,Dependencies and configuration in detail>>. |
|
|
|
|
|
[[beans-factory-class-instance-factory-method]] |
|
===== Instantiation using an instance factory method |
|
Similar to instantiation through a <<beans-factory-class-static-factory-method,static |
|
factory method>>, instantiation with an instance factory method invokes a non-static |
|
method of an existing bean from the container to create a new bean. To use this |
|
mechanism, leave the `class` attribute empty, and in the `factory-bean` attribute, |
|
specify the name of a bean in the current (or parent/ancestor) container that contains |
|
the instance method that is to be invoked to create the object. Set the name of the |
|
factory method itself with the `factory-method` attribute. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<!-- the factory bean, which contains a method called createInstance() --> |
|
<bean id="serviceLocator" class="examples.DefaultServiceLocator"> |
|
<!-- inject any dependencies required by this locator bean --> |
|
</bean> |
|
|
|
<!-- the bean to be created via the factory bean --> |
|
<bean id="clientService" |
|
factory-bean="serviceLocator" |
|
factory-method="createClientServiceInstance"/> |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class DefaultServiceLocator { |
|
|
|
private static ClientService clientService = new ClientServiceImpl(); |
|
private DefaultServiceLocator() {} |
|
|
|
public ClientService createClientServiceInstance() { |
|
return clientService; |
|
} |
|
} |
|
---- |
|
|
|
One factory class can also hold more than one factory method as shown here: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="serviceLocator" class="examples.DefaultServiceLocator"> |
|
<!-- inject any dependencies required by this locator bean --> |
|
</bean> |
|
|
|
<bean id="clientService" |
|
factory-bean="serviceLocator" |
|
factory-method="createClientServiceInstance"/> |
|
|
|
<bean id="accountService" |
|
factory-bean="serviceLocator" |
|
factory-method="createAccountServiceInstance"/> |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class DefaultServiceLocator { |
|
|
|
private static ClientService clientService = new ClientServiceImpl(); |
|
private static AccountService accountService = new AccountServiceImpl(); |
|
|
|
private DefaultServiceLocator() {} |
|
|
|
public ClientService createClientServiceInstance() { |
|
return clientService; |
|
} |
|
|
|
public AccountService createAccountServiceInstance() { |
|
return accountService; |
|
} |
|
|
|
} |
|
---- |
|
|
|
This approach shows that the factory bean itself can be managed and configured through |
|
dependency injection (DI). See <<beans-factory-properties-detailed,Dependencies and |
|
configuration in detail>>. |
|
|
|
[NOTE] |
|
==== |
|
In Spring documentation,__ factory bean__ refers to a bean that is configured in the |
|
Spring container that will create objects through an |
|
<<beans-factory-class-instance-factory-method,instance>> or |
|
<<beans-factory-class-static-factory-method,static>> factory method. By contrast, |
|
`FactoryBean` (notice the capitalization) refers to a Spring-specific |
|
<<beans-factory-extension-factorybean, `FactoryBean` >>. |
|
==== |
|
|
|
|
|
|
|
|
|
[[beans-dependencies]] |
|
=== Dependencies |
|
A typical enterprise application does not consist of a single object (or bean in the |
|
Spring parlance). Even the simplest application has a few objects that work together to |
|
present what the end-user sees as a coherent application. This next section explains how |
|
you go from defining a number of bean definitions that stand alone to a fully realized |
|
application where objects collaborate to achieve a goal. |
|
|
|
|
|
|
|
[[beans-factory-collaborators]] |
|
==== Dependency injection |
|
__Dependency injection__ (DI) is a process whereby objects define their dependencies, |
|
that is, the other objects they work with, only through constructor arguments, arguments |
|
to a factory method, or properties that are set on the object instance after it is |
|
constructed or returned from a factory method. The container then __injects__ those |
|
dependencies when it creates the bean. This process is fundamentally the inverse, hence |
|
the name __Inversion of Control__ (IoC), of the bean itself controlling the instantiation |
|
or location of its dependencies on its own by using direct construction of classes, or |
|
the __Service Locator__ pattern. |
|
|
|
Code is cleaner with the DI principle and decoupling is more effective when objects are |
|
provided with their dependencies. The object does not look up its dependencies, and does |
|
not know the location or class of the dependencies. As such, your classes become easier |
|
to test, in particular when the dependencies are on interfaces or abstract base classes, |
|
which allow for stub or mock implementations to be used in unit tests. |
|
|
|
DI exists in two major variants, <<beans-constructor-injection,Constructor-based |
|
dependency injection>> and <<beans-setter-injection,Setter-based dependency injection>>. |
|
|
|
|
|
[[beans-constructor-injection]] |
|
===== Constructor-based dependency injection |
|
__Constructor-based__ DI is accomplished by the container invoking a constructor with a |
|
number of arguments, each representing a dependency. Calling a `static` factory method |
|
with specific arguments to construct the bean is nearly equivalent, and this discussion |
|
treats arguments to a constructor and to a `static` factory method similarly. The |
|
following example shows a class that can only be dependency-injected with constructor |
|
injection. Notice that there is nothing __special__ about this class, it is a POJO that |
|
has no dependencies on container specific interfaces, base classes or annotations. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class SimpleMovieLister { |
|
|
|
// the SimpleMovieLister has a dependency on a MovieFinder |
|
private MovieFinder movieFinder; |
|
|
|
// a constructor so that the Spring container can inject a MovieFinder |
|
public SimpleMovieLister(MovieFinder movieFinder) { |
|
this.movieFinder = movieFinder; |
|
} |
|
|
|
// business logic that actually uses the injected MovieFinder is omitted... |
|
|
|
} |
|
---- |
|
|
|
[[beans-factory-ctor-arguments-resolution]] |
|
====== Constructor argument resolution |
|
Constructor argument resolution matching occurs using the argument's type. If no |
|
potential ambiguity exists in the constructor arguments of a bean definition, then the |
|
order in which the constructor arguments are defined in a bean definition is the order |
|
in which those arguments are supplied to the appropriate constructor when the bean is |
|
being instantiated. Consider the following class: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package x.y; |
|
|
|
public class Foo { |
|
|
|
public Foo(Bar bar, Baz baz) { |
|
// ... |
|
} |
|
|
|
} |
|
---- |
|
|
|
No potential ambiguity exists, assuming that `Bar` and `Baz` classes are not related by |
|
inheritance. Thus the following configuration works fine, and you do not need to specify |
|
the constructor argument indexes and/or types explicitly in the `<constructor-arg/>` |
|
element. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<bean id="foo" class="x.y.Foo"> |
|
<constructor-arg ref="bar"/> |
|
<constructor-arg ref="baz"/> |
|
</bean> |
|
|
|
<bean id="bar" class="x.y.Bar"/> |
|
|
|
<bean id="baz" class="x.y.Baz"/> |
|
</beans> |
|
---- |
|
|
|
When another bean is referenced, the type is known, and matching can occur (as was the |
|
case with the preceding example). When a simple type is used, such as |
|
`<value>true</value>`, Spring cannot determine the type of the value, and so cannot match |
|
by type without help. Consider the following class: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package examples; |
|
|
|
public class ExampleBean { |
|
|
|
// Number of years to calculate the Ultimate Answer |
|
private int years; |
|
|
|
// The Answer to Life, the Universe, and Everything |
|
private String ultimateAnswer; |
|
|
|
public ExampleBean(int years, String ultimateAnswer) { |
|
this.years = years; |
|
this.ultimateAnswer = ultimateAnswer; |
|
} |
|
|
|
} |
|
---- |
|
|
|
.[[beans-factory-ctor-arguments-type]]Constructor argument type matching |
|
-- |
|
In the preceding scenario, the container __can__ use type matching with simple types if |
|
you explicitly specify the type of the constructor argument using the `type` attribute. |
|
For example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="exampleBean" class="examples.ExampleBean"> |
|
<constructor-arg type="int" value="7500000"/> |
|
<constructor-arg type="java.lang.String" value="42"/> |
|
</bean> |
|
---- |
|
-- |
|
|
|
.[[beans-factory-ctor-arguments-index]]Constructor argument index |
|
-- |
|
Use the `index` attribute to specify explicitly the index of constructor arguments. For |
|
example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="exampleBean" class="examples.ExampleBean"> |
|
<constructor-arg index="0" value="7500000"/> |
|
<constructor-arg index="1" value="42"/> |
|
</bean> |
|
---- |
|
|
|
In addition to resolving the ambiguity of multiple simple values, specifying an index |
|
resolves ambiguity where a constructor has two arguments of the same type. Note that the |
|
__index is 0 based__. |
|
-- |
|
|
|
.[[beans-factory-ctor-arguments-name]]Constructor argument name |
|
-- |
|
You can also use the constructor parameter name for value disambiguation: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="exampleBean" class="examples.ExampleBean"> |
|
<constructor-arg name="years" value="7500000"/> |
|
<constructor-arg name="ultimateAnswer" value="42"/> |
|
</bean> |
|
---- |
|
|
|
Keep in mind that to make this work out of the box your code must be compiled with the |
|
debug flag enabled so that Spring can look up the parameter name from the constructor. |
|
If you can't compile your code with debug flag (or don't want to) you can use |
|
http://download.oracle.com/javase/6/docs/api/java/beans/ConstructorProperties.html[@ConstructorProperties] |
|
JDK annotation to explicitly name your constructor arguments. The sample class would |
|
then have to look as follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package examples; |
|
|
|
public class ExampleBean { |
|
|
|
// Fields omitted |
|
|
|
@ConstructorProperties({"years", "ultimateAnswer"}) |
|
public ExampleBean(int years, String ultimateAnswer) { |
|
this.years = years; |
|
this.ultimateAnswer = ultimateAnswer; |
|
} |
|
|
|
} |
|
---- |
|
-- |
|
|
|
|
|
[[beans-setter-injection]] |
|
===== Setter-based dependency injection |
|
__Setter-based__ DI is accomplished by the container calling setter methods on your |
|
beans after invoking a no-argument constructor or no-argument `static` factory method to |
|
instantiate your bean. |
|
|
|
The following example shows a class that can only be dependency-injected using pure |
|
setter injection. This class is conventional Java. It is a POJO that has no dependencies |
|
on container specific interfaces, base classes or annotations. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class SimpleMovieLister { |
|
|
|
// the SimpleMovieLister has a dependency on the MovieFinder |
|
private MovieFinder movieFinder; |
|
|
|
// a setter method so that the Spring container can inject a MovieFinder |
|
public void setMovieFinder(MovieFinder movieFinder) { |
|
this.movieFinder = movieFinder; |
|
} |
|
|
|
// business logic that actually uses the injected MovieFinder is omitted... |
|
|
|
} |
|
---- |
|
|
|
The `ApplicationContext` supports constructor-based and setter-based DI for the beans it |
|
manages. It also supports setter-based DI after some dependencies have already been |
|
injected through the constructor approach. You configure the dependencies in the form of |
|
a `BeanDefinition`, which you use in conjunction with `PropertyEditor` instances to |
|
convert properties from one format to another. However, most Spring users do not work |
|
with these classes directly (i.e., programmatically) but rather with XML `bean` |
|
definitions, annotated components (i.e., classes annotated with `@Component`, |
|
`@Controller`, etc.), or `@Bean` methods in Java-based `@Configuration` classes. These |
|
sources are then converted internally into instances of `BeanDefinition` and used to |
|
load an entire Spring IoC container instance. |
|
|
|
.Constructor-based or setter-based DI? |
|
**** |
|
Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to |
|
use constructors for _mandatory dependencies_ and setter methods or configuration methods |
|
for _optional dependencies_. Note that use of the <<beans-required-annotation,@Required>> |
|
annotation on a setter method can be used to make the property a required dependency. |
|
|
|
The Spring team generally advocates constructor injection as it enables one to implement |
|
application components as _immutable objects_ and to ensure that required dependencies |
|
are not `null`. Furthermore constructor-injected components are always returned to client |
|
(calling) code in a fully initialized state. As a side note, a large number of constructor |
|
arguments is a _bad code smell_, implying that the class likely has too many |
|
responsibilities and should be refactored to better address proper separation of concerns. |
|
|
|
Setter injection should primarily only be used for optional dependencies that can be |
|
assigned reasonable default values within the class. Otherwise, not-null checks must be |
|
performed everywhere the code uses the dependency. One benefit of setter injection is that |
|
setter methods make objects of that class amenable to reconfiguration or re-injection |
|
later. Management through <<jmx,JMX MBeans>> is therefore a compelling use case for setter |
|
injection. |
|
|
|
Use the DI style that makes the most sense for a particular class. Sometimes, when dealing |
|
with third-party classes for which you do not have the source, the choice is made for you. |
|
For example, if a third-party class does not expose any setter methods, then constructor |
|
injection may be the only available form of DI. |
|
**** |
|
|
|
|
|
[[beans-dependency-resolution]] |
|
===== Dependency resolution process |
|
The container performs bean dependency resolution as follows: |
|
|
|
* The `ApplicationContext` is created and initialized with configuration metadata that |
|
describes all the beans. Configuration metadata can be specified via XML, Java code, or |
|
annotations. |
|
* For each bean, its dependencies are expressed in the form of properties, constructor |
|
arguments, or arguments to the static-factory method if you are using that instead of |
|
a normal constructor. These dependencies are provided to the bean, __when the bean is |
|
actually created__. |
|
* Each property or constructor argument is an actual definition of the value to set, or |
|
a reference to another bean in the container. |
|
* Each property or constructor argument which is a value is converted from its specified |
|
format to the actual type of that property or constructor argument. By default Spring |
|
can convert a value supplied in string format to all built-in types, such as `int`, |
|
`long`, `String`, `boolean`, etc. |
|
|
|
The Spring container validates the configuration of each bean as the container is created. |
|
However, the bean properties themselves are not set until the bean __is actually created__. |
|
Beans that are singleton-scoped and set to be pre-instantiated (the default) are created |
|
when the container is created. Scopes are defined in <<beans-factory-scopes>>. Otherwise, |
|
the bean is created only when it is requested. Creation of a bean potentially causes a |
|
graph of beans to be created, as the bean's dependencies and its dependencies' |
|
dependencies (and so on) are created and assigned. Note that resolution mismatches among |
|
those dependencies may show up late, i.e. on first creation of the affected bean. |
|
|
|
.Circular dependencies |
|
**** |
|
If you use predominantly constructor injection, it is possible to create an unresolvable |
|
circular dependency scenario. |
|
|
|
For example: Class A requires an instance of class B through constructor injection, and |
|
class B requires an instance of class A through constructor injection. If you configure |
|
beans for classes A and B to be injected into each other, the Spring IoC container |
|
detects this circular reference at runtime, and throws a |
|
`BeanCurrentlyInCreationException`. |
|
|
|
One possible solution is to edit the source code of some classes to be configured by |
|
setters rather than constructors. Alternatively, avoid constructor injection and use |
|
setter injection only. In other words, although it is not recommended, you can configure |
|
circular dependencies with setter injection. |
|
|
|
Unlike the __typical__ case (with no circular dependencies), a circular dependency |
|
between bean A and bean B forces one of the beans to be injected into the other prior to |
|
being fully initialized itself (a classic chicken/egg scenario). |
|
**** |
|
|
|
You can generally trust Spring to do the right thing. It detects configuration problems, |
|
such as references to non-existent beans and circular dependencies, at container |
|
load-time. Spring sets properties and resolves dependencies as late as possible, when |
|
the bean is actually created. This means that a Spring container which has loaded |
|
correctly can later generate an exception when you request an object if there is a |
|
problem creating that object or one of its dependencies. For example, the bean throws an |
|
exception as a result of a missing or invalid property. This potentially delayed |
|
visibility of some configuration issues is why `ApplicationContext` implementations by |
|
default pre-instantiate singleton beans. At the cost of some upfront time and memory to |
|
create these beans before they are actually needed, you discover configuration issues |
|
when the `ApplicationContext` is created, not later. You can still override this default |
|
behavior so that singleton beans will lazy-initialize, rather than be pre-instantiated. |
|
|
|
If no circular dependencies exist, when one or more collaborating beans are being |
|
injected into a dependent bean, each collaborating bean is __totally__ configured prior |
|
to being injected into the dependent bean. This means that if bean A has a dependency on |
|
bean B, the Spring IoC container completely configures bean B prior to invoking the |
|
setter method on bean A. In other words, the bean is instantiated (if not a |
|
pre-instantiated singleton), its dependencies are set, and the relevant lifecycle |
|
methods (such as a <<beans-factory-lifecycle-initializingbean,configured init method>> |
|
or the <<beans-factory-lifecycle-initializingbean,InitializingBean callback method>>) |
|
are invoked. |
|
|
|
|
|
[[beans-some-examples]] |
|
===== Examples of dependency injection |
|
The following example uses XML-based configuration metadata for setter-based DI. A small |
|
part of a Spring XML configuration file specifies some bean definitions: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="exampleBean" class="examples.ExampleBean"> |
|
<!-- setter injection using the nested ref element --> |
|
<property name="beanOne"> |
|
<ref bean="anotherExampleBean"/> |
|
</property> |
|
|
|
<!-- setter injection using the neater ref attribute --> |
|
<property name="beanTwo" ref="yetAnotherBean"/> |
|
<property name="integerProperty" value="1"/> |
|
</bean> |
|
|
|
<bean id="anotherExampleBean" class="examples.AnotherBean"/> |
|
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/> |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class ExampleBean { |
|
|
|
private AnotherBean beanOne; |
|
private YetAnotherBean beanTwo; |
|
private int i; |
|
|
|
public void setBeanOne(AnotherBean beanOne) { |
|
this.beanOne = beanOne; |
|
} |
|
|
|
public void setBeanTwo(YetAnotherBean beanTwo) { |
|
this.beanTwo = beanTwo; |
|
} |
|
|
|
public void setIntegerProperty(int i) { |
|
this.i = i; |
|
} |
|
|
|
} |
|
---- |
|
|
|
In the preceding example, setters are declared to match against the properties specified |
|
in the XML file. The following example uses constructor-based DI: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="exampleBean" class="examples.ExampleBean"> |
|
<!-- constructor injection using the nested ref element --> |
|
<constructor-arg> |
|
<ref bean="anotherExampleBean"/> |
|
</constructor-arg> |
|
|
|
<!-- constructor injection using the neater ref attribute --> |
|
<constructor-arg ref="yetAnotherBean"/> |
|
|
|
<constructor-arg type="int" value="1"/> |
|
</bean> |
|
|
|
<bean id="anotherExampleBean" class="examples.AnotherBean"/> |
|
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/> |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class ExampleBean { |
|
|
|
private AnotherBean beanOne; |
|
private YetAnotherBean beanTwo; |
|
private int i; |
|
|
|
public ExampleBean( |
|
AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) { |
|
this.beanOne = anotherBean; |
|
this.beanTwo = yetAnotherBean; |
|
this.i = i; |
|
} |
|
|
|
} |
|
---- |
|
|
|
The constructor arguments specified in the bean definition will be used as arguments to |
|
the constructor of the `ExampleBean`. |
|
|
|
Now consider a variant of this example, where instead of using a constructor, Spring is |
|
told to call a `static` factory method to return an instance of the object: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance"> |
|
<constructor-arg ref="anotherExampleBean"/> |
|
<constructor-arg ref="yetAnotherBean"/> |
|
<constructor-arg value="1"/> |
|
</bean> |
|
|
|
<bean id="anotherExampleBean" class="examples.AnotherBean"/> |
|
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/> |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class ExampleBean { |
|
|
|
// a private constructor |
|
private ExampleBean(...) { |
|
... |
|
} |
|
|
|
// a static factory method; the arguments to this method can be |
|
// considered the dependencies of the bean that is returned, |
|
// regardless of how those arguments are actually used. |
|
public static ExampleBean createInstance ( |
|
AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) { |
|
|
|
ExampleBean eb = new ExampleBean (...); |
|
// some other operations... |
|
return eb; |
|
} |
|
|
|
} |
|
---- |
|
|
|
Arguments to the `static` factory method are supplied via `<constructor-arg/>` elements, |
|
exactly the same as if a constructor had actually been used. The type of the class being |
|
returned by the factory method does not have to be of the same type as the class that |
|
contains the `static` factory method, although in this example it is. An instance |
|
(non-static) factory method would be used in an essentially identical fashion (aside |
|
from the use of the `factory-bean` attribute instead of the `class` attribute), so |
|
details will not be discussed here. |
|
|
|
|
|
|
|
[[beans-factory-properties-detailed]] |
|
==== Dependencies and configuration in detail |
|
As mentioned in the previous section, you can define bean properties and constructor |
|
arguments as references to other managed beans (collaborators), or as values defined |
|
inline. Spring's XML-based configuration metadata supports sub-element types within its |
|
`<property/>` and `<constructor-arg/>` elements for this purpose. |
|
|
|
|
|
[[beans-value-element]] |
|
===== Straight values (primitives, Strings, and so on) |
|
|
|
The `value` attribute of the `<property/>` element specifies a property or constructor |
|
argument as a human-readable string representation. Spring's |
|
<<core-convert-ConversionService-API, conversion service>> is used to convert these |
|
values from a `String` to the actual type of the property or argument. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> |
|
<!-- results in a setDriverClassName(String) call --> |
|
<property name="driverClassName" value="com.mysql.jdbc.Driver"/> |
|
<property name="url" value="jdbc:mysql://localhost:3306/mydb"/> |
|
<property name="username" value="root"/> |
|
<property name="password" value="masterkaoli"/> |
|
</bean> |
|
---- |
|
|
|
The following example uses the <<beans-p-namespace,p-namespace>> for even more succinct |
|
XML configuration. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:p="http://www.springframework.org/schema/p" |
|
xsi:schemaLocation="http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd"> |
|
|
|
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" |
|
destroy-method="close" |
|
p:driverClassName="com.mysql.jdbc.Driver" |
|
p:url="jdbc:mysql://localhost:3306/mydb" |
|
p:username="root" |
|
p:password="masterkaoli"/> |
|
|
|
</beans> |
|
---- |
|
|
|
The preceding XML is more succinct; however, typos are discovered at runtime rather than |
|
design time, unless you use an IDE such as http://www.jetbrains.com/idea/[IntelliJ |
|
IDEA] or the https://spring.io/tools/sts[Spring Tool Suite] (STS) |
|
that support automatic property completion when you create bean definitions. Such IDE |
|
assistance is highly recommended. |
|
|
|
You can also configure a `java.util.Properties` instance as: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="mappings" |
|
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> |
|
|
|
<!-- typed as a java.util.Properties --> |
|
<property name="properties"> |
|
<value> |
|
jdbc.driver.className=com.mysql.jdbc.Driver |
|
jdbc.url=jdbc:mysql://localhost:3306/mydb |
|
</value> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
The Spring container converts the text inside the `<value/>` element into a |
|
`java.util.Properties` instance by using the JavaBeans `PropertyEditor` mechanism. This |
|
is a nice shortcut, and is one of a few places where the Spring team do favor the use of |
|
the nested `<value/>` element over the `value` attribute style. |
|
|
|
[[beans-idref-element]] |
|
====== The idref element |
|
|
|
The `idref` element is simply an error-proof way to pass the __id__ (string value - not |
|
a reference) of another bean in the container to a `<constructor-arg/>` or `<property/>` |
|
element. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="theTargetBean" class="..."/> |
|
|
|
<bean id="theClientBean" class="..."> |
|
<property name="targetName"> |
|
<idref bean="theTargetBean" /> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
The above bean definition snippet is __exactly__ equivalent (at runtime) to the |
|
following snippet: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="theTargetBean" class="..." /> |
|
|
|
<bean id="client" class="..."> |
|
<property name="targetName" value="theTargetBean" /> |
|
</bean> |
|
---- |
|
|
|
The first form is preferable to the second, because using the `idref` tag allows the |
|
container to validate __at deployment time__ that the referenced, named bean actually |
|
exists. In the second variation, no validation is performed on the value that is passed |
|
to the `targetName` property of the `client` bean. Typos are only discovered (with most |
|
likely fatal results) when the `client` bean is actually instantiated. If the `client` |
|
bean is a <<beans-factory-scopes,prototype>> bean, this typo and the resulting exception |
|
may only be discovered long after the container is deployed. |
|
|
|
[NOTE] |
|
==== |
|
The `local` attribute on the `idref` element is no longer supported in the 4.0 beans xsd |
|
since it does not provide value over a regular `bean` reference anymore. Simply change |
|
your existing `idref local` references to `idref bean` when upgrading to the 4.0 schema. |
|
==== |
|
|
|
A common place (at least in versions earlier than Spring 2.0) where the `<idref/>` element |
|
brings value is in the configuration of <<aop-pfb-1,AOP interceptors>> in a |
|
`ProxyFactoryBean` bean definition. Using `<idref/>` elements when you specify the |
|
interceptor names prevents you from misspelling an interceptor id. |
|
|
|
|
|
[[beans-ref-element]] |
|
===== References to other beans (collaborators) |
|
The `ref` element is the final element inside a `<constructor-arg/>` or `<property/>` |
|
definition element. Here you set the value of the specified property of a bean to be a |
|
reference to another bean (a collaborator) managed by the container. The referenced bean |
|
is a dependency of the bean whose property will be set, and it is initialized on demand |
|
as needed before the property is set. (If the collaborator is a singleton bean, it may |
|
be initialized already by the container.) All references are ultimately a reference to |
|
another object. Scoping and validation depend on whether you specify the id/name of the |
|
other object through the `bean`, `local,` or `parent` attributes. |
|
|
|
Specifying the target bean through the `bean` attribute of the `<ref/>` tag is the most |
|
general form, and allows creation of a reference to any bean in the same container or |
|
parent container, regardless of whether it is in the same XML file. The value of the |
|
`bean` attribute may be the same as the `id` attribute of the target bean, or as one of |
|
the values in the `name` attribute of the target bean. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<ref bean="someBean"/> |
|
---- |
|
|
|
Specifying the target bean through the `parent` attribute creates a reference to a bean |
|
that is in a parent container of the current container. The value of the `parent` |
|
attribute may be the same as either the `id` attribute of the target bean, or one of the |
|
values in the `name` attribute of the target bean, and the target bean must be in a |
|
parent container of the current one. You use this bean reference variant mainly when you |
|
have a hierarchy of containers and you want to wrap an existing bean in a parent |
|
container with a proxy that will have the same name as the parent bean. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<!-- in the parent context --> |
|
<bean id="accountService" class="com.foo.SimpleAccountService"> |
|
<!-- insert dependencies as required as here --> |
|
</bean> |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<!-- in the child (descendant) context --> |
|
<bean id="accountService" <!-- bean name is the same as the parent bean --> |
|
class="org.springframework.aop.framework.ProxyFactoryBean"> |
|
<property name="target"> |
|
<ref parent="accountService"/> <!-- notice how we refer to the parent bean --> |
|
</property> |
|
<!-- insert other configuration and dependencies as required here --> |
|
</bean> |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
The `local` attribute on the `ref` element is no longer supported in the 4.0 beans xsd |
|
since it does not provide value over a regular `bean` reference anymore. Simply change |
|
your existing `ref local` references to `ref bean` when upgrading to the 4.0 schema. |
|
==== |
|
|
|
|
|
[[beans-inner-beans]] |
|
===== Inner beans |
|
A `<bean/>` element inside the `<property/>` or `<constructor-arg/>` elements defines a |
|
so-called __inner bean__. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="outer" class="..."> |
|
<!-- instead of using a reference to a target bean, simply define the target bean inline --> |
|
<property name="target"> |
|
<bean class="com.example.Person"> <!-- this is the inner bean --> |
|
<property name="name" value="Fiona Apple"/> |
|
<property name="age" value="25"/> |
|
</bean> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
An inner bean definition does not require a defined id or name; the container ignores |
|
these values. It also ignores the `scope` flag. Inner beans are __always__ anonymous and |
|
they are __always__ created with the outer bean. It is __not__ possible to inject inner |
|
beans into collaborating beans other than into the enclosing bean. |
|
|
|
|
|
[[beans-collection-elements]] |
|
===== Collections |
|
In the `<list/>`, `<set/>`, `<map/>`, and `<props/>` elements, you set the properties |
|
and arguments of the Java `Collection` types `List`, `Set`, `Map`, and `Properties`, |
|
respectively. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="moreComplexObject" class="example.ComplexObject"> |
|
<!-- results in a setAdminEmails(java.util.Properties) call --> |
|
<property name="adminEmails"> |
|
<props> |
|
<prop key="administrator">administrator@example.org</prop> |
|
<prop key="support">support@example.org</prop> |
|
<prop key="development">development@example.org</prop> |
|
</props> |
|
</property> |
|
<!-- results in a setSomeList(java.util.List) call --> |
|
<property name="someList"> |
|
<list> |
|
<value>a list element followed by a reference</value> |
|
<ref bean="myDataSource" /> |
|
</list> |
|
</property> |
|
<!-- results in a setSomeMap(java.util.Map) call --> |
|
<property name="someMap"> |
|
<map> |
|
<entry key="an entry" value="just some string"/> |
|
<entry key ="a ref" value-ref="myDataSource"/> |
|
</map> |
|
</property> |
|
<!-- results in a setSomeSet(java.util.Set) call --> |
|
<property name="someSet"> |
|
<set> |
|
<value>just some string</value> |
|
<ref bean="myDataSource" /> |
|
</set> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
__The value of a map key or value, or a set value, can also again be any of the |
|
following elements:__ |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
bean | ref | idref | list | set | map | props | value | null |
|
---- |
|
|
|
[[beans-collection-elements-merging]] |
|
====== Collection merging |
|
The Spring container also supports the __merging__ of collections. An application |
|
developer can define a parent-style `<list/>`, `<map/>`, `<set/>` or `<props/>` element, |
|
and have child-style `<list/>`, `<map/>`, `<set/>` or `<props/>` elements inherit and |
|
override values from the parent collection. That is, the child collection's values are |
|
the result of merging the elements of the parent and child collections, with the child's |
|
collection elements overriding values specified in the parent collection. |
|
|
|
__This section on merging discusses the parent-child bean mechanism. Readers unfamiliar |
|
with parent and child bean definitions may wish to read the |
|
<<beans-child-bean-definitions,relevant section>> before continuing.__ |
|
|
|
The following example demonstrates collection merging: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<bean id="parent" abstract="true" class="example.ComplexObject"> |
|
<property name="adminEmails"> |
|
<props> |
|
<prop key="administrator">administrator@example.com</prop> |
|
<prop key="support">support@example.com</prop> |
|
</props> |
|
</property> |
|
</bean> |
|
<bean id="child" parent="parent"> |
|
<property name="adminEmails"> |
|
<!-- the merge is specified on the child collection definition --> |
|
<props merge="true"> |
|
<prop key="sales">sales@example.com</prop> |
|
<prop key="support">support@example.co.uk</prop> |
|
</props> |
|
</property> |
|
</bean> |
|
<beans> |
|
---- |
|
|
|
Notice the use of the `merge=true` attribute on the `<props/>` element of the |
|
`adminEmails` property of the `child` bean definition. When the `child` bean is resolved |
|
and instantiated by the container, the resulting instance has an `adminEmails` |
|
`Properties` collection that contains the result of the merging of the child's |
|
`adminEmails` collection with the parent's `adminEmails` collection. |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
administrator=administrator@example.com |
|
sales=sales@example.com |
|
support=support@example.co.uk |
|
---- |
|
|
|
The child `Properties` collection's value set inherits all property elements from the |
|
parent `<props/>`, and the child's value for the `support` value overrides the value in |
|
the parent collection. |
|
|
|
This merging behavior applies similarly to the `<list/>`, `<map/>`, and `<set/>` |
|
collection types. In the specific case of the `<list/>` element, the semantics |
|
associated with the `List` collection type, that is, the notion of an `ordered` |
|
collection of values, is maintained; the parent's values precede all of the child list's |
|
values. In the case of the `Map`, `Set`, and `Properties` collection types, no ordering |
|
exists. Hence no ordering semantics are in effect for the collection types that underlie |
|
the associated `Map`, `Set`, and `Properties` implementation types that the container |
|
uses internally. |
|
|
|
[[beans-collection-merge-limitations]] |
|
====== Limitations of collection merging |
|
You cannot merge different collection types (such as a `Map` and a `List`), and if you |
|
do attempt to do so an appropriate `Exception` is thrown. The `merge` attribute must be |
|
specified on the lower, inherited, child definition; specifying the `merge` attribute on |
|
a parent collection definition is redundant and will not result in the desired merging. |
|
|
|
[[beans-collection-elements-strongly-typed]] |
|
====== Strongly-typed collection |
|
With the introduction of generic types in Java 5, you can use strongly typed collections. |
|
That is, it is possible to declare a `Collection` type such that it can only contain |
|
`String` elements (for example). If you are using Spring to dependency-inject a |
|
strongly-typed `Collection` into a bean, you can take advantage of Spring's |
|
type-conversion support such that the elements of your strongly-typed `Collection` |
|
instances are converted to the appropriate type prior to being added to the `Collection`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class Foo { |
|
|
|
private Map<String, Float> accounts; |
|
|
|
public void setAccounts(Map<String, Float> accounts) { |
|
this.accounts = accounts; |
|
} |
|
} |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<bean id="foo" class="x.y.Foo"> |
|
<property name="accounts"> |
|
<map> |
|
<entry key="one" value="9.99"/> |
|
<entry key="two" value="2.75"/> |
|
<entry key="six" value="3.99"/> |
|
</map> |
|
</property> |
|
</bean> |
|
</beans> |
|
---- |
|
|
|
When the `accounts` property of the `foo` bean is prepared for injection, the generics |
|
information about the element type of the strongly-typed `Map<String, Float>` is |
|
available by reflection. Thus Spring's type conversion infrastructure recognizes the |
|
various value elements as being of type `Float`, and the string values `9.99, 2.75`, and |
|
`3.99` are converted into an actual `Float` type. |
|
|
|
|
|
[[beans-null-element]] |
|
===== Null and empty string values |
|
Spring treats empty arguments for properties and the like as empty `Strings`. The |
|
following XML-based configuration metadata snippet sets the email property to the empty |
|
`String` value (""). |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="ExampleBean"> |
|
<property name="email" value=""/> |
|
</bean> |
|
---- |
|
|
|
The preceding example is equivalent to the following Java code: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
exampleBean.setEmail("") |
|
---- |
|
|
|
The `<null/>` element handles `null` values. For example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="ExampleBean"> |
|
<property name="email"> |
|
<null/> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
The above configuration is equivalent to the following Java code: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
exampleBean.setEmail(null) |
|
---- |
|
|
|
|
|
[[beans-p-namespace]] |
|
===== XML shortcut with the p-namespace |
|
The p-namespace enables you to use the `bean` element's attributes, instead of nested |
|
`<property/>` elements, to describe your property values and/or collaborating beans. |
|
|
|
Spring supports extensible configuration formats <<xsd-config,with namespaces>>, which are |
|
based on an XML Schema definition. The `beans` configuration format discussed in this |
|
chapter is defined in an XML Schema document. However, the p-namespace is not defined in |
|
an XSD file and exists only in the core of Spring. |
|
|
|
The following example shows two XML snippets that resolve to the same result: The first |
|
uses standard XML format and the second uses the p-namespace. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:p="http://www.springframework.org/schema/p" |
|
xsi:schemaLocation="http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd"> |
|
|
|
<bean name="classic" class="com.example.ExampleBean"> |
|
<property name="email" value="foo@bar.com"/> |
|
</bean> |
|
|
|
<bean name="p-namespace" class="com.example.ExampleBean" |
|
p:email="foo@bar.com"/> |
|
</beans> |
|
---- |
|
|
|
The example shows an attribute in the p-namespace called email in the bean definition. |
|
This tells Spring to include a property declaration. As previously mentioned, the |
|
p-namespace does not have a schema definition, so you can set the name of the attribute |
|
to the property name. |
|
|
|
This next example includes two more bean definitions that both have a reference to |
|
another bean: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:p="http://www.springframework.org/schema/p" |
|
xsi:schemaLocation="http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd"> |
|
|
|
<bean name="john-classic" class="com.example.Person"> |
|
<property name="name" value="John Doe"/> |
|
<property name="spouse" ref="jane"/> |
|
</bean> |
|
|
|
<bean name="john-modern" |
|
class="com.example.Person" |
|
p:name="John Doe" |
|
p:spouse-ref="jane"/> |
|
|
|
<bean name="jane" class="com.example.Person"> |
|
<property name="name" value="Jane Doe"/> |
|
</bean> |
|
</beans> |
|
---- |
|
|
|
As you can see, this example includes not only a property value using the p-namespace, |
|
but also uses a special format to declare property references. Whereas the first bean |
|
definition uses `<property name="spouse" ref="jane"/>` to create a reference from bean |
|
`john` to bean `jane`, the second bean definition uses `p:spouse-ref="jane"` as an |
|
attribute to do the exact same thing. In this case `spouse` is the property name, |
|
whereas the `-ref` part indicates that this is not a straight value but rather a |
|
reference to another bean. |
|
|
|
[NOTE] |
|
==== |
|
The p-namespace is not as flexible as the standard XML format. For example, the format |
|
for declaring property references clashes with properties that end in `Ref`, whereas the |
|
standard XML format does not. We recommend that you choose your approach carefully and |
|
communicate this to your team members, to avoid producing XML documents that use all |
|
three approaches at the same time. |
|
==== |
|
|
|
|
|
[[beans-c-namespace]] |
|
===== XML shortcut with the c-namespace |
|
Similar to the <<beans-p-namespace>>, the __c-namespace__, newly introduced in Spring |
|
3.1, allows usage of inlined attributes for configuring the constructor arguments rather |
|
then nested `constructor-arg` elements. |
|
|
|
Let's review the examples from <<beans-constructor-injection>> with the `c:` namespace: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:c="http://www.springframework.org/schema/c" |
|
xsi:schemaLocation="http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd"> |
|
|
|
<bean id="bar" class="x.y.Bar"/> |
|
<bean id="baz" class="x.y.Baz"/> |
|
|
|
<!-- traditional declaration --> |
|
<bean id="foo" class="x.y.Foo"> |
|
<constructor-arg ref="bar"/> |
|
<constructor-arg ref="baz"/> |
|
<constructor-arg value="foo@bar.com"/> |
|
</bean> |
|
|
|
<!-- c-namespace declaration --> |
|
<bean id="foo" class="x.y.Foo" c:bar-ref="bar" c:baz-ref="baz" c:email="foo@bar.com"/> |
|
|
|
</beans> |
|
---- |
|
|
|
The `c:` namespace uses the same conventions as the `p:` one (trailing `-ref` for bean |
|
references) for setting the constructor arguments by their names. And just as well, it |
|
needs to be declared even though it is not defined in an XSD schema (but it exists |
|
inside the Spring core). |
|
|
|
For the rare cases where the constructor argument names are not available (usually if |
|
the bytecode was compiled without debugging information), one can use fallback to the |
|
argument indexes: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<!-- c-namespace index declaration --> |
|
<bean id="foo" class="x.y.Foo" c:_0-ref="bar" c:_1-ref="baz"/> |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
Due to the XML grammar, the index notation requires the presence of the leading `_` as |
|
XML attribute names cannot start with a number (even though some IDE allow it). |
|
==== |
|
|
|
In practice, the constructor resolution |
|
<<beans-factory-ctor-arguments-resolution,mechanism>> is quite efficient in matching |
|
arguments so unless one really needs to, we recommend using the name notation |
|
through-out your configuration. |
|
|
|
|
|
[[beans-compound-property-names]] |
|
===== Compound property names |
|
You can use compound or nested property names when you set bean properties, as long as |
|
all components of the path except the final property name are not `null`. Consider the |
|
following bean definition. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="foo" class="foo.Bar"> |
|
<property name="fred.bob.sammy" value="123" /> |
|
</bean> |
|
---- |
|
|
|
The `foo` bean has a `fred` property, which has a `bob` property, which has a `sammy` |
|
property, and that final `sammy` property is being set to the value `123`. In order for |
|
this to work, the `fred` property of `foo`, and the `bob` property of `fred` must not be |
|
`null` after the bean is constructed, or a `NullPointerException` is thrown. |
|
|
|
|
|
|
|
[[beans-factory-dependson]] |
|
==== Using depends-on |
|
|
|
If a bean is a dependency of another that usually means that one bean is set as a |
|
property of another. Typically you accomplish this with the <<beans-ref-element, `<ref/>` |
|
element>> in XML-based configuration metadata. However, sometimes dependencies between |
|
beans are less direct; for example, a static initializer in a class needs to be |
|
triggered, such as database driver registration. The `depends-on` attribute can |
|
explicitly force one or more beans to be initialized before the bean using this element |
|
is initialized. The following example uses the `depends-on` attribute to express a |
|
dependency on a single bean: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="beanOne" class="ExampleBean" depends-on="manager"/> |
|
<bean id="manager" class="ManagerBean" /> |
|
---- |
|
|
|
To express a dependency on multiple beans, supply a list of bean names as the value of |
|
the `depends-on` attribute, with commas, whitespace and semicolons, used as valid |
|
delimiters: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao"> |
|
<property name="manager" ref="manager" /> |
|
</bean> |
|
|
|
<bean id="manager" class="ManagerBean" /> |
|
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" /> |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
The `depends-on` attribute in the bean definition can specify both an initialization |
|
time dependency and, in the case of <<beans-factory-scopes-singleton,singleton>> beans |
|
only, a corresponding destroy time dependency. Dependent beans that define a |
|
`depends-on` relationship with a given bean are destroyed first, prior to the given bean |
|
itself being destroyed. Thus `depends-on` can also control shutdown order. |
|
==== |
|
|
|
|
|
|
|
[[beans-factory-lazy-init]] |
|
==== Lazy-initialized beans |
|
By default, `ApplicationContext` implementations eagerly create and configure all |
|
<<beans-factory-scopes-singleton,singleton>> beans as part of the initialization |
|
process. Generally, this pre-instantiation is desirable, because errors in the |
|
configuration or surrounding environment are discovered immediately, as opposed to hours |
|
or even days later. When this behavior is __not__ desirable, you can prevent |
|
pre-instantiation of a singleton bean by marking the bean definition as |
|
lazy-initialized. A lazy-initialized bean tells the IoC container to create a bean |
|
instance when it is first requested, rather than at startup. |
|
|
|
In XML, this behavior is controlled by the `lazy-init` attribute on the `<bean/>` |
|
element; for example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/> |
|
<bean name="not.lazy" class="com.foo.AnotherBean"/> |
|
---- |
|
|
|
When the preceding configuration is consumed by an `ApplicationContext`, the bean named |
|
`lazy` is not eagerly pre-instantiated when the `ApplicationContext` is starting up, |
|
whereas the `not.lazy` bean is eagerly pre-instantiated. |
|
|
|
However, when a lazy-initialized bean is a dependency of a singleton bean that is |
|
__not__ lazy-initialized, the `ApplicationContext` creates the lazy-initialized bean at |
|
startup, because it must satisfy the singleton's dependencies. The lazy-initialized bean |
|
is injected into a singleton bean elsewhere that is not lazy-initialized. |
|
|
|
You can also control lazy-initialization at the container level by using the |
|
`default-lazy-init` attribute on the `<beans/>` element; for example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans default-lazy-init="true"> |
|
<!-- no beans will be pre-instantiated... --> |
|
</beans> |
|
---- |
|
|
|
|
|
|
|
[[beans-factory-autowire]] |
|
==== Autowiring collaborators |
|
The Spring container can __autowire__ relationships between collaborating beans. You can |
|
allow Spring to resolve collaborators (other beans) automatically for your bean by |
|
inspecting the contents of the `ApplicationContext`. Autowiring has the following |
|
advantages: |
|
|
|
* Autowiring can significantly reduce the need to specify properties or constructor |
|
arguments. (Other mechanisms such as a bean template |
|
<<beans-child-bean-definitions,discussed elsewhere in this chapter>> are also valuable |
|
in this regard.) |
|
* Autowiring can update a configuration as your objects evolve. For example, if you need |
|
to add a dependency to a class, that dependency can be satisfied automatically without |
|
you needing to modify the configuration. Thus autowiring can be especially useful |
|
during development, without negating the option of switching to explicit wiring when |
|
the code base becomes more stable. |
|
|
|
When using XML-based configuration metadata footnote:[See |
|
pass:specialcharacters,macros[<<beans-factory-collaborators>>]], you specify autowire |
|
mode for a bean definition with the `autowire` attribute of the `<bean/>` element. The |
|
autowiring functionality has five modes. You specify autowiring __per__ bean and thus |
|
can choose which ones to autowire. |
|
|
|
[[beans-factory-autowiring-modes-tbl]] |
|
.Autowiring modes |
|
|=== |
|
| Mode| Explanation |
|
|
|
| no |
|
| (Default) No autowiring. Bean references must be defined via a `ref` element. Changing |
|
the default setting is not recommended for larger deployments, because specifying |
|
collaborators explicitly gives greater control and clarity. To some extent, it |
|
documents the structure of a system. |
|
|
|
| byName |
|
| Autowiring by property name. Spring looks for a bean with the same name as the |
|
property that needs to be autowired. For example, if a bean definition is set to |
|
autowire by name, and it contains a __master__ property (that is, it has a |
|
__setMaster(..)__ method), Spring looks for a bean definition named `master`, and uses |
|
it to set the property. |
|
|
|
| byType |
|
| Allows a property to be autowired if exactly one bean of the property type exists in |
|
the container. If more than one exists, a fatal exception is thrown, which indicates |
|
that you may not use __byType__ autowiring for that bean. If there are no matching |
|
beans, nothing happens; the property is not set. |
|
|
|
| constructor |
|
| Analogous to __byType__, but applies to constructor arguments. If there is not exactly |
|
one bean of the constructor argument type in the container, a fatal error is raised. |
|
|=== |
|
|
|
With __byType__ or __constructor__ autowiring mode, you can wire arrays and |
|
typed-collections. In such cases __all__ autowire candidates within the container that |
|
match the expected type are provided to satisfy the dependency. You can autowire |
|
strongly-typed Maps if the expected key type is `String`. An autowired Maps values will |
|
consist of all bean instances that match the expected type, and the Maps keys will |
|
contain the corresponding bean names. |
|
|
|
You can combine autowire behavior with dependency checking, which is performed after |
|
autowiring completes. |
|
|
|
|
|
[[beans-autowired-exceptions]] |
|
===== Limitations and disadvantages of autowiring |
|
Autowiring works best when it is used consistently across a project. If autowiring is |
|
not used in general, it might be confusing to developers to use it to wire only one or |
|
two bean definitions. |
|
|
|
Consider the limitations and disadvantages of autowiring: |
|
|
|
* Explicit dependencies in `property` and `constructor-arg` settings always override |
|
autowiring. You cannot autowire so-called __simple__ properties such as primitives, |
|
`Strings`, and `Classes` (and arrays of such simple properties). This limitation is |
|
by-design. |
|
* Autowiring is less exact than explicit wiring. Although, as noted in the above table, |
|
Spring is careful to avoid guessing in case of ambiguity that might have unexpected |
|
results, the relationships between your Spring-managed objects are no longer |
|
documented explicitly. |
|
* Wiring information may not be available to tools that may generate documentation from |
|
a Spring container. |
|
* Multiple bean definitions within the container may match the type specified by the |
|
setter method or constructor argument to be autowired. For arrays, collections, or |
|
Maps, this is not necessarily a problem. However for dependencies that expect a single |
|
value, this ambiguity is not arbitrarily resolved. If no unique bean definition is |
|
available, an exception is thrown. |
|
|
|
In the latter scenario, you have several options: |
|
|
|
* Abandon autowiring in favor of explicit wiring. |
|
* Avoid autowiring for a bean definition by setting its `autowire-candidate` attributes |
|
to `false` as described in the next section. |
|
* Designate a single bean definition as the __primary__ candidate by setting the |
|
`primary` attribute of its `<bean/>` element to `true`. |
|
* Implement the more fine-grained control available |
|
with annotation-based configuration, as described in <<beans-annotation-config>>. |
|
|
|
|
|
[[beans-factory-autowire-candidate]] |
|
===== Excluding a bean from autowiring |
|
On a per-bean basis, you can exclude a bean from autowiring. In Spring's XML format, set |
|
the `autowire-candidate` attribute of the `<bean/>` element to `false`; the container |
|
makes that specific bean definition unavailable to the autowiring infrastructure |
|
(including annotation style configurations such as <<beans-autowired-annotation, |
|
`@Autowired`>>). |
|
|
|
You can also limit autowire candidates based on pattern-matching against bean names. The |
|
top-level `<beans/>` element accepts one or more patterns within its |
|
`default-autowire-candidates` attribute. For example, to limit autowire candidate status |
|
to any bean whose name ends with __Repository,__ provide a value of *Repository. To |
|
provide multiple patterns, define them in a comma-separated list. An explicit value of |
|
`true` or `false` for a bean definitions `autowire-candidate` attribute always takes |
|
precedence, and for such beans, the pattern matching rules do not apply. |
|
|
|
These techniques are useful for beans that you never want to be injected into other |
|
beans by autowiring. It does not mean that an excluded bean cannot itself be configured |
|
using autowiring. Rather, the bean itself is not a candidate for autowiring other beans. |
|
|
|
|
|
|
|
[[beans-factory-method-injection]] |
|
==== Method injection |
|
In most application scenarios, most beans in the container are |
|
<<beans-factory-scopes-singleton,singletons>>. When a singleton bean needs to |
|
collaborate with another singleton bean, or a non-singleton bean needs to collaborate |
|
with another non-singleton bean, you typically handle the dependency by defining one |
|
bean as a property of the other. A problem arises when the bean lifecycles are |
|
different. Suppose singleton bean A needs to use non-singleton (prototype) bean B, |
|
perhaps on each method invocation on A. The container only creates the singleton bean A |
|
once, and thus only gets one opportunity to set the properties. The container cannot |
|
provide bean A with a new instance of bean B every time one is needed. |
|
|
|
A solution is to forego some inversion of control. You can <<beans-factory-aware,make |
|
bean A aware of the container>> by implementing the `ApplicationContextAware` interface, |
|
and by <<beans-factory-client,making a getBean("B") call to the container>> ask for (a |
|
typically new) bean B instance every time bean A needs it. The following is an example |
|
of this approach: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// a class that uses a stateful Command-style class to perform some processing |
|
package fiona.apple; |
|
|
|
// Spring-API imports |
|
import org.springframework.beans.BeansException; |
|
import org.springframework.context.ApplicationContext; |
|
import org.springframework.context.ApplicationContextAware; |
|
|
|
public class CommandManager implements ApplicationContextAware { |
|
|
|
private ApplicationContext applicationContext; |
|
|
|
public Object process(Map commandState) { |
|
// grab a new instance of the appropriate Command |
|
Command command = createCommand(); |
|
// set the state on the (hopefully brand new) Command instance |
|
command.setState(commandState); |
|
return command.execute(); |
|
} |
|
|
|
protected Command createCommand() { |
|
// notice the Spring API dependency! |
|
return this.applicationContext.getBean("command", Command.class); |
|
} |
|
|
|
public void setApplicationContext( |
|
ApplicationContext applicationContext) throws BeansException { |
|
this.applicationContext = applicationContext; |
|
} |
|
} |
|
---- |
|
|
|
The preceding is not desirable, because the business code is aware of and coupled to the |
|
Spring Framework. Method Injection, a somewhat advanced feature of the Spring IoC |
|
container, allows this use case to be handled in a clean fashion. |
|
|
|
**** |
|
You can read more about the motivation for Method Injection in |
|
https://spring.io/blog/2004/08/06/method-injection/[this blog entry]. |
|
**** |
|
|
|
|
|
[[beans-factory-lookup-method-injection]] |
|
===== Lookup method injection |
|
Lookup method injection is the ability of the container to override methods on |
|
__container managed beans__, to return the lookup result for another named bean in the |
|
container. The lookup typically involves a prototype bean as in the scenario described |
|
in the preceding section. The Spring Framework implements this method injection by using |
|
bytecode generation from the CGLIB library to generate dynamically a subclass that |
|
overrides the method. |
|
|
|
[NOTE] |
|
==== |
|
* For this dynamic subclassing to work, the class that the Spring bean container will |
|
subclass cannot be `final`, and the method to be overridden cannot be `final` either. |
|
* Unit-testing a class that has an `abstract` method requires you to subclass the class |
|
yourself and to supply a stub implementation of the `abstract` method. |
|
* Concrete methods are also necessary for component scanning which requires concrete |
|
classes to pick up. |
|
* A further key limitation is that lookup methods won't work with factory methods and |
|
in particular not with `@Bean` methods in configuration classes, since the container |
|
is not in charge of creating the instance in that case and therefore cannot create |
|
a runtime-generated subclass on the fly. |
|
* Finally, objects that have been the target of method injection cannot be serialized. |
|
==== |
|
|
|
Looking at the `CommandManager` class in the previous code snippet, you see that the |
|
Spring container will dynamically override the implementation of the `createCommand()` |
|
method. Your `CommandManager` class will not have any Spring dependencies, as can be |
|
seen in the reworked example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package fiona.apple; |
|
|
|
// no more Spring imports! |
|
|
|
public abstract class CommandManager { |
|
|
|
public Object process(Object commandState) { |
|
// grab a new instance of the appropriate Command interface |
|
Command command = createCommand(); |
|
// set the state on the (hopefully brand new) Command instance |
|
command.setState(commandState); |
|
return command.execute(); |
|
} |
|
|
|
// okay... but where is the implementation of this method? |
|
protected abstract Command createCommand(); |
|
} |
|
---- |
|
|
|
In the client class containing the method to be injected (the `CommandManager` in this |
|
case), the method to be injected requires a signature of the following form: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<public|protected> [abstract] <return-type> theMethodName(no-arguments); |
|
---- |
|
|
|
If the method is `abstract`, the dynamically-generated subclass implements the method. |
|
Otherwise, the dynamically-generated subclass overrides the concrete method defined in |
|
the original class. For example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<!-- a stateful bean deployed as a prototype (non-singleton) --> |
|
<bean id="command" class="fiona.apple.AsyncCommand" scope="prototype"> |
|
<!-- inject dependencies here as required --> |
|
</bean> |
|
|
|
<!-- commandProcessor uses statefulCommandHelper --> |
|
<bean id="commandManager" class="fiona.apple.CommandManager"> |
|
<lookup-method name="createCommand" bean="command"/> |
|
</bean> |
|
---- |
|
|
|
The bean identified as __commandManager__ calls its own method `createCommand()` |
|
whenever it needs a new instance of the __command__ bean. You must be careful to deploy |
|
the `command` bean as a prototype, if that is actually what is needed. If it is deployed |
|
as a <<beans-factory-scopes-singleton,singleton>>, the same instance of the `command` |
|
bean is returned each time. |
|
|
|
[TIP] |
|
==== |
|
The interested reader may also find the `ServiceLocatorFactoryBean` (in the |
|
`org.springframework.beans.factory.config` package) to be of use. The approach used in |
|
ServiceLocatorFactoryBean is similar to that of another utility class, |
|
`ObjectFactoryCreatingFactoryBean`, but it allows you to specify your own lookup |
|
interface as opposed to a Spring-specific lookup interface. Consult the javadocs of |
|
these classes for additional information. |
|
==== |
|
|
|
|
|
[[beans-factory-arbitrary-method-replacement]] |
|
===== Arbitrary method replacement |
|
A less useful form of method injection than lookup method Injection is the ability to |
|
replace arbitrary methods in a managed bean with another method implementation. Users |
|
may safely skip the rest of this section until the functionality is actually needed. |
|
|
|
With XML-based configuration metadata, you can use the `replaced-method` element to |
|
replace an existing method implementation with another, for a deployed bean. Consider |
|
the following class, with a method computeValue, which we want to override: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MyValueCalculator { |
|
|
|
public String computeValue(String input) { |
|
// some real code... |
|
} |
|
|
|
// some other methods... |
|
|
|
} |
|
---- |
|
|
|
A class implementing the `org.springframework.beans.factory.support.MethodReplacer` |
|
interface provides the new method definition. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
/** |
|
* meant to be used to override the existing computeValue(String) |
|
* implementation in MyValueCalculator |
|
*/ |
|
public class ReplacementComputeValue implements MethodReplacer { |
|
|
|
public Object reimplement(Object o, Method m, Object[] args) throws Throwable { |
|
// get the input value, work with it, and return a computed result |
|
String input = (String) args[0]; |
|
... |
|
return ...; |
|
} |
|
} |
|
---- |
|
|
|
The bean definition to deploy the original class and specify the method override would |
|
look like this: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="myValueCalculator" class="x.y.z.MyValueCalculator"> |
|
<!-- arbitrary method replacement --> |
|
<replaced-method name="computeValue" replacer="replacementComputeValue"> |
|
<arg-type>String</arg-type> |
|
</replaced-method> |
|
</bean> |
|
|
|
<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/> |
|
---- |
|
|
|
You can use one or more contained `<arg-type/>` elements within the `<replaced-method/>` |
|
element to indicate the method signature of the method being overridden. The signature |
|
for the arguments is necessary only if the method is overloaded and multiple variants |
|
exist within the class. For convenience, the type string for an argument may be a |
|
substring of the fully qualified type name. For example, the following all match |
|
`java.lang.String`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
java.lang.String |
|
String |
|
Str |
|
---- |
|
|
|
Because the number of arguments is often enough to distinguish between each possible |
|
choice, this shortcut can save a lot of typing, by allowing you to type only the |
|
shortest string that will match an argument type. |
|
|
|
|
|
|
|
|
|
[[beans-factory-scopes]] |
|
=== Bean scopes |
|
When you create a bean definition, you create a __recipe__ for creating actual instances |
|
of the class defined by that bean definition. The idea that a bean definition is a |
|
recipe is important, because it means that, as with a class, you can create many object |
|
instances from a single recipe. |
|
|
|
You can control not only the various dependencies and configuration values that are to |
|
be plugged into an object that is created from a particular bean definition, but also |
|
the __scope__ of the objects created from a particular bean definition. This approach is |
|
powerful and flexible in that you can __choose__ the scope of the objects you create |
|
through configuration instead of having to bake in the scope of an object at the Java |
|
class level. Beans can be defined to be deployed in one of a number of scopes: out of |
|
the box, the Spring Framework supports five scopes, three of which are available only if |
|
you use a web-aware `ApplicationContext`. |
|
|
|
The following scopes are supported out of the box. You can also create |
|
<<beans-factory-scopes-custom,a custom scope.>> |
|
|
|
[[beans-factory-scopes-tbl]] |
|
.Bean scopes |
|
|=== |
|
| Scope| Description |
|
|
|
| <<beans-factory-scopes-singleton,singleton>> |
|
| (Default) Scopes a single bean definition to a single object instance per Spring IoC |
|
container. |
|
|
|
| <<beans-factory-scopes-prototype,prototype>> |
|
| Scopes a single bean definition to any number of object instances. |
|
|
|
| <<beans-factory-scopes-request,request>> |
|
| Scopes a single bean definition to the lifecycle of a single HTTP request; that is, |
|
each HTTP request has its own instance of a bean created off the back of a single bean |
|
definition. Only valid in the context of a web-aware Spring `ApplicationContext`. |
|
|
|
| <<beans-factory-scopes-session,session>> |
|
| Scopes a single bean definition to the lifecycle of an HTTP `Session`. Only valid in |
|
the context of a web-aware Spring `ApplicationContext`. |
|
|
|
| <<beans-factory-scopes-global-session,global session>> |
|
| Scopes a single bean definition to the lifecycle of a global HTTP `Session`. Typically |
|
only valid when used in a portlet context. Only valid in the context of a web-aware |
|
Spring `ApplicationContext`. |
|
|
|
| <<beans-factory-scopes-application,application>> |
|
| Scopes a single bean definition to the lifecycle of a `ServletContext`. Only valid in |
|
the context of a web-aware Spring `ApplicationContext`. |
|
|=== |
|
|
|
[NOTE] |
|
==== |
|
As of Spring 3.0, a __thread scope__ is available, but is not registered by default. For |
|
more information, see the documentation for |
|
{javadoc-baseurl}/org/springframework/context/support/SimpleThreadScope.html[`SimpleThreadScope`]. |
|
For instructions on how to register this or any other custom scope, see |
|
<<beans-factory-scopes-custom-using>>. |
|
==== |
|
|
|
|
|
|
|
[[beans-factory-scopes-singleton]] |
|
==== The singleton scope |
|
Only one __shared__ instance of a singleton bean is managed, and all requests for beans |
|
with an id or ids matching that bean definition result in that one specific bean |
|
instance being returned by the Spring container. |
|
|
|
To put it another way, when you define a bean definition and it is scoped as a |
|
singleton, the Spring IoC container creates __exactly one__ instance of the object |
|
defined by that bean definition. This single instance is stored in a cache of such |
|
singleton beans, and __all subsequent requests and references__ for that named bean |
|
return the cached object. |
|
|
|
image::images/singleton.png[width=400] |
|
|
|
Spring's concept of a singleton bean differs from the Singleton pattern as defined in |
|
the Gang of Four (GoF) patterns book. The GoF Singleton hard-codes the scope of an |
|
object such that one __and only one__ instance of a particular class is created __per |
|
ClassLoader__. The scope of the Spring singleton is best described as __per container |
|
and per bean__. This means that if you define one bean for a particular class in a |
|
single Spring container, then the Spring container creates one __and only one__ instance |
|
of the class defined by that bean definition. __The singleton scope is the default scope |
|
in Spring__. To define a bean as a singleton in XML, you would write, for example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="accountService" class="com.foo.DefaultAccountService"/> |
|
|
|
<!-- the following is equivalent, though redundant (singleton scope is the default) --> |
|
<bean id="accountService" class="com.foo.DefaultAccountService" scope="singleton"/> |
|
---- |
|
|
|
|
|
|
|
[[beans-factory-scopes-prototype]] |
|
==== The prototype scope |
|
The non-singleton, prototype scope of bean deployment results in the __creation of a new |
|
bean instance__ every time a request for that specific bean is made. That is, the bean |
|
is injected into another bean or you request it through a `getBean()` method call on the |
|
container. As a rule, use the prototype scope for all stateful beans and the singleton |
|
scope for stateless beans. |
|
|
|
The following diagram illustrates the Spring prototype scope. __A data access object |
|
(DAO) is not typically configured as a prototype, because a typical DAO does not hold |
|
any conversational state; it was just easier for this author to reuse the core of the |
|
singleton diagram.__ |
|
|
|
image::images/prototype.png[width=400] |
|
|
|
The following example defines a bean as a prototype in XML: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="accountService" class="com.foo.DefaultAccountService" scope="prototype"/> |
|
---- |
|
|
|
In contrast to the other scopes, Spring does not manage the complete lifecycle of a |
|
prototype bean: the container instantiates, configures, and otherwise assembles a |
|
prototype object, and hands it to the client, with no further record of that prototype |
|
instance. Thus, although__ initialization__ lifecycle callback methods are called on all |
|
objects regardless of scope, in the case of prototypes, configured __destruction__ |
|
lifecycle callbacks are __not__ called. The client code must clean up prototype-scoped |
|
objects and release expensive resources that the prototype bean(s) are holding. To get |
|
the Spring container to release resources held by prototype-scoped beans, try using a |
|
custom <<beans-factory-extension-bpp,bean post-processor>>, which holds a reference to |
|
beans that need to be cleaned up. |
|
|
|
In some respects, the Spring container's role in regard to a prototype-scoped bean is a |
|
replacement for the Java `new` operator. All lifecycle management past that point must |
|
be handled by the client. (For details on the lifecycle of a bean in the Spring |
|
container, see <<beans-factory-lifecycle>>.) |
|
|
|
|
|
|
|
[[beans-factory-scopes-sing-prot-interaction]] |
|
==== Singleton beans with prototype-bean dependencies |
|
When you use singleton-scoped beans with dependencies on prototype beans, be aware that |
|
__dependencies are resolved at instantiation time__. Thus if you dependency-inject a |
|
prototype-scoped bean into a singleton-scoped bean, a new prototype bean is instantiated |
|
and then dependency-injected into the singleton bean. The prototype instance is the sole |
|
instance that is ever supplied to the singleton-scoped bean. |
|
|
|
However, suppose you want the singleton-scoped bean to acquire a new instance of the |
|
prototype-scoped bean repeatedly at runtime. You cannot dependency-inject a |
|
prototype-scoped bean into your singleton bean, because that injection occurs only |
|
__once__, when the Spring container is instantiating the singleton bean and resolving |
|
and injecting its dependencies. If you need a new instance of a prototype bean at |
|
runtime more than once, see <<beans-factory-method-injection>> |
|
|
|
|
|
|
|
[[beans-factory-scopes-other]] |
|
==== Request, session, and global session scopes |
|
The `request`, `session`, and `global session` scopes are __only__ available if you use |
|
a web-aware Spring `ApplicationContext` implementation (such as |
|
`XmlWebApplicationContext`). If you use these scopes with regular Spring IoC containers |
|
such as the `ClassPathXmlApplicationContext`, you get an `IllegalStateException` |
|
complaining about an unknown bean scope. |
|
|
|
|
|
[[beans-factory-scopes-other-web-configuration]] |
|
===== Initial web configuration |
|
To support the scoping of beans at the `request`, `session`, and `global session` levels |
|
(web-scoped beans), some minor initial configuration is required before you define your |
|
beans. (This initial setup is __not__ required for the standard scopes, singleton and |
|
prototype.) |
|
|
|
How you accomplish this initial setup depends on your particular Servlet environment.. |
|
|
|
If you access scoped beans within Spring Web MVC, in effect, within a request that is |
|
processed by the Spring `DispatcherServlet`, or `DispatcherPortlet`, then no special |
|
setup is necessary: `DispatcherServlet` and `DispatcherPortlet` already expose all |
|
relevant state. |
|
|
|
If you use a Servlet 2.5 web container, with requests processed outside of Spring's |
|
DispatcherServlet (for example, when using JSF or Struts), you need to register the |
|
`org.springframework.web.context.request.RequestContextListener` `ServletRequestListener`. |
|
For Servlet 3.0+, this can done programmatically via the `WebApplicationInitializer` |
|
interface. Alternatively, or for older containers, add the following declaration to |
|
your web application's `web.xml` file: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<web-app> |
|
... |
|
<listener> |
|
<listener-class> |
|
org.springframework.web.context.request.RequestContextListener |
|
</listener-class> |
|
</listener> |
|
... |
|
</web-app> |
|
---- |
|
|
|
Alternatively, if there are issues with your listener setup, consider the provided |
|
`RequestContextFilter`. The filter mapping depends on the surrounding web |
|
application configuration, so you have to change it as appropriate. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<web-app> |
|
... |
|
<filter> |
|
<filter-name>requestContextFilter</filter-name> |
|
<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class> |
|
</filter> |
|
<filter-mapping> |
|
<filter-name>requestContextFilter</filter-name> |
|
<url-pattern>/*</url-pattern> |
|
</filter-mapping> |
|
... |
|
</web-app> |
|
---- |
|
|
|
`DispatcherServlet`, `RequestContextListener` and `RequestContextFilter` all do exactly |
|
the same thing, namely bind the HTTP request object to the `Thread` that is servicing |
|
that request. This makes beans that are request- and session-scoped available further |
|
down the call chain. |
|
|
|
|
|
[[beans-factory-scopes-request]] |
|
===== Request scope |
|
Consider the following bean definition: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="loginAction" class="com.foo.LoginAction" scope="request"/> |
|
---- |
|
|
|
The Spring container creates a new instance of the `LoginAction` bean by using the |
|
`loginAction` bean definition for each and every HTTP request. That is, the |
|
`loginAction` bean is scoped at the HTTP request level. You can change the internal |
|
state of the instance that is created as much as you want, because other instances |
|
created from the same `loginAction` bean definition will not see these changes in state; |
|
they are particular to an individual request. When the request completes processing, the |
|
bean that is scoped to the request is discarded. |
|
|
|
|
|
[[beans-factory-scopes-session]] |
|
===== Session scope |
|
Consider the following bean definition: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/> |
|
---- |
|
|
|
The Spring container creates a new instance of the `UserPreferences` bean by using the |
|
`userPreferences` bean definition for the lifetime of a single HTTP `Session`. In other |
|
words, the `userPreferences` bean is effectively scoped at the HTTP `Session` level. As |
|
with `request-scoped` beans, you can change the internal state of the instance that is |
|
created as much as you want, knowing that other HTTP `Session` instances that are also |
|
using instances created from the same `userPreferences` bean definition do not see these |
|
changes in state, because they are particular to an individual HTTP `Session`. When the |
|
HTTP `Session` is eventually discarded, the bean that is scoped to that particular HTTP |
|
`Session` is also discarded. |
|
|
|
|
|
[[beans-factory-scopes-global-session]] |
|
===== Global session scope |
|
Consider the following bean definition: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="userPreferences" class="com.foo.UserPreferences" scope="globalSession"/> |
|
---- |
|
|
|
The `global session` scope is similar to the standard HTTP `Session` scope |
|
(<<beans-factory-scopes-session,described above>>), and applies only in the context of |
|
portlet-based web applications. The portlet specification defines the notion of a global |
|
`Session` that is shared among all portlets that make up a single portlet web |
|
application. Beans defined at the `global session` scope are scoped (or bound) to the |
|
lifetime of the global portlet `Session`. |
|
|
|
If you write a standard Servlet-based web application and you define one or more beans |
|
as having `global session` scope, the standard HTTP `Session` scope is used, and no |
|
error is raised. |
|
|
|
|
|
[[beans-factory-scopes-application]] |
|
===== Application scope |
|
Consider the following bean definition: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="appPreferences" class="com.foo.AppPreferences" scope="application"/> |
|
---- |
|
|
|
The Spring container creates a new instance of the `AppPreferences` bean by using the |
|
`appPreferences` bean definition once for the entire web application. That is, the |
|
`appPreferences` bean is scoped at the `ServletContext` level, stored as a regular |
|
`ServletContext` attribute. This is somewhat similar to a Spring singleton bean but |
|
differs in two important ways: It is a singleton per `ServletContext`, not per Spring |
|
'ApplicationContext' (or which there may be several in any given web application), |
|
and it is actually exposed and therefore visible as a `ServletContext` attribute. |
|
|
|
|
|
[[beans-factory-scopes-other-injection]] |
|
===== Scoped beans as dependencies |
|
The Spring IoC container manages not only the instantiation of your objects (beans), but |
|
also the wiring up of collaborators (or dependencies). If you want to inject (for |
|
example) an HTTP request scoped bean into another bean, you must inject an AOP proxy in |
|
place of the scoped bean. That is, you need to inject a proxy object that exposes the |
|
same public interface as the scoped object but that can also retrieve the real, target |
|
object from the relevant scope (for example, an HTTP request) and delegate method calls |
|
onto the real object. |
|
|
|
[NOTE] |
|
==== |
|
You __do not__ need to use the `<aop:scoped-proxy/>` in conjunction with beans that are |
|
scoped as `singletons` or `prototypes`. |
|
==== |
|
|
|
The configuration in the following example is only one line, but it is important to |
|
understand the "why" as well as the "how" behind it. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:aop="http://www.springframework.org/schema/aop" |
|
xsi:schemaLocation="http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/aop |
|
http://www.springframework.org/schema/aop/spring-aop.xsd"> |
|
|
|
<!-- an HTTP Session-scoped bean exposed as a proxy --> |
|
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"> |
|
<!-- instructs the container to proxy the surrounding bean --> |
|
<aop:scoped-proxy/> |
|
</bean> |
|
|
|
<!-- a singleton-scoped bean injected with a proxy to the above bean --> |
|
<bean id="userService" class="com.foo.SimpleUserService"> |
|
<!-- a reference to the proxied userPreferences bean --> |
|
<property name="userPreferences" ref="userPreferences"/> |
|
</bean> |
|
</beans> |
|
---- |
|
|
|
To create such a proxy, you insert a child `<aop:scoped-proxy/>` element into a scoped |
|
bean definition. See <<beans-factory-scopes-other-injection-proxies>> and |
|
<<xsd-config>>.) Why do definitions of beans scoped at the `request`, `session`, |
|
`globalSession` and custom-scope levels require the `<aop:scoped-proxy/>` element ? |
|
Let's examine the following singleton bean definition and contrast it with what you need |
|
to define for the aforementioned scopes. (The following `userPreferences` bean |
|
definition as it stands is __incomplete.)__ |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/> |
|
|
|
<bean id="userManager" class="com.foo.UserManager"> |
|
<property name="userPreferences" ref="userPreferences"/> |
|
</bean> |
|
---- |
|
|
|
In the preceding example, the singleton bean `userManager` is injected with a reference |
|
to the HTTP `Session`-scoped bean `userPreferences`. The salient point here is that the |
|
`userManager` bean is a singleton: it will be instantiated __exactly once__ per |
|
container, and its dependencies (in this case only one, the `userPreferences` bean) are |
|
also injected only once. This means that the `userManager` bean will only operate on the |
|
exact same `userPreferences` object, that is, the one that it was originally injected |
|
with. |
|
|
|
This is __not__ the behavior you want when injecting a shorter-lived scoped bean into a |
|
longer-lived scoped bean, for example injecting an HTTP `Session`-scoped collaborating |
|
bean as a dependency into singleton bean. Rather, you need a single `userManager` |
|
object, and for the lifetime of an HTTP `Session`, you need a `userPreferences` object |
|
that is specific to said HTTP `Session`. Thus the container creates an object that |
|
exposes the exact same public interface as the `UserPreferences` class (ideally an |
|
object that __is a__ `UserPreferences` instance) which can fetch the real |
|
`UserPreferences` object from the scoping mechanism (HTTP request, `Session`, etc.). The |
|
container injects this proxy object into the `userManager` bean, which is unaware that |
|
this `UserPreferences` reference is a proxy. In this example, when a `UserManager` |
|
instance invokes a method on the dependency-injected `UserPreferences` object, it |
|
actually is invoking a method on the proxy. The proxy then fetches the real |
|
`UserPreferences` object from (in this case) the HTTP `Session`, and delegates the |
|
method invocation onto the retrieved real `UserPreferences` object. |
|
|
|
Thus you need the following, correct and complete, configuration when injecting |
|
`request-`, `session-`, and `globalSession-scoped` beans into collaborating objects: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"> |
|
<aop:scoped-proxy/> |
|
</bean> |
|
<bean id="userManager" class="com.foo.UserManager"> |
|
<property name="userPreferences" ref="userPreferences"/> |
|
</bean> |
|
---- |
|
|
|
[[beans-factory-scopes-other-injection-proxies]] |
|
====== Choosing the type of proxy to create |
|
By default, when the Spring container creates a proxy for a bean that is marked up with |
|
the `<aop:scoped-proxy/>` element, __a CGLIB-based class proxy is created__. |
|
|
|
[NOTE] |
|
==== |
|
CGLIB proxies only intercept public method calls! Do not call non-public methods |
|
on such a proxy; they will not be delegated to the actual scoped target object. |
|
==== |
|
|
|
Alternatively, you can configure the Spring container to create standard JDK |
|
interface-based proxies for such scoped beans, by specifying `false` for the value of |
|
the `proxy-target-class` attribute of the `<aop:scoped-proxy/>` element. Using JDK |
|
interface-based proxies means that you do not need additional libraries in your |
|
application classpath to effect such proxying. However, it also means that the class of |
|
the scoped bean must implement at least one interface, and __that all__ collaborators |
|
into which the scoped bean is injected must reference the bean through one of its |
|
interfaces. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<!-- DefaultUserPreferences implements the UserPreferences interface --> |
|
<bean id="userPreferences" class="com.foo.DefaultUserPreferences" scope="session"> |
|
<aop:scoped-proxy proxy-target-class="false"/> |
|
</bean> |
|
<bean id="userManager" class="com.foo.UserManager"> |
|
<property name="userPreferences" ref="userPreferences"/> |
|
</bean> |
|
---- |
|
|
|
For more detailed information about choosing class-based or interface-based proxying, |
|
see <<aop-proxying>>. |
|
|
|
|
|
|
|
[[beans-factory-scopes-custom]] |
|
==== Custom scopes |
|
The bean scoping mechanism is extensible; You can define your own |
|
scopes, or even redefine existing scopes, although the latter is considered bad practice |
|
and you __cannot__ override the built-in `singleton` and `prototype` scopes. |
|
|
|
|
|
[[beans-factory-scopes-custom-creating]] |
|
===== Creating a custom scope |
|
To integrate your custom scope(s) into the Spring container, you need to implement the |
|
`org.springframework.beans.factory.config.Scope` interface, which is described in this |
|
section. For an idea of how to implement your own scopes, see the `Scope` |
|
implementations that are supplied with the Spring Framework itself and the |
|
{javadoc-baseurl}/org/springframework/beans/factory/config/Scope.html[`Scope` javadocs], |
|
which explains the methods you need to implement in more detail. |
|
|
|
The `Scope` interface has four methods to get objects from the scope, remove them from |
|
the scope, and allow them to be destroyed. |
|
|
|
The following method returns the object from the underlying scope. The session scope |
|
implementation, for example, returns the session-scoped bean (and if it does not exist, |
|
the method returns a new instance of the bean, after having bound it to the session for |
|
future reference). |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Object get(String name, ObjectFactory objectFactory) |
|
---- |
|
|
|
The following method removes the object from the underlying scope. The session scope |
|
implementation for example, removes the session-scoped bean from the underlying session. |
|
The object should be returned, but you can return null if the object with the specified |
|
name is not found. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Object remove(String name) |
|
---- |
|
|
|
The following method registers the callbacks the scope should execute when it is |
|
destroyed or when the specified object in the scope is destroyed. Refer to the javadocs |
|
or a Spring scope implementation for more information on destruction callbacks. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
void registerDestructionCallback(String name, Runnable destructionCallback) |
|
---- |
|
|
|
The following method obtains the conversation identifier for the underlying scope. This |
|
identifier is different for each scope. For a session scoped implementation, this |
|
identifier can be the session identifier. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
String getConversationId() |
|
---- |
|
|
|
|
|
[[beans-factory-scopes-custom-using]] |
|
===== Using a custom scope |
|
After you write and test one or more custom `Scope` implementations, you need to make |
|
the Spring container aware of your new scope(s). The following method is the central |
|
method to register a new `Scope` with the Spring container: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
void registerScope(String scopeName, Scope scope); |
|
---- |
|
|
|
This method is declared on the `ConfigurableBeanFactory` interface, which is available |
|
on most of the concrete `ApplicationContext` implementations that ship with Spring via |
|
the BeanFactory property. |
|
|
|
The first argument to the `registerScope(..)` method is the unique name associated with |
|
a scope; examples of such names in the Spring container itself are `singleton` and |
|
`prototype`. The second argument to the `registerScope(..)` method is an actual instance |
|
of the custom `Scope` implementation that you wish to register and use. |
|
|
|
Suppose that you write your custom `Scope` implementation, and then register it as below. |
|
|
|
[NOTE] |
|
==== |
|
The example below uses `SimpleThreadScope` which is included with Spring, but not |
|
registered by default. The instructions would be the same for your own custom `Scope` |
|
implementations. |
|
==== |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Scope threadScope = new SimpleThreadScope(); |
|
beanFactory.registerScope("thread", threadScope); |
|
---- |
|
|
|
You then create bean definitions that adhere to the scoping rules of your custom `Scope`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="..." class="..." scope="thread"> |
|
---- |
|
|
|
With a custom `Scope` implementation, you are not limited to programmatic registration |
|
of the scope. You can also do the `Scope` registration declaratively, using the |
|
`CustomScopeConfigurer` class: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:aop="http://www.springframework.org/schema/aop" |
|
xsi:schemaLocation="http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/aop |
|
http://www.springframework.org/schema/aop/spring-aop.xsd"> |
|
|
|
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"> |
|
<property name="scopes"> |
|
<map> |
|
<entry key="thread"> |
|
<bean class="org.springframework.context.support.SimpleThreadScope"/> |
|
</entry> |
|
</map> |
|
</property> |
|
</bean> |
|
|
|
<bean id="bar" class="x.y.Bar" scope="thread"> |
|
<property name="name" value="Rick"/> |
|
<aop:scoped-proxy/> |
|
</bean> |
|
|
|
<bean id="foo" class="x.y.Foo"> |
|
<property name="bar" ref="bar"/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
When you place `<aop:scoped-proxy/>` in a `FactoryBean` implementation, it is the factory |
|
bean itself that is scoped, not the object returned from `getObject()`. |
|
==== |
|
|
|
|
|
|
|
|
|
[[beans-factory-nature]] |
|
=== Customizing the nature of a bean |
|
|
|
|
|
|
|
[[beans-factory-lifecycle]] |
|
==== Lifecycle callbacks |
|
To interact with the container's management of the bean lifecycle, you can implement the |
|
Spring `InitializingBean` and `DisposableBean` interfaces. The container calls |
|
`afterPropertiesSet()` for the former and `destroy()` for the latter to allow the bean |
|
to perform certain actions upon initialization and destruction of your beans. |
|
|
|
[TIP] |
|
==== |
|
|
|
The JSR-250 `@PostConstruct` and `@PreDestroy` annotations are generally considered best |
|
practice for receiving lifecycle callbacks in a modern Spring application. Using these |
|
annotations means that your beans are not coupled to Spring specific interfaces. For |
|
details see <<beans-postconstruct-and-predestroy-annotations>>. |
|
|
|
If you don't want to use the JSR-250 annotations but you are still looking to remove |
|
coupling consider the use of init-method and destroy-method object definition metadata. |
|
==== |
|
|
|
Internally, the Spring Framework uses `BeanPostProcessor` implementations to process any |
|
callback interfaces it can find and call the appropriate methods. If you need custom |
|
features or other lifecycle behavior Spring does not offer out-of-the-box, you can |
|
implement a `BeanPostProcessor` yourself. For more information, see |
|
<<beans-factory-extension>>. |
|
|
|
In addition to the initialization and destruction callbacks, Spring-managed objects may |
|
also implement the `Lifecycle` interface so that those objects can participate in the |
|
startup and shutdown process as driven by the container's own lifecycle. |
|
|
|
The lifecycle callback interfaces are described in this section. |
|
|
|
|
|
[[beans-factory-lifecycle-initializingbean]] |
|
===== Initialization callbacks |
|
The `org.springframework.beans.factory.InitializingBean` interface allows a bean to |
|
perform initialization work after all necessary properties on the bean have been set by |
|
the container. The `InitializingBean` interface specifies a single method: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
void afterPropertiesSet() throws Exception; |
|
---- |
|
|
|
It is recommended that you do not use the `InitializingBean` interface because it |
|
unnecessarily couples the code to Spring. Alternatively, use |
|
the <<beans-postconstruct-and-predestroy-annotations, `@PostConstruct`>> annotation or |
|
specify a POJO initialization method. In the case of XML-based configuration metadata, |
|
you use the `init-method` attribute to specify the name of the method that has a void |
|
no-argument signature. With Java config you use the `initMethod` attribute of `@Bean`, |
|
see <<beans-java-lifecycle-callbacks>>. For example, the following: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/> |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class ExampleBean { |
|
|
|
public void init() { |
|
// do some initialization work |
|
} |
|
|
|
} |
|
---- |
|
|
|
...is exactly the same as... |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/> |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class AnotherExampleBean implements InitializingBean { |
|
|
|
public void afterPropertiesSet() { |
|
// do some initialization work |
|
} |
|
|
|
} |
|
---- |
|
|
|
but does not couple the code to Spring. |
|
|
|
|
|
[[beans-factory-lifecycle-disposablebean]] |
|
===== Destruction callbacks |
|
Implementing the `org.springframework.beans.factory.DisposableBean` interface allows a |
|
bean to get a callback when the container containing it is destroyed. The |
|
`DisposableBean` interface specifies a single method: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
void destroy() throws Exception; |
|
---- |
|
|
|
It is recommended that you do not use the `DisposableBean` callback interface because it |
|
unnecessarily couples the code to Spring. Alternatively, use |
|
the <<beans-postconstruct-and-predestroy-annotations, `@PreDestroy`>> annotation or |
|
specify a generic method that is supported by bean definitions. With XML-based |
|
configuration metadata, you use the `destroy-method` attribute on the `<bean/>`. With |
|
Java config you use the `destroyMethod` attribute of `@Bean`, see |
|
<<beans-java-lifecycle-callbacks>>. For example, the following definition: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/> |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class ExampleBean { |
|
|
|
public void cleanup() { |
|
// do some destruction work (like releasing pooled connections) |
|
} |
|
|
|
} |
|
---- |
|
|
|
is exactly the same as: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/> |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class AnotherExampleBean implements DisposableBean { |
|
|
|
public void destroy() { |
|
// do some destruction work (like releasing pooled connections) |
|
} |
|
|
|
} |
|
---- |
|
|
|
but does not couple the code to Spring. |
|
|
|
[TIP] |
|
==== |
|
The `destroy-method` attribute of a `<bean>` element can be assigned a special |
|
`(inferred)` value which instructs Spring to automatically detect a public `close` or |
|
`shutdown` method on the specific bean class. This special `(inferred)` value can also be |
|
set on the `default-destroy-method` attribute of a `<beans>` element to apply this |
|
behavior to an entire set of beans (see |
|
<<beans-factory-lifecycle-default-init-destroy-methods>>). Note that this is the |
|
default behavior with Java config. |
|
==== |
|
|
|
[[beans-factory-lifecycle-default-init-destroy-methods]] |
|
===== Default initialization and destroy methods |
|
When you write initialization and destroy method callbacks that do not use the |
|
Spring-specific `InitializingBean` and `DisposableBean` callback interfaces, you |
|
typically write methods with names such as `init()`, `initialize()`, `dispose()`, and so |
|
on. Ideally, the names of such lifecycle callback methods are standardized across a |
|
project so that all developers use the same method names and ensure consistency. |
|
|
|
You can configure the Spring container to `look` for named initialization and destroy |
|
callback method names on __every__ bean. This means that you, as an application |
|
developer, can write your application classes and use an initialization callback called |
|
`init()`, without having to configure an `init-method="init"` attribute with each bean |
|
definition. The Spring IoC container calls that method when the bean is created (and in |
|
accordance with the standard lifecycle callback contract described previously). This |
|
feature also enforces a consistent naming convention for initialization and destroy |
|
method callbacks. |
|
|
|
Suppose that your initialization callback methods are named `init()` and destroy |
|
callback methods are named `destroy()`. Your class will resemble the class in the |
|
following example. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class DefaultBlogService implements BlogService { |
|
|
|
private BlogDao blogDao; |
|
|
|
public void setBlogDao(BlogDao blogDao) { |
|
this.blogDao = blogDao; |
|
} |
|
|
|
// this is (unsurprisingly) the initialization callback method |
|
public void init() { |
|
if (this.blogDao == null) { |
|
throw new IllegalStateException("The [blogDao] property must be set."); |
|
} |
|
} |
|
|
|
} |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans default-init-method="init"> |
|
|
|
<bean id="blogService" class="com.foo.DefaultBlogService"> |
|
<property name="blogDao" ref="blogDao" /> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
The presence of the `default-init-method` attribute on the top-level `<beans/>` element |
|
attribute causes the Spring IoC container to recognize a method called `init` on beans |
|
as the initialization method callback. When a bean is created and assembled, if the bean |
|
class has such a method, it is invoked at the appropriate time. |
|
|
|
You configure destroy method callbacks similarly (in XML, that is) by using the |
|
`default-destroy-method` attribute on the top-level `<beans/>` element. |
|
|
|
Where existing bean classes already have callback methods that are named at variance |
|
with the convention, you can override the default by specifying (in XML, that is) the |
|
method name using the `init-method` and `destroy-method` attributes of the <bean/> |
|
itself. |
|
|
|
The Spring container guarantees that a configured initialization callback is called |
|
immediately after a bean is supplied with all dependencies. Thus the initialization |
|
callback is called on the raw bean reference, which means that AOP interceptors and so |
|
forth are not yet applied to the bean. A target bean is fully created __first__, |
|
__then__ an AOP proxy (for example) with its interceptor chain is applied. If the target |
|
bean and the proxy are defined separately, your code can even interact with the raw |
|
target bean, bypassing the proxy. Hence, it would be inconsistent to apply the |
|
interceptors to the init method, because doing so would couple the lifecycle of the |
|
target bean with its proxy/interceptors and leave strange semantics when your code |
|
interacts directly to the raw target bean. |
|
|
|
|
|
[[beans-factory-lifecycle-combined-effects]] |
|
===== Combining lifecycle mechanisms |
|
As of Spring 2.5, you have three options for controlling bean lifecycle behavior: the |
|
<<beans-factory-lifecycle-initializingbean, `InitializingBean`>> and |
|
<<beans-factory-lifecycle-disposablebean, `DisposableBean`>> callback interfaces; custom |
|
`init()` and `destroy()` methods; and the |
|
<<beans-postconstruct-and-predestroy-annotations, `@PostConstruct` and `@PreDestroy` |
|
annotations>>. You can combine these mechanisms to control a given bean. |
|
|
|
[NOTE] |
|
==== |
|
If multiple lifecycle mechanisms are configured for a bean, and each mechanism is |
|
configured with a different method name, then each configured method is executed in the |
|
order listed below. However, if the same method name is configured - for example, |
|
`init()` for an initialization method - for more than one of these lifecycle mechanisms, |
|
that method is executed once, as explained in the preceding section. |
|
==== |
|
|
|
Multiple lifecycle mechanisms configured for the same bean, with different |
|
initialization methods, are called as follows: |
|
|
|
* Methods annotated with `@PostConstruct` |
|
* `afterPropertiesSet()` as defined by the `InitializingBean` callback interface |
|
* A custom configured `init()` method |
|
|
|
Destroy methods are called in the same order: |
|
|
|
* Methods annotated with `@PreDestroy` |
|
* `destroy()` as defined by the `DisposableBean` callback interface |
|
* A custom configured `destroy()` method |
|
|
|
|
|
[[beans-factory-lifecycle-processor]] |
|
===== Startup and shutdown callbacks |
|
The `Lifecycle` interface defines the essential methods for any object that has its own |
|
lifecycle requirements (e.g. starts and stops some background process): |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface Lifecycle { |
|
|
|
void start(); |
|
|
|
void stop(); |
|
|
|
boolean isRunning(); |
|
|
|
} |
|
---- |
|
|
|
Any Spring-managed object may implement that interface. Then, when the |
|
`ApplicationContext` itself receives start and stop signals, e.g. for a stop/restart |
|
scenario at runtime, it will cascade those calls to all `Lifecycle` implementations |
|
defined within that context. It does this by delegating to a `LifecycleProcessor`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface LifecycleProcessor extends Lifecycle { |
|
|
|
void onRefresh(); |
|
|
|
void onClose(); |
|
|
|
} |
|
---- |
|
|
|
Notice that the `LifecycleProcessor` is itself an extension of the `Lifecycle` |
|
interface. It also adds two other methods for reacting to the context being refreshed |
|
and closed. |
|
|
|
[TIP] |
|
==== |
|
Note that the regular `org.springframework.context.Lifecycle` interface is just a plain |
|
contract for explicit start/stop notifications and does NOT imply auto-startup at context |
|
refresh time. Consider implementing `org.springframework.context.SmartLifecycle` instead |
|
for fine-grained control over auto-startup of a specific bean (including startup phases). |
|
==== |
|
|
|
The order of startup and shutdown invocations can be important. If a "depends-on" |
|
relationship exists between any two objects, the dependent side will start __after__ its |
|
dependency, and it will stop __before__ its dependency. However, at times the direct |
|
dependencies are unknown. You may only know that objects of a certain type should start |
|
prior to objects of another type. In those cases, the `SmartLifecycle` interface defines |
|
another option, namely the `getPhase()` method as defined on its super-interface, |
|
`Phased`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface Phased { |
|
|
|
int getPhase(); |
|
|
|
} |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface SmartLifecycle extends Lifecycle, Phased { |
|
|
|
boolean isAutoStartup(); |
|
|
|
void stop(Runnable callback); |
|
|
|
} |
|
---- |
|
|
|
When starting, the objects with the lowest phase start first, and when stopping, the |
|
reverse order is followed. Therefore, an object that implements `SmartLifecycle` and |
|
whose `getPhase()` method returns `Integer.MIN_VALUE` would be among the first to start |
|
and the last to stop. At the other end of the spectrum, a phase value of |
|
`Integer.MAX_VALUE` would indicate that the object should be started last and stopped |
|
first (likely because it depends on other processes to be running). When considering the |
|
phase value, it's also important to know that the default phase for any "normal" |
|
`Lifecycle` object that does not implement `SmartLifecycle` would be 0. Therefore, any |
|
negative phase value would indicate that an object should start before those standard |
|
components (and stop after them), and vice versa for any positive phase value. |
|
|
|
As you can see the stop method defined by `SmartLifecycle` accepts a callback. Any |
|
implementation __must__ invoke that callback's `run()` method after that implementation's |
|
shutdown process is complete. That enables asynchronous shutdown where necessary since |
|
the default implementation of the `LifecycleProcessor` interface, |
|
`DefaultLifecycleProcessor`, will wait up to its timeout value for the group of objects |
|
within each phase to invoke that callback. The default per-phase timeout is 30 seconds. |
|
You can override the default lifecycle processor instance by defining a bean named |
|
"lifecycleProcessor" within the context. If you only want to modify the timeout, then |
|
defining the following would be sufficient: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor"> |
|
<!-- timeout value in milliseconds --> |
|
<property name="timeoutPerShutdownPhase" value="10000"/> |
|
</bean> |
|
---- |
|
|
|
As mentioned, the `LifecycleProcessor` interface defines callback methods for the |
|
refreshing and closing of the context as well. The latter will simply drive the shutdown |
|
process as if `stop()` had been called explicitly, but it will happen when the context is |
|
closing. The 'refresh' callback on the other hand enables another feature of |
|
`SmartLifecycle` beans. When the context is refreshed (after all objects have been |
|
instantiated and initialized), that callback will be invoked, and at that point the |
|
default lifecycle processor will check the boolean value returned by each |
|
`SmartLifecycle` object's `isAutoStartup()` method. If "true", then that object will be |
|
started at that point rather than waiting for an explicit invocation of the context's or |
|
its own `start()` method (unlike the context refresh, the context start does not happen |
|
automatically for a standard context implementation). The "phase" value as well as any |
|
"depends-on" relationships will determine the startup order in the same way as described |
|
above. |
|
|
|
|
|
[[beans-factory-shutdown]] |
|
===== Shutting down the Spring IoC container gracefully in non-web applications |
|
[NOTE] |
|
==== |
|
This section applies only to non-web applications. Spring's web-based |
|
`ApplicationContext` implementations already have code in place to shut down the Spring |
|
IoC container gracefully when the relevant web application is shut down. |
|
==== |
|
|
|
If you are using Spring's IoC container in a non-web application environment; for |
|
example, in a rich client desktop environment; you register a shutdown hook with the |
|
JVM. Doing so ensures a graceful shutdown and calls the relevant destroy methods on your |
|
singleton beans so that all resources are released. Of course, you must still configure |
|
and implement these destroy callbacks correctly. |
|
|
|
To register a shutdown hook, you call the `registerShutdownHook()` method that is |
|
declared on the `AbstractApplicationContext` class: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import org.springframework.context.support.AbstractApplicationContext; |
|
import org.springframework.context.support.ClassPathXmlApplicationContext; |
|
|
|
public final class Boot { |
|
|
|
public static void main(final String[] args) throws Exception { |
|
|
|
AbstractApplicationContext ctx = new ClassPathXmlApplicationContext( |
|
new String []{"beans.xml"}); |
|
|
|
// add a shutdown hook for the above context... |
|
ctx.registerShutdownHook(); |
|
|
|
// app runs here... |
|
|
|
// main method exits, hook is called prior to the app shutting down... |
|
|
|
} |
|
} |
|
---- |
|
|
|
|
|
|
|
[[beans-factory-aware]] |
|
==== ApplicationContextAware and BeanNameAware |
|
|
|
When an `ApplicationContext` creates an object instance that implements the |
|
`org.springframework.context.ApplicationContextAware` interface, the instance is provided |
|
with a reference to that `ApplicationContext`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface ApplicationContextAware { |
|
|
|
void setApplicationContext(ApplicationContext applicationContext) throws BeansException; |
|
|
|
} |
|
---- |
|
|
|
Thus beans can manipulate programmatically the `ApplicationContext` that created them, |
|
through the `ApplicationContext` interface, or by casting the reference to a known |
|
subclass of this interface, such as `ConfigurableApplicationContext`, which exposes |
|
additional functionality. One use would be the programmatic retrieval of other beans. |
|
Sometimes this capability is useful; however, in general you should avoid it, because it |
|
couples the code to Spring and does not follow the Inversion of Control style, where |
|
collaborators are provided to beans as properties. Other methods of the |
|
`ApplicationContext` provide access to file resources, publishing application events, and |
|
accessing a `MessageSource`. These additional features are described in |
|
<<context-introduction>> |
|
|
|
As of Spring 2.5, autowiring is another alternative to obtain reference to the |
|
`ApplicationContext`. The "traditional" `constructor` and `byType` autowiring modes (as |
|
described in <<beans-factory-autowire>>) can provide a dependency of type |
|
`ApplicationContext` for a constructor argument or setter method parameter, |
|
respectively. For more flexibility, including the ability to autowire fields and |
|
multiple parameter methods, use the new annotation-based autowiring features. If you do, |
|
the `ApplicationContext` is autowired into a field, constructor argument, or method |
|
parameter that is expecting the `ApplicationContext` type if the field, constructor, or |
|
method in question carries the `@Autowired` annotation. For more information, see |
|
<<beans-autowired-annotation>>. |
|
|
|
When an `ApplicationContext` creates a class that implements the |
|
`org.springframework.beans.factory.BeanNameAware` interface, the class is provided with |
|
a reference to the name defined in its associated object definition. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface BeanNameAware { |
|
|
|
void setBeanName(string name) throws BeansException; |
|
|
|
} |
|
---- |
|
|
|
The callback is invoked after population of normal bean properties but before an |
|
initialization callback such as `InitializingBean` __afterPropertiesSet__ or a custom |
|
init-method. |
|
|
|
|
|
|
|
[[aware-list]] |
|
==== Other Aware interfaces |
|
|
|
Besides `ApplicationContextAware` and `BeanNameAware` discussed above, Spring offers a |
|
range of `Aware` interfaces that allow beans to indicate to the container that they |
|
require a certain __infrastructure__ dependency. The most important `Aware` interfaces |
|
are summarized below - as a general rule, the name is a good indication of the |
|
dependency type: |
|
|
|
[[beans-factory-nature-aware-list]] |
|
.Aware interfaces |
|
|=== |
|
| Name| Injected Dependency| Explained in... |
|
|
|
| `ApplicationContextAware` |
|
| Declaring `ApplicationContext` |
|
| <<beans-factory-aware>> |
|
|
|
| `ApplicationEventPublisherAware` |
|
| Event publisher of the enclosing `ApplicationContext` |
|
| <<context-introduction>> |
|
|
|
| `BeanClassLoaderAware` |
|
| Class loader used to load the bean classes. |
|
| <<beans-factory-class>> |
|
|
|
| `BeanFactoryAware` |
|
| Declaring `BeanFactory` |
|
| <<beans-factory-aware>> |
|
|
|
| `BeanNameAware` |
|
| Name of the declaring bean |
|
| <<beans-factory-aware>> |
|
|
|
| `BootstrapContextAware` |
|
| Resource adapter `BootstrapContext` the container runs in. Typically available only in |
|
JCA aware ++ApplicationContext++s |
|
| <<cci>> |
|
|
|
| `LoadTimeWeaverAware` |
|
| Defined __weaver__ for processing class definition at load time |
|
| <<aop-aj-ltw>> |
|
|
|
| `MessageSourceAware` |
|
| Configured strategy for resolving messages (with support for parametrization and |
|
internationalization) |
|
| <<context-introduction>> |
|
|
|
| `NotificationPublisherAware` |
|
| Spring JMX notification publisher |
|
| <<jmx-notifications>> |
|
|
|
| `PortletConfigAware` |
|
| Current `PortletConfig` the container runs in. Valid only in a web-aware Spring |
|
`ApplicationContext` |
|
| <<portlet>> |
|
|
|
| `PortletContextAware` |
|
| Current `PortletContext` the container runs in. Valid only in a web-aware Spring |
|
`ApplicationContext` |
|
| <<portlet>> |
|
|
|
| `ResourceLoaderAware` |
|
| Configured loader for low-level access to resources |
|
| <<resources>> |
|
|
|
| `ServletConfigAware` |
|
| Current `ServletConfig` the container runs in. Valid only in a web-aware Spring |
|
`ApplicationContext` |
|
| <<mvc>> |
|
|
|
| `ServletContextAware` |
|
| Current `ServletContext` the container runs in. Valid only in a web-aware Spring |
|
`ApplicationContext` |
|
| <<mvc>> |
|
|=== |
|
|
|
Note again that usage of these interfaces ties your code to the Spring API and does not |
|
follow the Inversion of Control style. As such, they are recommended for infrastructure |
|
beans that require programmatic access to the container. |
|
|
|
|
|
|
|
|
|
[[beans-child-bean-definitions]] |
|
=== Bean definition inheritance |
|
A bean definition can contain a lot of configuration information, including constructor |
|
arguments, property values, and container-specific information such as initialization |
|
method, static factory method name, and so on. A child bean definition inherits |
|
configuration data from a parent definition. The child definition can override some |
|
values, or add others, as needed. Using parent and child bean definitions can save a lot |
|
of typing. Effectively, this is a form of templating. |
|
|
|
If you work with an `ApplicationContext` interface programmatically, child bean |
|
definitions are represented by the `ChildBeanDefinition` class. Most users do not work |
|
with them on this level, instead configuring bean definitions declaratively in something |
|
like the `ClassPathXmlApplicationContext`. When you use XML-based configuration |
|
metadata, you indicate a child bean definition by using the `parent` attribute, |
|
specifying the parent bean as the value of this attribute. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="inheritedTestBean" abstract="true" |
|
class="org.springframework.beans.TestBean"> |
|
<property name="name" value="parent"/> |
|
<property name="age" value="1"/> |
|
</bean> |
|
|
|
<bean id="inheritsWithDifferentClass" |
|
class="org.springframework.beans.DerivedTestBean" |
|
**parent="inheritedTestBean"** init-method="initialize"> |
|
<property name="name" value="override"/> |
|
<!-- the age property value of 1 will be inherited from parent --> |
|
</bean> |
|
---- |
|
|
|
A child bean definition uses the bean class from the parent definition if none is |
|
specified, but can also override it. In the latter case, the child bean class must be |
|
compatible with the parent, that is, it must accept the parent's property values. |
|
|
|
A child bean definition inherits scope, constructor argument values, property values, and |
|
method overrides from the parent, with the option to add new values. Any scope, initialization |
|
method, destroy method, and/or `static` factory method settings that you specify will |
|
override the corresponding parent settings. |
|
|
|
The remaining settings are __always__ taken from the child definition: __depends on__, |
|
__autowire mode__, __dependency check__, __singleton__, __lazy init__. |
|
|
|
The preceding example explicitly marks the parent bean definition as abstract by using |
|
the `abstract` attribute. If the parent definition does not specify a class, explicitly |
|
marking the parent bean definition as `abstract` is required, as follows: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="inheritedTestBeanWithoutClass" abstract="true"> |
|
<property name="name" value="parent"/> |
|
<property name="age" value="1"/> |
|
</bean> |
|
|
|
<bean id="inheritsWithClass" class="org.springframework.beans.DerivedTestBean" |
|
parent="inheritedTestBeanWithoutClass" init-method="initialize"> |
|
<property name="name" value="override"/> |
|
<!-- age will inherit the value of 1 from the parent bean definition--> |
|
</bean> |
|
---- |
|
|
|
The parent bean cannot be instantiated on its own because it is incomplete, and it is |
|
also explicitly marked as `abstract`. When a definition is `abstract` like this, it is |
|
usable only as a pure template bean definition that serves as a parent definition for |
|
child definitions. Trying to use such an `abstract` parent bean on its own, by referring |
|
to it as a ref property of another bean or doing an explicit `getBean()` call with the |
|
parent bean id, returns an error. Similarly, the container's internal |
|
`preInstantiateSingletons()` method ignores bean definitions that are defined as |
|
abstract. |
|
|
|
[NOTE] |
|
==== |
|
`ApplicationContext` pre-instantiates all singletons by default. Therefore, it is |
|
important (at least for singleton beans) that if you have a (parent) bean definition |
|
which you intend to use only as a template, and this definition specifies a class, you |
|
must make sure to set the __abstract__ attribute to __true__, otherwise the application |
|
context will actually (attempt to) pre-instantiate the `abstract` bean. |
|
==== |
|
|
|
|
|
|
|
|
|
[[beans-factory-extension]] |
|
=== Container Extension Points |
|
Typically, an application developer does not need to subclass `ApplicationContext` |
|
implementation classes. Instead, the Spring IoC container can be extended by plugging in |
|
implementations of special integration interfaces. The next few sections describe these |
|
integration interfaces. |
|
|
|
|
|
|
|
[[beans-factory-extension-bpp]] |
|
==== Customizing beans using a BeanPostProcessor |
|
|
|
The `BeanPostProcessor` interface defines __callback methods__ that you can implement to |
|
provide your own (or override the container's default) instantiation logic, |
|
dependency-resolution logic, and so forth. If you want to implement some custom logic |
|
after the Spring container finishes instantiating, configuring, and initializing a bean, |
|
you can plug in one or more `BeanPostProcessor` implementations. |
|
|
|
You can configure multiple `BeanPostProcessor` instances, and you can control the order |
|
in which these ++BeanPostProcessor++s execute by setting the `order` property. You can |
|
set this property only if the `BeanPostProcessor` implements the `Ordered` interface; if |
|
you write your own `BeanPostProcessor` you should consider implementing the `Ordered` |
|
interface too. For further details, consult the javadocs of the `BeanPostProcessor` and |
|
`Ordered` interfaces. See also the note below on |
|
<<beans-factory-programmatically-registering-beanpostprocessors, programmatic |
|
registration of `BeanPostProcessors`>> |
|
|
|
[NOTE] |
|
==== |
|
++BeanPostProcessor++s operate on bean (or object) __instances__; that is to say, the |
|
Spring IoC container instantiates a bean instance and __then__ ++BeanPostProcessor++s do |
|
their work. |
|
|
|
++BeanPostProcessor++s are scoped __per-container__. This is only relevant if you are |
|
using container hierarchies. If you define a `BeanPostProcessor` in one container, it |
|
will __only__ post-process the beans in that container. In other words, beans that are |
|
defined in one container are not post-processed by a `BeanPostProcessor` defined in |
|
another container, even if both containers are part of the same hierarchy. |
|
|
|
To change the actual bean definition (i.e., the __blueprint__ that defines the bean), |
|
you instead need to use a `BeanFactoryPostProcessor` as described in |
|
<<beans-factory-extension-factory-postprocessors>>. |
|
==== |
|
|
|
The `org.springframework.beans.factory.config.BeanPostProcessor` interface consists of |
|
exactly two callback methods. When such a class is registered as a post-processor with |
|
the container, for each bean instance that is created by the container, the |
|
post-processor gets a callback from the container both __before__ container |
|
initialization methods (such as InitializingBean's __afterPropertiesSet()__ and any |
|
declared init method) are called as well as __after__ any bean initialization callbacks. |
|
The post-processor can take any action with the bean instance, including ignoring the |
|
callback completely. A bean post-processor typically checks for callback interfaces or |
|
may wrap a bean with a proxy. Some Spring AOP infrastructure classes are implemented as |
|
bean post-processors in order to provide proxy-wrapping logic. |
|
|
|
An `ApplicationContext` __automatically detects__ any beans that are defined in the |
|
configuration metadata which implement the `BeanPostProcessor` interface. The |
|
`ApplicationContext` registers these beans as post-processors so that they can be called |
|
later upon bean creation. Bean post-processors can be deployed in the container just |
|
like any other beans. |
|
|
|
Note that when declaring a ++BeanPostProcessor++ using an `@Bean` factory method on a |
|
configuration class, the return type of the factory method should be the implementation |
|
class itself or at least the `org.springframework.beans.factory.config.BeanPostProcessor` |
|
interface, clearly indicating the post-processor nature of that bean. Otherwise, the |
|
`ApplicationContext` won't be able to autodetect it by type before fully creating it. |
|
Since a ++BeanPostProcessor++ needs to be instantiated early in order to apply to the |
|
initialization of other beans in the context, this early type detection is critical. |
|
|
|
[NOTE] |
|
==== |
|
|
|
*Programmatically registering BeanPostProcessors* |
|
|
|
While the recommended approach for `BeanPostProcessor` registration is through |
|
`ApplicationContext` auto-detection (as described above), it is also possible to |
|
register them __programmatically__ against a `ConfigurableBeanFactory` using the |
|
`addBeanPostProcessor` method. This can be useful when needing to evaluate conditional |
|
logic before registration, or even for copying bean post processors across contexts in a |
|
hierarchy. Note however that `BeanPostProcessors` added programmatically __do not |
|
respect the `Ordered` interface__. Here it is the __order of registration__ that |
|
dictates the order of execution. Note also that `BeanPostProcessors` registered |
|
programmatically are always processed before those registered through auto-detection, |
|
regardless of any explicit ordering. |
|
==== |
|
|
|
[NOTE] |
|
==== |
|
|
|
*BeanPostProcessors and AOP auto-proxying* |
|
|
|
Classes that implement the `BeanPostProcessor` interface are __special__ and are treated |
|
differently by the container. All `BeanPostProcessors` __and beans that they reference |
|
directly__ are instantiated on startup, as part of the special startup phase of the |
|
`ApplicationContext`. Next, all `BeanPostProcessors` are registered in a sorted fashion |
|
and applied to all further beans in the container. Because AOP auto-proxying is |
|
implemented as a `BeanPostProcessor` itself, neither `BeanPostProcessors` nor the beans |
|
they reference directly are eligible for auto-proxying, and thus do not have aspects |
|
woven into them. |
|
|
|
For any such bean, you should see an informational log message: "__Bean foo is not |
|
eligible for getting processed by all BeanPostProcessor interfaces (for example: not |
|
eligible for auto-proxying)__". |
|
|
|
Note that if you have beans wired into your `BeanPostProcessor` using autowiring or |
|
`@Resource` (which may fall back to autowiring), Spring might access unexpected beans |
|
when searching for type-matching dependency candidates, and therefore make them |
|
ineligible for auto-proxying or other kinds of bean post-processing. For example, if you |
|
have a dependency annotated with `@Resource` where the field/setter name does not |
|
directly correspond to the declared name of a bean and no name attribute is used, then |
|
Spring will access other beans for matching them by type. |
|
==== |
|
|
|
The following examples show how to write, register, and use `BeanPostProcessors` in an |
|
`ApplicationContext`. |
|
|
|
|
|
[[beans-factory-extension-bpp-examples-hw]] |
|
===== Example: Hello World, BeanPostProcessor-style |
|
|
|
This first example illustrates basic usage. The example shows a custom |
|
`BeanPostProcessor` implementation that invokes the `toString()` method of each bean as |
|
it is created by the container and prints the resulting string to the system console. |
|
|
|
Find below the custom `BeanPostProcessor` implementation class definition: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package scripting; |
|
|
|
import org.springframework.beans.factory.config.BeanPostProcessor; |
|
import org.springframework.beans.BeansException; |
|
|
|
public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor { |
|
|
|
// simply return the instantiated bean as-is |
|
public Object postProcessBeforeInitialization(Object bean, |
|
String beanName) throws BeansException { |
|
return bean; // we could potentially return any object reference here... |
|
} |
|
|
|
public Object postProcessAfterInitialization(Object bean, |
|
String beanName) throws BeansException { |
|
System.out.println("Bean ''" + beanName + "'' created : " + bean.toString()); |
|
return bean; |
|
} |
|
|
|
} |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:lang="http://www.springframework.org/schema/lang" |
|
xsi:schemaLocation="http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/lang |
|
http://www.springframework.org/schema/lang/spring-lang.xsd"> |
|
|
|
<lang:groovy id="messenger" |
|
script-source="classpath:org/springframework/scripting/groovy/Messenger.groovy"> |
|
<lang:property name="message" value="Fiona Apple Is Just So Dreamy."/> |
|
</lang:groovy> |
|
|
|
<!-- |
|
when the above bean (messenger) is instantiated, this custom |
|
BeanPostProcessor implementation will output the fact to the system console |
|
--> |
|
<bean class="scripting.InstantiationTracingBeanPostProcessor"/> |
|
|
|
</beans> |
|
---- |
|
|
|
Notice how the `InstantiationTracingBeanPostProcessor` is simply defined. It does not |
|
even have a name, and because it is a bean it can be dependency-injected just like any |
|
other bean. (The preceding configuration also defines a bean that is backed by a Groovy |
|
script. The Spring dynamic language support is detailed in the chapter entitled |
|
<<dynamic-language>>.) |
|
|
|
The following simple Java application executes the preceding code and configuration: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import org.springframework.context.ApplicationContext; |
|
import org.springframework.context.support.ClassPathXmlApplicationContext; |
|
import org.springframework.scripting.Messenger; |
|
|
|
public final class Boot { |
|
|
|
public static void main(final String[] args) throws Exception { |
|
ApplicationContext ctx = new ClassPathXmlApplicationContext("scripting/beans.xml"); |
|
Messenger messenger = (Messenger) ctx.getBean("messenger"); |
|
System.out.println(messenger); |
|
} |
|
|
|
} |
|
---- |
|
|
|
The output of the preceding application resembles the following: |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Bean 'messenger' created : org.springframework.scripting.groovy.GroovyMessenger@272961 |
|
org.springframework.scripting.groovy.GroovyMessenger@272961 |
|
---- |
|
|
|
|
|
[[beans-factory-extension-bpp-examples-rabpp]] |
|
===== Example: The RequiredAnnotationBeanPostProcessor |
|
|
|
Using callback interfaces or annotations in conjunction with a custom |
|
`BeanPostProcessor` implementation is a common means of extending the Spring IoC |
|
container. An example is Spring's `RequiredAnnotationBeanPostProcessor` - a |
|
`BeanPostProcessor` implementation that ships with the Spring distribution which ensures |
|
that JavaBean properties on beans that are marked with an (arbitrary) annotation are |
|
actually (configured to be) dependency-injected with a value. |
|
|
|
|
|
|
|
[[beans-factory-extension-factory-postprocessors]] |
|
==== Customizing configuration metadata with a BeanFactoryPostProcessor |
|
|
|
The next extension point that we will look at is the |
|
`org.springframework.beans.factory.config.BeanFactoryPostProcessor`. The semantics of |
|
this interface are similar to those of the `BeanPostProcessor`, with one major |
|
difference: `BeanFactoryPostProcessor` operates on the __bean configuration metadata__; |
|
that is, the Spring IoC container allows a `BeanFactoryPostProcessor` to read the |
|
configuration metadata and potentially change it __before__ the container instantiates |
|
any beans other than `BeanFactoryPostProcessors`. |
|
|
|
You can configure multiple `BeanFactoryPostProcessors`, and you can control the order in |
|
which these `BeanFactoryPostProcessors` execute by setting the `order` property. |
|
However, you can only set this property if the `BeanFactoryPostProcessor` implements the |
|
`Ordered` interface. If you write your own `BeanFactoryPostProcessor`, you should |
|
consider implementing the `Ordered` interface too. Consult the javadocs of the |
|
`BeanFactoryPostProcessor` and `Ordered` interfaces for more details. |
|
|
|
[NOTE] |
|
==== |
|
If you want to change the actual bean __instances__ (i.e., the objects that are created |
|
from the configuration metadata), then you instead need to use a `BeanPostProcessor` |
|
(described above in <<beans-factory-extension-bpp>>). While it is technically possible |
|
to work with bean instances within a `BeanFactoryPostProcessor` (e.g., using |
|
`BeanFactory.getBean()`), doing so causes premature bean instantiation, violating the |
|
standard container lifecycle. This may cause negative side effects such as bypassing |
|
bean post processing. |
|
|
|
Also, `BeanFactoryPostProcessors` are scoped __per-container__. This is only relevant if |
|
you are using container hierarchies. If you define a `BeanFactoryPostProcessor` in one |
|
container, it will __only__ be applied to the bean definitions in that container. Bean |
|
definitions in one container will not be post-processed by `BeanFactoryPostProcessors` |
|
in another container, even if both containers are part of the same hierarchy. |
|
==== |
|
|
|
A bean factory post-processor is executed automatically when it is declared inside an |
|
`ApplicationContext`, in order to apply changes to the configuration metadata that |
|
define the container. Spring includes a number of predefined bean factory |
|
post-processors, such as `PropertyOverrideConfigurer` and |
|
`PropertyPlaceholderConfigurer`. A custom `BeanFactoryPostProcessor` can also be used, |
|
for example, to register custom property editors. |
|
|
|
[[null]] |
|
|
|
An `ApplicationContext` automatically detects any beans that are deployed into it that |
|
implement the `BeanFactoryPostProcessor` interface. It uses these beans as bean factory |
|
post-processors, at the appropriate time. You can deploy these post-processor beans as |
|
you would any other bean. |
|
|
|
[NOTE] |
|
==== |
|
As with ++BeanPostProcessor++s , you typically do not want to configure |
|
++BeanFactoryPostProcessor++s for lazy initialization. If no other bean references a |
|
`Bean(Factory)PostProcessor`, that post-processor will not get instantiated at all. |
|
Thus, marking it for lazy initialization will be ignored, and the |
|
`Bean(Factory)PostProcessor` will be instantiated eagerly even if you set the |
|
`default-lazy-init` attribute to `true` on the declaration of your `<beans />` element. |
|
==== |
|
|
|
|
|
[[beans-factory-placeholderconfigurer]] |
|
===== Example: the Class name substitution PropertyPlaceholderConfigurer |
|
|
|
You use the `PropertyPlaceholderConfigurer` to externalize property values from a bean |
|
definition in a separate file using the standard Java `Properties` format. Doing so |
|
enables the person deploying an application to customize environment-specific properties |
|
such as database URLs and passwords, without the complexity or risk of modifying the |
|
main XML definition file or files for the container. |
|
|
|
Consider the following XML-based configuration metadata fragment, where a `DataSource` |
|
with placeholder values is defined. The example shows properties configured from an |
|
external `Properties` file. At runtime, a `PropertyPlaceholderConfigurer` is applied to |
|
the metadata that will replace some properties of the DataSource. The values to replace |
|
are specified as __placeholders__ of the form `${property-name}` which follows the Ant / |
|
log4j / JSP EL style. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> |
|
<property name="locations" value="classpath:com/foo/jdbc.properties"/> |
|
</bean> |
|
|
|
<bean id="dataSource" destroy-method="close" |
|
class="org.apache.commons.dbcp.BasicDataSource"> |
|
<property name="driverClassName" value="${jdbc.driverClassName}"/> |
|
<property name="url" value="${jdbc.url}"/> |
|
<property name="username" value="${jdbc.username}"/> |
|
<property name="password" value="${jdbc.password}"/> |
|
</bean> |
|
---- |
|
|
|
The actual values come from another file in the standard Java `Properties` format: |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
jdbc.driverClassName=org.hsqldb.jdbcDriver |
|
jdbc.url=jdbc:hsqldb:hsql://production:9002 |
|
jdbc.username=sa |
|
jdbc.password=root |
|
---- |
|
|
|
Therefore, the string `${jdbc.username}` is replaced at runtime with the value 'sa', and |
|
the same applies for other placeholder values that match keys in the properties file. |
|
The `PropertyPlaceholderConfigurer` checks for placeholders in most properties and |
|
attributes of a bean definition. Furthermore, the placeholder prefix and suffix can be |
|
customized. |
|
|
|
With the `context` namespace introduced in Spring 2.5, it is possible to configure |
|
property placeholders with a dedicated configuration element. One or more locations can |
|
be provided as a comma-separated list in the `location` attribute. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<context:property-placeholder location="classpath:com/foo/jdbc.properties"/> |
|
---- |
|
|
|
The `PropertyPlaceholderConfigurer` not only looks for properties in the `Properties` |
|
file you specify. By default it also checks against the Java `System` properties if it |
|
cannot find a property in the specified properties files. You can customize this |
|
behavior by setting the `systemPropertiesMode` property of the configurer with one of |
|
the following three supported integer values: |
|
|
|
* __never__ (0): Never check system properties |
|
* __fallback__ (1): Check system properties if not resolvable in the specified |
|
properties files. This is the default. |
|
* __override__ (2): Check system properties first, before trying the specified |
|
properties files. This allows system properties to override any other property source. |
|
|
|
Consult the `PropertyPlaceholderConfigurer` javadocs for more information. |
|
|
|
[TIP] |
|
==== |
|
|
|
You can use the `PropertyPlaceholderConfigurer` to substitute class names, which is |
|
sometimes useful when you have to pick a particular implementation class at runtime. For |
|
example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> |
|
<property name="locations"> |
|
<value>classpath:com/foo/strategy.properties</value> |
|
</property> |
|
<property name="properties"> |
|
<value>custom.strategy.class=com.foo.DefaultStrategy</value> |
|
</property> |
|
</bean> |
|
|
|
<bean id="serviceStrategy" class="${custom.strategy.class}"/> |
|
---- |
|
|
|
If the class cannot be resolved at runtime to a valid class, resolution of the bean |
|
fails when it is about to be created, which is during the `preInstantiateSingletons()` |
|
phase of an `ApplicationContext` for a non-lazy-init bean. |
|
==== |
|
|
|
|
|
[[beans-factory-overrideconfigurer]] |
|
===== Example: the PropertyOverrideConfigurer |
|
|
|
The `PropertyOverrideConfigurer`, another bean factory post-processor, resembles the |
|
`PropertyPlaceholderConfigurer`, but unlike the latter, the original definitions can |
|
have default values or no values at all for bean properties. If an overriding |
|
`Properties` file does not have an entry for a certain bean property, the default |
|
context definition is used. |
|
|
|
Note that the bean definition is __not__ aware of being overridden, so it is not |
|
immediately obvious from the XML definition file that the override configurer is being |
|
used. In case of multiple `PropertyOverrideConfigurer` instances that define different |
|
values for the same bean property, the last one wins, due to the overriding mechanism. |
|
|
|
Properties file configuration lines take this format: |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
beanName.property=value |
|
---- |
|
|
|
For example: |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
dataSource.driverClassName=com.mysql.jdbc.Driver |
|
dataSource.url=jdbc:mysql:mydb |
|
---- |
|
|
|
This example file can be used with a container definition that contains a bean called |
|
__dataSource__, which has __driver__ and __url__ properties. |
|
|
|
Compound property names are also supported, as long as every component of the path |
|
except the final property being overridden is already non-null (presumably initialized |
|
by the constructors). In this example... |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
foo.fred.bob.sammy=123 |
|
---- |
|
|
|
... the `sammy` property of the `bob` property of the `fred` property of the `foo` bean |
|
is set to the scalar value `123`. |
|
|
|
[NOTE] |
|
==== |
|
Specified override values are always __literal__ values; they are not translated into |
|
bean references. This convention also applies when the original value in the XML bean |
|
definition specifies a bean reference. |
|
==== |
|
|
|
With the `context` namespace introduced in Spring 2.5, it is possible to configure |
|
property overriding with a dedicated configuration element: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<context:property-override location="classpath:override.properties"/> |
|
---- |
|
|
|
|
|
|
|
[[beans-factory-extension-factorybean]] |
|
==== Customizing instantiation logic with a FactoryBean |
|
|
|
Implement the `org.springframework.beans.factory.FactoryBean` interface for objects that |
|
__are themselves factories__. |
|
|
|
The `FactoryBean` interface is a point of pluggability into the Spring IoC container's |
|
instantiation logic. If you have complex initialization code that is better expressed in |
|
Java as opposed to a (potentially) verbose amount of XML, you can create your own |
|
`FactoryBean`, write the complex initialization inside that class, and then plug your |
|
custom `FactoryBean` into the container. |
|
|
|
The `FactoryBean` interface provides three methods: |
|
|
|
* `Object getObject()`: returns an instance of the object this factory creates. The |
|
instance can possibly be shared, depending on whether this factory returns singletons |
|
or prototypes. |
|
* `boolean isSingleton()`: returns `true` if this `FactoryBean` returns singletons, |
|
`false` otherwise. |
|
* `Class getObjectType()`: returns the object type returned by the `getObject()` method |
|
or `null` if the type is not known in advance. |
|
|
|
The `FactoryBean` concept and interface is used in a number of places within the Spring |
|
Framework; more than 50 implementations of the `FactoryBean` interface ship with Spring |
|
itself. |
|
|
|
When you need to ask a container for an actual `FactoryBean` instance itself instead of |
|
the bean it produces, preface the bean's id with the ampersand symbol ( `&`) when |
|
calling the `getBean()` method of the `ApplicationContext`. So for a given `FactoryBean` |
|
with an id of `myBean`, invoking `getBean("myBean")` on the container returns the |
|
product of the `FactoryBean`; whereas, invoking `getBean("&myBean")` returns the |
|
`FactoryBean` instance itself. |
|
|
|
|
|
|
|
|
|
[[beans-annotation-config]] |
|
=== Annotation-based container configuration |
|
|
|
.Are annotations better than XML for configuring Spring? |
|
**** |
|
The introduction of annotation-based configurations raised the question of whether this |
|
approach is 'better' than XML. The short answer is __it depends__. The long answer is |
|
that each approach has its pros and cons, and usually it is up to the developer to |
|
decide which strategy suits them better. Due to the way they are defined, annotations |
|
provide a lot of context in their declaration, leading to shorter and more concise |
|
configuration. However, XML excels at wiring up components without touching their source |
|
code or recompiling them. Some developers prefer having the wiring close to the source |
|
while others argue that annotated classes are no longer POJOs and, furthermore, that the |
|
configuration becomes decentralized and harder to control. |
|
|
|
No matter the choice, Spring can accommodate both styles and even mix them together. |
|
It's worth pointing out that through its <<beans-java,JavaConfig>> option, Spring allows |
|
annotations to be used in a non-invasive way, without touching the target components |
|
source code and that in terms of tooling, all configuration styles are supported by the |
|
https://spring.io/tools/sts[Spring Tool Suite]. |
|
**** |
|
|
|
An alternative to XML setups is provided by annotation-based configuration which rely on |
|
the bytecode metadata for wiring up components instead of angle-bracket declarations. |
|
Instead of using XML to describe a bean wiring, the developer moves the configuration |
|
into the component class itself by using annotations on the relevant class, method, or |
|
field declaration. As mentioned in <<beans-factory-extension-bpp-examples-rabpp>>, using |
|
a `BeanPostProcessor` in conjunction with annotations is a common means of extending the |
|
Spring IoC container. For example, Spring 2.0 introduced the possibility of enforcing |
|
required properties with the <<beans-required-annotation,@Required>> annotation. Spring |
|
2.5 made it possible to follow that same general approach to drive Spring's dependency |
|
injection. Essentially, the `@Autowired` annotation provides the same capabilities as |
|
described in <<beans-factory-autowire>> but with more fine-grained control and wider |
|
applicability. Spring 2.5 also added support for JSR-250 annotations such as |
|
`@PostConstruct`, and `@PreDestroy`. Spring 3.0 added support for JSR-330 (Dependency |
|
Injection for Java) annotations contained in the javax.inject package such as `@Inject` |
|
and `@Named`. Details about those annotations can be found in the |
|
<<beans-standard-annotations,relevant section>>. |
|
[NOTE] |
|
==== |
|
Annotation injection is performed __before__ XML injection, thus the latter |
|
configuration will override the former for properties wired through both approaches. |
|
==== |
|
As always, you can register them as individual bean definitions, but they can also be |
|
implicitly registered by including the following tag in an XML-based Spring |
|
configuration (notice the inclusion of the `context` namespace): |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:context="http://www.springframework.org/schema/context" |
|
xsi:schemaLocation="http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/context |
|
http://www.springframework.org/schema/context/spring-context.xsd"> |
|
|
|
<context:annotation-config/> |
|
|
|
</beans> |
|
---- |
|
|
|
(The implicitly registered post-processors include |
|
{javadoc-baseurl}/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.html[`AutowiredAnnotationBeanPostProcessor`], |
|
{javadoc-baseurl}/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.html[`CommonAnnotationBeanPostProcessor`], |
|
{javadoc-baseurl}/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.html[`PersistenceAnnotationBeanPostProcessor`], |
|
as well as the aforementioned |
|
{javadoc-baseurl}/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.html[`RequiredAnnotationBeanPostProcessor`].) |
|
|
|
[NOTE] |
|
==== |
|
`<context:annotation-config/>` only looks for annotations on beans in the same |
|
application context in which it is defined. This means that, if you put |
|
`<context:annotation-config/>` in a `WebApplicationContext` for a `DispatcherServlet`, |
|
it only checks for `@Autowired` beans in your controllers, and not your services. See |
|
<<mvc-servlet>> for more information. |
|
==== |
|
|
|
|
|
|
|
[[beans-required-annotation]] |
|
==== @Required |
|
|
|
The `@Required` annotation applies to bean property setter methods, as in the following |
|
example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class SimpleMovieLister { |
|
|
|
private MovieFinder movieFinder; |
|
|
|
@Required |
|
public void setMovieFinder(MovieFinder movieFinder) { |
|
this.movieFinder = movieFinder; |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
This annotation simply indicates that the affected bean property must be populated at |
|
configuration time, through an explicit property value in a bean definition or through |
|
autowiring. The container throws an exception if the affected bean property has not been |
|
populated; this allows for eager and explicit failure, avoiding ++NullPointerException++s |
|
or the like later on. It is still recommended that you put assertions into the bean |
|
class itself, for example, into an init method. Doing so enforces those required |
|
references and values even when you use the class outside of a container. |
|
|
|
|
|
|
|
[[beans-autowired-annotation]] |
|
==== @Autowired |
|
|
|
As expected, you can apply the `@Autowired` annotation to "traditional" setter methods: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class SimpleMovieLister { |
|
|
|
private MovieFinder movieFinder; |
|
|
|
@Autowired |
|
public void setMovieFinder(MovieFinder movieFinder) { |
|
this.movieFinder = movieFinder; |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
JSR 330's @Inject annotation can be used in place of Spring's `@Autowired` annotation in |
|
the examples below. See <<beans-standard-annotations,here>> for more details |
|
==== |
|
|
|
You can also apply the annotation to methods with arbitrary names and/or multiple |
|
arguments: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MovieRecommender { |
|
|
|
private MovieCatalog movieCatalog; |
|
|
|
private CustomerPreferenceDao customerPreferenceDao; |
|
|
|
@Autowired |
|
public void prepare(MovieCatalog movieCatalog, |
|
CustomerPreferenceDao customerPreferenceDao) { |
|
this.movieCatalog = movieCatalog; |
|
this.customerPreferenceDao = customerPreferenceDao; |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
You can apply `@Autowired` to constructors and fields: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MovieRecommender { |
|
|
|
@Autowired |
|
private MovieCatalog movieCatalog; |
|
|
|
private CustomerPreferenceDao customerPreferenceDao; |
|
|
|
@Autowired |
|
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) { |
|
this.customerPreferenceDao = customerPreferenceDao; |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
It is also possible to provide __all__ beans of a particular type from the |
|
`ApplicationContext` by adding the annotation to a field or method that expects an array |
|
of that type: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MovieRecommender { |
|
|
|
@Autowired |
|
private MovieCatalog[] movieCatalogs; |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
The same applies for typed collections: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MovieRecommender { |
|
|
|
private Set<MovieCatalog> movieCatalogs; |
|
|
|
@Autowired |
|
public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) { |
|
this.movieCatalogs = movieCatalogs; |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
[TIP] |
|
==== |
|
Your beans can implement the `org.springframework.core.Ordered` interface or either use |
|
the `@Order` or standard `@Priority` annotation if you want items in the array or list |
|
to be sorted into a specific order. |
|
==== |
|
|
|
|
|
Even typed Maps can be autowired as long as the expected key type is `String`. The Map |
|
values will contain all beans of the expected type, and the keys will contain the |
|
corresponding bean names: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MovieRecommender { |
|
|
|
private Map<String, MovieCatalog> movieCatalogs; |
|
|
|
@Autowired |
|
public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) { |
|
this.movieCatalogs = movieCatalogs; |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
By default, the autowiring fails whenever __zero__ candidate beans are available; the |
|
default behavior is to treat annotated methods, constructors, and fields as |
|
indicating __required__ dependencies. This behavior can be changed as demonstrated below. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class SimpleMovieLister { |
|
|
|
private MovieFinder movieFinder; |
|
|
|
@Autowired(required=false) |
|
public void setMovieFinder(MovieFinder movieFinder) { |
|
this.movieFinder = movieFinder; |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
Only __one annotated constructor per-class__ can be marked as __required__, but multiple |
|
non-required constructors can be annotated. In that case, each is considered among the |
|
candidates and Spring uses the __greediest__ constructor whose dependencies can be |
|
satisfied, that is the constructor that has the largest number of arguments. |
|
|
|
`@Autowired`'s __required__ attribute is recommended over the `@Required` annotation. |
|
The __required__ attribute indicates that the property is not required for autowiring |
|
purposes, the property is ignored if it cannot be autowired. `@Required`, on the other |
|
hand, is stronger in that it enforces the property that was set by any means supported |
|
by the container. If no value is injected, a corresponding exception is raised. |
|
==== |
|
|
|
You can also use `@Autowired` for interfaces that are well-known resolvable |
|
dependencies: `BeanFactory`, `ApplicationContext`, `Environment`, `ResourceLoader`, |
|
`ApplicationEventPublisher`, and `MessageSource`. These interfaces and their extended |
|
interfaces, such as `ConfigurableApplicationContext` or `ResourcePatternResolver`, are |
|
automatically resolved, with no special setup necessary. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MovieRecommender { |
|
|
|
@Autowired |
|
private ApplicationContext context; |
|
|
|
public MovieRecommender() { |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
`@Autowired`, `@Inject`, `@Resource`, and `@Value` annotations are handled by a Spring |
|
`BeanPostProcessor` implementations which in turn means that you __cannot__ apply these |
|
annotations within your own `BeanPostProcessor` or `BeanFactoryPostProcessor` types (if |
|
any). These types must be 'wired up' explicitly via XML or using a Spring `@Bean` method. |
|
==== |
|
|
|
|
|
|
|
[[beans-autowired-annotation-qualifiers]] |
|
==== Fine-tuning annotation-based autowiring with qualifiers |
|
Because autowiring by type may lead to multiple candidates, it is often necessary to |
|
have more control over the selection process. One way to accomplish this is with |
|
Spring's `@Qualifier` annotation. You can associate qualifier values with specific |
|
arguments, narrowing the set of type matches so that a specific bean is chosen for each |
|
argument. In the simplest case, this can be a plain descriptive value: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MovieRecommender { |
|
|
|
@Autowired |
|
**@Qualifier("main")** |
|
private MovieCatalog movieCatalog; |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
The `@Qualifier` annotation can also be specified on individual constructor arguments or |
|
method parameters: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MovieRecommender { |
|
|
|
private MovieCatalog movieCatalog; |
|
|
|
private CustomerPreferenceDao customerPreferenceDao; |
|
|
|
@Autowired |
|
public void prepare(**@Qualifier("main")**MovieCatalog movieCatalog, |
|
CustomerPreferenceDao customerPreferenceDao) { |
|
this.movieCatalog = movieCatalog; |
|
this.customerPreferenceDao = customerPreferenceDao; |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
The corresponding bean definitions appear as follows. The bean with qualifier value |
|
"main" is wired with the constructor argument that is qualified with the same value. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:context="http://www.springframework.org/schema/context" |
|
xsi:schemaLocation="http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/context |
|
http://www.springframework.org/schema/context/spring-context.xsd"> |
|
|
|
<context:annotation-config/> |
|
|
|
<bean class="example.SimpleMovieCatalog"> |
|
**<qualifier value="main"/>** |
|
|
|
<!-- inject any dependencies required by this bean --> |
|
</bean> |
|
|
|
<bean class="example.SimpleMovieCatalog"> |
|
**<qualifier value="action"/>** |
|
|
|
<!-- inject any dependencies required by this bean --> |
|
</bean> |
|
|
|
<bean id="movieRecommender" class="example.MovieRecommender"/> |
|
|
|
</beans> |
|
---- |
|
|
|
For a fallback match, the bean name is considered a default qualifier value. Thus you |
|
can define the bean with an id "main" instead of the nested qualifier element, leading |
|
to the same matching result. However, although you can use this convention to refer to |
|
specific beans by name, `@Autowired` is fundamentally about type-driven injection with |
|
optional semantic qualifiers. This means that qualifier values, even with the bean name |
|
fallback, always have narrowing semantics within the set of type matches; they do not |
|
semantically express a reference to a unique bean id. Good qualifier values are "main" |
|
or "EMEA" or "persistent", expressing characteristics of a specific component that are |
|
independent from the bean id, which may be auto-generated in case of an anonymous bean |
|
definition like the one in the preceding example. |
|
|
|
Qualifiers also apply to typed collections, as discussed above, for example, to |
|
`Set<MovieCatalog>`. In this case, all matching beans according to the declared |
|
qualifiers are injected as a collection. This implies that qualifiers do not have to be |
|
unique; they rather simply constitute filtering criteria. For example, you can define |
|
multiple `MovieCatalog` beans with the same qualifier value "action"; all of which would |
|
be injected into a `Set<MovieCatalog>` annotated with `@Qualifier("action")`. |
|
|
|
[TIP] |
|
==== |
|
|
|
If you intend to express annotation-driven injection by name, do not primarily use |
|
`@Autowired`, even if is technically capable of referring to a bean name through |
|
`@Qualifier` values. Instead, use the JSR-250 `@Resource` annotation, which is |
|
semantically defined to identify a specific target component by its unique name, with |
|
the declared type being irrelevant for the matching process. |
|
|
|
As a specific consequence of this semantic difference, beans that are themselves defined |
|
as a collection or map type cannot be injected through `@Autowired`, because type |
|
matching is not properly applicable to them. Use `@Resource` for such beans, referring |
|
to the specific collection or map bean by unique name. |
|
|
|
`@Autowired` applies to fields, constructors, and multi-argument methods, allowing for |
|
narrowing through qualifier annotations at the parameter level. By contrast, `@Resource` |
|
is supported only for fields and bean property setter methods with a single argument. As |
|
a consequence, stick with qualifiers if your injection target is a constructor or a |
|
multi-argument method. |
|
==== |
|
|
|
You can create your own custom qualifier annotations. Simply define an annotation and |
|
provide the `@Qualifier` annotation within your definition: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Target({ElementType.FIELD, ElementType.PARAMETER}) |
|
@Retention(RetentionPolicy.RUNTIME) |
|
**@Qualifier** |
|
public @interface Genre { |
|
|
|
String value(); |
|
} |
|
---- |
|
|
|
Then you can provide the custom qualifier on autowired fields and parameters: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MovieRecommender { |
|
|
|
@Autowired |
|
**@Genre("Action")** |
|
private MovieCatalog actionCatalog; |
|
private MovieCatalog comedyCatalog; |
|
|
|
@Autowired |
|
public void setComedyCatalog(**@Genre("Comedy")** MovieCatalog comedyCatalog) { |
|
this.comedyCatalog = comedyCatalog; |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
Next, provide the information for the candidate bean definitions. You can add |
|
`<qualifier/>` tags as sub-elements of the `<bean/>` tag and then specify the `type` and |
|
`value` to match your custom qualifier annotations. The type is matched against the |
|
fully-qualified class name of the annotation. Or, as a convenience if no risk of |
|
conflicting names exists, you can use the short class name. Both approaches are |
|
demonstrated in the following example. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:context="http://www.springframework.org/schema/context" |
|
xsi:schemaLocation="http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/context |
|
http://www.springframework.org/schema/context/spring-context.xsd"> |
|
|
|
<context:annotation-config/> |
|
|
|
<bean class="example.SimpleMovieCatalog"> |
|
**<qualifier type="Genre" value="Action"/>** |
|
<!-- inject any dependencies required by this bean --> |
|
</bean> |
|
|
|
<bean class="example.SimpleMovieCatalog"> |
|
**_<qualifier type="example.Genre" value="Comedy"/>** |
|
<!-- inject any dependencies required by this bean --> |
|
</bean> |
|
|
|
<bean id="movieRecommender" class="example.MovieRecommender"/> |
|
|
|
</beans> |
|
---- |
|
|
|
In <<beans-classpath-scanning>>, you will see an annotation-based alternative to |
|
providing the qualifier metadata in XML. Specifically, see <<beans-scanning-qualifiers>>. |
|
|
|
In some cases, it may be sufficient to use an annotation without a value. This may be |
|
useful when the annotation serves a more generic purpose and can be applied across |
|
several different types of dependencies. For example, you may provide an __offline__ |
|
catalog that would be searched when no Internet connection is available. First define |
|
the simple annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Target({ElementType.FIELD, ElementType.PARAMETER}) |
|
@Retention(RetentionPolicy.RUNTIME) |
|
@Qualifier |
|
public @interface Offline { |
|
|
|
} |
|
---- |
|
|
|
Then add the annotation to the field or property to be autowired: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MovieRecommender { |
|
|
|
@Autowired |
|
**@Offline** |
|
private MovieCatalog offlineCatalog; |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
Now the bean definition only needs a qualifier `type`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="example.SimpleMovieCatalog"> |
|
**<qualifier type="Offline"/>** |
|
<!-- inject any dependencies required by this bean --> |
|
</bean> |
|
---- |
|
|
|
You can also define custom qualifier annotations that accept named attributes in |
|
addition to or instead of the simple `value` attribute. If multiple attribute values are |
|
then specified on a field or parameter to be autowired, a bean definition must match |
|
__all__ such attribute values to be considered an autowire candidate. As an example, |
|
consider the following annotation definition: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Target({ElementType.FIELD, ElementType.PARAMETER}) |
|
@Retention(RetentionPolicy.RUNTIME) |
|
@Qualifier |
|
public @interface MovieQualifier { |
|
|
|
String genre(); |
|
|
|
Format format(); |
|
|
|
} |
|
---- |
|
|
|
In this case `Format` is an enum: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public enum Format { |
|
VHS, DVD, BLURAY |
|
} |
|
---- |
|
|
|
The fields to be autowired are annotated with the custom qualifier and include values |
|
for both attributes: `genre` and `format`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MovieRecommender { |
|
|
|
@Autowired |
|
@MovieQualifier(format=Format.VHS, genre="Action") |
|
private MovieCatalog actionVhsCatalog; |
|
|
|
@Autowired |
|
@MovieQualifier(format=Format.VHS, genre="Comedy") |
|
private MovieCatalog comedyVhsCatalog; |
|
|
|
@Autowired |
|
@MovieQualifier(format=Format.DVD, genre="Action") |
|
private MovieCatalog actionDvdCatalog; |
|
|
|
@Autowired |
|
@MovieQualifier(format=Format.BLURAY, genre="Comedy") |
|
private MovieCatalog comedyBluRayCatalog; |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
Finally, the bean definitions should contain matching qualifier values. This example |
|
also demonstrates that bean __meta__ attributes may be used instead of the |
|
`<qualifier/>` sub-elements. If available, the `<qualifier/>` and its attributes take |
|
precedence, but the autowiring mechanism falls back on the values provided within the |
|
`<meta/>` tags if no such qualifier is present, as in the last two bean definitions in |
|
the following example. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:context="http://www.springframework.org/schema/context" |
|
xsi:schemaLocation="http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/context |
|
http://www.springframework.org/schema/context/spring-context.xsd"> |
|
|
|
<context:annotation-config/> |
|
|
|
<bean class="example.SimpleMovieCatalog"> |
|
<qualifier type="MovieQualifier"> |
|
<attribute key="format" value="VHS"/> |
|
<attribute key="genre" value="Action"/> |
|
</qualifier> |
|
<!-- inject any dependencies required by this bean --> |
|
</bean> |
|
|
|
<bean class="example.SimpleMovieCatalog"> |
|
<qualifier type="MovieQualifier"> |
|
<attribute key="format" value="VHS"/> |
|
<attribute key="genre" value="Comedy"/> |
|
</qualifier> |
|
<!-- inject any dependencies required by this bean --> |
|
</bean> |
|
|
|
<bean class="example.SimpleMovieCatalog"> |
|
<meta key="format" value="DVD"/> |
|
<meta key="genre" value="Action"/> |
|
<!-- inject any dependencies required by this bean --> |
|
</bean> |
|
|
|
<bean class="example.SimpleMovieCatalog"> |
|
<meta key="format" value="BLURAY"/> |
|
<meta key="genre" value="Comedy"/> |
|
<!-- inject any dependencies required by this bean --> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
|
|
|
|
[[beans-generics-as-qualifiers]] |
|
==== Using generics as autowiring qualifiers |
|
In addition to the `@Qualifier` annotation, it is also possible to use Java generic types |
|
as an implicit form of qualification. For example, suppose you have the following |
|
configuration: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
public class MyConfiguration { |
|
|
|
@Bean |
|
public StringStore stringStore() { |
|
return new StringStore(); |
|
} |
|
|
|
@Bean |
|
public IntegerStore integerStore() { |
|
return new IntegerStore(); |
|
} |
|
|
|
} |
|
---- |
|
|
|
Assuming that beans above implement a generic interface, i.e. `Store<String>` and |
|
`Store<Integer>`, you can `@Autowire` the `Store` interface and the __generic__ will |
|
be used as a qualifier: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Autowired |
|
private Store<String> s1; // <String> qualifier, injects the stringStore bean |
|
|
|
@Autowired |
|
private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean |
|
---- |
|
|
|
Generic qualifiers also apply when autowiring Lists, Maps and Arrays: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// Inject all Store beans as long as they have an <Integer> generic |
|
// Store<String> beans will not appear in this list |
|
@Autowired |
|
private List<Store<Integer>> s; |
|
---- |
|
|
|
|
|
|
|
|
|
[[beans-custom-autowire-configurer]] |
|
==== CustomAutowireConfigurer |
|
|
|
The |
|
{javadoc-baseurl}/org/springframework/beans/factory/annotation/CustomAutowireConfigurer.html[`CustomAutowireConfigurer`] |
|
is a `BeanFactoryPostProcessor` that enables you to register your own custom qualifier |
|
annotation types even if they are not annotated with Spring's `@Qualifier` annotation. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="customAutowireConfigurer" |
|
class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer"> |
|
<property name="customQualifierTypes"> |
|
<set> |
|
<value>example.CustomQualifier</value> |
|
</set> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
The `AutowireCandidateResolver` determines autowire candidates by: |
|
|
|
* the `autowire-candidate` value of each bean definition |
|
* any `default-autowire-candidates` pattern(s) available on the `<beans/>` element |
|
* the presence of `@Qualifier` annotations and any custom annotations registered |
|
with the `CustomAutowireConfigurer` |
|
|
|
When multiple beans qualify as autowire candidates, the determination of a "primary" is |
|
the following: if exactly one bean definition among the candidates has a `primary` |
|
attribute set to `true`, it will be selected. |
|
|
|
|
|
|
|
[[beans-resource-annotation]] |
|
==== @Resource |
|
|
|
Spring also supports injection using the JSR-250 `@Resource` annotation on fields or |
|
bean property setter methods. This is a common pattern in Java EE 5 and 6, for example |
|
in JSF 1.2 managed beans or JAX-WS 2.0 endpoints. Spring supports this pattern for |
|
Spring-managed objects as well. |
|
|
|
`@Resource` takes a name attribute, and by default Spring interprets that value as the |
|
bean name to be injected. In other words, it follows __by-name__ semantics, as |
|
demonstrated in this example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class SimpleMovieLister { |
|
|
|
private MovieFinder movieFinder; |
|
|
|
**@Resource(name="myMovieFinder")** |
|
public void setMovieFinder(MovieFinder movieFinder) { |
|
this.movieFinder = movieFinder; |
|
} |
|
|
|
} |
|
---- |
|
|
|
If no name is specified explicitly, the default name is derived from the field name or |
|
setter method. In case of a field, it takes the field name; in case of a setter method, |
|
it takes the bean property name. So the following example is going to have the bean with |
|
name "movieFinder" injected into its setter method: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class SimpleMovieLister { |
|
|
|
private MovieFinder movieFinder; |
|
|
|
**@Resource** |
|
public void setMovieFinder(MovieFinder movieFinder) { |
|
this.movieFinder = movieFinder; |
|
} |
|
|
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
The name provided with the annotation is resolved as a bean name by the |
|
`ApplicationContext` of which the `CommonAnnotationBeanPostProcessor` is aware. The |
|
names can be resolved through JNDI if you configure Spring's |
|
{javadoc-baseurl}/org/springframework/jndi/support/SimpleJndiBeanFactory.html[`SimpleJndiBeanFactory`] |
|
explicitly. However, it is recommended that you rely on the default behavior and simply |
|
use Spring's JNDI lookup capabilities to preserve the level of indirection. |
|
==== |
|
|
|
In the exclusive case of `@Resource` usage with no explicit name specified, and similar |
|
to `@Autowired`, `@Resource` finds a primary type match instead of a specific named bean |
|
and resolves well-known resolvable dependencies: the `BeanFactory`, |
|
`ApplicationContext`, `ResourceLoader`, `ApplicationEventPublisher`, and `MessageSource` |
|
interfaces. |
|
|
|
Thus in the following example, the `customerPreferenceDao` field first looks for a bean |
|
named customerPreferenceDao, then falls back to a primary type match for the type |
|
`CustomerPreferenceDao`. The "context" field is injected based on the known resolvable |
|
dependency type `ApplicationContext`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MovieRecommender { |
|
|
|
@Resource |
|
private CustomerPreferenceDao customerPreferenceDao; |
|
|
|
@Resource |
|
private ApplicationContext context; |
|
|
|
public MovieRecommender() { |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
|
|
|
|
[[beans-postconstruct-and-predestroy-annotations]] |
|
==== @PostConstruct and @PreDestroy |
|
|
|
The `CommonAnnotationBeanPostProcessor` not only recognizes the `@Resource` annotation |
|
but also the JSR-250 __lifecycle__ annotations. Introduced in Spring 2.5, the support |
|
for these annotations offers yet another alternative to those described in |
|
<<beans-factory-lifecycle-initializingbean,initialization callbacks>> and |
|
<<beans-factory-lifecycle-disposablebean,destruction callbacks>>. Provided that the |
|
`CommonAnnotationBeanPostProcessor` is registered within the Spring |
|
`ApplicationContext`, a method carrying one of these annotations is invoked at the same |
|
point in the lifecycle as the corresponding Spring lifecycle interface method or |
|
explicitly declared callback method. In the example below, the cache will be |
|
pre-populated upon initialization and cleared upon destruction. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class CachingMovieLister { |
|
|
|
@PostConstruct |
|
public void populateMovieCache() { |
|
// populates the movie cache upon initialization... |
|
} |
|
|
|
@PreDestroy |
|
public void clearMovieCache() { |
|
// clears the movie cache upon destruction... |
|
} |
|
|
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
For details about the effects of combining various lifecycle mechanisms, see |
|
<<beans-factory-lifecycle-combined-effects>>. |
|
==== |
|
|
|
|
|
|
|
|
|
[[beans-classpath-scanning]] |
|
=== Classpath scanning and managed components |
|
Most examples in this chapter use XML to specify the configuration metadata that |
|
produces each `BeanDefinition` within the Spring container. The previous section |
|
(<<beans-annotation-config>>) demonstrates how to provide a lot of the configuration |
|
metadata through source-level annotations. Even in those examples, however, the "base" |
|
bean definitions are explicitly defined in the XML file, while the annotations only |
|
drive the dependency injection. This section describes an option for implicitly |
|
detecting the __candidate components__ by scanning the classpath. Candidate components |
|
are classes that match against a filter criteria and have a corresponding bean |
|
definition registered with the container. This removes the need to use XML to perform |
|
bean registration, instead you can use annotations (for example @Component), AspectJ |
|
type expressions, or your own custom filter criteria to select which classes will have |
|
bean definitions registered with the container. |
|
|
|
[NOTE] |
|
==== |
|
Starting with Spring 3.0, many features provided by the Spring JavaConfig project are |
|
part of the core Spring Framework. This allows you to define beans using Java rather |
|
than using the traditional XML files. Take a look at the `@Configuration`, `@Bean`, |
|
`@Import`, and `@DependsOn` annotations for examples of how to use these new features. |
|
==== |
|
|
|
|
|
|
|
[[beans-stereotype-annotations]] |
|
==== @Component and further stereotype annotations |
|
|
|
The `@Repository` annotation is a marker for any class that fulfills the role or |
|
__stereotype__ (also known as Data Access Object or DAO) of a repository. Among the uses |
|
of this marker is the automatic translation of exceptions as described in |
|
<<orm-exception-translation>>. |
|
|
|
Spring provides further stereotype annotations: `@Component`, `@Service`, and |
|
`@Controller`. `@Component` is a generic stereotype for any Spring-managed component. |
|
`@Repository`, `@Service`, and `@Controller` are specializations of `@Component` for |
|
more specific use cases, for example, in the persistence, service, and presentation |
|
layers, respectively. Therefore, you can annotate your component classes with |
|
`@Component`, but by annotating them with `@Repository`, `@Service`, or `@Controller` |
|
instead, your classes are more properly suited for processing by tools or associating |
|
with aspects. For example, these stereotype annotations make ideal targets for |
|
pointcuts. It is also possible that `@Repository`, `@Service`, and `@Controller` may |
|
carry additional semantics in future releases of the Spring Framework. Thus, if you are |
|
choosing between using `@Component` or `@Service` for your service layer, `@Service` is |
|
clearly the better choice. Similarly, as stated above, `@Repository` is already |
|
supported as a marker for automatic exception translation in your persistence layer. |
|
|
|
|
|
|
|
[[beans-meta-annotations]] |
|
==== Meta-annotations |
|
Many of the annotations provided by Spring can be used as "meta-annotations" in |
|
your own code. A meta-annotation is simply an annotation, that can be applied to another |
|
annotation. For example, The `@Service` annotation mentioned above is meta-annotated with |
|
with `@Component`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Target({ElementType.TYPE}) |
|
@Retention(RetentionPolicy.RUNTIME) |
|
@Documented |
|
**@Component** // Spring will see this and treat @Service in the same way as @Component |
|
public @interface Service { |
|
|
|
// .... |
|
|
|
} |
|
---- |
|
|
|
Meta-annotations can also be combined together to create __composed annotations__. For |
|
example, the `@RestController` annotation from Spring MVC is __composed__ of |
|
`@Controller` and `@ResponseBody`. |
|
|
|
With the exception of `value()`, meta-annotated types may redeclare attributes from the |
|
source annotation to allow user customization. This can be particularly useful when you |
|
want to only expose a subset of the source annotation attributes. For example, here is a |
|
custom `@Scope` annotation that defines `session` scope, but still allows customization |
|
of the `proxyMode`. |
|
|
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Target({ElementType.TYPE}) |
|
@Retention(RetentionPolicy.RUNTIME) |
|
@Documented |
|
**@Scope("session")** |
|
public @interface SessionScope { |
|
|
|
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT |
|
|
|
} |
|
---- |
|
|
|
|
|
|
|
|
|
[[beans-scanning-autodetection]] |
|
==== Automatically detecting classes and registering bean definitions |
|
Spring can automatically detect stereotyped classes and register corresponding |
|
++BeanDefinition++s with the `ApplicationContext`. For example, the following two classes |
|
are eligible for such autodetection: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Service |
|
public class SimpleMovieLister { |
|
|
|
private MovieFinder movieFinder; |
|
|
|
@Autowired |
|
public SimpleMovieLister(MovieFinder movieFinder) { |
|
this.movieFinder = movieFinder; |
|
} |
|
|
|
} |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Repository |
|
public class JpaMovieFinder implements MovieFinder { |
|
// implementation elided for clarity |
|
} |
|
---- |
|
|
|
To autodetect these classes and register the corresponding beans, you need to add |
|
`@ComponentScan` to your `@Configuration` class, where the `basePackages` attribute |
|
is a common parent package for the two classes. (Alternatively, you can specify a |
|
comma/semicolon/space-separated list that includes the parent package of each class.) |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@ComponentScan(basePackages = "org.example") |
|
public class AppConfig { |
|
... |
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
for concision, the above may have used the `value` attribute of the |
|
annotation, i.e. `ComponentScan("org.example")` |
|
==== |
|
|
|
The following is an alternative using XML |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:context="http://www.springframework.org/schema/context" |
|
xsi:schemaLocation="http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/context |
|
http://www.springframework.org/schema/context/spring-context.xsd"> |
|
|
|
<context:component-scan base-package="org.example"/> |
|
|
|
</beans> |
|
---- |
|
|
|
[TIP] |
|
==== |
|
|
|
The use of `<context:component-scan>` implicitly enables the functionality of |
|
`<context:annotation-config>`. There is usually no need to include the |
|
`<context:annotation-config>` element when using `<context:component-scan>`. |
|
==== |
|
|
|
[NOTE] |
|
==== |
|
The scanning of classpath packages requires the presence of corresponding directory |
|
entries in the classpath. When you build JARs with Ant, make sure that you do __not__ |
|
activate the files-only switch of the JAR task. Also, classpath directories may not |
|
get exposed based on security policies in some environments, e.g. standalone apps on |
|
JDK 1.7.0_45 and higher (which requires 'Trusted-Library' setup in your manifests; see |
|
http://stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources). |
|
==== |
|
|
|
Furthermore, the `AutowiredAnnotationBeanPostProcessor` and |
|
`CommonAnnotationBeanPostProcessor` are both included implicitly when you use the |
|
component-scan element. That means that the two components are autodetected __and__ |
|
wired together - all without any bean configuration metadata provided in XML. |
|
|
|
[NOTE] |
|
==== |
|
You can disable the registration of `AutowiredAnnotationBeanPostProcessor` and |
|
`CommonAnnotationBeanPostProcessor` by including the __annotation-config__ attribute |
|
with a value of false. |
|
==== |
|
|
|
|
|
|
|
[[beans-scanning-filters]] |
|
==== Using filters to customize scanning |
|
By default, classes annotated with `@Component`, `@Repository`, `@Service`, |
|
`@Controller`, or a custom annotation that itself is annotated with `@Component` are the |
|
only detected candidate components. However, you can modify and extend this behavior |
|
simply by applying custom filters. Add them as __includeFilters__ or __excludeFilters__ |
|
parameters of the `@ComponentScan` annotation (or as __include-filter__ or __exclude-filter__ |
|
sub-elements of the `component-scan` element). Each filter element requires the `type` |
|
and `expression` attributes. The following table describes the filtering options. |
|
|
|
[[beans-scanning-filters-tbl]] |
|
.Filter Types |
|
|=== |
|
| Filter Type| Example Expression| Description |
|
|
|
| annotation (default) |
|
| `org.example.SomeAnnotation` |
|
| An annotation to be present at the type level in target components. |
|
|
|
| assignable |
|
| `org.example.SomeClass` |
|
| A class (or interface) that the target components are assignable to (extend/implement). |
|
|
|
| aspectj |
|
| `org.example..*Service+` |
|
| An AspectJ type expression to be matched by the target components. |
|
|
|
| regex |
|
| `org\.example\.Default.*` |
|
| A regex expression to be matched by the target components class names. |
|
|
|
| custom |
|
| `org.example.MyTypeFilter` |
|
| A custom implementation of the `org.springframework.core.type .TypeFilter` interface. |
|
|=== |
|
|
|
The following example shows the configuration ignoring all `@Repository` annotations |
|
and using "stub" repositories instead. |
|
|
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@ComponentScan(basePackages = "org.example", |
|
includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"), |
|
excludeFilters = @Filter(Repository.class)) |
|
public class AppConfig { |
|
... |
|
} |
|
---- |
|
|
|
and the equivalent using XML |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<context:component-scan base-package="org.example"> |
|
<context:include-filter type="regex" |
|
expression=".*Stub.*Repository"/> |
|
<context:exclude-filter type="annotation" |
|
expression="org.springframework.stereotype.Repository"/> |
|
</context:component-scan> |
|
</beans> |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
You can also disable the default filters by setting `useDefaultFilters=false` on the annotation or |
|
providing `use-default-filters="false"` as an attribute of the <component-scan/> element. This |
|
will in effect disable automatic detection of classes annotated with `@Component`, `@Repository`, |
|
`@Service`, or `@Controller`. |
|
==== |
|
|
|
|
|
|
|
[[beans-factorybeans-annotations]] |
|
==== Defining bean metadata within components |
|
Spring components can also contribute bean definition metadata to the container. You do |
|
this with the same `@Bean` annotation used to define bean metadata within |
|
`@Configuration` annotated classes. Here is a simple example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Component |
|
public class FactoryMethodComponent { |
|
|
|
@Bean |
|
@Qualifier("public") |
|
public TestBean publicInstance() { |
|
return new TestBean("publicInstance"); |
|
} |
|
|
|
public void doWork() { |
|
// Component method implementation omitted |
|
} |
|
|
|
} |
|
---- |
|
|
|
This class is a Spring component that has application-specific code contained in its |
|
`doWork()` method. However, it also contributes a bean definition that has a factory |
|
method referring to the method `publicInstance()`. The `@Bean` annotation identifies the |
|
factory method and other bean definition properties, such as a qualifier value through |
|
the `@Qualifier` annotation. Other method level annotations that can be specified are |
|
`@Scope`, `@Lazy`, and custom qualifier annotations. |
|
|
|
[TIP] |
|
==== |
|
In addition to its role for component initialization, the `@Lazy` annotation may also be |
|
placed on injection points marked with `@Autowired` or `@Inject`. In this context, it |
|
leads to the injection of a lazy-resolution proxy. |
|
==== |
|
|
|
Autowired fields and methods are supported as previously discussed, with additional |
|
support for autowiring of `@Bean` methods: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Component |
|
public class FactoryMethodComponent { |
|
|
|
private static int i; |
|
|
|
@Bean |
|
@Qualifier("public") |
|
public TestBean publicInstance() { |
|
return new TestBean("publicInstance"); |
|
} |
|
|
|
// use of a custom qualifier and autowiring of method parameters |
|
|
|
@Bean |
|
protected TestBean protectedInstance( |
|
@Qualifier("public") TestBean spouse, |
|
@Value("#{privateInstance.age}") String country) { |
|
TestBean tb = new TestBean("protectedInstance", 1); |
|
tb.setSpouse(spouse); |
|
tb.setCountry(country); |
|
return tb; |
|
} |
|
|
|
@Bean |
|
@Scope(BeanDefinition.SCOPE_SINGLETON) |
|
private TestBean privateInstance() { |
|
return new TestBean("privateInstance", i++); |
|
} |
|
|
|
@Bean |
|
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS) |
|
public TestBean requestScopedInstance() { |
|
return new TestBean("requestScopedInstance", 3); |
|
} |
|
|
|
} |
|
---- |
|
|
|
The example autowires the `String` method parameter `country` to the value of the `Age` |
|
property on another bean named `privateInstance`. A Spring Expression Language element |
|
defines the value of the property through the notation `#{ <expression> }`. For `@Value` |
|
annotations, an expression resolver is preconfigured to look for bean names when |
|
resolving expression text. |
|
|
|
The `@Bean` methods in a Spring component are processed differently than their |
|
counterparts inside a Spring `@Configuration` class. The difference is that `@Component` |
|
classes are not enhanced with CGLIB to intercept the invocation of methods and fields. |
|
CGLIB proxying is the means by which invoking methods or fields within `@Bean` methods |
|
in `@Configuration` classes creates bean metadata references to collaborating objects; |
|
such methods are __not__ invoked with normal Java semantics. In contrast, invoking a |
|
method or field in an `@Bean` method within a `@Component` class __has__ standard Java |
|
semantics. |
|
|
|
|
|
|
|
[[beans-scanning-name-generator]] |
|
==== Naming autodetected components |
|
When a component is autodetected as part of the scanning process, its bean name is |
|
generated by the `BeanNameGenerator` strategy known to that scanner. By default, any |
|
Spring stereotype annotation ( `@Component`, `@Repository`, `@Service`, and |
|
`@Controller`) that contains a `name` value will thereby provide that name to the |
|
corresponding bean definition. |
|
|
|
If such an annotation contains no `name` value or for any other detected component (such |
|
as those discovered by custom filters), the default bean name generator returns the |
|
uncapitalized non-qualified class name. For example, if the following two components |
|
were detected, the names would be myMovieLister and movieFinderImpl: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Service("myMovieLister") |
|
public class SimpleMovieLister { |
|
// ... |
|
} |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Repository |
|
public class MovieFinderImpl implements MovieFinder { |
|
// ... |
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
If you do not want to rely on the default bean-naming strategy, you can provide a custom |
|
bean-naming strategy. First, implement the |
|
{javadoc-baseurl}/org/springframework/beans/factory/support/BeanNameGenerator.html[`BeanNameGenerator`] |
|
interface, and be sure to include a default no-arg constructor. Then, provide the |
|
fully-qualified class name when configuring the scanner: |
|
==== |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class) |
|
public class AppConfig { |
|
... |
|
} |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<context:component-scan base-package="org.example" |
|
name-generator="org.example.MyNameGenerator" /> |
|
</beans> |
|
---- |
|
|
|
As a general rule, consider specifying the name with the annotation whenever other |
|
components may be making explicit references to it. On the other hand, the |
|
auto-generated names are adequate whenever the container is responsible for wiring. |
|
|
|
|
|
|
|
[[beans-scanning-scope-resolver]] |
|
==== Providing a scope for autodetected components |
|
As with Spring-managed components in general, the default and most common scope for |
|
autodetected components is singleton. However, sometimes you need other scopes, which |
|
Spring 2.5 provides with a new `@Scope` annotation. Simply provide the name of the scope |
|
within the annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Scope("prototype") |
|
@Repository |
|
public class MovieFinderImpl implements MovieFinder { |
|
// ... |
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
To provide a custom strategy for scope resolution rather than relying on the |
|
annotation-based approach, implement the |
|
{javadoc-baseurl}/org/springframework/context/annotation/ScopeMetadataResolver.html[`ScopeMetadataResolver`] |
|
interface, and be sure to include a default no-arg constructor. Then, provide the |
|
fully-qualified class name when configuring the scanner: |
|
==== |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class) |
|
public class AppConfig { |
|
... |
|
} |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<context:component-scan base-package="org.example" |
|
scope-resolver="org.example.MyScopeResolver" /> |
|
</beans> |
|
---- |
|
|
|
When using certain non-singleton scopes, it may be necessary to generate proxies for the |
|
scoped objects. The reasoning is described in <<beans-factory-scopes-other-injection>>. |
|
For this purpose, a __scoped-proxy__ attribute is available on the component-scan |
|
element. The three possible values are: no, interfaces, and targetClass. For example, |
|
the following configuration will result in standard JDK dynamic proxies: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES) |
|
public class AppConfig { |
|
... |
|
} |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<context:component-scan base-package="org.example" |
|
scoped-proxy="interfaces" /> |
|
</beans> |
|
---- |
|
|
|
|
|
|
|
[[beans-scanning-qualifiers]] |
|
==== Providing qualifier metadata with annotations |
|
The `@Qualifier` annotation is discussed in <<beans-autowired-annotation-qualifiers>>. |
|
The examples in that section demonstrate the use of the `@Qualifier` annotation and |
|
custom qualifier annotations to provide fine-grained control when you resolve autowire |
|
candidates. Because those examples were based on XML bean definitions, the qualifier |
|
metadata was provided on the candidate bean definitions using the `qualifier` or `meta` |
|
sub-elements of the `bean` element in the XML. When relying upon classpath scanning for |
|
autodetection of components, you provide the qualifier metadata with type-level |
|
annotations on the candidate class. The following three examples demonstrate this |
|
technique: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Component |
|
**@Qualifier("Action")** |
|
public class ActionMovieCatalog implements MovieCatalog { |
|
// ... |
|
} |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Component |
|
**@Genre("Action")** |
|
public class ActionMovieCatalog implements MovieCatalog { |
|
// ... |
|
} |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Component |
|
**@Offline** |
|
public class CachingMovieCatalog implements MovieCatalog { |
|
// ... |
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
As with most annotation-based alternatives, keep in mind that the annotation metadata is |
|
bound to the class definition itself, while the use of XML allows for multiple beans |
|
__of the same type__ to provide variations in their qualifier metadata, because that |
|
metadata is provided per-instance rather than per-class. |
|
==== |
|
|
|
|
|
|
|
|
|
[[beans-standard-annotations]] |
|
=== Using JSR 330 Standard Annotations |
|
Starting with Spring 3.0, Spring offers support for JSR-330 standard annotations |
|
(Dependency Injection). Those annotations are scanned in the same way as the Spring |
|
annotations. You just need to have the relevant jars in your classpath. |
|
|
|
[NOTE] |
|
==== |
|
If you are using Maven, the `javax.inject` artifact is available in the standard Maven |
|
repository ( |
|
http://repo1.maven.org/maven2/javax/inject/javax.inject/1/[http://repo1.maven.org/maven2/javax/inject/javax.inject/1/]). |
|
You can add the following dependency to your file pom.xml: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<dependency> |
|
<groupId>javax.inject</groupId> |
|
<artifactId>javax.inject</artifactId> |
|
<version>1</version> |
|
</dependency> |
|
---- |
|
==== |
|
|
|
|
|
|
|
[[beans-inject-named]] |
|
==== Dependency Injection with @Inject and @Named |
|
|
|
Instead of `@Autowired`, `@javax.inject.Inject` may be used as follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import javax.inject.Inject; |
|
|
|
public class SimpleMovieLister { |
|
|
|
private MovieFinder movieFinder; |
|
|
|
@Inject |
|
public void setMovieFinder(MovieFinder movieFinder) { |
|
this.movieFinder = movieFinder; |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
As with `@Autowired`, it is possible to use `@Inject` at the class-level, field-level, |
|
method-level and constructor-argument level. If you would like to use a qualified name |
|
for the dependency that should be injected, you should use the `@Named` annotation as |
|
follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import javax.inject.Inject; |
|
import javax.inject.Named; |
|
|
|
public class SimpleMovieLister { |
|
|
|
private MovieFinder movieFinder; |
|
|
|
@Inject |
|
public void setMovieFinder(@Named("main") MovieFinder movieFinder) { |
|
this.movieFinder = movieFinder; |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
|
|
|
|
[[beans-named]] |
|
==== @Named: a standard equivalent to the @Component annotation |
|
|
|
Instead of `@Component`, `@javax.inject.Named` may be used as follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import javax.inject.Inject; |
|
import javax.inject.Named; |
|
|
|
@Named("movieListener") |
|
public class SimpleMovieLister { |
|
|
|
private MovieFinder movieFinder; |
|
|
|
@Inject |
|
public void setMovieFinder(MovieFinder movieFinder) { |
|
this.movieFinder = movieFinder; |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
It is very common to use `@Component` without |
|
specifying a name for the component. `@Named` |
|
can be used in a similar fashion: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import javax.inject.Inject; |
|
import javax.inject.Named; |
|
|
|
@Named |
|
public class SimpleMovieLister { |
|
|
|
private MovieFinder movieFinder; |
|
|
|
@Inject |
|
public void setMovieFinder(MovieFinder movieFinder) { |
|
this.movieFinder = movieFinder; |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
When using `@Named`, it is possible to use |
|
component-scanning in the exact same way as when using Spring annotations: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@ComponentScan(basePackages = "org.example") |
|
public class AppConfig { |
|
... |
|
} |
|
---- |
|
|
|
|
|
|
|
[[beans-standard-annotations-limitations]] |
|
==== Limitations of the standard approach |
|
When working with standard annotations, it is important to know that some significant |
|
features are not available as shown in the table below: |
|
|
|
[[annotations-comparison]] |
|
.Spring annotations vs. standard annotations |
|
|=== |
|
| Spring| javax.inject.*| javax.inject restrictions / comments |
|
|
|
| @Autowired |
|
| @Inject |
|
| @Inject has no 'required' attribute |
|
|
|
| @Component |
|
| @Named |
|
| - |
|
|
|
| @Scope("singleton") |
|
| @Singleton |
|
| The JSR-330 default scope is like Spring's `prototype`. However, in order to keep it |
|
consistent with Spring's general defaults, a JSR-330 bean declared in the Spring |
|
container is a `singleton` by default. In order to use a scope other than `singleton`, |
|
you should use Spring's `@Scope` annotation. |
|
|
|
`javax.inject` also provides a |
|
http://download.oracle.com/javaee/6/api/javax/inject/Scope.html[@Scope] annotation. |
|
Nevertheless, this one is only intended to be used for creating your own annotations. |
|
|
|
| @Qualifier |
|
| @Named |
|
| - |
|
|
|
| @Value |
|
| - |
|
| no equivalent |
|
|
|
| @Required |
|
| - |
|
| no equivalent |
|
|
|
| @Lazy |
|
| - |
|
| no equivalent |
|
|=== |
|
|
|
|
|
|
|
|
|
[[beans-java]] |
|
=== Java-based container configuration |
|
|
|
|
|
|
|
[[beans-java-basic-concepts]] |
|
==== Basic concepts: @Bean and @Configuration |
|
|
|
The central artifacts in Spring's new Java-configuration support are |
|
`@Configuration`-annotated classes and `@Bean`-annotated methods. |
|
|
|
The `@Bean` annotation is used to indicate that a method instantiates, configures and |
|
initializes a new object to be managed by the Spring IoC container. For those familiar |
|
with Spring's `<beans/>` XML configuration the `@Bean` annotation plays the same role as |
|
the `<bean/>` element. You can use `@Bean` annotated methods with any Spring |
|
`@Component`, however, they are most often used with `@Configuration` beans. |
|
|
|
Annotating a class with `@Configuration` indicates that its primary purpose is as a |
|
source of bean definitions. Furthermore, `@Configuration` classes allow inter-bean |
|
dependencies to be defined by simply calling other `@Bean` methods in the same class. |
|
The simplest possible `@Configuration` class would read as follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
public class AppConfig { |
|
|
|
@Bean |
|
public MyService myService() { |
|
return new MyServiceImpl(); |
|
} |
|
|
|
} |
|
---- |
|
|
|
The `AppConfig` class above would be equivalent to the following Spring `<beans/>` XML: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<bean id="myService" class="com.acme.services.MyServiceImpl"/> |
|
</beans> |
|
---- |
|
|
|
The `@Bean` and `@Configuration` annotations will be discussed in depth in the sections |
|
below. First, however, we'll cover the various ways of creating a spring container using |
|
Java-based configuration. |
|
|
|
.Full @Configuration vs 'lite' @Beans mode? |
|
**** |
|
When `@Bean` methods are declared within classes that are __not__ annotated with |
|
`@Configuration` they are referred to as being processed in a 'lite' mode. For example, |
|
bean methods declared in a `@Component` or even in a __plain old class__ will be |
|
considered 'lite'. |
|
|
|
Unlike full `@Configuration`, lite `@Bean` methods cannot easily declare inter-bean |
|
dependencies. Usually one `@Bean` method should not invoke another `@Bean` method when |
|
operating in 'lite' mode. |
|
|
|
Only using `@Bean` methods within `@Configuration` classes is a recommended approach of |
|
ensuring that 'full' mode is always used. This will prevent the same `@Bean` method from |
|
accidentally being invoked multiple times and helps to reduce subtle bugs that can be |
|
hard to track down when operating in 'lite' mode. |
|
**** |
|
|
|
|
|
[[beans-java-instantiating-container]] |
|
==== Instantiating the Spring container using AnnotationConfigApplicationContext |
|
The sections below document Spring's `AnnotationConfigApplicationContext`, new in Spring |
|
3.0. This versatile `ApplicationContext` implementation is capable of accepting not only |
|
`@Configuration` classes as input, but also plain `@Component` classes and classes |
|
annotated with JSR-330 metadata. |
|
|
|
When `@Configuration` classes are provided as input, the `@Configuration` class itself |
|
is registered as a bean definition, and all declared `@Bean` methods within the class |
|
are also registered as bean definitions. |
|
|
|
When `@Component` and JSR-330 classes are provided, they are registered as bean |
|
definitions, and it is assumed that DI metadata such as `@Autowired` or `@Inject` are |
|
used within those classes where necessary. |
|
|
|
|
|
[[beans-java-instantiating-container-contstructor]] |
|
===== Simple construction |
|
In much the same way that Spring XML files are used as input when instantiating a |
|
`ClassPathXmlApplicationContext`, `@Configuration` classes may be used as input when |
|
instantiating an `AnnotationConfigApplicationContext`. This allows for completely |
|
XML-free usage of the Spring container: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public static void main(String[] args) { |
|
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); |
|
MyService myService = ctx.getBean(MyService.class); |
|
myService.doStuff(); |
|
} |
|
---- |
|
|
|
As mentioned above, `AnnotationConfigApplicationContext` is not limited to working only |
|
with `@Configuration` classes. Any `@Component` or JSR-330 annotated class may be supplied |
|
as input to the constructor. For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public static void main(String[] args) { |
|
ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class); |
|
MyService myService = ctx.getBean(MyService.class); |
|
myService.doStuff(); |
|
} |
|
---- |
|
|
|
The above assumes that `MyServiceImpl`, `Dependency1` and `Dependency2` use Spring |
|
dependency injection annotations such as `@Autowired`. |
|
|
|
|
|
[[beans-java-instantiating-container-register]] |
|
===== Building the container programmatically using register(Class<?>...) |
|
|
|
An `AnnotationConfigApplicationContext` may be instantiated using a no-arg constructor |
|
and then configured using the `register()` method. This approach is particularly useful |
|
when programmatically building an `AnnotationConfigApplicationContext`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public static void main(String[] args) { |
|
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); |
|
ctx.register(AppConfig.class, OtherConfig.class); |
|
ctx.register(AdditionalConfig.class); |
|
ctx.refresh(); |
|
MyService myService = ctx.getBean(MyService.class); |
|
myService.doStuff(); |
|
} |
|
---- |
|
|
|
|
|
[[beans-java-instantiating-container-scan]] |
|
===== Enabling component scanning with scan(String...) |
|
|
|
To enable component scanning, just annotate your `@Configuration` class as follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@ComponentScan(basePackages = "com.acme") |
|
public class AppConfig { |
|
... |
|
} |
|
---- |
|
|
|
[TIP] |
|
==== |
|
|
|
Experienced Spring users will be familiar with the XML declaration equivalent from |
|
Spring's `context:` namespace |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<context:component-scan base-package="com.acme"/> |
|
</beans> |
|
---- |
|
==== |
|
|
|
|
|
In the example above, the `com.acme` package will be scanned, looking for any |
|
`@Component`-annotated classes, and those classes will be registered as Spring bean |
|
definitions within the container. `AnnotationConfigApplicationContext` exposes the |
|
`scan(String...)` method to allow for the same component-scanning functionality: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public static void main(String[] args) { |
|
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); |
|
ctx.scan("com.acme"); |
|
ctx.refresh(); |
|
MyService myService = ctx.getBean(MyService.class); |
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
Remember that `@Configuration` classes are <<beans-meta-annotations,meta-annotated>> |
|
with `@Component`, so they are candidates for component-scanning! In the example above, |
|
assuming that `AppConfig` is declared within the `com.acme` package (or any package |
|
underneath), it will be picked up during the call to `scan()`, and upon `refresh()` all |
|
its `@Bean` methods will be processed and registered as bean definitions within the |
|
container. |
|
==== |
|
|
|
|
|
[[beans-java-instantiating-container-web]] |
|
===== Support for web applications with AnnotationConfigWebApplicationContext |
|
|
|
A `WebApplicationContext` variant of `AnnotationConfigApplicationContext` is available |
|
with `AnnotationConfigWebApplicationContext`. This implementation may be used when |
|
configuring the Spring `ContextLoaderListener` servlet listener, Spring MVC |
|
`DispatcherServlet`, etc. What follows is a `web.xml` snippet that configures a typical |
|
Spring MVC web application. Note the use of the `contextClass` context-param and |
|
init-param: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<web-app> |
|
<!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext |
|
instead of the default XmlWebApplicationContext --> |
|
<context-param> |
|
<param-name>contextClass</param-name> |
|
<param-value> |
|
org.springframework.web.context.support.AnnotationConfigWebApplicationContext |
|
</param-value> |
|
</context-param> |
|
|
|
<!-- Configuration locations must consist of one or more comma- or space-delimited |
|
fully-qualified @Configuration classes. Fully-qualified packages may also be |
|
specified for component-scanning --> |
|
<context-param> |
|
<param-name>contextConfigLocation</param-name> |
|
<param-value>com.acme.AppConfig</param-value> |
|
</context-param> |
|
|
|
<!-- Bootstrap the root application context as usual using ContextLoaderListener --> |
|
<listener> |
|
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> |
|
</listener> |
|
|
|
<!-- Declare a Spring MVC DispatcherServlet as usual --> |
|
<servlet> |
|
<servlet-name>dispatcher</servlet-name> |
|
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> |
|
<!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext |
|
instead of the default XmlWebApplicationContext --> |
|
<init-param> |
|
<param-name>contextClass</param-name> |
|
<param-value> |
|
org.springframework.web.context.support.AnnotationConfigWebApplicationContext |
|
</param-value> |
|
</init-param> |
|
<!-- Again, config locations must consist of one or more comma- or space-delimited |
|
and fully-qualified @Configuration classes --> |
|
<init-param> |
|
<param-name>contextConfigLocation</param-name> |
|
<param-value>com.acme.web.MvcConfig</param-value> |
|
</init-param> |
|
</servlet> |
|
|
|
<!-- map all requests for /app/* to the dispatcher servlet --> |
|
<servlet-mapping> |
|
<servlet-name>dispatcher</servlet-name> |
|
<url-pattern>/app/*</url-pattern> |
|
</servlet-mapping> |
|
</web-app> |
|
---- |
|
|
|
|
|
|
|
[[beans-java-bean-annotation]] |
|
==== Using the @Bean annotation |
|
|
|
`@Bean` is a method-level annotation and a direct analog of the XML `<bean/>` element. |
|
The annotation supports some of the attributes offered by `<bean/>`, such as: |
|
<<beans-factory-lifecycle-initializingbean,init-method>>, |
|
<<beans-factory-lifecycle-disposablebean,destroy-method>>, |
|
<<beans-factory-autowire,autowiring>> and `name`. |
|
|
|
You can use the `@Bean` annotation in a `@Configuration`-annotated or in a |
|
`@Component`-annotated class. |
|
|
|
|
|
[[beans-java-declaring-a-bean]] |
|
===== Declaring a bean |
|
To declare a bean, simply annotate a method with the `@Bean` annotation. You use this |
|
method to register a bean definition within an `ApplicationContext` of the type |
|
specified as the method's return value. By default, the bean name will be the same as |
|
the method name. The following is a simple example of a `@Bean` method declaration: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
public class AppConfig { |
|
|
|
@Bean |
|
public TransferService transferService() { |
|
return new TransferServiceImpl(); |
|
} |
|
|
|
} |
|
---- |
|
|
|
The preceding configuration is exactly equivalent to the following Spring XML: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<bean id="transferService" class="com.acme.TransferServiceImpl"/> |
|
</beans> |
|
---- |
|
|
|
Both declarations make a bean named `transferService` available in the |
|
`ApplicationContext`, bound to an object instance of type `TransferServiceImpl`: |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
transferService -> com.acme.TransferServiceImpl |
|
---- |
|
|
|
|
|
[[beans-java-lifecycle-callbacks]] |
|
===== Receiving lifecycle callbacks |
|
Any classes defined with the `@Bean` annotation support the regular lifecycle callbacks |
|
and can use the `@PostConstruct` and `@PreDestroy` annotations from JSR-250, see |
|
<<beans-postconstruct-and-predestroy-annotations,JSR-250 annotations>> for further |
|
details. |
|
|
|
The regular Spring <<beans-factory-nature,lifecycle>> callbacks are fully supported as |
|
well. If a bean implements `InitializingBean`, `DisposableBean`, or `Lifecycle`, their |
|
respective methods are called by the container. |
|
|
|
The standard set of `*Aware` interfaces such as <<beans-beanfactory,BeanFactoryAware>>, |
|
<<beans-factory-aware,BeanNameAware>>, |
|
<<context-functionality-messagesource,MessageSourceAware>>, |
|
<<beans-factory-aware,ApplicationContextAware>>, and so on are also fully supported. |
|
|
|
The `@Bean` annotation supports specifying arbitrary initialization and destruction |
|
callback methods, much like Spring XML's `init-method` and `destroy-method` attributes |
|
on the `bean` element: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class Foo { |
|
public void init() { |
|
// initialization logic |
|
} |
|
} |
|
|
|
public class Bar { |
|
public void cleanup() { |
|
// destruction logic |
|
} |
|
} |
|
|
|
@Configuration |
|
public class AppConfig { |
|
|
|
@Bean(initMethod = "init") |
|
public Foo foo() { |
|
return new Foo(); |
|
} |
|
|
|
@Bean(destroyMethod = "cleanup") |
|
public Bar bar() { |
|
return new Bar(); |
|
} |
|
|
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
By default, beans defined using Java config that have a public `close` or `shutdown` |
|
method are automatically enlisted with a destruction callback. If you have a public |
|
`close` or `shutdown` method and you do not wish for it to be called when the container |
|
shuts down, simply add `@Bean(destroyMethod="")` to your bean definition to disable the |
|
default `(inferred)` mode. |
|
==== |
|
|
|
Of course, in the case of `Foo` above, it would be equally as valid to call the `init()` |
|
method directly during construction: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
public class AppConfig { |
|
@Bean |
|
public Foo foo() { |
|
Foo foo = new Foo(); |
|
foo.init(); |
|
return foo; |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
[TIP] |
|
==== |
|
|
|
When you work directly in Java, you can do anything you like with your objects and do |
|
not always need to rely on the container lifecycle! |
|
==== |
|
|
|
|
|
[[beans-java-specifying-bean-scope]] |
|
===== Specifying bean scope |
|
|
|
[[beans-java-available-scopes]] |
|
====== Using the @Scope annotation |
|
|
|
You can specify that your beans defined with the `@Bean` annotation should have a |
|
specific scope. You can use any of the standard scopes specified in the |
|
<<beans-factory-scopes,Bean Scopes>> section. |
|
|
|
The default scope is `singleton`, but you can override this with the `@Scope` annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
public class MyConfiguration { |
|
|
|
@Bean |
|
**@Scope("prototype")** |
|
public Encryptor encryptor() { |
|
// ... |
|
} |
|
|
|
} |
|
---- |
|
|
|
[[beans-java-scoped-proxy]] |
|
====== @Scope and scoped-proxy |
|
|
|
Spring offers a convenient way of working with scoped dependencies through |
|
<<beans-factory-scopes-other-injection,scoped proxies>>. The easiest way to create such |
|
a proxy when using the XML configuration is the `<aop:scoped-proxy/>` element. |
|
Configuring your beans in Java with a @Scope annotation offers equivalent support with |
|
the proxyMode attribute. The default is no proxy ( `ScopedProxyMode.NO`), but you can |
|
specify `ScopedProxyMode.TARGET_CLASS` or `ScopedProxyMode.INTERFACES`. |
|
|
|
If you port the scoped proxy example from the XML reference documentation (see preceding |
|
link) to our `@Bean` using Java, it would look like the following: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// an HTTP Session-scoped bean exposed as a proxy |
|
@Bean |
|
**@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)** |
|
public UserPreferences userPreferences() { |
|
return new UserPreferences(); |
|
} |
|
|
|
@Bean |
|
public Service userService() { |
|
UserService service = new SimpleUserService(); |
|
// a reference to the proxied userPreferences bean |
|
service.setUserPreferences(userPreferences()); |
|
return service; |
|
} |
|
---- |
|
|
|
|
|
[[beans-java-customizing-bean-naming]] |
|
===== Customizing bean naming |
|
By default, configuration classes use a `@Bean` method's name as the name of the |
|
resulting bean. This functionality can be overridden, however, with the `name` attribute. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
public class AppConfig { |
|
|
|
@Bean(name = "myFoo") |
|
public Foo foo() { |
|
return new Foo(); |
|
} |
|
|
|
} |
|
---- |
|
|
|
|
|
[[beans-java-bean-aliasing]] |
|
===== Bean aliasing |
|
As discussed in <<beans-beanname>>, it is sometimes desirable to give a single bean |
|
multiple names, otherwise known as__bean aliasing__. The `name` attribute of the `@Bean` |
|
annotation accepts a String array for this purpose. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
public class AppConfig { |
|
|
|
@Bean(name = { "dataSource", "subsystemA-dataSource", "subsystemB-dataSource" }) |
|
public DataSource dataSource() { |
|
// instantiate, configure and return DataSource bean... |
|
} |
|
|
|
} |
|
---- |
|
|
|
|
|
[[beans-java-bean-description]] |
|
===== Bean description |
|
Sometimes it is helpful to provide a more detailed textual description of a bean. This can |
|
be particularly useful when beans are exposed (perhaps via JMX) for monitoring purposes. |
|
|
|
To add a description to a `@Bean` the |
|
{javadoc-baseurl}/org/springframework/context/annotation/Description.html[`@Description`] |
|
annotation can be used: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
public class AppConfig { |
|
|
|
@Bean |
|
**@Description("Provides a basic example of a bean")** |
|
public Foo foo() { |
|
return new Foo(); |
|
} |
|
|
|
} |
|
---- |
|
|
|
|
|
[[beans-java-configuration-annotation]] |
|
==== Using the @Configuration annotation |
|
|
|
`@Configuration` is a class-level annotation indicating that an object is a source of |
|
bean definitions. `@Configuration` classes declare beans via public `@Bean` annotated |
|
methods. Calls to `@Bean` methods on `@Configuration` classes can also be used to define |
|
inter-bean dependencies. See <<beans-java-basic-concepts>> for a general introduction. |
|
|
|
|
|
[[beans-java-injecting-dependencies]] |
|
===== Injecting inter-bean dependencies |
|
When ++@Bean++s have dependencies on one another, expressing that dependency is as simple |
|
as having one bean method call another: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
public class AppConfig { |
|
|
|
@Bean |
|
public Foo foo() { |
|
return new Foo(bar()); |
|
} |
|
|
|
@Bean |
|
public Bar bar() { |
|
return new Bar(); |
|
} |
|
|
|
} |
|
---- |
|
|
|
In the example above, the `foo` bean receives a reference to `bar` via constructor |
|
injection. |
|
|
|
[NOTE] |
|
==== |
|
This method of declaring inter-bean dependencies only works when the `@Bean` method is |
|
declared within a `@Configuration` class. You cannot declare inter-bean dependencies |
|
using plain `@Component` classes. |
|
==== |
|
|
|
|
|
[[beans-java-method-injection]] |
|
===== Lookup method injection |
|
As noted earlier, <<beans-factory-method-injection,lookup method injection>> is an |
|
advanced feature that you should use rarely. It is useful in cases where a |
|
singleton-scoped bean has a dependency on a prototype-scoped bean. Using Java for this |
|
type of configuration provides a natural means for implementing this pattern. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public abstract class CommandManager { |
|
public Object process(Object commandState) { |
|
// grab a new instance of the appropriate Command interface |
|
Command command = createCommand(); |
|
|
|
// set the state on the (hopefully brand new) Command instance |
|
command.setState(commandState); |
|
return command.execute(); |
|
} |
|
|
|
// okay... but where is the implementation of this method? |
|
protected abstract Command createCommand(); |
|
} |
|
---- |
|
|
|
Using Java-configuration support , you can create a subclass of `CommandManager` where |
|
the abstract `createCommand()` method is overridden in such a way that it looks up a new |
|
(prototype) command object: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Bean |
|
@Scope("prototype") |
|
public AsyncCommand asyncCommand() { |
|
AsyncCommand command = new AsyncCommand(); |
|
// inject dependencies here as required |
|
return command; |
|
} |
|
|
|
@Bean |
|
public CommandManager commandManager() { |
|
// return new anonymous implementation of CommandManager with command() overridden |
|
// to return a new prototype Command object |
|
return new CommandManager() { |
|
protected Command createCommand() { |
|
return asyncCommand(); |
|
} |
|
} |
|
} |
|
---- |
|
|
|
|
|
[[beans-java-further-information-java-config]] |
|
===== Further information about how Java-based configuration works internally |
|
The following example shows a `@Bean` annotated method being called twice: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
public class AppConfig { |
|
|
|
@Bean |
|
public ClientService clientService1() { |
|
ClientServiceImpl clientService = new ClientServiceImpl(); |
|
clientService.setClientDao(clientDao()); |
|
return clientService; |
|
} |
|
|
|
@Bean |
|
public ClientService clientService2() { |
|
ClientServiceImpl clientService = new ClientServiceImpl(); |
|
clientService.setClientDao(clientDao()); |
|
return clientService; |
|
} |
|
|
|
@Bean |
|
public ClientDao clientDao() { |
|
return new ClientDaoImpl(); |
|
} |
|
|
|
} |
|
---- |
|
|
|
`clientDao()` has been called once in `clientService1()` and once in `clientService2()`. |
|
Since this method creates a new instance of `ClientDaoImpl` and returns it, you would |
|
normally expect having 2 instances (one for each service). That definitely would be |
|
problematic: in Spring, instantiated beans have a `singleton` scope by default. This is |
|
where the magic comes in: All `@Configuration` classes are subclassed at startup-time |
|
with `CGLIB`. In the subclass, the child method checks the container first for any |
|
cached (scoped) beans before it calls the parent method and creates a new instance. Note |
|
that as of Spring 3.2, it is no longer necessary to add CGLIB to your classpath because |
|
CGLIB classes have been repackaged under org.springframework and included directly |
|
within the spring-core JAR. |
|
|
|
[NOTE] |
|
==== |
|
The behavior could be different according to the scope of your bean. We are talking |
|
about singletons here. |
|
==== |
|
|
|
[NOTE] |
|
==== |
|
There are a few restrictions due to the fact that CGLIB dynamically adds features at |
|
startup-time: |
|
|
|
* Configuration classes should not be final |
|
* They should have a constructor with no arguments |
|
==== |
|
|
|
|
|
|
|
[[beans-java-composing-configuration-classes]] |
|
==== Composing Java-based configurations |
|
|
|
|
|
[[beans-java-using-import]] |
|
===== Using the @Import annotation |
|
|
|
Much as the `<import/>` element is used within Spring XML files to aid in modularizing |
|
configurations, the `@Import` annotation allows for loading `@Bean` definitions from |
|
another configuration class: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
public class ConfigA { |
|
|
|
@Bean |
|
public A a() { |
|
return new A(); |
|
} |
|
|
|
} |
|
|
|
@Configuration |
|
@Import(ConfigA.class) |
|
public class ConfigB { |
|
|
|
@Bean |
|
public B b() { |
|
return new B(); |
|
} |
|
|
|
} |
|
---- |
|
|
|
Now, rather than needing to specify both `ConfigA.class` and `ConfigB.class` when |
|
instantiating the context, only `ConfigB` needs to be supplied explicitly: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public static void main(String[] args) { |
|
ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class); |
|
|
|
// now both beans A and B will be available... |
|
A a = ctx.getBean(A.class); |
|
B b = ctx.getBean(B.class); |
|
} |
|
---- |
|
|
|
This approach simplifies container instantiation, as only one class needs to be dealt |
|
with, rather than requiring the developer to remember a potentially large number of |
|
`@Configuration` classes during construction. |
|
|
|
[[beans-java-injecting-imported-beans]] |
|
====== Injecting dependencies on imported @Bean definitions |
|
|
|
The example above works, but is simplistic. In most practical scenarios, beans will have |
|
dependencies on one another across configuration classes. When using XML, this is not an |
|
issue, per se, because there is no compiler involved, and one can simply declare |
|
`ref="someBean"` and trust that Spring will work it out during container initialization. |
|
Of course, when using `@Configuration` classes, the Java compiler places constraints on |
|
the configuration model, in that references to other beans must be valid Java syntax. |
|
|
|
Fortunately, solving this problem is simple. Remember that `@Configuration` classes are |
|
ultimately just another bean in the container - this means that they can take advantage |
|
of `@Autowired` injection metadata just like any other bean! |
|
|
|
Let's consider a more real-world scenario with several `@Configuration` classes, each |
|
depending on beans declared in the others: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
public class ServiceConfig { |
|
|
|
@Autowired |
|
private AccountRepository accountRepository; |
|
|
|
@Bean |
|
public TransferService transferService() { |
|
return new TransferServiceImpl(accountRepository); |
|
} |
|
|
|
} |
|
|
|
@Configuration |
|
public class RepositoryConfig { |
|
|
|
@Autowired |
|
private DataSource dataSource; |
|
|
|
@Bean |
|
public AccountRepository accountRepository() { |
|
return new JdbcAccountRepository(dataSource); |
|
} |
|
|
|
} |
|
|
|
@Configuration |
|
@Import({ServiceConfig.class, RepositoryConfig.class}) |
|
public class SystemTestConfig { |
|
|
|
@Bean |
|
public DataSource dataSource() { |
|
// return new DataSource |
|
} |
|
|
|
} |
|
|
|
public static void main(String[] args) { |
|
ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class); |
|
// everything wires up across configuration classes... |
|
TransferService transferService = ctx.getBean(TransferService.class); |
|
transferService.transfer(100.00, "A123", "C456"); |
|
} |
|
---- |
|
|
|
.[[beans-java-injecting-imported-beans-fq]]Fully-qualifying imported beans for ease of navigation |
|
-- |
|
In the scenario above, using `@Autowired` works well and provides the desired |
|
modularity, but determining exactly where the autowired bean definitions are declared is |
|
still somewhat ambiguous. For example, as a developer looking at `ServiceConfig`, how do |
|
you know exactly where the `@Autowired AccountRepository` bean is declared? It's not |
|
explicit in the code, and this may be just fine. Remember that the |
|
https://spring.io/tools/sts[Spring Tool Suite] provides tooling that |
|
can render graphs showing how everything is wired up - that may be all you need. Also, |
|
your Java IDE can easily find all declarations and uses of the `AccountRepository` type, |
|
and will quickly show you the location of `@Bean` methods that return that type. |
|
|
|
In cases where this ambiguity is not acceptable and you wish to have direct navigation |
|
from within your IDE from one `@Configuration` class to another, consider autowiring the |
|
configuration classes themselves: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
public class ServiceConfig { |
|
|
|
@Autowired |
|
private RepositoryConfig repositoryConfig; |
|
|
|
@Bean |
|
public TransferService transferService() { |
|
// navigate 'through' the config class to the @Bean method! |
|
return new TransferServiceImpl(repositoryConfig.accountRepository()); |
|
} |
|
|
|
} |
|
---- |
|
|
|
In the situation above, it is completely explicit where `AccountRepository` is defined. |
|
However, `ServiceConfig` is now tightly coupled to `RepositoryConfig`; that's the |
|
tradeoff. This tight coupling can be somewhat mitigated by using interface-based or |
|
abstract class-based `@Configuration` classes. Consider the following: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
public class ServiceConfig { |
|
|
|
@Autowired |
|
private RepositoryConfig repositoryConfig; |
|
|
|
@Bean |
|
public TransferService transferService() { |
|
return new TransferServiceImpl(repositoryConfig.accountRepository()); |
|
} |
|
} |
|
|
|
@Configuration |
|
public interface RepositoryConfig { |
|
|
|
@Bean |
|
AccountRepository accountRepository(); |
|
|
|
} |
|
|
|
@Configuration |
|
public class DefaultRepositoryConfig implements RepositoryConfig { |
|
|
|
@Bean |
|
public AccountRepository accountRepository() { |
|
return new JdbcAccountRepository(...); |
|
} |
|
|
|
} |
|
|
|
@Configuration |
|
@Import({ServiceConfig.class, DefaultRepositoryConfig.class}) // import the concrete config! |
|
public class SystemTestConfig { |
|
|
|
@Bean |
|
public DataSource dataSource() { |
|
// return DataSource |
|
} |
|
|
|
} |
|
|
|
public static void main(String[] args) { |
|
ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class); |
|
TransferService transferService = ctx.getBean(TransferService.class); |
|
transferService.transfer(100.00, "A123", "C456"); |
|
} |
|
---- |
|
|
|
Now `ServiceConfig` is loosely coupled with respect to the concrete |
|
`DefaultRepositoryConfig`, and built-in IDE tooling is still useful: it will be easy for |
|
the developer to get a type hierarchy of `RepositoryConfig` implementations. In this |
|
way, navigating `@Configuration` classes and their dependencies becomes no different |
|
than the usual process of navigating interface-based code. |
|
-- |
|
|
|
|
|
[[beans-java-conditional]] |
|
===== Conditionally including @Configuration classes or @Beans |
|
It is often useful to conditionally enable to disable a complete `@Configuration` class, |
|
or even individual `@Bean` methods, based on some arbitrary system state. One common |
|
example of this it to use the `@Profile` annotation to active beans only when a specific |
|
profile has been enabled in the Spring `Environment` (see <<beans-definition-profiles>> |
|
for details). |
|
|
|
The `@Profile` annotation is actually implemented using a much more flexible annotation |
|
called {javadoc-baseurl}/org/springframework/context/annotation/Conditional.html[`@Conditional`]. |
|
The `@Conditional` annotation indicates specific |
|
`org.springframework.context.annotation.Condition` implementations that should be |
|
consulted before a `@Bean` is registered. |
|
|
|
Implementations of the `Condition` interface simply provide a `matches(...)` |
|
method that returns `true` or `false`. For example, here is the actual |
|
`Condition` implementation used for `@Profile`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Override |
|
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { |
|
if (context.getEnvironment() != null) { |
|
// Read the @Profile annotation attributes |
|
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName()); |
|
if (attrs != null) { |
|
for (Object value : attrs.get("value")) { |
|
if (context.getEnvironment().acceptsProfiles(((String[]) value))) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
---- |
|
|
|
See the {javadoc-baseurl}/org/springframework/context/annotation/Conditional.html[ |
|
`@Conditional` javadocs] for more detail. |
|
|
|
[[beans-java-combining]] |
|
===== Combining Java and XML configuration |
|
Spring's `@Configuration` class support does not aim to be a 100% complete replacement |
|
for Spring XML. Some facilities such as Spring XML namespaces remain an ideal way to |
|
configure the container. In cases where XML is convenient or necessary, you have a |
|
choice: either instantiate the container in an "XML-centric" way using, for example, |
|
`ClassPathXmlApplicationContext`, or in a "Java-centric" fashion using |
|
`AnnotationConfigApplicationContext` and the `@ImportResource` annotation to import XML |
|
as needed. |
|
|
|
[[beans-java-combining-xml-centric]] |
|
====== XML-centric use of @Configuration classes |
|
|
|
It may be preferable to bootstrap the Spring container from XML and include |
|
`@Configuration` classes in an ad-hoc fashion. For example, in a large existing codebase |
|
that uses Spring XML, it will be easier to create `@Configuration` classes on an |
|
as-needed basis and include them from the existing XML files. Below you'll find the |
|
options for using `@Configuration` classes in this kind of "XML-centric" situation. |
|
|
|
.[[beans-java-combining-xml-centric-declare-as-bean]]Declaring @Configuration classes as plain Spring `<bean/>` elements |
|
-- |
|
Remember that `@Configuration` classes are ultimately just bean definitions in the |
|
container. In this example, we create a `@Configuration` class named `AppConfig` and |
|
include it within `system-test-config.xml` as a `<bean/>` definition. Because |
|
`<context:annotation-config/>` is switched on, the container will recognize the |
|
`@Configuration` annotation, and process the `@Bean` methods declared in `AppConfig` |
|
properly. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
public class AppConfig { |
|
|
|
@Autowired |
|
private DataSource dataSource; |
|
|
|
@Bean |
|
public AccountRepository accountRepository() { |
|
return new JdbcAccountRepository(dataSource); |
|
} |
|
|
|
@Bean |
|
public TransferService transferService() { |
|
return new TransferService(accountRepository()); |
|
} |
|
|
|
} |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
system-test-config.xml |
|
<beans> |
|
<!-- enable processing of annotations such as @Autowired and @Configuration --> |
|
<context:annotation-config/> |
|
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/> |
|
|
|
<bean class="com.acme.AppConfig"/> |
|
|
|
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource"> |
|
<property name="url" value="${jdbc.url}"/> |
|
<property name="username" value="${jdbc.username}"/> |
|
<property name="password" value="${jdbc.password}"/> |
|
</bean> |
|
</beans> |
|
---- |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
jdbc.properties |
|
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb |
|
jdbc.username=sa |
|
jdbc.password= |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public static void main(String[] args) { |
|
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml"); |
|
TransferService transferService = ctx.getBean(TransferService.class); |
|
// ... |
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
In `system-test-config.xml` above, the `AppConfig<bean/>` does not declare an `id` |
|
element. While it would be acceptable to do so, it is unnecessary given that no other |
|
bean will ever refer to it, and it is unlikely that it will be explicitly fetched from |
|
the container by name. Likewise with the `DataSource` bean - it is only ever autowired |
|
by type, so an explicit bean id is not strictly required. |
|
==== |
|
-- |
|
|
|
.[[beans-java-combining-xml-centric-component-scan]] Using <context:component-scan/> to pick up `@Configuration` classes |
|
-- |
|
Because `@Configuration` is meta-annotated with `@Component`, `@Configuration`-annotated |
|
classes are automatically candidates for component scanning. Using the same scenario as |
|
above, we can redefine `system-test-config.xml` to take advantage of component-scanning. |
|
Note that in this case, we don't need to explicitly declare |
|
`<context:annotation-config/>`, because `<context:component-scan/>` enables all the same |
|
functionality. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
system-test-config.xml |
|
<beans> |
|
<!-- picks up and registers AppConfig as a bean definition --> |
|
<context:component-scan base-package="com.acme"/> |
|
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/> |
|
|
|
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource"> |
|
<property name="url" value="${jdbc.url}"/> |
|
<property name="username" value="${jdbc.username}"/> |
|
<property name="password" value="${jdbc.password}"/> |
|
</bean> |
|
</beans> |
|
---- |
|
-- |
|
|
|
[[beans-java-combining-java-centric]] |
|
====== @Configuration class-centric use of XML with @ImportResource |
|
|
|
In applications where `@Configuration` classes are the primary mechanism for configuring |
|
the container, it will still likely be necessary to use at least some XML. In these |
|
scenarios, simply use `@ImportResource` and define only as much XML as is needed. Doing |
|
so achieves a "Java-centric" approach to configuring the container and keeps XML to a |
|
bare minimum. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@ImportResource("classpath:/com/acme/properties-config.xml") |
|
public class AppConfig { |
|
|
|
@Value("${jdbc.url}") |
|
private String url; |
|
|
|
@Value("${jdbc.username}") |
|
private String username; |
|
|
|
@Value("${jdbc.password}") |
|
private String password; |
|
|
|
@Bean |
|
public DataSource dataSource() { |
|
return new DriverManagerDataSource(url, username, password); |
|
} |
|
|
|
} |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
properties-config.xml |
|
<beans> |
|
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/> |
|
</beans> |
|
---- |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
jdbc.properties |
|
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb |
|
jdbc.username=sa |
|
jdbc.password= |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public static void main(String[] args) { |
|
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); |
|
TransferService transferService = ctx.getBean(TransferService.class); |
|
// ... |
|
} |
|
---- |
|
|
|
[[beans-environment]] |
|
=== Environment abstraction |
|
|
|
The {javadoc-baseurl}/org/springframework/core/env/Environment.html[`Environment`] |
|
is an abstraction integrated in the container that models two key |
|
aspects of the application environment: <<beans-definition-profiles,_profiles_>> |
|
and <<beans-property-source-abstraction,_properties_>>. |
|
|
|
A _profile_ is a named, logical group of bean definitions to be registered with the |
|
container only if the given profile is active. Beans may be assigned to a profile |
|
whether defined in XML or via annotations. The role of the `Environment` object with |
|
relation to profiles is in determining which profiles (if any) are currently active, |
|
and which profiles (if any) should be active by default. |
|
|
|
Properties play an important role in almost all applications, and may originate from |
|
a variety of sources: properties files, JVM system properties, system environment |
|
variables, JNDI, servlet context parameters, ad-hoc Properties objects, Maps, and so |
|
on. The role of the `Environment` object with relation to properties is to provide the |
|
user with a convenient service interface for configuring property sources and resolving |
|
properties from them. |
|
|
|
[[beans-definition-profiles]] |
|
==== Bean definition profiles |
|
|
|
Bean definition profiles is a mechanism in the core container that allows for |
|
registration of different beans in different environments. The word _environment_ |
|
can mean different things to different users and this feature can help with many |
|
use cases, including: |
|
|
|
* working against an in-memory datasource in development vs looking up that same |
|
datasource from JNDI when in QA or production |
|
* registering monitoring infrastructure only when deploying an application into a |
|
performance environment |
|
* registering customized implementations of beans for customer A vs. customer |
|
B deployments |
|
|
|
Let's consider the first use case in a practical application that requires a |
|
`DataSource`. In a test environment, the configuration may look like this: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Bean |
|
public DataSource dataSource() { |
|
return new EmbeddedDatabaseBuilder() |
|
.setType(EmbeddedDatabaseType.HSQL) |
|
.addScript("my-schema.sql") |
|
.addScript("my-test-data.sql") |
|
.build(); |
|
} |
|
---- |
|
|
|
Let's now consider how this application will be deployed into a QA or production |
|
environment, assuming that the datasource for the application will be registered |
|
with the production application server's JNDI directory. Our `dataSource` bean |
|
now looks like this: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Bean |
|
public DataSource dataSource() throws Exception { |
|
Context ctx = new InitialContext(); |
|
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource"); |
|
} |
|
---- |
|
|
|
The problem is how to switch between using these two variations based on the |
|
current environment. Over time, Spring users have devised a number of ways to |
|
get this done, usually relying on a combination of system environment variables |
|
and XML `<import/>` statements containing `${placeholder}` tokens that resolve |
|
to the correct configuration file path depending on the value of an environment |
|
variable. Bean definition profiles is a core container feature that provides a |
|
solution to this problem. |
|
|
|
If we generalize the example use case above of environment-specific bean |
|
definitions, we end up with the need to register certain bean definitions in |
|
certain contexts, while not in others. You could say that you want to register a |
|
certain profile of bean definitions in situation A, and a different profile in |
|
situation B. Let's first see how we can update our configuration to reflect |
|
this need. |
|
|
|
[[beans-definition-profiles-java]] |
|
===== @Profile |
|
|
|
The {javadoc-baseurl}/org/springframework/context/annotation/Profile.html[`@Profile`] |
|
annotation allows to indicate that a component is eligible for registration |
|
when one or more specified profiles are active. Using our example above, we |
|
can rewrite the _dataSource_ configuration as follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
**@Profile("dev")** |
|
public class StandaloneDataConfig { |
|
|
|
@Bean |
|
public DataSource dataSource() { |
|
return new EmbeddedDatabaseBuilder() |
|
.setType(EmbeddedDatabaseType.HSQL) |
|
.addScript("classpath:com/bank/config/sql/schema.sql") |
|
.addScript("classpath:com/bank/config/sql/test-data.sql") |
|
.build(); |
|
} |
|
} |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
**@Profile("production")** |
|
public class JndiDataConfig { |
|
|
|
@Bean |
|
public DataSource dataSource() throws Exception { |
|
Context ctx = new InitialContext(); |
|
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource"); |
|
} |
|
} |
|
---- |
|
|
|
|
|
`@Profile` can be used as a meta-annotation, for the purpose of composing |
|
custom stereotype annotations. The following example defines a `@Production` |
|
custom annotation that can be used as a drop-in replacement of |
|
`@Profile("production")`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Target(ElementType.TYPE) |
|
@Retention(RetentionPolicy.RUNTIME) |
|
**@Profile("production")** |
|
public @interface Production { |
|
} |
|
---- |
|
|
|
`@Profile` can also be specified at method-level to include only one particular |
|
bean of a configuration class: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
public class AppConfig { |
|
|
|
@Bean |
|
**@Profile("dev")** |
|
public DataSource devDataSource() { |
|
return new EmbeddedDatabaseBuilder() |
|
.setType(EmbeddedDatabaseType.HSQL) |
|
.addScript("classpath:com/bank/config/sql/schema.sql") |
|
.addScript("classpath:com/bank/config/sql/test-data.sql") |
|
.build(); |
|
} |
|
|
|
@Bean |
|
**@Profile("production")** |
|
public DataSource productionDataSource() throws Exception { |
|
Context ctx = new InitialContext(); |
|
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource"); |
|
} |
|
} |
|
---- |
|
|
|
[TIP] |
|
==== |
|
If a `@Configuration` class is marked with `@Profile`, all of the `@Bean` methods |
|
and `@Import` annotations associated with that class will be bypassed unless one |
|
or more of the specified profiles are active. If a `@Component` or `@Configuration` |
|
class is marked with `@Profile({"p1", "p2"})`, that class will not be registered/ |
|
processed unless profiles 'p1' and/or 'p2' have been activated. If a given profile |
|
is prefixed with the NOT operator (`!`), the annotated element will be registered |
|
if the profile is **not** active. e.g., for `@Profile({"p1", "!p2"})`, registration |
|
will occur if profile 'p1' is active or if profile 'p2' is not active. |
|
==== |
|
|
|
[[beans-definition-profiles-xml]] |
|
==== XML Bean definition profiles |
|
|
|
The XML counterpart is an update of the `beans` element that accepts a |
|
`profile` attribute. Our sample configuration above can be rewritten in two XML |
|
files as follows: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans profile="dev" |
|
xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:jdbc="http://www.springframework.org/schema/jdbc" |
|
xsi:schemaLocation="..."> |
|
|
|
<jdbc:embedded-database id="dataSource"> |
|
<jdbc:script location="classpath:com/bank/config/sql/schema.sql"/> |
|
<jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/> |
|
</jdbc:embedded-database> |
|
</beans> |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans profile="production" |
|
xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:jee="http://www.springframework.org/schema/jee" |
|
xsi:schemaLocation="..."> |
|
|
|
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/> |
|
</beans> |
|
---- |
|
|
|
It is also possible to avoid that split and nest `<beans/>` elements within the same file: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:jdbc="http://www.springframework.org/schema/jdbc" |
|
xmlns:jee="http://www.springframework.org/schema/jee" |
|
xsi:schemaLocation="..."> |
|
|
|
<!-- other bean definitions --> |
|
|
|
<beans profile="dev"> |
|
<jdbc:embedded-database id="dataSource"> |
|
<jdbc:script location="classpath:com/bank/config/sql/schema.sql"/> |
|
<jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/> |
|
</jdbc:embedded-database> |
|
</beans> |
|
|
|
<beans profile="production"> |
|
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/> |
|
</beans> |
|
</beans> |
|
---- |
|
|
|
The `spring-bean.xsd` has been constrained to allow such elements only as the |
|
last ones in the file. This should help provide flexibility without incurring |
|
clutter in the XML files. |
|
|
|
[[beans-definition-profiles-enable]] |
|
===== Enabling a profile |
|
|
|
Now that we have updated our configuration, we still need to instruct which |
|
profile is active. If we started our sample application right now, we would see |
|
a `NoSuchBeanDefinitionException` thrown, because the container could not find |
|
the Spring bean named `dataSource`. |
|
|
|
Activating a profile can be done in several ways, but the most straightforward |
|
is to do it programmatically against the `ApplicationContext` API: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); |
|
ctx.getEnvironment().setActiveProfiles("dev"); |
|
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class); |
|
ctx.refresh(); |
|
---- |
|
|
|
In addition, profiles may also be activated declaratively through the `spring.profiles.active` |
|
property which may be specified through system environment variables, JVM system properties, |
|
servlet context parameters in `web.xml` or even as an entry in JNDI (see |
|
<<beans-property-source-abstraction>>). |
|
|
|
Note that profiles are not an "either-or" proposition; it is possible to activate multiple |
|
profiles at once. Programmatically, simply provide multiple profile names to the |
|
`setActiveProfiles()` method, which accepts `String...` varargs: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ctx.getEnvironment().setActiveProfiles("profile1", "profile2"); |
|
---- |
|
|
|
Declaratively, `spring.profiles.active` may accept a comma-separated list of profile names: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
-Dspring.profiles.active="profile1,profile2" |
|
---- |
|
|
|
[[beans-definition-profiles-default]] |
|
===== Default profile |
|
|
|
The _default_ profile represents the profile that is enabled by default. Consider the |
|
following: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
**@Profile("default")** |
|
public class DefaultDataConfig { |
|
|
|
@Bean |
|
public DataSource dataSource() { |
|
return new EmbeddedDatabaseBuilder() |
|
.setType(EmbeddedDatabaseType.HSQL) |
|
.addScript("classpath:com/bank/config/sql/schema.sql") |
|
.build(); |
|
} |
|
} |
|
---- |
|
|
|
If no profile is active, the `dataSource` above will be created; this can be |
|
seen as a way to provide a _default_ definition for one or more beans. If any |
|
profile is enabled, the _default_ profile will not apply. |
|
|
|
The name of that default profile can be changed using `setDefaultProfiles` on |
|
the `Environment` or declaratively using the `spring.profiles.default` property. |
|
|
|
[[beans-property-source-abstraction]] |
|
==== PropertySource Abstraction |
|
|
|
Spring's Environment abstraction provides search operations over a configurable |
|
hierarchy of property sources. To explain fully, consider the following: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ApplicationContext ctx = new GenericApplicationContext(); |
|
Environment env = ctx.getEnvironment(); |
|
boolean containsFoo = env.containsProperty("foo"); |
|
System.out.println("Does my environment contain the ''foo'' property? " + containsFoo); |
|
---- |
|
|
|
In the snippet above, we see a high-level way of asking Spring whether the `foo` property is |
|
defined for the current environment. To answer this question, the `Environment` object performs |
|
a search over a set of {javadoc-baseurl}/org/springframework/core/env/PropertySource.html[`PropertySource`] |
|
objects. A `PropertySource` is a simple abstraction over any source of key-value pairs, and |
|
Spring's {javadoc-baseurl}/org/springframework/core/env/StandardEnvironment.html[`StandardEnvironment`] |
|
is configured with two PropertySource objects -- one representing the set of JVM system properties |
|
(_a la_ `System.getProperties()`) and one representing the set of system environment variables |
|
(_a la_ `System.getenv()`). |
|
|
|
[NOTE] |
|
==== |
|
These default property sources are present for `StandardEnvironment`, for use in standalone |
|
applications. {javadoc-baseurl}/org/springframework/web/context/support/StandardServletEnvironment.html[`StandardServletEnvironment`] |
|
is populated with additional default property sources including servlet config and servlet |
|
context parameters. {javadoc-baseurl}/org/springframework/web/portlet/context/StandardPortletEnvironment.html[`StandardPortletEnvironment`] |
|
similarly has access to portlet config and portlet context parameters as property sources. |
|
Both can optionally enable a {javadoc-baseurl}/org/springframework/jndi/JndiPropertySource.html[`JndiPropertySource`]. |
|
See Javadoc for details. |
|
==== |
|
|
|
Concretely, when using the `StandardEnvironment`, the call to `env.containsProperty("foo")` |
|
will return true if a `foo` system property or `foo` environment variable is present at |
|
runtime. |
|
|
|
[TIP] |
|
==== |
|
The search performed is hierarchical. By default, system properties have precedence over |
|
environment variables, so if the `foo` property happens to be set in both places during |
|
a call to `env.getProperty("foo")`, the system property value will 'win' and be returned |
|
preferentially over the environment variable. |
|
==== |
|
|
|
Most importantly, the entire mechanism is configurable. Perhaps you have a custom source |
|
of properties that you'd like to integrate into this search. No problem -- simply implement |
|
and instantiate your own `PropertySource` and add it to the set of `PropertySources` for the |
|
current `Environment`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ConfigurableApplicationContext ctx = new GenericApplicationContext(); |
|
MutablePropertySources sources = ctx.getEnvironment().getPropertySources(); |
|
sources.addFirst(new MyPropertySource()); |
|
---- |
|
|
|
In the code above, `MyPropertySource` has been added with highest precedence in the |
|
search. If it contains a `foo` property, it will be detected and returned ahead of |
|
any `foo` property in any other `PropertySource`. The |
|
{javadoc-baseurl}/org/springframework/core/env/MutablePropertySources.html[`MutablePropertySources`] |
|
API exposes a number of methods that allow for precise manipulation of the set of |
|
property sources. |
|
|
|
==== @PropertySource |
|
|
|
The {javadoc-baseurl}/org/springframework/context/annotation/PropertySource.html[`@PropertySource`] |
|
annotation provides a convenient and declarative mechanism for adding a `PropertySource` |
|
to Spring's `Environment`. |
|
|
|
Given a file "app.properties" containing the key/value pair `testbean.name=myTestBean`, |
|
the following `@Configuration` class uses `@PropertySource` in such a way that |
|
a call to `testBean.getName()` will return "myTestBean". |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
**@PropertySource("classpath:/com/myco/app.properties")** |
|
public class AppConfig { |
|
@Autowired |
|
Environment env; |
|
|
|
@Bean |
|
public TestBean testBean() { |
|
TestBean testBean = new TestBean(); |
|
testBean.setName(env.getProperty("testbean.name")); |
|
return testBean; |
|
} |
|
} |
|
---- |
|
|
|
Any `${...}` placeholders present in a `@PropertySource` resource location will |
|
be resolved against the set of property sources already registered against the |
|
environment. For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties") |
|
public class AppConfig { |
|
@Autowired |
|
Environment env; |
|
|
|
@Bean |
|
public TestBean testBean() { |
|
TestBean testBean = new TestBean(); |
|
testBean.setName(env.getProperty("testbean.name")); |
|
return testBean; |
|
} |
|
} |
|
---- |
|
|
|
Assuming that "my.placeholder" is present in one of the property sources already |
|
registered, e.g. system properties or environment variables, the placeholder will |
|
be resolved to the corresponding value. If not, then "default/path" will be used |
|
as a default. If no default is specified and a property cannot be resolved, an |
|
`IllegalArgumentException` will be thrown. |
|
|
|
|
|
==== Placeholder resolution in statements |
|
|
|
Historically, the value of placeholders in elements could be resolved only against |
|
JVM system properties or environment variables. No longer is this the case. Because |
|
the Environment abstraction is integrated throughout the container, it's easy to |
|
route resolution of placeholders through it. This means that you may configure the |
|
resolution process in any way you like: change the precedence of searching through |
|
system properties and environment variables, or remove them entirely; add your |
|
own property sources to the mix as appropriate. |
|
|
|
Concretely, the following statement works regardless of where the `customer` |
|
property is defined, as long as it is available in the `Environment`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<import resource="com/bank/service/${customer}-config.xml"/> |
|
</beans> |
|
---- |
|
|
|
|
|
[[context-load-time-weaver]] |
|
=== Registering a LoadTimeWeaver |
|
|
|
The `LoadTimeWeaver` is used by Spring to dynamically transform classes as they are |
|
loaded into the Java virtual machine (JVM). |
|
|
|
To enable load-time weaving add the `@EnableLoadTimeWeaving` to one of your |
|
`@Configuration` classes: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableLoadTimeWeaving |
|
public class AppConfig { |
|
|
|
} |
|
---- |
|
|
|
Alternatively for XML configuration use the `context:load-time-weaver` element: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<context:load-time-weaver/> |
|
</beans> |
|
---- |
|
|
|
Once configured for the `ApplicationContext`. Any bean within that `ApplicationContext` |
|
may implement `LoadTimeWeaverAware`, thereby receiving a reference to the load-time |
|
weaver instance. This is particularly useful in combination with <<orm-jpa,Spring's JPA |
|
support>> where load-time weaving may be necessary for JPA class transformation. Consult |
|
the `LocalContainerEntityManagerFactoryBean` javadocs for more detail. For more on |
|
AspectJ load-time weaving, see <<aop-aj-ltw>>. |
|
|
|
|
|
|
|
|
|
[[context-introduction]] |
|
=== Additional Capabilities of the ApplicationContext |
|
|
|
As was discussed in the chapter introduction, the `org.springframework.beans.factory` |
|
package provides basic functionality for managing and manipulating beans, including in a |
|
programmatic way. The `org.springframework.context` package adds the |
|
{javadoc-baseurl}/org/springframework/context/ApplicationContext.html[`ApplicationContext`] |
|
interface, which extends the `BeanFactory` interface, in addition to extending other |
|
interfaces to provide additional functionality in a more __application |
|
framework-oriented style__. Many people use the `ApplicationContext` in a completely |
|
declarative fashion, not even creating it programmatically, but instead relying on |
|
support classes such as `ContextLoader` to automatically instantiate an |
|
`ApplicationContext` as part of the normal startup process of a Java EE web application. |
|
|
|
To enhance `BeanFactory` functionality in a more framework-oriented style the context |
|
package also provides the following functionality: |
|
|
|
* __Access to messages in i18n-style__, through the `MessageSource` interface. |
|
* __Access to resources__, such as URLs and files, through the `ResourceLoader` interface. |
|
* __Event publication__ to beans implementing the `ApplicationListener` interface, |
|
through the use of the `ApplicationEventPublisher` interface. |
|
* __Loading of multiple (hierarchical) contexts__, allowing each to be focused on one |
|
particular layer, such as the web layer of an application, through the |
|
`HierarchicalBeanFactory` interface. |
|
|
|
|
|
|
|
[[context-functionality-messagesource]] |
|
==== Internationalization using MessageSource |
|
|
|
The `ApplicationContext` interface extends an interface called `MessageSource`, and |
|
therefore provides internationalization (i18n) functionality. Spring also provides the |
|
interface `HierarchicalMessageSource`, which can resolve messages hierarchically. |
|
Together these interfaces provide the foundation upon which Spring effects message |
|
resolution. The methods defined on these interfaces include: |
|
|
|
* `String getMessage(String code, Object[] args, String default, Locale loc)`: The basic |
|
method used to retrieve a message from the `MessageSource`. When no message is found |
|
for the specified locale, the default message is used. Any arguments passed in become |
|
replacement values, using the `MessageFormat` functionality provided by the standard |
|
library. |
|
* `String getMessage(String code, Object[] args, Locale loc)`: Essentially the same as |
|
the previous method, but with one difference: no default message can be specified; if |
|
the message cannot be found, a `NoSuchMessageException` is thrown. |
|
* `String getMessage(MessageSourceResolvable resolvable, Locale locale)`: All properties |
|
used in the preceding methods are also wrapped in a class named |
|
`MessageSourceResolvable`, which you can use with this method. |
|
|
|
When an `ApplicationContext` is loaded, it automatically searches for a `MessageSource` |
|
bean defined in the context. The bean must have the name `messageSource`. If such a bean |
|
is found, all calls to the preceding methods are delegated to the message source. If no |
|
message source is found, the `ApplicationContext` attempts to find a parent containing a |
|
bean with the same name. If it does, it uses that bean as the `MessageSource`. If the |
|
`ApplicationContext` cannot find any source for messages, an empty |
|
`DelegatingMessageSource` is instantiated in order to be able to accept calls to the |
|
methods defined above. |
|
|
|
Spring provides two `MessageSource` implementations, `ResourceBundleMessageSource` and |
|
`StaticMessageSource`. Both implement `HierarchicalMessageSource` in order to do nested |
|
messaging. The `StaticMessageSource` is rarely used but provides programmatic ways to |
|
add messages to the source. The `ResourceBundleMessageSource` is shown in the following |
|
example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<bean id="messageSource" |
|
class="org.springframework.context.support.ResourceBundleMessageSource"> |
|
<property name="basenames"> |
|
<list> |
|
<value>format</value> |
|
<value>exceptions</value> |
|
<value>windows</value> |
|
</list> |
|
</property> |
|
</bean> |
|
</beans> |
|
---- |
|
|
|
In the example it is assumed you have three resource bundles defined in your classpath |
|
called `format`, `exceptions` and `windows`. Any request to resolve a message will be |
|
handled in the JDK standard way of resolving messages through ResourceBundles. For the |
|
purposes of the example, assume the contents of two of the above resource bundle files |
|
are... |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
# in format.properties |
|
message=Alligators rock! |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
# in exceptions.properties |
|
argument.required=The '{0}' argument is required. |
|
---- |
|
|
|
A program to execute the `MessageSource` functionality is shown in the next example. |
|
Remember that all `ApplicationContext` implementations are also `MessageSource` |
|
implementations and so can be cast to the `MessageSource` interface. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public static void main(String[] args) { |
|
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml"); |
|
String message = resources.getMessage("message", null, "Default", null); |
|
System.out.println(message); |
|
} |
|
---- |
|
|
|
The resulting output from the above program will be... |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Alligators rock! |
|
---- |
|
|
|
So to summarize, the `MessageSource` is defined in a file called `beans.xml`, which |
|
exists at the root of your classpath. The `messageSource` bean definition refers to a |
|
number of resource bundles through its `basenames` property. The three files that are |
|
passed in the list to the `basenames` property exist as files at the root of your |
|
classpath and are called `format.properties`, `exceptions.properties`, and |
|
`windows.properties` respectively. |
|
|
|
The next example shows arguments passed to the message lookup; these arguments will be |
|
converted into Strings and inserted into placeholders in the lookup message. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<!-- this MessageSource is being used in a web application --> |
|
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> |
|
<property name="basename" value="exceptions"/> |
|
</bean> |
|
|
|
<!-- lets inject the above MessageSource into this POJO --> |
|
<bean id="example" class="com.foo.Example"> |
|
<property name="messages" ref="messageSource"/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class Example { |
|
|
|
private MessageSource messages; |
|
|
|
public void setMessages(MessageSource messages) { |
|
this.messages = messages; |
|
} |
|
|
|
public void execute() { |
|
String message = this.messages.getMessage("argument.required", |
|
new Object [] {"userDao"}, "Required", null); |
|
System.out.println(message); |
|
} |
|
|
|
} |
|
---- |
|
|
|
The resulting output from the invocation of the `execute()` method will be... |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
The userDao argument is required. |
|
---- |
|
|
|
With regard to internationalization (i18n), Spring's various `MessageResource` |
|
implementations follow the same locale resolution and fallback rules as the standard JDK |
|
`ResourceBundle`. In short, and continuing with the example `messageSource` defined |
|
previously, if you want to resolve messages against the British (`en-GB`) locale, you |
|
would create files called `format_en_GB.properties`, `exceptions_en_GB.properties`, and |
|
`windows_en_GB.properties` respectively. |
|
|
|
Typically, locale resolution is managed by the surrounding environment of the |
|
application. In this example, the locale against which (British) messages will be |
|
resolved is specified manually. |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
# in exceptions_en_GB.properties |
|
argument.required=Ebagum lad, the '{0}' argument is required, I say, required. |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public static void main(final String[] args) { |
|
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml"); |
|
String message = resources.getMessage("argument.required", |
|
new Object [] {"userDao"}, "Required", Locale.UK); |
|
System.out.println(message); |
|
} |
|
---- |
|
|
|
The resulting output from the running of the above program will be... |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Ebagum lad, the 'userDao' argument is required, I say, required. |
|
---- |
|
|
|
You can also use the `MessageSourceAware` interface to acquire a reference to any |
|
`MessageSource` that has been defined. Any bean that is defined in an |
|
`ApplicationContext` that implements the `MessageSourceAware` interface is injected with |
|
the application context's `MessageSource` when the bean is created and configured. |
|
|
|
[NOTE] |
|
==== |
|
__As an alternative to `ResourceBundleMessageSource`, Spring provides a |
|
`ReloadableResourceBundleMessageSource` class. This variant supports the same bundle |
|
file format but is more flexible than the standard JDK based |
|
`ResourceBundleMessageSource` implementation.__ In particular, it allows for reading |
|
files from any Spring resource location (not just from the classpath) and supports hot |
|
reloading of bundle property files (while efficiently caching them in between). Check |
|
out the `ReloadableResourceBundleMessageSource` javadocs for details. |
|
==== |
|
|
|
|
|
|
|
[[context-functionality-events]] |
|
==== Standard and Custom Events |
|
Event handling in the `ApplicationContext` is provided through the `ApplicationEvent` |
|
class and `ApplicationListener` interface. If a bean that implements the |
|
`ApplicationListener` interface is deployed into the context, every time an |
|
`ApplicationEvent` gets published to the `ApplicationContext`, that bean is notified. |
|
Essentially, this is the standard __Observer__ design pattern. Spring provides the |
|
following standard events: |
|
|
|
[[beans-ctx-events-tbl]] |
|
.Built-in Events |
|
|=== |
|
| Event| Explanation |
|
|
|
| `ContextRefreshedEvent` |
|
| Published when the `ApplicationContext` is initialized or refreshed, for example, |
|
using the `refresh()` method on the `ConfigurableApplicationContext` interface. |
|
"Initialized" here means that all beans are loaded, post-processor beans are detected |
|
and activated, singletons are pre-instantiated, and the `ApplicationContext` object is |
|
ready for use. As long as the context has not been closed, a refresh can be triggered |
|
multiple times, provided that the chosen `ApplicationContext` actually supports such |
|
"hot" refreshes. For example, `XmlWebApplicationContext` supports hot refreshes, but |
|
`GenericApplicationContext` does not. |
|
|
|
| `ContextStartedEvent` |
|
| Published when the `ApplicationContext` is started, using the `start()` method on the |
|
`ConfigurableApplicationContext` interface. "Started" here means that all `Lifecycle` |
|
beans receive an explicit start signal. Typically this signal is used to restart beans |
|
after an explicit stop, but it may also be used to start components that have not been |
|
configured for autostart , for example, components that have not already started on |
|
initialization. |
|
|
|
| `ContextStoppedEvent` |
|
| Published when the `ApplicationContext` is stopped, using the `stop()` method on the |
|
`ConfigurableApplicationContext` interface. "Stopped" here means that all `Lifecycle` |
|
beans receive an explicit stop signal. A stopped context may be restarted through a |
|
`start()` call. |
|
|
|
| `ContextClosedEvent` |
|
| Published when the `ApplicationContext` is closed, using the `close()` method on the |
|
`ConfigurableApplicationContext` interface. "Closed" here means that all singleton |
|
beans are destroyed. A closed context reaches its end of life; it cannot be refreshed |
|
or restarted. |
|
|
|
| `RequestHandledEvent` |
|
| A web-specific event telling all beans that an HTTP request has been serviced. This |
|
event is published __after__ the request is complete. This event is only applicable to |
|
web applications using Spring's `DispatcherServlet`. |
|
|=== |
|
|
|
You can also create and publish your own custom events. This example demonstrates a |
|
simple class that extends Spring's `ApplicationEvent` base class: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class BlackListEvent extends ApplicationEvent { |
|
|
|
private final String address; |
|
private final String test; |
|
|
|
public BlackListEvent(Object source, String address, String test) { |
|
super(source); |
|
this.address = address; |
|
this.test = test; |
|
} |
|
|
|
// accessor and other methods... |
|
|
|
} |
|
---- |
|
|
|
To publish a custom `ApplicationEvent`, call the `publishEvent()` method on an |
|
`ApplicationEventPublisher`. Typically this is done by creating a class that implements |
|
`ApplicationEventPublisherAware` and registering it as a Spring bean. The following |
|
example demonstrates such a class: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class EmailService implements ApplicationEventPublisherAware { |
|
|
|
private List<String> blackList; |
|
private ApplicationEventPublisher publisher; |
|
|
|
public void setBlackList(List<String> blackList) { |
|
this.blackList = blackList; |
|
} |
|
|
|
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { |
|
this.publisher = publisher; |
|
} |
|
|
|
public void sendEmail(String address, String text) { |
|
if (blackList.contains(address)) { |
|
BlackListEvent event = new BlackListEvent(this, address, text); |
|
publisher.publishEvent(event); |
|
return; |
|
} |
|
// send email... |
|
} |
|
|
|
} |
|
---- |
|
|
|
At configuration time, the Spring container will detect that `EmailService` implements |
|
`ApplicationEventPublisherAware` and will automatically call |
|
`setApplicationEventPublisher()`. In reality, the parameter passed in will be the Spring |
|
container itself; you're simply interacting with the application context via its |
|
`ApplicationEventPublisher` interface. |
|
|
|
To receive the custom `ApplicationEvent`, create a class that implements |
|
`ApplicationListener` and register it as a Spring bean. The following example |
|
demonstrates such a class: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class BlackListNotifier implements ApplicationListener<BlackListEvent> { |
|
|
|
private String notificationAddress; |
|
|
|
public void setNotificationAddress(String notificationAddress) { |
|
this.notificationAddress = notificationAddress; |
|
} |
|
|
|
public void onApplicationEvent(BlackListEvent event) { |
|
// notify appropriate parties via notificationAddress... |
|
} |
|
|
|
} |
|
---- |
|
|
|
Notice that `ApplicationListener` is generically parameterized with the type of your |
|
custom event, `BlackListEvent`. This means that the `onApplicationEvent()` method can |
|
remain type-safe, avoiding any need for downcasting. You may register as many event |
|
listeners as you wish, but note that by default event listeners receive events |
|
synchronously. This means the `publishEvent()` method blocks until all listeners have |
|
finished processing the event. One advantage of this synchronous and single-threaded |
|
approach is that when a listener receives an event, it operates inside the transaction |
|
context of the publisher if a transaction context is available. If another strategy for |
|
event publication becomes necessary, refer to the JavaDoc for Spring's |
|
`ApplicationEventMulticaster` interface. |
|
|
|
The following example shows the bean definitions used to register and configure each of |
|
the classes above: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="emailService" class="example.EmailService"> |
|
<property name="blackList"> |
|
<list> |
|
<value>known.spammer@example.org</value> |
|
<value>known.hacker@example.org</value> |
|
<value>john.doe@example.org</value> |
|
</list> |
|
</property> |
|
</bean> |
|
|
|
<bean id="blackListNotifier" class="example.BlackListNotifier"> |
|
<property name="notificationAddress" value="blacklist@example.org"/> |
|
</bean> |
|
---- |
|
|
|
Putting it all together, when the `sendEmail()` method of the `emailService` bean is |
|
called, if there are any emails that should be blacklisted, a custom event of type |
|
`BlackListEvent` is published. The `blackListNotifier` bean is registered as an |
|
`ApplicationListener` and thus receives the `BlackListEvent`, at which point it can |
|
notify appropriate parties. |
|
|
|
[NOTE] |
|
==== |
|
Spring's eventing mechanism is designed for simple communication between Spring beans |
|
within the same application context. However, for more sophisticated enterprise |
|
integration needs, the separately-maintained |
|
http://projects.spring.io/spring-integration/[Spring Integration] project provides |
|
complete support for building lightweight, |
|
http://www.enterpriseintegrationpatterns.com[pattern-oriented], event-driven |
|
architectures that build upon the well-known Spring programming model. |
|
==== |
|
|
|
|
|
|
|
[[context-functionality-resources]] |
|
==== Convenient access to low-level resources |
|
For optimal usage and understanding of application contexts, users should generally |
|
familiarize themselves with Spring's `Resource` abstraction, as described in the chapter |
|
<<resources>>. |
|
|
|
An application context is a `ResourceLoader`, which can be used to load ++Resource++s. A |
|
`Resource` is essentially a more feature rich version of the JDK class `java.net.URL`, |
|
in fact, the implementations of the `Resource` wrap an instance of `java.net.URL` where |
|
appropriate. A `Resource` can obtain low-level resources from almost any location in a |
|
transparent fashion, including from the classpath, a filesystem location, anywhere |
|
describable with a standard URL, and some other variations. If the resource location |
|
string is a simple path without any special prefixes, where those resources come from is |
|
specific and appropriate to the actual application context type. |
|
|
|
You can configure a bean deployed into the application context to implement the special |
|
callback interface, `ResourceLoaderAware`, to be automatically called back at |
|
initialization time with the application context itself passed in as the |
|
`ResourceLoader`. You can also expose properties of type `Resource`, to be used to |
|
access static resources; they will be injected into it like any other properties. You |
|
can specify those `Resource` properties as simple String paths, and rely on a special |
|
JavaBean `PropertyEditor` that is automatically registered by the context, to convert |
|
those text strings to actual `Resource` objects when the bean is deployed. |
|
|
|
The location path or paths supplied to an `ApplicationContext` constructor are actually |
|
resource strings, and in simple form are treated appropriately to the specific context |
|
implementation. `ClassPathXmlApplicationContext` treats a simple location path as a |
|
classpath location. You can also use location paths (resource strings) with special |
|
prefixes to force loading of definitions from the classpath or a URL, regardless of the |
|
actual context type. |
|
|
|
|
|
|
|
[[context-create]] |
|
==== Convenient ApplicationContext instantiation for web applications |
|
|
|
You can create `ApplicationContext` instances declaratively by using, for example, a |
|
`ContextLoader`. Of course you can also create `ApplicationContext` instances |
|
programmatically by using one of the `ApplicationContext` implementations. |
|
|
|
You can register an `ApplicationContext` using the `ContextLoaderListener` as follows: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<context-param> |
|
<param-name>contextConfigLocation</param-name> |
|
<param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value> |
|
</context-param> |
|
|
|
<listener> |
|
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> |
|
</listener> |
|
---- |
|
|
|
The listener inspects the `contextConfigLocation` parameter. If the parameter does not |
|
exist, the listener uses `/WEB-INF/applicationContext.xml` as a default. When the |
|
parameter __does__ exist, the listener separates the String by using predefined |
|
delimiters (comma, semicolon and whitespace) and uses the values as locations where |
|
application contexts will be searched. Ant-style path patterns are supported as well. |
|
Examples are `/WEB-INF/*Context.xml` for all files with names ending with "Context.xml", |
|
residing in the "WEB-INF" directory, and `/WEB-INF/**/*Context.xml`, for all such files |
|
in any subdirectory of "WEB-INF". |
|
|
|
|
|
|
|
[[context-deploy-rar]] |
|
==== Deploying a Spring ApplicationContext as a Java EE RAR file |
|
It is possible to deploy a Spring ApplicationContext as a RAR file, encapsulating the |
|
context and all of its required bean classes and library JARs in a Java EE RAR deployment |
|
unit. This is the equivalent of bootstrapping a standalone ApplicationContext, just hosted |
|
in Java EE environment, being able to access the Java EE servers facilities. RAR deployment |
|
is more natural alternative to scenario of deploying a headless WAR file, in effect, a WAR |
|
file without any HTTP entry points that is used only for bootstrapping a Spring |
|
ApplicationContext in a Java EE environment. |
|
|
|
RAR deployment is ideal for application contexts that do not need HTTP entry points but |
|
rather consist only of message endpoints and scheduled jobs. Beans in such a context can |
|
use application server resources such as the JTA transaction manager and JNDI-bound JDBC |
|
DataSources and JMS ConnectionFactory instances, and may also register with the |
|
platform's JMX server - all through Spring's standard transaction management and JNDI |
|
and JMX support facilities. Application components can also interact with the |
|
application server's JCA WorkManager through Spring's `TaskExecutor` abstraction. |
|
|
|
Check out the JavaDoc of the |
|
{javadoc-baseurl}/org/springframework/jca/context/SpringContextResourceAdapter.html[`SpringContextResourceAdapter`] |
|
class for the configuration details involved in RAR deployment. |
|
|
|
__For a simple deployment of a Spring ApplicationContext as a Java EE RAR file:__ package |
|
all application classes into a RAR file, which is a standard JAR file with a different |
|
file extension. Add all required library JARs into the root of the RAR archive. Add a |
|
"META-INF/ra.xml" deployment descriptor (as shown in ++SpringContextResourceAdapter++'s |
|
JavaDoc) and the corresponding Spring XML bean definition file(s) (typically |
|
"META-INF/applicationContext.xml"), and drop the resulting RAR file into your |
|
application server's deployment directory. |
|
|
|
[NOTE] |
|
==== |
|
Such RAR deployment units are usually self-contained; they do not expose components to |
|
the outside world, not even to other modules of the same application. Interaction with a |
|
RAR-based ApplicationContext usually occurs through JMS destinations that it shares with |
|
other modules. A RAR-based ApplicationContext may also, for example, schedule some jobs, |
|
reacting to new files in the file system (or the like). If it needs to allow synchronous |
|
access from the outside, it could for example export RMI endpoints, which of course may |
|
be used by other application modules on the same machine. |
|
==== |
|
|
|
|
|
|
|
|
|
[[beans-beanfactory]] |
|
=== The BeanFactory |
|
The `BeanFactory` provides the underlying basis for Spring's IoC functionality but it is |
|
only used directly in integration with other third-party frameworks and is now largely |
|
historical in nature for most users of Spring. The `BeanFactory` and related interfaces, |
|
such as `BeanFactoryAware`, `InitializingBean`, `DisposableBean`, are still present in |
|
Spring for the purposes of backward compatibility with the large number of third-party |
|
frameworks that integrate with Spring. Often third-party components that can not use |
|
more modern equivalents such as `@PostConstruct` or `@PreDestroy` in order to remain |
|
compatible with JDK 1.4 or to avoid a dependency on JSR-250. |
|
|
|
This section provides additional background into the differences between the |
|
`BeanFactory` and `ApplicationContext` and how one might access the IoC container |
|
directly through a classic singleton lookup. |
|
|
|
|
|
|
|
[[context-introduction-ctx-vs-beanfactory]] |
|
==== BeanFactory or ApplicationContext? |
|
|
|
Use an `ApplicationContext` unless you have a good reason for not doing so. |
|
|
|
Because the `ApplicationContext` includes all functionality of the `BeanFactory`, it is |
|
generally recommended over the `BeanFactory`, except for a few situations such as in |
|
embedded applications running on resource-constrained devices where memory consumption |
|
might be critical and a few extra kilobytes might make a difference. However, for |
|
most typical enterprise applications and systems, the `ApplicationContext` is what you |
|
will want to use. Spring makes __heavy__ use of the <<beans-factory-extension-bpp, |
|
`BeanPostProcessor` extension point>> (to effect proxying and so on). If you use only a |
|
plain `BeanFactory`, a fair amount of support such as transactions and AOP will not take |
|
effect, at least not without some extra steps on your part. This situation could be |
|
confusing because nothing is actually wrong with the configuration. |
|
|
|
The following table lists features provided by the `BeanFactory` and |
|
`ApplicationContext` interfaces and implementations. |
|
|
|
[[context-introduction-ctx-vs-beanfactory-feature-matrix]] |
|
.Feature Matrix |
|
|=== |
|
| Feature| `BeanFactory`| `ApplicationContext` |
|
|
|
| Bean instantiation/wiring |
|
| Yes |
|
| Yes |
|
|
|
| Automatic `BeanPostProcessor` registration |
|
| No |
|
| Yes |
|
|
|
| Automatic `BeanFactoryPostProcessor` registration |
|
| No |
|
| Yes |
|
|
|
| Convenient `MessageSource` access (for i18n) |
|
| No |
|
| Yes |
|
|
|
| `ApplicationEvent` publication |
|
| No |
|
| Yes |
|
|=== |
|
|
|
To explicitly register a bean post-processor with a `BeanFactory` implementation, you |
|
must write code like this: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ConfigurableBeanFactory factory = new XmlBeanFactory(...); |
|
|
|
// now register any needed BeanPostProcessor instances |
|
MyBeanPostProcessor postProcessor = new MyBeanPostProcessor(); |
|
factory.addBeanPostProcessor(postProcessor); |
|
|
|
// now start using the factory |
|
---- |
|
|
|
To explicitly register a `BeanFactoryPostProcessor` when using a `BeanFactory` |
|
implementation, you must write code like this: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
XmlBeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml")); |
|
|
|
// bring in some property values from a Properties file |
|
PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer(); |
|
cfg.setLocation(new FileSystemResource("jdbc.properties")); |
|
|
|
// now actually do the replacement |
|
cfg.postProcessBeanFactory(factory); |
|
---- |
|
|
|
In both cases, the explicit registration step is inconvenient, which is one reason why |
|
the various `ApplicationContext` implementations are preferred above plain `BeanFactory` |
|
implementations in the vast majority of Spring-backed applications, especially when |
|
using `BeanFactoryPostProcessors` and `BeanPostProcessors`. These mechanisms implement |
|
important functionality such as property placeholder replacement and AOP. |
|
|
|
|
|
|
|
[[beans-servicelocator]] |
|
==== Glue code and the evil singleton |
|
It is best to write most application code in a dependency-injection (DI) style, where |
|
that code is served out of a Spring IoC container, has its own dependencies supplied by |
|
the container when it is created, and is completely unaware of the container. However, |
|
for the small glue layers of code that are sometimes needed to tie other code together, |
|
you sometimes need a singleton (or quasi-singleton) style access to a Spring IoC |
|
container. For example, third-party code may try to construct new objects directly ( |
|
`Class.forName()` style), without the ability to get these objects out of a Spring IoC |
|
container.If the object constructed by the third-party code is a small stub or proxy, |
|
which then uses a singleton style access to a Spring IoC container to get a real object |
|
to delegate to, then inversion of control has still been achieved for the majority of |
|
the code (the object coming out of the container). Thus most code is still unaware of |
|
the container or how it is accessed, and remains decoupled from other code, with all |
|
ensuing benefits. EJBs may also use this stub/proxy approach to delegate to a plain Java |
|
implementation object, retrieved from a Spring IoC container. While the Spring IoC |
|
container itself ideally does not have to be a singleton, it may be unrealistic in terms |
|
of memory usage or initialization times (when using beans in the Spring IoC container |
|
such as a Hibernate `SessionFactory`) for each bean to use its own, non-singleton Spring |
|
IoC container. |
|
|
|
Looking up the application context in a service locator style is sometimes the only |
|
option for accessing shared Spring-managed components, such as in an EJB 2.1 |
|
environment, or when you want to share a single ApplicationContext as a parent to |
|
WebApplicationContexts across WAR files. In this case you should look into using the |
|
utility class |
|
{javadoc-baseurl}/org/springframework/context/access/ContextSingletonBeanFactoryLocator.html[`ContextSingletonBeanFactoryLocator`] |
|
locator that is described in this |
|
https://spring.io/blog/2007/06/11/using-a-shared-parent-application-context-in-a-multi-war-spring-application/[Spring |
|
team blog entry]. |
|
|
|
|
|
|
|
|
|
|
|
[[resources]] |
|
== Resources |
|
|
|
|
|
|
|
|
|
[[resources-introduction]] |
|
=== Introduction |
|
Java's standard `java.net.URL` class and standard handlers for various URL prefixes |
|
unfortunately are not quite adequate enough for all access to low-level resources. For |
|
example, there is no standardized `URL` implementation that may be used to access a |
|
resource that needs to be obtained from the classpath, or relative to a |
|
`ServletContext`. While it is possible to register new handlers for specialized `URL` |
|
prefixes (similar to existing handlers for prefixes such as `http:`), this is generally |
|
quite complicated, and the `URL` interface still lacks some desirable functionality, |
|
such as a method to check for the existence of the resource being pointed to. |
|
|
|
|
|
|
|
|
|
[[resources-resource]] |
|
=== The Resource interface |
|
|
|
Spring's `Resource` interface is meant to be a more capable interface for abstracting |
|
access to low-level resources. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface Resource extends InputStreamSource { |
|
|
|
boolean exists(); |
|
|
|
boolean isOpen(); |
|
|
|
URL getURL() throws IOException; |
|
|
|
File getFile() throws IOException; |
|
|
|
Resource createRelative(String relativePath) throws IOException; |
|
|
|
String getFilename(); |
|
|
|
String getDescription(); |
|
|
|
} |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface InputStreamSource { |
|
|
|
InputStream getInputStream() throws IOException; |
|
|
|
} |
|
---- |
|
|
|
Some of the most important methods from the `Resource` interface are: |
|
|
|
* `getInputStream()`: locates and opens the resource, returning an `InputStream` for |
|
reading from the resource. It is expected that each invocation returns a fresh |
|
`InputStream`. It is the responsibility of the caller to close the stream. |
|
* `exists()`: returns a `boolean` indicating whether this resource actually exists in |
|
physical form. |
|
* `isOpen()`: returns a `boolean` indicating whether this resource represents a handle |
|
with an open stream. If `true`, the `InputStream` cannot be read multiple times, and |
|
must be read once only and then closed to avoid resource leaks. Will be `false` for |
|
all usual resource implementations, with the exception of `InputStreamResource`. |
|
* `getDescription()`: returns a description for this resource, to be used for error |
|
output when working with the resource. This is often the fully qualified file name or |
|
the actual URL of the resource. |
|
|
|
Other methods allow you to obtain an actual `URL` or `File` object representing the |
|
resource (if the underlying implementation is compatible, and supports that |
|
functionality). |
|
|
|
The `Resource` abstraction is used extensively in Spring itself, as an argument type in |
|
many method signatures when a resource is needed. Other methods in some Spring APIs |
|
(such as the constructors to various `ApplicationContext` implementations), take a |
|
`String` which in unadorned or simple form is used to create a `Resource` appropriate to |
|
that context implementation, or via special prefixes on the `String` path, allow the |
|
caller to specify that a specific `Resource` implementation must be created and used. |
|
|
|
While the `Resource` interface is used a lot with Spring and by Spring, it's actually |
|
very useful to use as a general utility class by itself in your own code, for access to |
|
resources, even when your code doesn't know or care about any other parts of Spring. |
|
While this couples your code to Spring, it really only couples it to this small set of |
|
utility classes, which are serving as a more capable replacement for `URL`, and can be |
|
considered equivalent to any other library you would use for this purpose. |
|
|
|
It is important to note that the `Resource` abstraction does not replace functionality: |
|
it wraps it where possible. For example, a `UrlResource` wraps a URL, and uses the |
|
wrapped `URL` to do its work. |
|
|
|
|
|
|
|
|
|
[[resources-implementations]] |
|
=== Built-in Resource implementations |
|
|
|
There are a number of `Resource` implementations that come supplied straight out of the |
|
box in Spring: |
|
|
|
|
|
|
|
[[resources-implementations-urlresource]] |
|
==== UrlResource |
|
|
|
The `UrlResource` wraps a `java.net.URL`, and may be used to access any object that is |
|
normally accessible via a URL, such as files, an HTTP target, an FTP target, etc. All |
|
URLs have a standardized `String` representation, such that appropriate standardized |
|
prefixes are used to indicate one URL type from another. This includes `file:` for |
|
accessing filesystem paths, `http:` for accessing resources via the HTTP protocol, |
|
`ftp:` for accessing resources via FTP, etc. |
|
|
|
A `UrlResource` is created by Java code explicitly using the `UrlResource` constructor, |
|
but will often be created implicitly when you call an API method which takes a `String` |
|
argument which is meant to represent a path. For the latter case, a JavaBeans |
|
`PropertyEditor` will ultimately decide which type of `Resource` to create. If the path |
|
string contains a few well-known (to it, that is) prefixes such as `classpath:`, it will |
|
create an appropriate specialized `Resource` for that prefix. However, if it doesn't |
|
recognize the prefix, it will assume the this is just a standard URL string, and will |
|
create a `UrlResource`. |
|
|
|
|
|
|
|
[[resources-implementations-classpathresource]] |
|
==== ClassPathResource |
|
|
|
This class represents a resource which should be obtained from the classpath. This uses |
|
either the thread context class loader, a given class loader, or a given class for |
|
loading resources. |
|
|
|
This `Resource` implementation supports resolution as `java.io.File` if the class path |
|
resource resides in the file system, but not for classpath resources which reside in a |
|
jar and have not been expanded (by the servlet engine, or whatever the environment is) |
|
to the filesystem. To address this the various `Resource` implementations always support |
|
resolution as a `java.net.URL`. |
|
|
|
A `ClassPathResource` is created by Java code explicitly using the `ClassPathResource` |
|
constructor, but will often be created implicitly when you call an API method which |
|
takes a `String` argument which is meant to represent a path. For the latter case, a |
|
JavaBeans `PropertyEditor` will recognize the special prefix `classpath:` on the string |
|
path, and create a `ClassPathResource` in that case. |
|
|
|
|
|
|
|
[[resources-implementations-filesystemresource]] |
|
==== FileSystemResource |
|
|
|
This is a `Resource` implementation for `java.io.File` handles. It obviously supports |
|
resolution as a `File`, and as a `URL`. |
|
|
|
|
|
|
|
[[resources-implementations-servletcontextresource]] |
|
==== ServletContextResource |
|
|
|
This is a `Resource` implementation for `ServletContext` resources, interpreting |
|
relative paths within the relevant web application's root directory. |
|
|
|
This always supports stream access and URL access, but only allows `java.io.File` access |
|
when the web application archive is expanded and the resource is physically on the |
|
filesystem. Whether or not it's expanded and on the filesystem like this, or accessed |
|
directly from the JAR or somewhere else like a DB (it's conceivable) is actually |
|
dependent on the Servlet container. |
|
|
|
|
|
|
|
[[resources-implementations-inputstreamresource]] |
|
==== InputStreamResource |
|
|
|
A `Resource` implementation for a given `InputStream`. This should only be used if no |
|
specific `Resource` implementation is applicable. In particular, prefer |
|
`ByteArrayResource` or any of the file-based `Resource` implementations where possible. |
|
|
|
In contrast to other `Resource` implementations, this is a descriptor for an __already__ |
|
opened resource - therefore returning `true` from `isOpen()`. Do not use it if you need |
|
to keep the resource descriptor somewhere, or if you need to read a stream multiple |
|
times. |
|
|
|
|
|
|
|
[[resources-implementations-bytearrayresource]] |
|
==== ByteArrayResource |
|
|
|
This is a `Resource` implementation for a given byte array. It creates a |
|
`ByteArrayInputStream` for the given byte array. |
|
|
|
It's useful for loading content from any given byte array, without having to resort to a |
|
single-use `InputStreamResource`. |
|
|
|
|
|
|
|
|
|
[[resources-resourceloader]] |
|
=== The ResourceLoader |
|
|
|
The `ResourceLoader` interface is meant to be implemented by objects that can return |
|
(i.e. load) `Resource` instances. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface ResourceLoader { |
|
|
|
Resource getResource(String location); |
|
|
|
} |
|
---- |
|
|
|
All application contexts implement the `ResourceLoader` interface, and therefore all |
|
application contexts may be used to obtain `Resource` instances. |
|
|
|
When you call `getResource()` on a specific application context, and the location path |
|
specified doesn't have a specific prefix, you will get back a `Resource` type that is |
|
appropriate to that particular application context. For example, assume the following |
|
snippet of code was executed against a `ClassPathXmlApplicationContext` instance: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Resource template = ctx.getResource("some/resource/path/myTemplate.txt"); |
|
---- |
|
|
|
What would be returned would be a `ClassPathResource`; if the same method was executed |
|
against a `FileSystemXmlApplicationContext` instance, you'd get back a |
|
`FileSystemResource`. For a `WebApplicationContext`, you'd get back a |
|
`ServletContextResource`, and so on. |
|
|
|
As such, you can load resources in a fashion appropriate to the particular application |
|
context. |
|
|
|
On the other hand, you may also force `ClassPathResource` to be used, regardless of the |
|
application context type, by specifying the special `classpath:` prefix: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt"); |
|
---- |
|
|
|
Similarly, one can force a `UrlResource` to be used by specifying any of the standard |
|
`java.net.URL` prefixes: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt"); |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Resource template = ctx.getResource("http://myhost.com/resource/path/myTemplate.txt"); |
|
---- |
|
|
|
The following table summarizes the strategy for converting ++String++s to ++Resource++s: |
|
|
|
[[resources-resource-strings]] |
|
.Resource strings |
|
|=== |
|
| Prefix| Example| Explanation |
|
|
|
| classpath: |
|
| `classpath:com/myapp/config.xml` |
|
| Loaded from the classpath. |
|
|
|
| file: |
|
| `file:///data/config.xml` |
|
| Loaded as a `URL`, from the filesystem. footnote:[But see also |
|
pass:specialcharacters,macros[<<resources-filesystemresource-caveats>>].] |
|
|
|
| http: |
|
| `http://myserver/logo.png` |
|
| Loaded as a `URL`. |
|
|
|
| (none) |
|
| `/data/config.xml` |
|
| Depends on the underlying `ApplicationContext`. |
|
|=== |
|
|
|
|
|
|
|
|
|
[[resources-resourceloaderaware]] |
|
=== The ResourceLoaderAware interface |
|
|
|
The `ResourceLoaderAware` interface is a special marker interface, identifying objects |
|
that expect to be provided with a `ResourceLoader` reference. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface ResourceLoaderAware { |
|
|
|
void setResourceLoader(ResourceLoader resourceLoader); |
|
} |
|
---- |
|
|
|
When a class implements `ResourceLoaderAware` and is deployed into an application |
|
context (as a Spring-managed bean), it is recognized as `ResourceLoaderAware` by the |
|
application context. The application context will then invoke the |
|
`setResourceLoader(ResourceLoader)`, supplying itself as the argument (remember, all |
|
application contexts in Spring implement the `ResourceLoader` interface). |
|
|
|
Of course, since an `ApplicationContext` is a `ResourceLoader`, the bean could also |
|
implement the `ApplicationContextAware` interface and use the supplied application |
|
context directly to load resources, but in general, it's better to use the specialized |
|
`ResourceLoader` interface if that's all that's needed. The code would just be coupled |
|
to the resource loading interface, which can be considered a utility interface, and not |
|
the whole Spring `ApplicationContext` interface. |
|
|
|
As of Spring 2.5, you can rely upon autowiring of the `ResourceLoader` as an alternative |
|
to implementing the `ResourceLoaderAware` interface. The "traditional" `constructor` and |
|
`byType` autowiring modes (as described in <<beans-factory-autowire>>) are now capable |
|
of providing a dependency of type `ResourceLoader` for either a constructor argument or |
|
setter method parameter respectively. For more flexibility (including the ability to |
|
autowire fields and multiple parameter methods), consider using the new annotation-based |
|
autowiring features. In that case, the `ResourceLoader` will be autowired into a field, |
|
constructor argument, or method parameter that is expecting the `ResourceLoader` type as |
|
long as the field, constructor, or method in question carries the `@Autowired` |
|
annotation. For more information, see <<beans-autowired-annotation>>. |
|
|
|
|
|
|
|
|
|
[[resources-as-dependencies]] |
|
=== Resources as dependencies |
|
|
|
If the bean itself is going to determine and supply the resource path through some sort |
|
of dynamic process, it probably makes sense for the bean to use the `ResourceLoader` |
|
interface to load resources. Consider as an example the loading of a template of some |
|
sort, where the specific resource that is needed depends on the role of the user. If the |
|
resources are static, it makes sense to eliminate the use of the `ResourceLoader` |
|
interface completely, and just have the bean expose the `Resource` properties it needs, |
|
and expect that they will be injected into it. |
|
|
|
What makes it trivial to then inject these properties, is that all application contexts |
|
register and use a special JavaBeans `PropertyEditor` which can convert `String` paths |
|
to `Resource` objects. So if `myBean` has a template property of type `Resource`, it can |
|
be configured with a simple string for that resource, as follows: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="myBean" class="..."> |
|
<property name="template" value="some/resource/path/myTemplate.txt"/> |
|
</bean> |
|
---- |
|
|
|
Note that the resource path has no prefix, so because the application context itself is |
|
going to be used as the `ResourceLoader`, the resource itself will be loaded via a |
|
`ClassPathResource`, `FileSystemResource`, or `ServletContextResource` (as appropriate) |
|
depending on the exact type of the context. |
|
|
|
If there is a need to force a specific `Resource` type to be used, then a prefix may be |
|
used. The following two examples show how to force a `ClassPathResource` and a |
|
`UrlResource` (the latter being used to access a filesystem file). |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<property name="template" value="classpath:some/resource/path/myTemplate.txt"> |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<property name="template" value="file:///some/resource/path/myTemplate.txt"/> |
|
---- |
|
|
|
|
|
|
|
|
|
[[resources-app-ctx]] |
|
=== Application contexts and Resource paths |
|
|
|
|
|
|
|
[[resources-app-ctx-construction]] |
|
==== Constructing application contexts |
|
An application context constructor (for a specific application context type) generally |
|
takes a string or array of strings as the location path(s) of the resource(s) such as |
|
XML files that make up the definition of the context. |
|
|
|
When such a location path doesn't have a prefix, the specific `Resource` type built from |
|
that path and used to load the bean definitions, depends on and is appropriate to the |
|
specific application context. For example, if you create a |
|
`ClassPathXmlApplicationContext` as follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml"); |
|
---- |
|
|
|
The bean definitions will be loaded from the classpath, as a `ClassPathResource` will be |
|
used. But if you create a `FileSystemXmlApplicationContext` as follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ApplicationContext ctx = |
|
new FileSystemXmlApplicationContext("conf/appContext.xml"); |
|
---- |
|
|
|
The bean definition will be loaded from a filesystem location, in this case relative to |
|
the current working directory. |
|
|
|
Note that the use of the special classpath prefix or a standard URL prefix on the |
|
location path will override the default type of `Resource` created to load the |
|
definition. So this `FileSystemXmlApplicationContext`... |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ApplicationContext ctx = |
|
new FileSystemXmlApplicationContext("classpath:conf/appContext.xml"); |
|
---- |
|
|
|
... will actually load its bean definitions from the classpath. However, it is still a |
|
`FileSystemXmlApplicationContext`. If it is subsequently used as a `ResourceLoader`, any |
|
unprefixed paths will still be treated as filesystem paths. |
|
|
|
|
|
[[resources-app-ctx-classpathxml]] |
|
===== Constructing ClassPathXmlApplicationContext instances - shortcuts |
|
|
|
The `ClassPathXmlApplicationContext` exposes a number of constructors to enable |
|
convenient instantiation. The basic idea is that one supplies merely a string array |
|
containing just the filenames of the XML files themselves (without the leading path |
|
information), and one __also__ supplies a `Class`; the `ClassPathXmlApplicationContext` |
|
will derive the path information from the supplied class. |
|
|
|
An example will hopefully make this clear. Consider a directory layout that looks like |
|
this: |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
com/ |
|
foo/ |
|
services.xml |
|
daos.xml |
|
MessengerService.class |
|
---- |
|
|
|
A `ClassPathXmlApplicationContext` instance composed of the beans defined in the |
|
`'services.xml'` and `'daos.xml'` could be instantiated like so... |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ApplicationContext ctx = new ClassPathXmlApplicationContext( |
|
new String[] {"services.xml", "daos.xml"}, MessengerService.class); |
|
---- |
|
|
|
Please do consult the `ClassPathXmlApplicationContext` javadocs for details |
|
on the various constructors. |
|
|
|
|
|
|
|
[[resources-app-ctx-wildcards-in-resource-paths]] |
|
==== Wildcards in application context constructor resource paths |
|
The resource paths in application context constructor values may be a simple path (as |
|
shown above) which has a one-to-one mapping to a target Resource, or alternately may |
|
contain the special "classpath*:" prefix and/or internal Ant-style regular expressions |
|
(matched using Spring's `PathMatcher` utility). Both of the latter are effectively |
|
wildcards |
|
|
|
One use for this mechanism is when doing component-style application assembly. All |
|
components can 'publish' context definition fragments to a well-known location path, and |
|
when the final application context is created using the same path prefixed via |
|
`classpath*:`, all component fragments will be picked up automatically. |
|
|
|
Note that this wildcarding is specific to use of resource paths in application context |
|
constructors (or when using the `PathMatcher` utility class hierarchy directly), and is |
|
resolved at construction time. It has nothing to do with the `Resource` type itself. |
|
It's not possible to use the `classpath*:` prefix to construct an actual `Resource`, as |
|
a resource points to just one resource at a time. |
|
|
|
|
|
[[resources-app-ctx-ant-patterns-in-paths]] |
|
===== Ant-style Patterns |
|
When the path location contains an Ant-style pattern, for example: |
|
|
|
[literal] |
|
[subs="verbatim"] |
|
---- |
|
/WEB-INF/*-context.xml |
|
com/mycompany/**/applicationContext.xml |
|
file:C:/some/path/*-context.xml |
|
classpath:com/mycompany/**/applicationContext.xml |
|
---- |
|
|
|
... the resolver follows a more complex but defined procedure to try to resolve the |
|
wildcard. It produces a Resource for the path up to the last non-wildcard segment and |
|
obtains a URL from it. If this URL is not a "jar:" URL or container-specific variant |
|
(e.g. " `zip:`" in WebLogic, " `wsjar`" in WebSphere, etc.), then a `java.io.File` is |
|
obtained from it and used to resolve the wildcard by traversing the filesystem. In the |
|
case of a jar URL, the resolver either gets a `java.net.JarURLConnection` from it or |
|
manually parses the jar URL and then traverses the contents of the jar file to resolve |
|
the wildcards. |
|
|
|
[[resources-app-ctx-portability]] |
|
====== Implications on portability |
|
If the specified path is already a file URL (either explicitly, or implicitly because |
|
the base `ResourceLoader` is a filesystem one, then wildcarding is guaranteed to work in |
|
a completely portable fashion. |
|
|
|
If the specified path is a classpath location, then the resolver must obtain the last |
|
non-wildcard path segment URL via a `Classloader.getResource()` call. Since this is just |
|
a node of the path (not the file at the end) it is actually undefined (in the |
|
`ClassLoader` javadocs) exactly what sort of a URL is returned in this case. In |
|
practice, it is always a `java.io.File` representing the directory, where the classpath |
|
resource resolves to a filesystem location, or a jar URL of some sort, where the |
|
classpath resource resolves to a jar location. Still, there is a portability concern on |
|
this operation. |
|
|
|
If a jar URL is obtained for the last non-wildcard segment, the resolver must be able to |
|
get a `java.net.JarURLConnection` from it, or manually parse the jar URL, to be able to |
|
walk the contents of the jar, and resolve the wildcard. This will work in most |
|
environments, but will fail in others, and it is strongly recommended that the wildcard |
|
resolution of resources coming from jars be thoroughly tested in your specific |
|
environment before you rely on it. |
|
|
|
|
|
[[resources-classpath-wildcards]] |
|
===== The Classpath*: portability classpath*: prefix |
|
|
|
When constructing an XML-based application context, a location string may use the |
|
special `classpath*:` prefix: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ApplicationContext ctx = |
|
new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml"); |
|
---- |
|
|
|
This special prefix specifies that all classpath resources that match the given name |
|
must be obtained (internally, this essentially happens via a |
|
`ClassLoader.getResources(...)` call), and then merged to form the final application |
|
context definition. |
|
|
|
[NOTE] |
|
==== |
|
The wildcard classpath relies on the `getResources()` method of the underlying |
|
classloader. As most application servers nowadays supply their own classloader |
|
implementation, the behavior might differ especially when dealing with jar files. A |
|
simple test to check if `classpath*` works is to use the classloader to load a file from |
|
within a jar on the classpath: |
|
`getClass().getClassLoader().getResources("<someFileInsideTheJar>")`. Try this test with |
|
files that have the same name but are placed inside two different locations. In case an |
|
inappropriate result is returned, check the application server documentation for |
|
settings that might affect the classloader behavior. |
|
==== |
|
|
|
The " `classpath*:`" prefix can also be combined with a `PathMatcher` pattern in the |
|
rest of the location path, for example " `classpath*:META-INF/*-beans.xml`". In this |
|
case, the resolution strategy is fairly simple: a ClassLoader.getResources() call is |
|
used on the last non-wildcard path segment to get all the matching resources in the |
|
class loader hierarchy, and then off each resource the same PathMatcher resolution |
|
strategy described above is used for the wildcard subpath. |
|
|
|
|
|
[[resources-wildcards-in-path-other-stuff]] |
|
===== Other notes relating to wildcards |
|
Please note that " `classpath*:`" when combined with Ant-style patterns will only work |
|
reliably with at least one root directory before the pattern starts, unless the actual |
|
target files reside in the file system. This means that a pattern like " |
|
`classpath*:*.xml`" will not retrieve files from the root of jar files but rather only |
|
from the root of expanded directories. This originates from a limitation in the JDK's |
|
`ClassLoader.getResources()` method which only returns file system locations for a |
|
passed-in empty string (indicating potential roots to search). |
|
|
|
Ant-style patterns with " `classpath:`" resources are not guaranteed to find matching |
|
resources if the root package to search is available in multiple class path locations. |
|
This is because a resource such as |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
com/mycompany/package1/service-context.xml |
|
---- |
|
|
|
may be in only one location, but when a path such as |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
classpath:com/mycompany/**/service-context.xml |
|
---- |
|
|
|
is used to try to resolve it, the resolver will work off the (first) URL returned by |
|
`getResource("com/mycompany")`;. If this base package node exists in multiple |
|
classloader locations, the actual end resource may not be underneath. Therefore, |
|
preferably, use " `classpath*:`" with the same Ant-style pattern in such a case, which |
|
will search all class path locations that contain the root package. |
|
|
|
|
|
|
|
[[resources-filesystemresource-caveats]] |
|
==== FileSystemResource caveats |
|
|
|
A `FileSystemResource` that is not attached to a `FileSystemApplicationContext` (that |
|
is, a `FileSystemApplicationContext` is not the actual `ResourceLoader`) will treat |
|
absolute vs. relative paths as you would expect. Relative paths are relative to the |
|
current working directory, while absolute paths are relative to the root of the |
|
filesystem. |
|
|
|
For backwards compatibility (historical) reasons however, this changes when the |
|
`FileSystemApplicationContext` is the `ResourceLoader`. The |
|
`FileSystemApplicationContext` simply forces all attached `FileSystemResource` instances |
|
to treat all location paths as relative, whether they start with a leading slash or not. |
|
In practice, this means the following are equivalent: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ApplicationContext ctx = |
|
new FileSystemXmlApplicationContext("conf/context.xml"); |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ApplicationContext ctx = |
|
new FileSystemXmlApplicationContext("/conf/context.xml"); |
|
---- |
|
|
|
As are the following: (Even though it would make sense for them to be different, as one |
|
case is relative and the other absolute.) |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
FileSystemXmlApplicationContext ctx = ...; |
|
ctx.getResource("some/resource/path/myTemplate.txt"); |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
FileSystemXmlApplicationContext ctx = ...; |
|
ctx.getResource("/some/resource/path/myTemplate.txt"); |
|
---- |
|
|
|
In practice, if true absolute filesystem paths are needed, it is better to forgo the use |
|
of absolute paths with `FileSystemResource` / `FileSystemXmlApplicationContext`, and |
|
just force the use of a `UrlResource`, by using the `file:` URL prefix. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// actual context type doesn't matter, the Resource will always be UrlResource |
|
ctx.getResource("file:///some/resource/path/myTemplate.txt"); |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// force this FileSystemXmlApplicationContext to load its definition via a UrlResource |
|
ApplicationContext ctx = |
|
new FileSystemXmlApplicationContext("file:///conf/context.xml"); |
|
---- |
|
|
|
|
|
|
|
|
|
|
|
[[validation]] |
|
== Validation, Data Binding, and Type Conversion |
|
|
|
|
|
|
|
|
|
[[validation-introduction]] |
|
=== Introduction |
|
|
|
.JSR-303/JSR-349 Bean Validation |
|
**** |
|
Spring Framework 4.0 supports Bean Validation 1.0 (JSR-303) and Bean Validation 1.1 |
|
(JSR-349) in terms of setup support, also adapting it to Spring's `Validator` interface. |
|
|
|
An application can choose to enable Bean Validation once globally, as described in |
|
<<validation-beanvalidation>>, and use it exclusively for all validation needs. |
|
|
|
An application can also register additional Spring `Validator` instances per |
|
`DataBinder` instance, as described in <<validation-binder>>. This may be useful for |
|
plugging in validation logic without the use of annotations. |
|
**** |
|
|
|
There are pros and cons for considering validation as business logic, and Spring offers |
|
a design for validation (and data binding) that does not exclude either one of them. |
|
Specifically validation should not be tied to the web tier, should be easy to localize |
|
and it should be possible to plug in any validator available. Considering the above, |
|
Spring has come up with a `Validator` interface that is both basic ands eminently usable |
|
in every layer of an application. |
|
|
|
Data binding is useful for allowing user input to be dynamically bound to the domain |
|
model of an application (or whatever objects you use to process user input). Spring |
|
provides the so-called `DataBinder` to do exactly that. The `Validator` and the |
|
`DataBinder` make up the `validation` package, which is primarily used in but not |
|
limited to the MVC framework. |
|
|
|
The `BeanWrapper` is a fundamental concept in the Spring Framework and is used in a lot |
|
of places. However, you probably will not have the need to use the `BeanWrapper` |
|
directly. Because this is reference documentation however, we felt that some explanation |
|
might be in order. We will explain the `BeanWrapper` in this chapter since, if you were |
|
going to use it at all, you would most likely do so when trying to bind data to objects. |
|
|
|
Spring's DataBinder and the lower-level BeanWrapper both use PropertyEditors to parse |
|
and format property values. The `PropertyEditor` concept is part of the JavaBeans |
|
specification, and is also explained in this chapter. Spring 3 introduces a |
|
"core.convert" package that provides a general type conversion facility, as well as a |
|
higher-level "format" package for formatting UI field values. These new packages may be |
|
used as simpler alternatives to PropertyEditors, and will also be discussed in this |
|
chapter. |
|
|
|
|
|
|
|
|
|
[[validator]] |
|
=== Validation using Spring's Validator interface |
|
|
|
Spring features a `Validator` interface that you can use to validate objects. The |
|
`Validator` interface works using an `Errors` object so that while validating, |
|
validators can report validation failures to the `Errors` object. |
|
|
|
Let's consider a small data object: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class Person { |
|
|
|
private String name; |
|
private int age; |
|
|
|
// the usual getters and setters... |
|
} |
|
---- |
|
|
|
We're going to provide validation behavior for the `Person` class by implementing the |
|
following two methods of the `org.springframework.validation.Validator` interface: |
|
|
|
* `supports(Class)` - Can this `Validator` validate instances of the supplied `Class`? |
|
* `validate(Object, org.springframework.validation.Errors)` - validates the given object |
|
and in case of validation errors, registers those with the given `Errors` object |
|
|
|
Implementing a `Validator` is fairly straightforward, especially when you know of the |
|
`ValidationUtils` helper class that the Spring Framework also provides. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
public class PersonValidator implements Validator { |
|
|
|
/** |
|
* This Validator validates *just* Person instances |
|
*/ |
|
public boolean supports(Class clazz) { |
|
return Person.class.equals(clazz); |
|
} |
|
|
|
public void validate(Object obj, Errors e) { |
|
ValidationUtils.rejectIfEmpty(e, "name", "name.empty"); |
|
Person p = (Person) obj; |
|
if (p.getAge() < 0) { |
|
e.rejectValue("age", "negativevalue"); |
|
} else if (p.getAge() > 110) { |
|
e.rejectValue("age", "too.darn.old"); |
|
} |
|
} |
|
} |
|
---- |
|
|
|
As you can see, the `static` `rejectIfEmpty(..)` method on the `ValidationUtils` class |
|
is used to reject the `'name'` property if it is `null` or the empty string. Have a look |
|
at the `ValidationUtils` javadocs to see what functionality it provides besides the |
|
example shown previously. |
|
|
|
While it is certainly possible to implement a single `Validator` class to validate each |
|
of the nested objects in a rich object, it may be better to encapsulate the validation |
|
logic for each nested class of object in its own `Validator` implementation. A simple |
|
example of a __'rich'__ object would be a `Customer` that is composed of two `String` |
|
properties (a first and second name) and a complex `Address` object. `Address` objects |
|
may be used independently of `Customer` objects, and so a distinct `AddressValidator` |
|
has been implemented. If you want your `CustomerValidator` to reuse the logic contained |
|
within the `AddressValidator` class without resorting to copy-and-paste, you can |
|
dependency-inject or instantiate an `AddressValidator` within your `CustomerValidator`, |
|
and use it like so: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class CustomerValidator implements Validator { |
|
|
|
private final Validator addressValidator; |
|
|
|
public CustomerValidator(Validator addressValidator) { |
|
if (addressValidator == null) { |
|
throw new IllegalArgumentException("The supplied [Validator] is " + |
|
"required and must not be null."); |
|
} |
|
if (!addressValidator.supports(Address.class)) { |
|
throw new IllegalArgumentException("The supplied [Validator] must " + |
|
support the validation of [Address] instances."); |
|
} |
|
this.addressValidator = addressValidator; |
|
} |
|
|
|
/** |
|
* This Validator validates Customer instances, and any subclasses of Customer too |
|
*/ |
|
public boolean supports(Class clazz) { |
|
return Customer.class.isAssignableFrom(clazz); |
|
} |
|
|
|
public void validate(Object target, Errors errors) { |
|
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "field.required"); |
|
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "surname", "field.required"); |
|
Customer customer = (Customer) target; |
|
try { |
|
errors.pushNestedPath("address"); |
|
ValidationUtils.invokeValidator(this.addressValidator, customer.getAddress(), errors); |
|
} finally { |
|
errors.popNestedPath(); |
|
} |
|
} |
|
} |
|
---- |
|
|
|
Validation errors are reported to the `Errors` object passed to the validator. In case |
|
of Spring Web MVC you can use `<spring:bind/>` tag to inspect the error messages, but of |
|
course you can also inspect the errors object yourself. More information about the |
|
methods it offers can be found in the javadocs. |
|
|
|
|
|
|
|
|
|
[[validation-conversion]] |
|
=== Resolving codes to error messages |
|
We've talked about databinding and validation. Outputting messages corresponding to |
|
validation errors is the last thing we need to discuss. In the example we've shown |
|
above, we rejected the `name` and the `age` field. If we're going to output the error |
|
messages by using a `MessageSource`, we will do so using the error code we've given when |
|
rejecting the field ('name' and 'age' in this case). When you call (either directly, or |
|
indirectly, using for example the `ValidationUtils` class) `rejectValue` or one of the |
|
other `reject` methods from the `Errors` interface, the underlying implementation will |
|
not only register the code you've passed in, but also a number of additional error |
|
codes. What error codes it registers is determined by the `MessageCodesResolver` that is |
|
used. By default, the `DefaultMessageCodesResolver` is used, which for example not only |
|
registers a message with the code you gave, but also messages that include the field |
|
name you passed to the reject method. So in case you reject a field using |
|
`rejectValue("age", "too.darn.old")`, apart from the `too.darn.old` code, Spring will |
|
also register `too.darn.old.age` and `too.darn.old.age.int` (so the first will include |
|
the field name and the second will include the type of the field); this is done as a |
|
convenience to aid developers in targeting error messages and suchlike. |
|
|
|
More information on the `MessageCodesResolver` and the default strategy can be found |
|
online in the javadocs of |
|
{javadoc-baseurl}/org/springframework/validation/MessageCodesResolver.html[`MessageCodesResolver`] |
|
and |
|
{javadoc-baseurl}/org/springframework/validation/DefaultMessageCodesResolver.html[`DefaultMessageCodesResolver`], |
|
respectively. |
|
|
|
|
|
|
|
|
|
[[beans-beans]] |
|
=== Bean manipulation and the BeanWrapper |
|
|
|
The `org.springframework.beans` package adheres to the JavaBeans standard provided by |
|
Oracle. A JavaBean is simply a class with a default no-argument constructor, which follows |
|
a naming convention where (by way of an example) a property named `bingoMadness` would |
|
have a setter method `setBingoMadness(..)` and a getter method `getBingoMadness()`. For |
|
more information about JavaBeans and the specification, please refer to Oracle's website ( |
|
http://docs.oracle.com/javase/6/docs/api/java/beans/package-summary.html[javabeans]). |
|
|
|
One quite important class in the beans package is the `BeanWrapper` interface and its |
|
corresponding implementation ( `BeanWrapperImpl`). As quoted from the javadocs, the |
|
`BeanWrapper` offers functionality to set and get property values (individually or in |
|
bulk), get property descriptors, and to query properties to determine if they are |
|
readable or writable. Also, the `BeanWrapper` offers support for nested properties, |
|
enabling the setting of properties on sub-properties to an unlimited depth. Then, the |
|
`BeanWrapper` supports the ability to add standard JavaBeans `PropertyChangeListeners` |
|
and `VetoableChangeListeners`, without the need for supporting code in the target class. |
|
Last but not least, the `BeanWrapper` provides support for the setting of indexed |
|
properties. The `BeanWrapper` usually isn't used by application code directly, but by |
|
the `DataBinder` and the `BeanFactory`. |
|
|
|
The way the `BeanWrapper` works is partly indicated by its name: __it wraps a bean__ to |
|
perform actions on that bean, like setting and retrieving properties. |
|
|
|
|
|
|
|
[[beans-beans-conventions]] |
|
==== Setting and getting basic and nested properties |
|
Setting and getting properties is done using the `setPropertyValue(s)` and |
|
`getPropertyValue(s)` methods that both come with a couple of overloaded variants. |
|
They're all described in more detail in the javadocs Spring comes with. What's important |
|
to know is that there are a couple of conventions for indicating properties of an |
|
object. A couple of examples: |
|
|
|
[[beans-beans-conventions-properties-tbl]] |
|
.Examples of properties |
|
|=== |
|
| Expression| Explanation |
|
|
|
| `name` |
|
| Indicates the property `name` corresponding to the methods `getName()` or `isName()` |
|
and `setName(..)` |
|
|
|
| `account.name` |
|
| Indicates the nested property `name` of the property `account` corresponding e.g. to |
|
the methods `getAccount().setName()` or `getAccount().getName()` |
|
|
|
| `account[2]` |
|
| Indicates the __third__ element of the indexed property `account`. Indexed properties |
|
can be of type `array`, `list` or other __naturally ordered__ collection |
|
|
|
| `account[COMPANYNAME]` |
|
| Indicates the value of the map entry indexed by the key __COMPANYNAME__ of the Map |
|
property `account` |
|
|=== |
|
|
|
Below you'll find some examples of working with the `BeanWrapper` to get and set |
|
properties. |
|
|
|
__(This next section is not vitally important to you if you're not planning to work with |
|
the `BeanWrapper` directly. If you're just using the `DataBinder` and the `BeanFactory` |
|
and their out-of-the-box implementation, you should skip ahead to the section about |
|
`PropertyEditors`.)__ |
|
|
|
Consider the following two classes: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class Company { |
|
|
|
private String name; |
|
private Employee managingDirector; |
|
|
|
public String getName() { |
|
return this.name; |
|
} |
|
|
|
public void setName(String name) { |
|
this.name = name; |
|
} |
|
|
|
public Employee getManagingDirector() { |
|
return this.managingDirector; |
|
} |
|
|
|
public void setManagingDirector(Employee managingDirector) { |
|
this.managingDirector = managingDirector; |
|
} |
|
} |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class Employee { |
|
|
|
private String name; |
|
|
|
private float salary; |
|
|
|
public String getName() { |
|
return this.name; |
|
} |
|
|
|
public void setName(String name) { |
|
this.name = name; |
|
} |
|
|
|
public float getSalary() { |
|
return salary; |
|
} |
|
|
|
public void setSalary(float salary) { |
|
this.salary = salary; |
|
} |
|
} |
|
---- |
|
|
|
The following code snippets show some examples of how to retrieve and manipulate some of |
|
the properties of instantiated `Companies` and `Employees`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
BeanWrapper company = BeanWrapperImpl(new Company()); |
|
// setting the company name.. |
|
company.setPropertyValue("name", "Some Company Inc."); |
|
// ... can also be done like this: |
|
PropertyValue value = new PropertyValue("name", "Some Company Inc."); |
|
company.setPropertyValue(value); |
|
|
|
// ok, let's create the director and tie it to the company: |
|
BeanWrapper jim = BeanWrapperImpl(new Employee()); |
|
jim.setPropertyValue("name", "Jim Stravinsky"); |
|
company.setPropertyValue("managingDirector", jim.getWrappedInstance()); |
|
|
|
// retrieving the salary of the managingDirector through the company |
|
Float salary = (Float) company.getPropertyValue("managingDirector.salary"); |
|
---- |
|
|
|
|
|
|
|
[[beans-beans-conversion]] |
|
==== Built-in PropertyEditor implementations |
|
|
|
Spring uses the concept of `PropertyEditors` to effect the conversion between an |
|
`Object` and a `String`. If you think about it, it sometimes might be handy to be able |
|
to represent properties in a different way than the object itself. For example, a `Date` |
|
can be represented in a human readable way (as the `String` ' `2007-14-09`'), while |
|
we're still able to convert the human readable form back to the original date (or even |
|
better: convert any date entered in a human readable form, back to `Date` objects). This |
|
behavior can be achieved by __registering custom editors__, of type |
|
`java.beans.PropertyEditor`. Registering custom editors on a `BeanWrapper` or |
|
alternately in a specific IoC container as mentioned in the previous chapter, gives it |
|
the knowledge of how to convert properties to the desired type. Read more about |
|
`PropertyEditors` in the javadocs of the `java.beans` package provided by Oracle. |
|
|
|
A couple of examples where property editing is used in Spring: |
|
|
|
* __setting properties on beans__ is done using `PropertyEditors`. When mentioning |
|
`java.lang.String` as the value of a property of some bean you're declaring in XML |
|
file, Spring will (if the setter of the corresponding property has a |
|
`Class`-parameter) use the `ClassEditor` to try to resolve the parameter to a `Class` |
|
object. |
|
* __parsing HTTP request parameters__ in Spring's MVC framework is done using all kinds |
|
of `PropertyEditors` that you can manually bind in all subclasses of the |
|
`CommandController`. |
|
|
|
Spring has a number of built-in `PropertyEditors` to make life easy. Each of those is |
|
listed below and they are all located in the `org.springframework.beans.propertyeditors` |
|
package. Most, but not all (as indicated below), are registered by default by |
|
`BeanWrapperImpl`. Where the property editor is configurable in some fashion, you can of |
|
course still register your own variant to override the default one: |
|
|
|
[[beans-beans-property-editors-tbl]] |
|
.Built-in PropertyEditors |
|
|=== |
|
| Class| Explanation |
|
|
|
| `ByteArrayPropertyEditor` |
|
| Editor for byte arrays. Strings will simply be converted to their corresponding byte |
|
representations. Registered by default by `BeanWrapperImpl`. |
|
|
|
| `ClassEditor` |
|
| Parses Strings representing classes to actual classes and the other way around. When a |
|
class is not found, an `IllegalArgumentException` is thrown. Registered by default by |
|
`BeanWrapperImpl`. |
|
|
|
| `CustomBooleanEditor` |
|
| Customizable property editor for `Boolean` properties. Registered by default by |
|
`BeanWrapperImpl`, but, can be overridden by registering custom instance of it as |
|
custom editor. |
|
|
|
| `CustomCollectionEditor` |
|
| Property editor for Collections, converting any source `Collection` to a given target |
|
`Collection` type. |
|
|
|
| `CustomDateEditor` |
|
| Customizable property editor for java.util.Date, supporting a custom DateFormat. NOT |
|
registered by default. Must be user registered as needed with appropriate format. |
|
|
|
| `CustomNumberEditor` |
|
| Customizable property editor for any Number subclass like `Integer`, `Long`, `Float`, |
|
`Double`. Registered by default by `BeanWrapperImpl`, but can be overridden by |
|
registering custom instance of it as a custom editor. |
|
|
|
| `FileEditor` |
|
| Capable of resolving Strings to `java.io.File` objects. Registered by default by |
|
`BeanWrapperImpl`. |
|
|
|
| `InputStreamEditor` |
|
| One-way property editor, capable of taking a text string and producing (via an |
|
intermediate `ResourceEditor` and `Resource`) an `InputStream`, so `InputStream` |
|
properties may be directly set as Strings. Note that the default usage will not close |
|
the `InputStream` for you! Registered by default by `BeanWrapperImpl`. |
|
|
|
| `LocaleEditor` |
|
| Capable of resolving Strings to `Locale` objects and vice versa (the String format is |
|
[language]_[country]_[variant], which is the same thing the toString() method of |
|
Locale provides). Registered by default by `BeanWrapperImpl`. |
|
|
|
| `PatternEditor` |
|
| Capable of resolving Strings to `java.util.regex.Pattern` objects and vice versa. |
|
|
|
| `PropertiesEditor` |
|
| Capable of converting Strings (formatted using the format as defined in the javadocs |
|
of the `java.util.Properties` class) to `Properties` objects. Registered by default |
|
by `BeanWrapperImpl`. |
|
|
|
| `StringTrimmerEditor` |
|
| Property editor that trims Strings. Optionally allows transforming an empty string |
|
into a `null` value. NOT registered by default; must be user registered as needed. |
|
|
|
| `URLEditor` |
|
| Capable of resolving a String representation of a URL to an actual `URL` object. |
|
Registered by default by `BeanWrapperImpl`. |
|
|=== |
|
|
|
Spring uses the `java.beans.PropertyEditorManager` to set the search path for property |
|
editors that might be needed. The search path also includes `sun.bean.editors`, which |
|
includes `PropertyEditor` implementations for types such as `Font`, `Color`, and most of |
|
the primitive types. Note also that the standard JavaBeans infrastructure will |
|
automatically discover `PropertyEditor` classes (without you having to register them |
|
explicitly) if they are in the same package as the class they handle, and have the same |
|
name as that class, with `'Editor'` appended; for example, one could have the following |
|
class and package structure, which would be sufficient for the `FooEditor` class to be |
|
recognized and used as the `PropertyEditor` for `Foo`-typed properties. |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
com |
|
chank |
|
pop |
|
Foo |
|
FooEditor // the PropertyEditor for the Foo class |
|
---- |
|
|
|
Note that you can also use the standard `BeanInfo` JavaBeans mechanism here as well |
|
(described |
|
http://docs.oracle.com/javase/tutorial/javabeans/advanced/customization.html[in |
|
not-amazing-detail here]). Find below an example of using the `BeanInfo` mechanism for |
|
explicitly registering one or more `PropertyEditor` instances with the properties of an |
|
associated class. |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
com |
|
chank |
|
pop |
|
Foo |
|
FooBeanInfo // the BeanInfo for the Foo class |
|
---- |
|
|
|
Here is the Java source code for the referenced `FooBeanInfo` class. This would |
|
associate a `CustomNumberEditor` with the `age` property of the `Foo` class. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class FooBeanInfo extends SimpleBeanInfo { |
|
|
|
public PropertyDescriptor[] getPropertyDescriptors() { |
|
try { |
|
final PropertyEditor numberPE = new CustomNumberEditor(Integer.class, true); |
|
PropertyDescriptor ageDescriptor = new PropertyDescriptor("age", Foo.class) { |
|
public PropertyEditor createPropertyEditor(Object bean) { |
|
return numberPE; |
|
}; |
|
}; |
|
return new PropertyDescriptor[] { ageDescriptor }; |
|
} |
|
catch (IntrospectionException ex) { |
|
throw new Error(ex.toString()); |
|
} |
|
} |
|
} |
|
---- |
|
|
|
|
|
[[beans-beans-conversion-customeditor-registration]] |
|
===== Registering additional custom PropertyEditors |
|
|
|
When setting bean properties as a string value, a Spring IoC container ultimately uses |
|
standard JavaBeans `PropertyEditors` to convert these Strings to the complex type of the |
|
property. Spring pre-registers a number of custom `PropertyEditors` (for example, to |
|
convert a classname expressed as a string into a real `Class` object). Additionally, |
|
Java's standard JavaBeans `PropertyEditor` lookup mechanism allows a `PropertyEditor` |
|
for a class simply to be named appropriately and placed in the same package as the class |
|
it provides support for, to be found automatically. |
|
|
|
If there is a need to register other custom `PropertyEditors`, there are several |
|
mechanisms available. The most manual approach, which is not normally convenient or |
|
recommended, is to simply use the `registerCustomEditor()` method of the |
|
`ConfigurableBeanFactory` interface, assuming you have a `BeanFactory` reference. |
|
Another, slightly more convenient, mechanism is to use a special bean factory |
|
post-processor called `CustomEditorConfigurer`. Although bean factory post-processors |
|
can be used with `BeanFactory` implementations, the `CustomEditorConfigurer` has a |
|
nested property setup, so it is strongly recommended that it is used with the |
|
`ApplicationContext`, where it may be deployed in similar fashion to any other bean, and |
|
automatically detected and applied. |
|
|
|
Note that all bean factories and application contexts automatically use a number of |
|
built-in property editors, through their use of something called a `BeanWrapper` to |
|
handle property conversions. The standard property editors that the `BeanWrapper` |
|
registers are listed in <<beans-beans-conversion,the previous section>>. Additionally, |
|
`ApplicationContexts` also override or add an additional number of editors to handle |
|
resource lookups in a manner appropriate to the specific application context type. |
|
|
|
Standard JavaBeans `PropertyEditor` instances are used to convert property values |
|
expressed as strings to the actual complex type of the property. |
|
`CustomEditorConfigurer`, a bean factory post-processor, may be used to conveniently add |
|
support for additional `PropertyEditor` instances to an `ApplicationContext`. |
|
|
|
Consider a user class `ExoticType`, and another class `DependsOnExoticType` which needs |
|
`ExoticType` set as a property: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package example; |
|
|
|
public class ExoticType { |
|
|
|
private String name; |
|
|
|
public ExoticType(String name) { |
|
this.name = name; |
|
} |
|
} |
|
|
|
public class DependsOnExoticType { |
|
|
|
private ExoticType type; |
|
|
|
public void setType(ExoticType type) { |
|
this.type = type; |
|
} |
|
} |
|
---- |
|
|
|
When things are properly set up, we want to be able to assign the type property as a |
|
string, which a `PropertyEditor` will behind the scenes convert into an actual |
|
`ExoticType` instance: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="sample" class="example.DependsOnExoticType"> |
|
<property name="type" value="aNameForExoticType"/> |
|
</bean> |
|
---- |
|
|
|
The `PropertyEditor` implementation could look similar to this: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// converts string representation to ExoticType object |
|
package example; |
|
|
|
public class ExoticTypeEditor extends PropertyEditorSupport { |
|
|
|
public void setAsText(String text) { |
|
setValue(new ExoticType(text.toUpperCase())); |
|
} |
|
} |
|
---- |
|
|
|
Finally, we use `CustomEditorConfigurer` to register the new `PropertyEditor` with the |
|
`ApplicationContext`, which will then be able to use it as needed: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer"> |
|
<property name="customEditors"> |
|
<map> |
|
<entry key="example.ExoticType" value="example.ExoticTypeEditor"/> |
|
</map> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
[[beans-beans-conversion-customeditor-registration-per]] |
|
====== Using PropertyEditorRegistrars |
|
|
|
Another mechanism for registering property editors with the Spring container is to |
|
create and use a `PropertyEditorRegistrar`. This interface is particularly useful when |
|
you need to use the same set of property editors in several different situations: write |
|
a corresponding registrar and reuse that in each case. `PropertyEditorRegistrars` work |
|
in conjunction with an interface called `PropertyEditorRegistry`, an interface that is |
|
implemented by the Spring `BeanWrapper` (and `DataBinder`). `PropertyEditorRegistrars` |
|
are particularly convenient when used in conjunction with the `CustomEditorConfigurer` |
|
(introduced <<beans-beans-conversion-customeditor-registration,here>>), which exposes a |
|
property called `setPropertyEditorRegistrars(..)`: `PropertyEditorRegistrars` added to a |
|
`CustomEditorConfigurer` in this fashion can easily be shared with `DataBinder` and |
|
Spring MVC `Controllers`. Furthermore, it avoids the need for synchronization on custom |
|
editors: a `PropertyEditorRegistrar` is expected to create fresh `PropertyEditor` |
|
instances for each bean creation attempt. |
|
|
|
Using a `PropertyEditorRegistrar` is perhaps best illustrated with an example. First |
|
off, you need to create your own `PropertyEditorRegistrar` implementation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package com.foo.editors.spring; |
|
|
|
public final class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar { |
|
|
|
public void registerCustomEditors(PropertyEditorRegistry registry) { |
|
|
|
// it is expected that new PropertyEditor instances are created |
|
registry.registerCustomEditor(ExoticType.class, new ExoticTypeEditor()); |
|
|
|
// you could register as many custom property editors as are required here... |
|
} |
|
} |
|
---- |
|
|
|
See also the `org.springframework.beans.support.ResourceEditorRegistrar` for an example |
|
`PropertyEditorRegistrar` implementation. Notice how in its implementation of the |
|
`registerCustomEditors(..)` method it creates new instances of each property editor. |
|
|
|
Next we configure a `CustomEditorConfigurer` and inject an instance of our |
|
`CustomPropertyEditorRegistrar` into it: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer"> |
|
<property name="propertyEditorRegistrars"> |
|
<list> |
|
<ref bean="customPropertyEditorRegistrar"/> |
|
</list> |
|
</property> |
|
</bean> |
|
|
|
<bean id="customPropertyEditorRegistrar" |
|
class="com.foo.editors.spring.CustomPropertyEditorRegistrar"/> |
|
---- |
|
|
|
Finally, and in a bit of a departure from the focus of this chapter, for those of you |
|
using <<mvc,Spring's MVC web framework>>, using `PropertyEditorRegistrars` in |
|
conjunction with data-binding `Controllers` (such as `SimpleFormController`) can be very |
|
convenient. Find below an example of using a `PropertyEditorRegistrar` in the |
|
implementation of an `initBinder(..)` method: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public final class RegisterUserController extends SimpleFormController { |
|
|
|
private final PropertyEditorRegistrar customPropertyEditorRegistrar; |
|
|
|
public RegisterUserController(PropertyEditorRegistrar propertyEditorRegistrar) { |
|
this.customPropertyEditorRegistrar = propertyEditorRegistrar; |
|
} |
|
|
|
protected void initBinder(HttpServletRequest request, |
|
ServletRequestDataBinder binder) throws Exception { |
|
**this.customPropertyEditorRegistrar.registerCustomEditors(binder);** |
|
} |
|
|
|
// other methods to do with registering a User |
|
} |
|
---- |
|
|
|
This style of `PropertyEditor` registration can lead to concise code (the implementation |
|
of `initBinder(..)` is just one line long!), and allows common `PropertyEditor` |
|
registration code to be encapsulated in a class and then shared amongst as many |
|
`Controllers` as needed. |
|
|
|
|
|
|
|
|
|
[[core-convert]] |
|
=== Spring Type Conversion |
|
Spring 3 introduces a `core.convert` package that provides a general type conversion |
|
system. The system defines an SPI to implement type conversion logic, as well as an API |
|
to execute type conversions at runtime. Within a Spring container, this system can be |
|
used as an alternative to PropertyEditors to convert externalized bean property value |
|
strings to required property types. The public API may also be used anywhere in your |
|
application where type conversion is needed. |
|
|
|
|
|
|
|
[[core-convert-Converter-API]] |
|
==== Converter SPI |
|
The SPI to implement type conversion logic is simple and strongly typed: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.springframework.core.convert.converter; |
|
|
|
public interface Converter<S, T> { |
|
|
|
T convert(S source); |
|
|
|
} |
|
---- |
|
|
|
To create your own converter, simply implement the interface above. Parameterize `S` |
|
as the type you are converting from, and `T` as the type you are converting to. Such a |
|
converter can also be applied transparently if a collection or array of `S` needs to be |
|
converted to an array or collection of `T`, provided that a delegating array/collection |
|
converter has been registered as well (which `DefaultConversionService` does by default). |
|
|
|
For each call to `convert(S)`, the source argument is guaranteed to be NOT null. Your |
|
Converter may throw any unchecked exception if conversion fails; specifically, an |
|
`IllegalArgumentException` should be thrown to report an invalid source value. |
|
Take care to ensure that your `Converter` implementation is thread-safe. |
|
|
|
Several converter implementations are provided in the `core.convert.support` package as |
|
a convenience. These include converters from Strings to Numbers and other common types. |
|
Consider `StringToInteger` as an example for a typical `Converter` implementation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.springframework.core.convert.support; |
|
|
|
final class StringToInteger implements Converter<String, Integer> { |
|
|
|
public Integer convert(String source) { |
|
return Integer.valueOf(source); |
|
} |
|
|
|
} |
|
---- |
|
|
|
|
|
|
|
[[core-convert-ConverterFactory-SPI]] |
|
==== ConverterFactory |
|
When you need to centralize the conversion logic for an entire class hierarchy, for |
|
example, when converting from String to java.lang.Enum objects, implement |
|
`ConverterFactory`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.springframework.core.convert.converter; |
|
|
|
public interface ConverterFactory<S, R> { |
|
|
|
<T extends R> Converter<S, T> getConverter(Class<T> targetType); |
|
|
|
} |
|
---- |
|
|
|
Parameterize S to be the type you are converting from and R to be the base type defining |
|
the __range__ of classes you can convert to. Then implement getConverter(Class<T>), |
|
where T is a subclass of R. |
|
|
|
Consider the `StringToEnum` ConverterFactory as an example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.springframework.core.convert.support; |
|
|
|
final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> { |
|
|
|
public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) { |
|
return new StringToEnumConverter(targetType); |
|
} |
|
|
|
private final class StringToEnumConverter<T extends Enum> implements Converter<String, T> { |
|
|
|
private Class<T> enumType; |
|
|
|
public StringToEnumConverter(Class<T> enumType) { |
|
this.enumType = enumType; |
|
} |
|
|
|
public T convert(String source) { |
|
return (T) Enum.valueOf(this.enumType, source.trim()); |
|
} |
|
} |
|
} |
|
---- |
|
|
|
|
|
|
|
[[core-convert-GenericConverter-SPI]] |
|
==== GenericConverter |
|
When you require a sophisticated Converter implementation, consider the GenericConverter |
|
interface. With a more flexible but less strongly typed signature, a GenericConverter |
|
supports converting between multiple source and target types. In addition, a |
|
GenericConverter makes available source and target field context you can use when |
|
implementing your conversion logic. Such context allows a type conversion to be driven |
|
by a field annotation, or generic information declared on a field signature. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.springframework.core.convert.converter; |
|
|
|
public interface GenericConverter { |
|
|
|
public Set<ConvertiblePair> getConvertibleTypes(); |
|
|
|
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType); |
|
|
|
} |
|
---- |
|
|
|
To implement a GenericConverter, have getConvertibleTypes() return the supported |
|
source->target type pairs. Then implement convert(Object, TypeDescriptor, |
|
TypeDescriptor) to implement your conversion logic. The source TypeDescriptor provides |
|
access to the source field holding the value being converted. The target TypeDescriptor |
|
provides access to the target field where the converted value will be set. |
|
|
|
A good example of a GenericConverter is a converter that converts between a Java Array |
|
and a Collection. Such an ArrayToCollectionConverter introspects the field that declares |
|
the target Collection type to resolve the Collection's element type. This allows each |
|
element in the source array to be converted to the Collection element type before the |
|
Collection is set on the target field. |
|
|
|
[NOTE] |
|
==== |
|
Because GenericConverter is a more complex SPI interface, only use it when you need it. |
|
Favor Converter or ConverterFactory for basic type conversion needs. |
|
==== |
|
|
|
|
|
[[core-convert-ConditionalGenericConverter-SPI]] |
|
===== ConditionalGenericConverter |
|
Sometimes you only want a Converter to execute if a specific condition holds true. For |
|
example, you might only want to execute a Converter if a specific annotation is present |
|
on the target field. Or you might only want to execute a Converter if a specific method, |
|
such as static valueOf method, is defined on the target class. |
|
ConditionalGenericConverter is an subinterface of GenericConverter that allows you to |
|
define such custom matching criteria: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface ConditionalGenericConverter extends GenericConverter { |
|
|
|
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType); |
|
|
|
} |
|
---- |
|
|
|
A good example of a ConditionalGenericConverter is an EntityConverter that converts |
|
between an persistent entity identifier and an entity reference. Such a EntityConverter |
|
might only match if the target entity type declares a static finder method e.g. |
|
findAccount(Long). You would perform such a finder method check in the implementation of |
|
matches(TypeDescriptor, TypeDescriptor). |
|
|
|
|
|
|
|
[[core-convert-ConversionService-API]] |
|
==== ConversionService API |
|
The ConversionService defines a unified API for executing type conversion logic at |
|
runtime. Converters are often executed behind this facade interface: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.springframework.core.convert; |
|
|
|
public interface ConversionService { |
|
|
|
boolean canConvert(Class<?> sourceType, Class<?> targetType); |
|
|
|
<T> T convert(Object source, Class<T> targetType); |
|
|
|
boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType); |
|
|
|
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType); |
|
|
|
} |
|
---- |
|
|
|
Most ConversionService implementations also implement `ConverterRegistry`, which |
|
provides an SPI for registering converters. Internally, a ConversionService |
|
implementation delegates to its registered converters to carry out type conversion logic. |
|
|
|
A robust ConversionService implementation is provided in the `core.convert.support` |
|
package. `GenericConversionService` is the general-purpose implementation suitable for |
|
use in most environments. `ConversionServiceFactory` provides a convenient factory for |
|
creating common ConversionService configurations. |
|
|
|
|
|
|
|
[[core-convert-Spring-config]] |
|
==== Configuring a ConversionService |
|
A ConversionService is a stateless object designed to be instantiated at application |
|
startup, then shared between multiple threads. In a Spring application, you typically |
|
configure a ConversionService instance per Spring container (or ApplicationContext). |
|
That ConversionService will be picked up by Spring and then used whenever a type |
|
conversion needs to be performed by the framework. You may also inject this |
|
ConversionService into any of your beans and invoke it directly. |
|
|
|
[NOTE] |
|
==== |
|
If no ConversionService is registered with Spring, the original PropertyEditor-based |
|
system is used. |
|
==== |
|
|
|
To register a default ConversionService with Spring, add the following bean definition |
|
with id `conversionService`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="conversionService" |
|
class="org.springframework.context.support.ConversionServiceFactoryBean"/> |
|
---- |
|
|
|
A default ConversionService can convert between strings, numbers, enums, collections, |
|
maps, and other common types. To supplement or override the default converters with your |
|
own custom converter(s), set the `converters` property. Property values may implement |
|
either of the Converter, ConverterFactory, or GenericConverter interfaces. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="conversionService" |
|
class="org.springframework.context.support.ConversionServiceFactoryBean"> |
|
<property name="converters"> |
|
<set> |
|
<bean class="example.MyCustomConverter"/> |
|
</set> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
It is also common to use a ConversionService within a Spring MVC application. See |
|
<<format-configuring-formatting-mvc>> for details on use with `<mvc:annotation-driven/>`. |
|
|
|
In certain situations you may wish to apply formatting during conversion. See |
|
<<format-FormatterRegistry-SPI>> for details on using |
|
`FormattingConversionServiceFactoryBean`. |
|
|
|
|
|
|
|
[[core-convert-programmatic-usage]] |
|
==== Using a ConversionService programmatically |
|
To work with a ConversionService instance programmatically, simply inject a reference to |
|
it like you would for any other bean: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Service |
|
public class MyService { |
|
|
|
@Autowired |
|
public MyService(ConversionService conversionService) { |
|
this.conversionService = conversionService; |
|
} |
|
|
|
public void doIt() { |
|
this.conversionService.convert(...) |
|
} |
|
} |
|
---- |
|
|
|
For most use cases, the `convert` method specifying the _targetType_ can be used but it |
|
will not work with more complex types such as a collection of a parameterized element. |
|
If you want to convert a `List` of `Integer` to a `List` of `String` programmatically, |
|
for instance, you need to provide a formal definition of the source and target types. |
|
|
|
Fortunately, `TypeDescriptor` provides various options to make that straightforward: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
DefaultConversionService cs = new DefaultConversionService(); |
|
|
|
List<Integer> input = .... |
|
cs.convert(input, |
|
TypeDescriptor.forObject(input), // List<Integer> type descriptor |
|
TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class))); |
|
---- |
|
|
|
Note that `DefaultConversionService` registers converters automatically which are |
|
appropriate for most environments. This includes collection converters, scalar |
|
converters, and also basic `Object` to `String` converters. The same converters can |
|
be registered with any `ConverterRegistry` using the _static_ `addDefaultConverters` |
|
method on the `DefaultConversionService` class. |
|
|
|
Converters for value types will be reused for arrays and collections, so there is |
|
no need to create a specific converter to convert from a `Collection` of `S` to a |
|
`Collection` of `T`, assuming that standard collection handling is appropriate. |
|
|
|
|
|
|
|
|
|
[[format]] |
|
=== Spring Field Formatting |
|
As discussed in the previous section, <<core-convert, `core.convert`>> is a |
|
general-purpose type conversion system. It provides a unified ConversionService API as |
|
well as a strongly-typed Converter SPI for implementing conversion logic from one type |
|
to another. A Spring Container uses this system to bind bean property values. In |
|
addition, both the Spring Expression Language (SpEL) and DataBinder use this system to |
|
bind field values. For example, when SpEL needs to coerce a `Short` to a `Long` to |
|
complete an `expression.setValue(Object bean, Object value)` attempt, the core.convert |
|
system performs the coercion. |
|
|
|
Now consider the type conversion requirements of a typical client environment such as a |
|
web or desktop application. In such environments, you typically convert __from String__ |
|
to support the client postback process, as well as back __to String__ to support the |
|
view rendering process. In addition, you often need to localize String values. The more |
|
general __core.convert__ Converter SPI does not address such __formatting__ requirements |
|
directly. To directly address them, Spring 3 introduces a convenient Formatter SPI that |
|
provides a simple and robust alternative to PropertyEditors for client environments. |
|
|
|
In general, use the Converter SPI when you need to implement general-purpose type |
|
conversion logic; for example, for converting between a java.util.Date and and |
|
java.lang.Long. Use the Formatter SPI when you're working in a client environment, such |
|
as a web application, and need to parse and print localized field values. The |
|
ConversionService provides a unified type conversion API for both SPIs. |
|
|
|
|
|
|
|
[[format-Formatter-SPI]] |
|
==== Formatter SPI |
|
The Formatter SPI to implement field formatting logic is simple and strongly typed: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.springframework.format; |
|
|
|
public interface Formatter<T> extends Printer<T>, Parser<T> { |
|
} |
|
---- |
|
|
|
Where Formatter extends from the Printer and Parser building-block interfaces: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface Printer<T> { |
|
String print(T fieldValue, Locale locale); |
|
} |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import java.text.ParseException; |
|
|
|
public interface Parser<T> { |
|
T parse(String clientValue, Locale locale) throws ParseException; |
|
} |
|
---- |
|
|
|
To create your own Formatter, simply implement the Formatter interface above. |
|
Parameterize T to be the type of object you wish to format, for example, |
|
`java.util.Date`. Implement the `print()` operation to print an instance of T for |
|
display in the client locale. Implement the `parse()` operation to parse an instance of |
|
T from the formatted representation returned from the client locale. Your Formatter |
|
should throw a ParseException or IllegalArgumentException if a parse attempt fails. Take |
|
care to ensure your Formatter implementation is thread-safe. |
|
|
|
Several Formatter implementations are provided in `format` subpackages as a convenience. |
|
The `number` package provides a `NumberFormatter`, `CurrencyFormatter`, and |
|
`PercentFormatter` to format `java.lang.Number` objects using a `java.text.NumberFormat`. |
|
The `datetime` package provides a `DateFormatter` to format `java.util.Date` objects with |
|
a `java.text.DateFormat`. The `datetime.joda` package provides comprehensive datetime |
|
formatting support based on the http://joda-time.sourceforge.net[Joda Time library]. |
|
|
|
Consider `DateFormatter` as an example `Formatter` implementation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.springframework.format.datetime; |
|
|
|
public final class DateFormatter implements Formatter<Date> { |
|
|
|
private String pattern; |
|
|
|
public DateFormatter(String pattern) { |
|
this.pattern = pattern; |
|
} |
|
|
|
public String print(Date date, Locale locale) { |
|
if (date == null) { |
|
return ""; |
|
} |
|
return getDateFormat(locale).format(date); |
|
} |
|
|
|
public Date parse(String formatted, Locale locale) throws ParseException { |
|
if (formatted.length() == 0) { |
|
return null; |
|
} |
|
return getDateFormat(locale).parse(formatted); |
|
} |
|
|
|
protected DateFormat getDateFormat(Locale locale) { |
|
DateFormat dateFormat = new SimpleDateFormat(this.pattern, locale); |
|
dateFormat.setLenient(false); |
|
return dateFormat; |
|
} |
|
|
|
} |
|
---- |
|
|
|
The Spring team welcomes community-driven `Formatter` contributions; see |
|
https://jira.spring.io/browse/SPR[jira.spring.io] to contribute. |
|
|
|
|
|
|
|
[[format-CustomFormatAnnotations]] |
|
==== Annotation-driven Formatting |
|
As you will see, field formatting can be configured by field type or annotation. To bind |
|
an Annotation to a formatter, implement AnnotationFormatterFactory: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.springframework.format; |
|
|
|
public interface AnnotationFormatterFactory<A extends Annotation> { |
|
|
|
Set<Class<?>> getFieldTypes(); |
|
|
|
Printer<?> getPrinter(A annotation, Class<?> fieldType); |
|
|
|
Parser<?> getParser(A annotation, Class<?> fieldType); |
|
|
|
} |
|
---- |
|
|
|
Parameterize A to be the field annotationType you wish to associate formatting logic |
|
with, for example `org.springframework.format.annotation.DateTimeFormat`. Have |
|
`getFieldTypes()` return the types of fields the annotation may be used on. Have |
|
`getPrinter()` return a Printer to print the value of an annotated field. Have |
|
`getParser()` return a Parser to parse a clientValue for an annotated field. |
|
|
|
The example AnnotationFormatterFactory implementation below binds the @NumberFormat |
|
Annotation to a formatter. This annotation allows either a number style or pattern to be |
|
specified: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public final class NumberFormatAnnotationFormatterFactory |
|
implements AnnotationFormatterFactory<NumberFormat> { |
|
|
|
public Set<Class<?>> getFieldTypes() { |
|
return new HashSet<Class<?>>(asList(new Class<?>[] { |
|
Short.class, Integer.class, Long.class, Float.class, |
|
Double.class, BigDecimal.class, BigInteger.class })); |
|
} |
|
|
|
public Printer<Number> getPrinter(NumberFormat annotation, Class<?> fieldType) { |
|
return configureFormatterFrom(annotation, fieldType); |
|
} |
|
|
|
public Parser<Number> getParser(NumberFormat annotation, Class<?> fieldType) { |
|
return configureFormatterFrom(annotation, fieldType); |
|
} |
|
|
|
private Formatter<Number> configureFormatterFrom(NumberFormat annotation, |
|
Class<?> fieldType) { |
|
if (!annotation.pattern().isEmpty()) { |
|
return new NumberFormatter(annotation.pattern()); |
|
} else { |
|
Style style = annotation.style(); |
|
if (style == Style.PERCENT) { |
|
return new PercentFormatter(); |
|
} else if (style == Style.CURRENCY) { |
|
return new CurrencyFormatter(); |
|
} else { |
|
return new NumberFormatter(); |
|
} |
|
} |
|
} |
|
} |
|
---- |
|
|
|
To trigger formatting, simply annotate fields with @NumberFormat: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MyModel { |
|
|
|
@NumberFormat(style=Style.CURRENCY) |
|
private BigDecimal decimal; |
|
|
|
} |
|
---- |
|
|
|
|
|
[[format-annotations-api]] |
|
===== Format Annotation API |
|
A portable format annotation API exists in the `org.springframework.format.annotation` |
|
package. Use @NumberFormat to format java.lang.Number fields. Use @DateTimeFormat to |
|
format java.util.Date, java.util.Calendar, java.util.Long, or Joda Time fields. |
|
|
|
The example below uses @DateTimeFormat to format a java.util.Date as a ISO Date |
|
(yyyy-MM-dd): |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MyModel { |
|
|
|
@DateTimeFormat(iso=ISO.DATE) |
|
private Date date; |
|
|
|
} |
|
---- |
|
|
|
|
|
|
|
[[format-FormatterRegistry-SPI]] |
|
==== FormatterRegistry SPI |
|
The FormatterRegistry is an SPI for registering formatters and converters. |
|
`FormattingConversionService` is an implementation of FormatterRegistry suitable for |
|
most environments. This implementation may be configured programmatically or |
|
declaratively as a Spring bean using `FormattingConversionServiceFactoryBean`. Because |
|
this implementation also implements `ConversionService`, it can be directly configured |
|
for use with Spring's DataBinder and the Spring Expression Language (SpEL). |
|
|
|
Review the FormatterRegistry SPI below: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.springframework.format; |
|
|
|
public interface FormatterRegistry extends ConverterRegistry { |
|
|
|
void addFormatterForFieldType(Class<?> fieldType, Printer<?> printer, Parser<?> parser); |
|
|
|
void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter); |
|
|
|
void addFormatterForFieldType(Formatter<?> formatter); |
|
|
|
void addFormatterForAnnotation(AnnotationFormatterFactory<?, ?> factory); |
|
|
|
} |
|
---- |
|
|
|
As shown above, Formatters can be registered by fieldType or annotation. |
|
|
|
The FormatterRegistry SPI allows you to configure Formatting rules centrally, instead of |
|
duplicating such configuration across your Controllers. For example, you might want to |
|
enforce that all Date fields are formatted a certain way, or fields with a specific |
|
annotation are formatted in a certain way. With a shared FormatterRegistry, you define |
|
these rules once and they are applied whenever formatting is needed. |
|
|
|
|
|
|
|
[[format-FormatterRegistrar-SPI]] |
|
==== FormatterRegistrar SPI |
|
The FormatterRegistrar is an SPI for registering formatters and converters through the |
|
FormatterRegistry: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.springframework.format; |
|
|
|
public interface FormatterRegistrar { |
|
|
|
void registerFormatters(FormatterRegistry registry); |
|
|
|
} |
|
---- |
|
|
|
A FormatterRegistrar is useful when registering multiple related converters and |
|
formatters for a given formatting category, such as Date formatting. It can also be |
|
useful where declarative registration is insufficient. For example when a formatter |
|
needs to be indexed under a specific field type different from its own <T> or when |
|
registering a Printer/Parser pair. The next section provides more information on |
|
converter and formatter registration. |
|
|
|
|
|
|
|
[[format-configuring-formatting-mvc]] |
|
==== Configuring Formatting in Spring MVC |
|
In a Spring MVC application, you may configure a custom ConversionService instance |
|
explicitly as an attribute of the `annotation-driven` element of the MVC namespace. This |
|
ConversionService will then be used anytime a type conversion is required during |
|
Controller model binding. If not configured explicitly, Spring MVC will automatically |
|
register default formatters and converters for common types such as numbers and dates. |
|
|
|
To rely on default formatting rules, no custom configuration is required in your Spring |
|
MVC config XML: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:mvc="http://www.springframework.org/schema/mvc" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/mvc |
|
http://www.springframework.org/schema/mvc/spring-mvc.xsd"> |
|
|
|
<mvc:annotation-driven/> |
|
|
|
</beans> |
|
---- |
|
|
|
With this one-line of configuration, default formatters for Numbers and Date types will |
|
be installed, including support for the @NumberFormat and @DateTimeFormat annotations. |
|
Full support for the Joda Time formatting library is also installed if Joda Time is |
|
present on the classpath. |
|
|
|
To inject a ConversionService instance with custom formatters and converters registered, |
|
set the conversion-service attribute and then specify custom converters, formatters, or |
|
FormatterRegistrars as properties of the FormattingConversionServiceFactoryBean: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:mvc="http://www.springframework.org/schema/mvc" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/mvc |
|
http://www.springframework.org/schema/mvc/spring-mvc.xsd"> |
|
|
|
<mvc:annotation-driven conversion-service="conversionService"/> |
|
|
|
<bean id="conversionService" |
|
class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> |
|
<property name="converters"> |
|
<set> |
|
<bean class="org.example.MyConverter"/> |
|
</set> |
|
</property> |
|
<property name="formatters"> |
|
<set> |
|
<bean class="org.example.MyFormatter"/> |
|
<bean class="org.example.MyAnnotationFormatterFactory"/> |
|
</set> |
|
</property> |
|
<property name="formatterRegistrars"> |
|
<set> |
|
<bean class="org.example.MyFormatterRegistrar"/> |
|
</set> |
|
</property> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
See <<format-FormatterRegistrar-SPI>> and the `FormattingConversionServiceFactoryBean` |
|
for more information on when to use FormatterRegistrars. |
|
==== |
|
|
|
|
|
|
|
|
|
[[format-configuring-formatting-globaldatetimeformat]] |
|
=== Configuring a global date & time format |
|
By default, date and time fields that are not annotated with `@DateTimeFormat` are |
|
converted from strings using the the `DateFormat.SHORT` style. If you prefer, you can |
|
change this by defining your own global format. |
|
|
|
You will need to ensure that Spring does not register default formatters, and instead |
|
you should register all formatters manually. Use the |
|
`org.springframework.format.datetime.joda.JodaTimeFormatterRegistrar` or |
|
`org.springframework.format.datetime.DateFormatterRegistrar` class depending on whether |
|
you use the Joda Time library. |
|
|
|
For example, the following Java configuration will register a global ' `yyyyMMdd`' |
|
format. This example does not depend on the Joda Time library: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
public class AppConfig { |
|
|
|
@Bean |
|
public FormattingConversionService conversionService() { |
|
|
|
// Use the DefaultFormattingConversionService but do not register defaults |
|
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(false); |
|
|
|
// Ensure @NumberFormat is still supported |
|
conversionService.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory()); |
|
|
|
// Register date conversion with a specific global format |
|
DateFormatterRegistrar registrar = new DateFormatterRegistrar(); |
|
registrar.setFormatter(new DateFormatter("yyyyMMdd")); |
|
registrar.registerFormatters(conversionService); |
|
|
|
return conversionService; |
|
} |
|
} |
|
---- |
|
|
|
If you prefer XML based configuration you can use a |
|
`FormattingConversionServiceFactoryBean`. Here is the same example, this time using Joda |
|
Time: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd> |
|
|
|
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> |
|
<property name="registerDefaultFormatters" value="false" /> |
|
<property name="formatters"> |
|
<set> |
|
<bean class="org.springframework.format.number.NumberFormatAnnotationFormatterFactory" /> |
|
</set> |
|
</property> |
|
<property name="formatterRegistrars"> |
|
<set> |
|
<bean class="org.springframework.format.datetime.joda.JodaTimeFormatterRegistrar"> |
|
<property name="dateFormatter"> |
|
<bean class="org.springframework.format.datetime.joda.DateTimeFormatterFactoryBean"> |
|
<property name="pattern" value="yyyyMMdd"/> |
|
</bean> |
|
</property> |
|
</bean> |
|
</set> |
|
</property> |
|
</bean> |
|
</beans> |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
Joda Time provides separate distinct types to represent `date`, `time` and `date-time` |
|
values. The `dateFormatter`, `timeFormatter` and `dateTimeFormatter` properties of the |
|
`JodaTimeFormatterRegistrar` should be used to configure the different formats for each |
|
type. The `DateTimeFormatterFactoryBean` provides a convenient way to create formatters. |
|
==== |
|
|
|
If you are using Spring MVC remember to explicitly configure the conversion service that |
|
is used. For Java based `@Configuration` this means extending the |
|
`WebMvcConfigurationSupport` class and overriding the `mvcConversionService()` method. |
|
For XML you should use the `'conversion-service'` attribute of the |
|
`mvc:annotation-driven` element. See <<format-configuring-formatting-mvc>> for details. |
|
|
|
|
|
|
|
|
|
[[validation-beanvalidation]] |
|
=== Spring Validation |
|
Spring 3 introduces several enhancements to its validation support. First, the JSR-303 |
|
Bean Validation API is now fully supported. Second, when used programmatically, Spring's |
|
DataBinder can now validate objects as well as bind to them. Third, Spring MVC now has |
|
support for declaratively validating `@Controller` inputs. |
|
|
|
|
|
|
|
[[validation-beanvalidation-overview]] |
|
==== Overview of the JSR-303 Bean Validation API |
|
JSR-303 standardizes validation constraint declaration and metadata for the Java |
|
platform. Using this API, you annotate domain model properties with declarative |
|
validation constraints and the runtime enforces them. There are a number of built-in |
|
constraints you can take advantage of. You may also define your own custom constraints. |
|
|
|
To illustrate, consider a simple PersonForm model with two properties: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class PersonForm { |
|
private String name; |
|
private int age; |
|
} |
|
---- |
|
|
|
JSR-303 allows you to define declarative validation constraints against such properties: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class PersonForm { |
|
|
|
@NotNull |
|
@Size(max=64) |
|
private String name; |
|
|
|
@Min(0) |
|
private int age; |
|
|
|
} |
|
---- |
|
|
|
When an instance of this class is validated by a JSR-303 Validator, these constraints |
|
will be enforced. |
|
|
|
For general information on JSR-303/JSR-349, see the http://beanvalidation.org/[Bean |
|
Validation website]. For information on the specific capabilities of the default |
|
reference implementation, see the https://www.hibernate.org/412.html[Hibernate |
|
Validator] documentation. To learn how to setup a Bean Validation provider as a Spring |
|
bean, keep reading. |
|
|
|
|
|
|
|
[[validation-beanvalidation-spring]] |
|
==== Configuring a Bean Validation Provider |
|
Spring provides full support for the Bean Validation API. This includes convenient |
|
support for bootstrapping a JSR-303/JSR-349 Bean Validation provider as a Spring bean. |
|
This allows for a `javax.validation.ValidatorFactory` or `javax.validation.Validator` to |
|
be injected wherever validation is needed in your application. |
|
|
|
Use the `LocalValidatorFactoryBean` to configure a default Validator as a Spring bean: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="validator" |
|
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/> |
|
---- |
|
|
|
The basic configuration above will trigger Bean Validation to initialize using its |
|
default bootstrap mechanism. A JSR-303/JSR-349 provider, such as Hibernate Validator, |
|
is expected to be present in the classpath and will be detected automatically. |
|
|
|
|
|
[[validation-beanvalidation-spring-inject]] |
|
===== Injecting a Validator |
|
`LocalValidatorFactoryBean` implements both `javax.validation.ValidatorFactory` and |
|
`javax.validation.Validator`, as well as Spring's |
|
`org.springframework.validation.Validator`. You may inject a reference to either of |
|
these interfaces into beans that need to invoke validation logic. |
|
|
|
Inject a reference to `javax.validation.Validator` if you prefer to work with the Bean |
|
Validation API directly: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import javax.validation.Validator; |
|
|
|
@Service |
|
public class MyService { |
|
|
|
@Autowired |
|
private Validator validator; |
|
---- |
|
|
|
Inject a reference to `org.springframework.validation.Validator` if your bean requires |
|
the Spring Validation API: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import org.springframework.validation.Validator; |
|
|
|
@Service |
|
public class MyService { |
|
|
|
@Autowired |
|
private Validator validator; |
|
|
|
} |
|
---- |
|
|
|
|
|
[[validation-beanvalidation-spring-constraints]] |
|
===== Configuring Custom Constraints |
|
Each Bean Validation constraint consists of two parts. First, a `@Constraint` annotation |
|
that declares the constraint and its configurable properties. Second, an implementation |
|
of the `javax.validation.ConstraintValidator` interface that implements the constraint's |
|
behavior. To associate a declaration with an implementation, each `@Constraint` annotation |
|
references a corresponding ValidationConstraint implementation class. At runtime, a |
|
`ConstraintValidatorFactory` instantiates the referenced implementation when the |
|
constraint annotation is encountered in your domain model. |
|
|
|
By default, the `LocalValidatorFactoryBean` configures a `SpringConstraintValidatorFactory` |
|
that uses Spring to create ConstraintValidator instances. This allows your custom |
|
ConstraintValidators to benefit from dependency injection like any other Spring bean. |
|
|
|
Shown below is an example of a custom `@Constraint` declaration, followed by an associated |
|
`ConstraintValidator` implementation that uses Spring for dependency injection: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Target({ElementType.METHOD, ElementType.FIELD}) |
|
@Retention(RetentionPolicy.RUNTIME) |
|
@Constraint(validatedBy=MyConstraintValidator.class) |
|
public @interface MyConstraint { |
|
} |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import javax.validation.ConstraintValidator; |
|
|
|
public class MyConstraintValidator implements ConstraintValidator { |
|
|
|
@Autowired; |
|
private Foo aDependency; |
|
|
|
... |
|
} |
|
---- |
|
|
|
As you can see, a ConstraintValidator implementation may have its dependencies |
|
@Autowired like any other Spring bean. |
|
|
|
|
|
[[validation-beanvalidation-spring-method]] |
|
===== Spring-driven Method Validation |
|
The method validation feature supported by Bean Validation 1.1, and as a custom |
|
extension also by Hibernate Validator 4.3, can be integrated into a Spring context |
|
through a `MethodValidationPostProcessor` bean definition: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/> |
|
---- |
|
|
|
In order to be eligible for Spring-driven method validation, all target classes need |
|
to be annotated with Spring's `@Validated` annotation, optionally declaring the |
|
validation groups to use. Check out the `MethodValidationPostProcessor` javadocs |
|
for setup details with Hibernate Validator and Bean Validation 1.1 providers. |
|
|
|
|
|
[[validation-beanvalidation-spring-other]] |
|
===== Additional Configuration Options |
|
The default `LocalValidatorFactoryBean` configuration should prove sufficient for most |
|
cases. There are a number of configuration options for various Bean Validation |
|
constructs, from message interpolation to traversal resolution. See the |
|
`LocalValidatorFactoryBean` javadocs for more information on these options. |
|
|
|
|
|
|
|
[[validation-binder]] |
|
==== Configuring a DataBinder |
|
Since Spring 3, a DataBinder instance can be configured with a Validator. Once |
|
configured, the Validator may be invoked by calling `binder.validate()`. Any validation |
|
Errors are automatically added to the binder's BindingResult. |
|
|
|
When working with the DataBinder programmatically, this can be used to invoke validation |
|
logic after binding to a target object: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Foo target = new Foo(); |
|
DataBinder binder = new DataBinder(target); |
|
binder.setValidator(new FooValidator()); |
|
|
|
// bind to the target object |
|
binder.bind(propertyValues); |
|
|
|
// validate the target object |
|
binder.validate(); |
|
|
|
// get BindingResult that includes any validation errors |
|
BindingResult results = binder.getBindingResult(); |
|
---- |
|
|
|
A DataBinder can also be configured with multiple `Validator` instances via |
|
`dataBinder.addValidators` and `dataBinder.replaceValidators`. This is useful when |
|
combining globally configured Bean Validation with a Spring `Validator` configured |
|
locally on a DataBinder instance. See <<validation-mvc-configuring>>. |
|
|
|
|
|
|
|
[[validation-mvc]] |
|
==== Spring MVC 3 Validation |
|
Beginning with Spring 3, Spring MVC has the ability to automatically validate |
|
`@Controller` inputs. In previous versions it was up to the developer to manually invoke |
|
validation logic. |
|
|
|
|
|
[[validation-mvc-triggering]] |
|
===== Triggering @Controller Input Validation |
|
To trigger validation of a `@Controller` input, simply annotate the input argument as |
|
++@Valid++: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
public class MyController { |
|
|
|
@RequestMapping("/foo", method=RequestMethod.POST) |
|
public void processFoo(**@Valid** Foo foo) { /* ... */ } |
|
---- |
|
|
|
Spring MVC will validate a @Valid object after binding so-long as an appropriate |
|
Validator has been configured. |
|
|
|
[NOTE] |
|
==== |
|
The @Valid annotation is part of the standard JSR-303 Bean Validation API, and is not a |
|
Spring-specific construct. |
|
==== |
|
|
|
|
|
[[validation-mvc-configuring]] |
|
===== Configuring a Validator for use by Spring MVC |
|
The `Validator` instance invoked when a `@Valid` method argument is encountered may be |
|
configured in two ways. First, you may call `binder.setValidator(Validator)` within a |
|
++@Controller++'s `@InitBinder` callback. This allows you to configure a `Validator` |
|
instance per `@Controller` class: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
public class MyController { |
|
|
|
@InitBinder |
|
protected void initBinder(WebDataBinder binder) { |
|
binder.setValidator(new FooValidator()); |
|
} |
|
|
|
@RequestMapping("/foo", method=RequestMethod.POST) |
|
public void processFoo(@Valid Foo foo) { ... } |
|
|
|
} |
|
---- |
|
|
|
Second, you may call `setValidator(Validator)` on the global `WebBindingInitializer`. This |
|
allows you to configure a `Validator` instance across all `@Controller` classes. This can be |
|
achieved easily by using the Spring MVC namespace: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:mvc="http://www.springframework.org/schema/mvc" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/mvc |
|
http://www.springframework.org/schema/mvc/spring-mvc.xsd"> |
|
|
|
<mvc:annotation-driven validator="globalValidator"/> |
|
|
|
</beans> |
|
---- |
|
|
|
To combine a global and a local validator, configure the global validator as shown above |
|
and then add a local validator: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
public class MyController { |
|
|
|
@InitBinder |
|
protected void initBinder(WebDataBinder binder) { |
|
binder.addValidators(new FooValidator()); |
|
} |
|
|
|
} |
|
---- |
|
|
|
|
|
[[validation-mvc-jsr303]] |
|
===== Configuring a JSR-303/JSR-349 Validator for use by Spring MVC |
|
With Bean Validation, a single `javax.validation.Validator` instance typically validates |
|
__all__ model objects that declare validation constraints. To configure such a JSR-303 |
|
backed Validator with Spring MVC, simply add a Bean Validation provider, such as |
|
Hibernate Validator, to your classpath. Spring MVC will detect it and automatically |
|
enable Bean Validation support across all Controllers. |
|
|
|
The Spring MVC configuration required to enable Bean Validation support is shown below: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:mvc="http://www.springframework.org/schema/mvc" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/mvc |
|
http://www.springframework.org/schema/mvc/spring-mvc.xsd"> |
|
|
|
<!-- JSR-303/JSR-349 support will be detected on classpath and enabled automatically --> |
|
<mvc:annotation-driven/> |
|
|
|
</beans> |
|
---- |
|
|
|
With this minimal configuration, anytime a `@Valid` `@Controller` input is encountered, it |
|
will be validated by the Bean Validation provider. That provider, in turn, will enforce |
|
any constraints declared against the input. Any ++ConstraintViolation++s will automatically |
|
be exposed as errors in the `BindingResult` renderable by standard Spring MVC form tags. |
|
|
|
|
|
|
|
|
|
|
|
[[expressions]] |
|
== Spring Expression Language (SpEL) |
|
|
|
|
|
|
|
|
|
[[expressions-intro]] |
|
=== Introduction |
|
The Spring Expression Language (SpEL for short) is a powerful expression language that |
|
supports querying and manipulating an object graph at runtime. The language syntax is |
|
similar to Unified EL but offers additional features, most notably method invocation and |
|
basic string templating functionality. |
|
|
|
While there are several other Java expression languages available, OGNL, MVEL, and JBoss |
|
EL, to name a few, the Spring Expression Language was created to provide the Spring |
|
community with a single well supported expression language that can be used across all |
|
the products in the Spring portfolio. Its language features are driven by the |
|
requirements of the projects in the Spring portfolio, including tooling requirements for |
|
code completion support within the eclipse based Spring Tool Suite. That said, |
|
SpEL is based on a technology agnostic API allowing other expression language |
|
implementations to be integrated should the need arise. |
|
|
|
While SpEL serves as the foundation for expression evaluation within the Spring |
|
portfolio, it is not directly tied to Spring and can be used independently. In order to |
|
be self contained, many of the examples in this chapter use SpEL as if it were an |
|
independent expression language. This requires creating a few bootstrapping |
|
infrastructure classes such as the parser. Most Spring users will not need to deal with |
|
this infrastructure and will instead only author expression strings for evaluation. An |
|
example of this typical use is the integration of SpEL into creating XML or annotated |
|
based bean definitions as shown in the section <<expressions-beandef,Expression support |
|
for defining bean definitions.>> |
|
|
|
This chapter covers the features of the expression language, its API, and its language |
|
syntax. In several places an Inventor and Inventor's Society class are used as the |
|
target objects for expression evaluation. These class declarations and the data used to |
|
populate them are listed at the end of the chapter. |
|
|
|
|
|
|
|
|
|
[[expressions-features]] |
|
=== Feature Overview |
|
The expression language supports the following functionality |
|
|
|
* Literal expressions |
|
* Boolean and relational operators |
|
* Regular expressions |
|
* Class expressions |
|
* Accessing properties, arrays, lists, maps |
|
* Method invocation |
|
* Relational operators |
|
* Assignment |
|
* Calling constructors |
|
* Bean references |
|
* Array construction |
|
* Inline lists |
|
* Inline maps |
|
* Ternary operator |
|
* Variables |
|
* User defined functions |
|
* Collection projection |
|
* Collection selection |
|
* Templated expressions |
|
|
|
|
|
|
|
|
|
[[expressions-evaluation]] |
|
=== Expression Evaluation using Spring's Expression Interface |
|
This section introduces the simple use of SpEL interfaces and its expression language. |
|
The complete language reference can be found in the section |
|
<<expressions-language-ref,Language Reference>>. |
|
|
|
The following code introduces the SpEL API to evaluate the literal string expression |
|
'Hello World'. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ExpressionParser parser = new SpelExpressionParser(); |
|
Expression exp = parser.parseExpression("**''Hello World''**"); |
|
String message = (String) exp.getValue(); |
|
---- |
|
|
|
The value of the message variable is simply 'Hello World'. |
|
|
|
The SpEL classes and interfaces you are most likely to use are located in the packages |
|
`org.springframework.expression` and its sub packages and `spel.support`. |
|
|
|
The interface `ExpressionParser` is responsible for parsing an expression string. In |
|
this example the expression string is a string literal denoted by the surrounding single |
|
quotes. The interface `Expression` is responsible for evaluating the previously defined |
|
expression string. There are two exceptions that can be thrown, `ParseException` and |
|
`EvaluationException` when calling '`parser.parseExpression`' and '`exp.getValue`' |
|
respectively. |
|
|
|
SpEL supports a wide range of features, such as calling methods, accessing properties, |
|
and calling constructors. |
|
|
|
As an example of method invocation, we call the 'concat' method on the string literal. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ExpressionParser parser = new SpelExpressionParser(); |
|
Expression exp = parser.parseExpression("**''Hello World''.concat(''!'')**"); |
|
String message = (String) exp.getValue(); |
|
---- |
|
|
|
The value of message is now 'Hello World!'. |
|
|
|
As an example of calling a JavaBean property, the String property 'Bytes' can be called |
|
as shown below. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ExpressionParser parser = new SpelExpressionParser(); |
|
|
|
// invokes 'getBytes()' |
|
Expression exp = parser.parseExpression("**''Hello World''.bytes**"); |
|
byte[] bytes = (byte[]) exp.getValue(); |
|
---- |
|
|
|
SpEL also supports nested properties using standard 'dot' notation, i.e. |
|
prop1.prop2.prop3 and the setting of property values |
|
|
|
Public fields may also be accessed. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ExpressionParser parser = new SpelExpressionParser(); |
|
|
|
// invokes 'getBytes().length' |
|
Expression exp = parser.parseExpression("**''Hello World''.bytes.length**"); |
|
int length = (Integer) exp.getValue(); |
|
---- |
|
|
|
The String's constructor can be called instead of using a string literal. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ExpressionParser parser = new SpelExpressionParser(); |
|
Expression exp = parser.parseExpression("**new String(''hello world'').toUpperCase()**"); |
|
String message = exp.getValue(String.class); |
|
---- |
|
|
|
Note the use of the generic method `public <T> T getValue(Class<T> desiredResultType)`. |
|
Using this method removes the need to cast the value of the expression to the desired |
|
result type. An `EvaluationException` will be thrown if the value cannot be cast to the |
|
type `T` or converted using the registered type converter. |
|
|
|
The more common usage of SpEL is to provide an expression string that is evaluated |
|
against a specific object instance (called the root object). There are two options here |
|
and which to choose depends on whether the object against which the expression is being |
|
evaluated will be changing with each call to evaluate the expression. In the following |
|
example we retrieve the `name` property from an instance of the Inventor class. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// Create and set a calendar |
|
GregorianCalendar c = new GregorianCalendar(); |
|
c.set(1856, 7, 9); |
|
|
|
// The constructor arguments are name, birthday, and nationality. |
|
Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian"); |
|
|
|
ExpressionParser parser = new SpelExpressionParser(); |
|
Expression exp = parser.parseExpression("**name**"); |
|
|
|
EvaluationContext context = new StandardEvaluationContext(tesla); |
|
String name = (String) exp.getValue(context); |
|
---- |
|
|
|
In the last line, the value of the string variable 'name' will be set to "Nikola Tesla". |
|
The class StandardEvaluationContext is where you can specify which object the "name" |
|
property will be evaluated against. This is the mechanism to use if the root object is |
|
unlikely to change, it can simply be set once in the evaluation context. If the root |
|
object is likely to change repeatedly, it can be supplied on each call to `getValue`, as |
|
this next example shows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
/ Create and set a calendar |
|
GregorianCalendar c = new GregorianCalendar(); |
|
c.set(1856, 7, 9); |
|
|
|
// The constructor arguments are name, birthday, and nationality. |
|
Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian"); |
|
|
|
ExpressionParser parser = new SpelExpressionParser(); |
|
Expression exp = parser.parseExpression("**name**"); |
|
String name = (String) exp.getValue(tesla); |
|
---- |
|
|
|
In this case the inventor `tesla` has been supplied directly to `getValue` and the |
|
expression evaluation infrastructure creates and manages a default evaluation context |
|
internally - it did not require one to be supplied. |
|
|
|
The StandardEvaluationContext is relatively expensive to construct and during repeated |
|
usage it builds up cached state that enables subsequent expression evaluations to be |
|
performed more quickly. For this reason it is better to cache and reuse them where |
|
possible, rather than construct a new one for each expression evaluation. |
|
|
|
In some cases it can be desirable to use a configured evaluation context and yet still |
|
supply a different root object on each call to `getValue`. `getValue` allows both to be |
|
specified on the same call. In these situations the root object passed on the call is |
|
considered to override any (which maybe null) specified on the evaluation context. |
|
|
|
[NOTE] |
|
==== |
|
In standalone usage of SpEL there is a need to create the parser, parse expressions and |
|
perhaps provide evaluation contexts and a root context object. However, more common |
|
usage is to provide only the SpEL expression string as part of a configuration file, for |
|
example for Spring bean or Spring Web Flow definitions. In this case, the parser, |
|
evaluation context, root object and any predefined variables are all set up implicitly, |
|
requiring the user to specify nothing other than the expressions. |
|
==== |
|
As a final introductory example, the use of a boolean operator is shown using the |
|
Inventor object in the previous example. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Expression exp = parser.parseExpression("name == ''Nikola Tesla''"); |
|
boolean result = exp.getValue(context, Boolean.class); // evaluates to true |
|
---- |
|
|
|
|
|
|
|
[[expressions-evaluation-context]] |
|
==== The EvaluationContext interface |
|
The interface `EvaluationContext` is used when evaluating an expression to resolve |
|
properties, methods, fields, and to help perform type conversion. The out-of-the-box |
|
implementation, `StandardEvaluationContext`, uses reflection to manipulate the object, |
|
caching `java.lang.reflect`'s `Method`, `Field`, and `Constructor` instances for |
|
increased performance. |
|
|
|
The `StandardEvaluationContext` is where you may specify the root object to evaluate |
|
against via the method `setRootObject()` or passing the root object into the |
|
constructor. You can also specify variables and functions that will be used in the |
|
expression using the methods `setVariable()` and `registerFunction()`. The use of |
|
variables and functions are described in the language reference sections |
|
<<expressions-ref-variables,Variables>> and <<expressions-ref-functions,Functions>>. The |
|
`StandardEvaluationContext` is also where you can register custom |
|
++ConstructorResolver++s, ++MethodResolver++s, and ++PropertyAccessor++s to extend how SpEL |
|
evaluates expressions. Please refer to the JavaDoc of these classes for more details. |
|
|
|
|
|
[[expressions-type-conversion]] |
|
===== Type Conversion |
|
By default SpEL uses the conversion service available in Spring core ( |
|
`org.springframework.core.convert.ConversionService`). This conversion service comes |
|
with many converters built in for common conversions but is also fully extensible so |
|
custom conversions between types can be added. Additionally it has the key capability |
|
that it is generics aware. This means that when working with generic types in |
|
expressions, SpEL will attempt conversions to maintain type correctness for any objects |
|
it encounters. |
|
|
|
What does this mean in practice? Suppose assignment, using `setValue()`, is being used |
|
to set a `List` property. The type of the property is actually `List<Boolean>`. SpEL |
|
will recognize that the elements of the list need to be converted to `Boolean` before |
|
being placed in it. A simple example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
class Simple { |
|
public List<Boolean> booleanList = new ArrayList<Boolean>(); |
|
} |
|
|
|
Simple simple = new Simple(); |
|
|
|
simple.booleanList.add(true); |
|
|
|
StandardEvaluationContext simpleContext = new StandardEvaluationContext(simple); |
|
|
|
// false is passed in here as a string. SpEL and the conversion service will |
|
// correctly recognize that it needs to be a Boolean and convert it |
|
parser.parseExpression("booleanList[0]").setValue(simpleContext, "false"); |
|
|
|
// b will be false |
|
Boolean b = simple.booleanList.get(0); |
|
---- |
|
|
|
[[expressions-parser-configuration]] |
|
==== Parser configuration |
|
It is possible to configure the SpEL expression parser using a parser configuration object |
|
(`org.springframework.expression.spel.SpelParserConfiguration`). The configuration |
|
object controls the behaviour of some of the expression components. For example, if |
|
indexing into an array or collection and the element at the specified index is `null` |
|
it is possible to automatically create the element. This is useful when using expressions made up of a |
|
chain of property references. If indexing into an array or list |
|
and specifying an index that is beyond the end of the current size of the array or |
|
list it is possible to automatically grow the array or list to accommodate that index. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
class Demo { |
|
public List<String> list; |
|
} |
|
|
|
// Turn on: |
|
// - auto null reference initialization |
|
// - auto collection growing |
|
SpelParserConfiguration config = new SpelParserConfiguration(true,true); |
|
|
|
ExpressionParser parser = new SpelExpressionParser(config); |
|
|
|
Expression expression = parser.parseExpression("list[3]"); |
|
|
|
Demo demo = new Demo(); |
|
|
|
Object o = expression.getValue(demo); |
|
|
|
// demo.list will now be a real collection of 4 entries |
|
// Each entry is a new empty String |
|
---- |
|
|
|
It is also possible to configure the behaviour of the SpEL expression compiler. |
|
|
|
[[expressions-spel-compilation]] |
|
==== SpEL compilation |
|
|
|
Spring Framework 4.1 includes a basic expression compiler. Expressions are usually |
|
interpreted which provides a lot of dynamic flexibility during evaluation but |
|
does not provide the optimum performance. For occasional expression usage |
|
this is fine, but when used by other components like Spring Integration, |
|
performance can be very important and there is no real need for the dynamism. |
|
|
|
The new SpEL compiler is intended to address this need. The |
|
compiler will generate a real Java class on the fly during evaluation that embodies the |
|
expression behaviour and use that to achieve much faster expression |
|
evaluation. Due to the lack of typing around expressions the compiler |
|
uses information gathered during the interpreted evaluations of an |
|
expression when performing compilation. For example, it does not know the type |
|
of a property reference purely from the expression but during the first |
|
interpreted evaluation it will find out what it is. Of course, basing the |
|
compilation on this information could cause trouble later if the types of |
|
the various expression elements change over time. For this reason compilation |
|
is best suited to expressions whose type information is not going to change |
|
on repeated evaluations. |
|
|
|
For a basic expression like this: |
|
|
|
`someArray[0].someProperty.someOtherProperty < 0.1` |
|
|
|
which involves array access, some property derefencing and numeric operations, the performance |
|
gain can be very noticeable. In an example microbenchmark run of 50000 iterations, it was |
|
taking 75ms to evaluate using only the interpreter and just 3ms using the compiled version |
|
of the expression. |
|
|
|
[[expressions-compiler-configuration]] |
|
===== Compiler configuration |
|
|
|
The compiler is not turned on by default, but there are two ways to turn |
|
it on. It can be turned on using the parser configuration process discussed earlier or |
|
via a system property when SpEL usage is embedded inside another component. This section |
|
discusses both of these options. |
|
|
|
Is is important to understand that there are a few modes the compiler can operate in, captured |
|
in an enum (`org.springframework.expression.spel.SpelCompilerMode`). The modes are as follows: |
|
|
|
- `OFF` - The compiler is switched off; this is the default. |
|
- `IMMEDIATE` - In immediate mode the expressions are compiled as soon as possible. This |
|
is typically after the first interpreted evaluation. If the compiled expression fails |
|
(typically due to a type changing, as described above) then the caller of the expression |
|
evaluation will receive an exception. |
|
- `MIXED` - In mixed mode the expressions silently switch between interpreted and compiled |
|
mode over time. After some number of interpreted runs they will switch to compiled |
|
form and if something goes wrong with the compiled form (like a type changing, as |
|
described above) then the expression will automatically switch back to interpreted form |
|
again. Sometime later it may generate another compiled form and switch to it. Basically |
|
the exception that the user gets in `IMMEDIATE` mode is instead handled internally. |
|
|
|
`IMMEDIATE` mode exists because `MIXED` mode could cause issues for expressions that |
|
have side effects. If a compiled expression blows up after partially succeeding it |
|
may have already done something that has affected the state of the system. If this |
|
has happened the caller may not want it to silently re-run in interpreted mode |
|
since part of the expression may be running twice. |
|
|
|
After selecting a mode, use the `SpelParserConfiguration` to configure the parser: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, |
|
this.getClass().getClassLoader()); |
|
|
|
SpelExpressionParser parser = new SpelExpressionParser(config); |
|
|
|
Expression expr = parser.parseExpression("payload"); |
|
|
|
MyMessage message = new MyMessage(); |
|
|
|
Object payload = expr.getValue(message); |
|
---- |
|
|
|
When specifying the compiler mode it is also possible to specify a classloader (passing null is allowed). |
|
Compiled expressions will be defined in a child classloader created under any that is supplied. |
|
It is important to ensure if a classloader is specified it can see all the types involved in |
|
the expression evaluation process. |
|
If none is specified then a default classloader will be used (typically the context classloader for |
|
the thread that is running during expression evaluation). |
|
|
|
The second way to configure the compiler is for use when SpEL is embedded inside some other |
|
component and it may not be possible to configure via a configuration object. |
|
In these cases it is possible to use a system property. The property |
|
`spring.expression.compiler.mode` can be set to one of the `SpelCompilerMode` |
|
enum values (`off`, `immediate` or `mixed`). |
|
|
|
[[expressions-compiler-limitations]] |
|
===== Compiler limitations |
|
|
|
With Spring Framework 4.1 the basic compilation framework is in place. However, the framework does not |
|
yet support compiling every kind of expression. The initial focus has been on the common expressions that are |
|
likely to be used in performance critical contexts. These kinds of expression cannot be compiled |
|
at the moment: |
|
|
|
- expressions involving assignment |
|
- expressions relying on the conversion service |
|
- expressions using custom resolvers or accessors |
|
- expressions using selection or projection |
|
|
|
More and more types of expression will be compilable in the future. |
|
|
|
[[expressions-beandef]] |
|
=== Expression support for defining bean definitions |
|
SpEL expressions can be used with XML or annotation-based configuration metadata for |
|
defining ++BeanDefinition++s. In both cases the syntax to define the expression is of the |
|
form `#{ <expression string> }`. |
|
|
|
|
|
|
|
[[expressions-beandef-xml-based]] |
|
==== XML based configuration |
|
A property or constructor-arg value can be set using expressions as shown below. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="numberGuess" class="org.spring.samples.NumberGuess"> |
|
<property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/> |
|
|
|
<!-- other properties --> |
|
</bean> |
|
---- |
|
|
|
The variable `systemProperties` is predefined, so you can use it in your expressions as |
|
shown below. Note that you do not have to prefix the predefined variable with the `#` |
|
symbol in this context. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="taxCalculator" class="org.spring.samples.TaxCalculator"> |
|
<property name="defaultLocale" value="#{ systemProperties[''user.region''] }"/> |
|
|
|
<!-- other properties --> |
|
</bean> |
|
---- |
|
|
|
You can also refer to other bean properties by name, for example. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="numberGuess" class="org.spring.samples.NumberGuess"> |
|
<property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/> |
|
|
|
<!-- other properties --> |
|
</bean> |
|
|
|
<bean id="shapeGuess" class="org.spring.samples.ShapeGuess"> |
|
<property name="initialShapeSeed" value="#{ numberGuess.randomNumber }"/> |
|
|
|
<!-- other properties --> |
|
</bean> |
|
---- |
|
|
|
|
|
|
|
[[expressions-beandef-annotation-based]] |
|
==== Annotation-based configuration |
|
The `@Value` annotation can be placed on fields, methods and method/constructor |
|
parameters to specify a default value. |
|
|
|
Here is an example to set the default value of a field variable. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public static class FieldValueTestBean |
|
|
|
@Value("#{ systemProperties[''user.region''] }") |
|
private String defaultLocale; |
|
|
|
public void setDefaultLocale(String defaultLocale) { |
|
this.defaultLocale = defaultLocale; |
|
} |
|
|
|
public String getDefaultLocale() { |
|
return this.defaultLocale; |
|
} |
|
|
|
} |
|
---- |
|
|
|
The equivalent but on a property setter method is shown below. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public static class PropertyValueTestBean |
|
|
|
private String defaultLocale; |
|
|
|
@Value("#{ systemProperties[''user.region''] }") |
|
public void setDefaultLocale(String defaultLocale) { |
|
this.defaultLocale = defaultLocale; |
|
} |
|
|
|
public String getDefaultLocale() { |
|
return this.defaultLocale; |
|
} |
|
|
|
} |
|
---- |
|
|
|
Autowired methods and constructors can also use the `@Value` annotation. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class SimpleMovieLister { |
|
|
|
private MovieFinder movieFinder; |
|
private String defaultLocale; |
|
|
|
@Autowired |
|
public void configure(MovieFinder movieFinder, |
|
@Value("#{ systemProperties[''user.region''] }") String defaultLocale) { |
|
this.movieFinder = movieFinder; |
|
this.defaultLocale = defaultLocale; |
|
} |
|
|
|
// ... |
|
} |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MovieRecommender { |
|
|
|
private String defaultLocale; |
|
|
|
private CustomerPreferenceDao customerPreferenceDao; |
|
|
|
@Autowired |
|
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao, |
|
@Value("#{systemProperties[''user.country'']}") String defaultLocale) { |
|
this.customerPreferenceDao = customerPreferenceDao; |
|
this.defaultLocale = defaultLocale; |
|
} |
|
|
|
// ... |
|
} |
|
---- |
|
|
|
|
|
|
|
|
|
[[expressions-language-ref]] |
|
=== Language Reference |
|
|
|
|
|
|
|
[[expressions-ref-literal]] |
|
==== Literal expressions |
|
The types of literal expressions supported are strings, dates, numeric values (int, |
|
real, and hex), boolean and null. Strings are delimited by single quotes. To put a |
|
single quote itself in a string use two single quote characters. The following listing |
|
shows simple usage of literals. Typically they would not be used in isolation like this, |
|
but as part of a more complex expression, for example using a literal on one side of a |
|
logical comparison operator. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ExpressionParser parser = new SpelExpressionParser(); |
|
|
|
// evals to "Hello World" |
|
String helloWorld = (String) parser.parseExpression("''Hello World''").getValue(); |
|
|
|
double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue(); |
|
|
|
// evals to 2147483647 |
|
int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue(); |
|
|
|
boolean trueValue = (Boolean) parser.parseExpression("true").getValue(); |
|
|
|
Object nullValue = parser.parseExpression("null").getValue(); |
|
---- |
|
|
|
Numbers support the use of the negative sign, exponential notation, and decimal points. |
|
By default real numbers are parsed using Double.parseDouble(). |
|
|
|
|
|
|
|
[[expressions-properties-arrays]] |
|
==== Properties, Arrays, Lists, Maps, Indexers |
|
Navigating with property references is easy: just use a period to indicate a nested |
|
property value. The instances of the `Inventor` class, pupin, and tesla, were populated with |
|
data listed in the section <<expressions-example-classes,Classes used in the examples>>. |
|
To navigate "down" and get Tesla's year of birth and Pupin's city of birth the following |
|
expressions are used. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// evals to 1856 |
|
int year = (Integer) parser.parseExpression("Birthdate.Year + 1900").getValue(context); |
|
|
|
String city = (String) parser.parseExpression("placeOfBirth.City").getValue(context); |
|
---- |
|
|
|
Case insensitivity is allowed for the first letter of property names. The contents of |
|
arrays and lists are obtained using square bracket notation. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ExpressionParser parser = new SpelExpressionParser(); |
|
|
|
// Inventions Array |
|
StandardEvaluationContext teslaContext = new StandardEvaluationContext(tesla); |
|
|
|
// evaluates to "Induction motor" |
|
String invention = parser.parseExpression("inventions[3]").getValue( |
|
teslaContext, String.class); |
|
|
|
// Members List |
|
StandardEvaluationContext societyContext = new StandardEvaluationContext(ieee); |
|
|
|
// evaluates to "Nikola Tesla" |
|
String name = parser.parseExpression("Members[0].Name").getValue( |
|
societyContext, String.class); |
|
|
|
// List and Array navigation |
|
// evaluates to "Wireless communication" |
|
String invention = parser.parseExpression("Members[0].Inventions[6]").getValue( |
|
societyContext, String.class); |
|
---- |
|
|
|
The contents of maps are obtained by specifying the literal key value within the |
|
brackets. In this case, because keys for the Officers map are strings, we can specify |
|
string literals. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// Officer's Dictionary |
|
|
|
Inventor pupin = parser.parseExpression("Officers[''president'']").getValue( |
|
societyContext, Inventor.class); |
|
|
|
// evaluates to "Idvor" |
|
String city = parser.parseExpression("Officers[''president''].PlaceOfBirth.City").getValue( |
|
societyContext, String.class); |
|
|
|
// setting values |
|
parser.parseExpression("Officers[''advisors''][0].PlaceOfBirth.Country").setValue( |
|
societyContext, "Croatia"); |
|
---- |
|
|
|
|
|
|
|
[[expressions-inline-lists]] |
|
==== Inline lists |
|
Lists can be expressed directly in an expression using `{}` notation. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// evaluates to a Java list containing the four numbers |
|
List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context); |
|
|
|
List listOfLists = (List) parser.parseExpression("{{''a'',''b''},{''x'',''y''}}").getValue(context); |
|
---- |
|
|
|
`{}` by itself means an empty list. For performance reasons, if the list is itself |
|
entirely composed of fixed literals then a constant list is created to represent the |
|
expression, rather than building a new list on each evaluation. |
|
|
|
[[expressions-inline-maps]] |
|
==== Inline Maps |
|
Maps can also be expressed directly in an expression using `{key:value}` notation. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// evaluates to a Java map containing the two entries |
|
Map inventorInfo = (Map) parser.parseExpression("{name:''Nikola'',dob:''10-July-1856''}").getValue(context); |
|
|
|
Map mapOfMaps = (Map) parser.parseExpression("{name:{first:''Nikola'',last:''Tesla''},dob:{day:10,month:''July'',year:1856}}").getValue(context); |
|
---- |
|
`{:}` by itself means an empty map. For performance reasons, if the map is itself composed |
|
of fixed literals or other nested constant structures (lists or maps) then a constant map is created |
|
to represent the expression, rather than building a new map on each evaluation. Quoting of the map keys |
|
is optional, the examples above are not using quoted keys. |
|
|
|
[[expressions-array-construction]] |
|
==== Array construction |
|
Arrays can be built using the familiar Java syntax, optionally supplying an initializer |
|
to have the array populated at construction time. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue(context); |
|
|
|
// Array with initializer |
|
int[] numbers2 = (int[]) parser.parseExpression("new int[]{1,2,3}").getValue(context); |
|
|
|
// Multi dimensional array |
|
int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(context); |
|
---- |
|
|
|
It is not currently allowed to supply an initializer when constructing a |
|
multi-dimensional array. |
|
|
|
|
|
|
|
[[expressions-methods]] |
|
==== Methods |
|
Methods are invoked using typical Java programming syntax. You may also invoke methods |
|
on literals. Varargs are also supported. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// string literal, evaluates to "bc" |
|
String c = parser.parseExpression("''abc''.substring(2, 3)").getValue(String.class); |
|
|
|
// evaluates to true |
|
boolean isMember = parser.parseExpression("isMember(''Mihajlo Pupin'')").getValue( |
|
societyContext, Boolean.class); |
|
---- |
|
|
|
|
|
|
|
[[expressions-operators]] |
|
==== Operators |
|
|
|
|
|
[[expressions-operators-relational]] |
|
===== Relational operators |
|
The relational operators; equal, not equal, less than, less than or equal, greater than, |
|
and greater than or equal are supported using standard operator notation. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// evaluates to true |
|
boolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean.class); |
|
|
|
// evaluates to false |
|
boolean falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean.class); |
|
|
|
// evaluates to true |
|
boolean trueValue = parser.parseExpression("''black'' < ''block''").getValue(Boolean.class); |
|
---- |
|
|
|
In addition to standard relational operators SpEL supports the `instanceof` and regular |
|
expression based `matches` operator. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// evaluates to false |
|
boolean falseValue = parser.parseExpression( |
|
"''xyz'' instanceof T(int)").getValue(Boolean.class); |
|
|
|
// evaluates to true |
|
boolean trueValue = parser.parseExpression( |
|
"''5.00'' matches ''\^-?\\d+(\\.\\d{2})?$''").getValue(Boolean.class); |
|
|
|
//evaluates to false |
|
boolean falseValue = parser.parseExpression( |
|
"''5.0067'' matches ''\^-?\\d+(\\.\\d{2})?$''").getValue(Boolean.class); |
|
---- |
|
|
|
Each symbolic operator can also be specified as a purely alphabetic equivalent. This |
|
avoids problems where the symbols used have special meaning for the document type in |
|
which the expression is embedded (eg. an XML document). The textual equivalents are |
|
shown here: `lt` (`<`), `gt` (`>`), `le` (`<=`), `ge` (`>=`), `eq` (`==`), |
|
`ne` (`!=`), `div` (`/`), `mod` (`%`), `not` (`!`). These are case insensitive. |
|
|
|
|
|
[[expressions-operators-logical]] |
|
===== Logical operators |
|
The logical operators that are supported are and, or, and not. Their use is demonstrated |
|
below. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// -- AND -- |
|
|
|
// evaluates to false |
|
boolean falseValue = parser.parseExpression("true and false").getValue(Boolean.class); |
|
|
|
// evaluates to true |
|
String expression = "isMember(''Nikola Tesla'') and isMember(''Mihajlo Pupin'')"; |
|
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class); |
|
|
|
// -- OR -- |
|
|
|
// evaluates to true |
|
boolean trueValue = parser.parseExpression("true or false").getValue(Boolean.class); |
|
|
|
// evaluates to true |
|
String expression = "isMember(''Nikola Tesla'') or isMember(''Albert Einstein'')"; |
|
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class); |
|
|
|
// -- NOT -- |
|
|
|
// evaluates to false |
|
boolean falseValue = parser.parseExpression("!true").getValue(Boolean.class); |
|
|
|
// -- AND and NOT -- |
|
String expression = "isMember(''Nikola Tesla'') and !isMember(''Mihajlo Pupin'')"; |
|
boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class); |
|
---- |
|
|
|
|
|
[[expressions-operators-mathematical]] |
|
===== Mathematical operators |
|
The addition operator can be used on both numbers and strings. Subtraction, multiplication |
|
and division can be used only on numbers. Other mathematical operators supported are |
|
modulus (%) and exponential power (^). Standard operator precedence is enforced. These |
|
operators are demonstrated below. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// Addition |
|
int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2 |
|
|
|
String testString = parser.parseExpression( |
|
"''test'' + '' '' + ''string''").getValue(String.class); // 'test string' |
|
|
|
// Subtraction |
|
int four = parser.parseExpression("1 - -3").getValue(Integer.class); // 4 |
|
|
|
double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class); // -9000 |
|
|
|
// Multiplication |
|
int six = parser.parseExpression("-2 * -3").getValue(Integer.class); // 6 |
|
|
|
double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); // 24.0 |
|
|
|
// Division |
|
int minusTwo = parser.parseExpression("6 / -3").getValue(Integer.class); // -2 |
|
|
|
double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class); // 1.0 |
|
|
|
// Modulus |
|
int three = parser.parseExpression("7 % 4").getValue(Integer.class); // 3 |
|
|
|
int one = parser.parseExpression("8 / 5 % 2").getValue(Integer.class); // 1 |
|
|
|
// Operator precedence |
|
int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21 |
|
---- |
|
|
|
|
|
|
|
[[expressions-assignment]] |
|
==== Assignment |
|
Setting of a property is done by using the assignment operator. This would typically be |
|
done within a call to `setValue` but can also be done inside a call to `getValue`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Inventor inventor = new Inventor(); |
|
StandardEvaluationContext inventorContext = new StandardEvaluationContext(inventor); |
|
|
|
parser.parseExpression("Name").setValue(inventorContext, "Alexander Seovic2"); |
|
|
|
// alternatively |
|
|
|
String aleks = parser.parseExpression( |
|
"Name = ''Alexandar Seovic''").getValue(inventorContext, String.class); |
|
---- |
|
|
|
|
|
|
|
[[expressions-types]] |
|
==== Types |
|
The special `T` operator can be used to specify an instance of java.lang.Class (the |
|
_type_). Static methods are invoked using this operator as well. The |
|
`StandardEvaluationContext` uses a `TypeLocator` to find types and the |
|
`StandardTypeLocator` (which can be replaced) is built with an understanding of the |
|
java.lang package. This means T() references to types within java.lang do not need to be |
|
fully qualified, but all other type references must be. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class); |
|
|
|
Class stringClass = parser.parseExpression("T(String)").getValue(Class.class); |
|
|
|
boolean trueValue = parser.parseExpression( |
|
"T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR") |
|
.getValue(Boolean.class); |
|
---- |
|
|
|
|
|
|
|
[[expressions-constructors]] |
|
==== Constructors |
|
Constructors can be invoked using the new operator. The fully qualified class name |
|
should be used for all but the primitive type and String (where int, float, etc, can be |
|
used). |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Inventor einstein = p.parseExpression( |
|
"new org.spring.samples.spel.inventor.Inventor(''Albert Einstein'', ''German'')") |
|
.getValue(Inventor.class); |
|
|
|
//create new inventor instance within add method of List |
|
p.parseExpression( |
|
"Members.add(new org.spring.samples.spel.inventor.Inventor( |
|
''Albert Einstein'', ''German''))").getValue(societyContext); |
|
---- |
|
|
|
|
|
|
|
[[expressions-ref-variables]] |
|
==== Variables |
|
Variables can be referenced in the expression using the syntax `#variableName`. Variables |
|
are set using the method setVariable on the `StandardEvaluationContext`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); |
|
StandardEvaluationContext context = new StandardEvaluationContext(tesla); |
|
context.setVariable("newName", "Mike Tesla"); |
|
|
|
parser.parseExpression("Name = #newName").getValue(context); |
|
|
|
System.out.println(tesla.getName()) // "Mike Tesla" |
|
---- |
|
|
|
|
|
[[expressions-this-root]] |
|
===== The #this and #root variables |
|
The variable #this is always defined and refers to the current evaluation object |
|
(against which unqualified references are resolved). The variable #root is always |
|
defined and refers to the root context object. Although #this may vary as components of |
|
an expression are evaluated, #root always refers to the root. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// create an array of integers |
|
List<Integer> primes = new ArrayList<Integer>(); |
|
primes.addAll(Arrays.asList(2,3,5,7,11,13,17)); |
|
|
|
// create parser and set variable 'primes' as the array of integers |
|
ExpressionParser parser = new SpelExpressionParser(); |
|
StandardEvaluationContext context = new StandardEvaluationContext(); |
|
context.setVariable("primes",primes); |
|
|
|
// all prime numbers > 10 from the list (using selection ?{...}) |
|
// evaluates to [11, 13, 17] |
|
List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression( |
|
"#primes.?[#this>10]").getValue(context); |
|
---- |
|
|
|
|
|
|
|
[[expressions-ref-functions]] |
|
==== Functions |
|
You can extend SpEL by registering user defined functions that can be called within the |
|
expression string. The function is registered with the `StandardEvaluationContext` using |
|
the method. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public void registerFunction(String name, Method m) |
|
---- |
|
|
|
A reference to a Java Method provides the implementation of the function. For example, a |
|
utility method to reverse a string is shown below. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public abstract class StringUtils { |
|
|
|
public static String reverseString(String input) { |
|
StringBuilder backwards = new StringBuilder(); |
|
for (int i = 0; i < input.length(); i++) |
|
backwards.append(input.charAt(input.length() - 1 - i)); |
|
} |
|
return backwards.toString(); |
|
} |
|
} |
|
---- |
|
|
|
This method is then registered with the evaluation context and can be used within an |
|
expression string. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ExpressionParser parser = new SpelExpressionParser(); |
|
StandardEvaluationContext context = new StandardEvaluationContext(); |
|
|
|
context.registerFunction("reverseString", |
|
StringUtils.class.getDeclaredMethod("reverseString", new Class[] { String.class })); |
|
|
|
String helloWorldReversed = parser.parseExpression( |
|
"#reverseString(''hello'')").getValue(context, String.class); |
|
---- |
|
|
|
|
|
|
|
[[expressions-bean-references]] |
|
==== Bean references |
|
If the evaluation context has been configured with a bean resolver it is possible to |
|
lookup beans from an expression using the (@) symbol. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ExpressionParser parser = new SpelExpressionParser(); |
|
StandardEvaluationContext context = new StandardEvaluationContext(); |
|
context.setBeanResolver(new MyBeanResolver()); |
|
|
|
// This will end up calling resolve(context,"foo") on MyBeanResolver during evaluation |
|
Object bean = parser.parseExpression("@foo").getValue(context); |
|
---- |
|
|
|
|
|
|
|
[[expressions-operator-ternary]] |
|
==== Ternary Operator (If-Then-Else) |
|
You can use the ternary operator for performing if-then-else conditional logic inside |
|
the expression. A minimal example is: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
String falseString = parser.parseExpression( |
|
"false ? ''trueExp'' : ''falseExp''").getValue(String.class); |
|
---- |
|
|
|
In this case, the boolean false results in returning the string value 'falseExp'. A more |
|
realistic example is shown below. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
parser.parseExpression("Name").setValue(societyContext, "IEEE"); |
|
societyContext.setVariable("queryName", "Nikola Tesla"); |
|
|
|
expression = "isMember(#queryName)? #queryName + '' is a member of the '' " + |
|
"+ Name + '' Society'' : #queryName + '' is not a member of the '' + Name + '' Society''"; |
|
|
|
String queryResultString = parser.parseExpression(expression) |
|
.getValue(societyContext, String.class); |
|
// queryResultString = "Nikola Tesla is a member of the IEEE Society" |
|
---- |
|
|
|
Also see the next section on the Elvis operator for an even shorter syntax for the |
|
ternary operator. |
|
|
|
|
|
|
|
[[expressions-operator-elvis]] |
|
==== The Elvis Operator |
|
The Elvis operator is a shortening of the ternary operator syntax and is used in the |
|
http://groovy.codehaus.org/Operators#Operators-ElvisOperator(%3F%3A)[Groovy] language. |
|
With the ternary operator syntax you usually have to repeat a variable twice, for |
|
example: |
|
|
|
[source,groovy,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
String name = "Elvis Presley"; |
|
String displayName = name != null ? name : "Unknown"; |
|
---- |
|
|
|
Instead you can use the Elvis operator, named for the resemblance to Elvis' hair style. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ExpressionParser parser = new SpelExpressionParser(); |
|
|
|
String name = parser.parseExpression("null?:''Unknown''").getValue(String.class); |
|
|
|
System.out.println(name); // 'Unknown' |
|
---- |
|
|
|
Here is a more complex example. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ExpressionParser parser = new SpelExpressionParser(); |
|
|
|
Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); |
|
StandardEvaluationContext context = new StandardEvaluationContext(tesla); |
|
|
|
String name = parser.parseExpression("Name?:''Elvis Presley''").getValue(context, String.class); |
|
|
|
System.out.println(name); // Nikola Tesla |
|
|
|
tesla.setName(null); |
|
|
|
name = parser.parseExpression("Name?:''Elvis Presley''").getValue(context, String.class); |
|
|
|
System.out.println(name); // Elvis Presley |
|
---- |
|
|
|
|
|
|
|
[[expressions-operator-safe-navigation]] |
|
==== Safe Navigation operator |
|
The Safe Navigation operator is used to avoid a `NullPointerException` and comes from |
|
the http://groovy.codehaus.org/Operators#Operators-SafeNavigationOperator(%3F.)[Groovy] |
|
language. Typically when you have a reference to an object you might need to verify that |
|
it is not null before accessing methods or properties of the object. To avoid this, the |
|
safe navigation operator will simply return null instead of throwing an exception. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ExpressionParser parser = new SpelExpressionParser(); |
|
|
|
Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); |
|
tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan")); |
|
|
|
StandardEvaluationContext context = new StandardEvaluationContext(tesla); |
|
|
|
String city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, String.class); |
|
System.out.println(city); // Smiljan |
|
|
|
tesla.setPlaceOfBirth(null); |
|
|
|
city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, String.class); |
|
|
|
System.out.println(city); // null - does not throw NullPointerException!!! |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
The Elvis operator can be used to apply default values in expressions, e.g. in an |
|
`@Value` expression: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Value("#{systemProperties[''pop3.port''] ?: 25}") |
|
---- |
|
|
|
This will inject a system property `pop3.port` if it is defined or 25 if not. |
|
==== |
|
|
|
|
|
|
|
[[expressions-collection-selection]] |
|
==== Collection Selection |
|
Selection is a powerful expression language feature that allows you to transform some |
|
source collection into another by selecting from its entries. |
|
|
|
Selection uses the syntax `?[selectionExpression]`. This will filter the collection and |
|
return a new collection containing a subset of the original elements. For example, |
|
selection would allow us to easily get a list of Serbian inventors: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
List<Inventor> list = (List<Inventor>) parser.parseExpression( |
|
"Members.?[Nationality == ''Serbian'']").getValue(societyContext); |
|
---- |
|
|
|
Selection is possible upon both lists and maps. In the former case the selection |
|
criteria is evaluated against each individual list element whilst against a map the |
|
selection criteria is evaluated against each map entry (objects of the Java type |
|
`Map.Entry`). Map entries have their key and value accessible as properties for use in |
|
the selection. |
|
|
|
This expression will return a new map consisting of those elements of the original map |
|
where the entry value is less than 27. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Map newMap = parser.parseExpression("map.?[value<27]").getValue(); |
|
---- |
|
|
|
In addition to returning all the selected elements, it is possible to retrieve just the |
|
first or the last value. To obtain the first entry matching the selection the syntax is |
|
`^[...]` whilst to obtain the last matching selection the syntax is `$[...]`. |
|
|
|
|
|
|
|
[[expressions-collection-projection]] |
|
==== Collection Projection |
|
Projection allows a collection to drive the evaluation of a sub-expression and the |
|
result is a new collection. The syntax for projection is `![projectionExpression]`. Most |
|
easily understood by example, suppose we have a list of inventors but want the list of |
|
cities where they were born. Effectively we want to evaluate 'placeOfBirth.city' for |
|
every entry in the inventor list. Using projection: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// returns ['Smiljan', 'Idvor' ] |
|
List placesOfBirth = (List)parser.parseExpression("Members.![placeOfBirth.city]"); |
|
---- |
|
|
|
A map can also be used to drive projection and in this case the projection expression is |
|
evaluated against each entry in the map (represented as a Java `Map.Entry`). The result |
|
of a projection across a map is a list consisting of the evaluation of the projection |
|
expression against each map entry. |
|
|
|
|
|
|
|
[[expressions-templating]] |
|
==== Expression templating |
|
Expression templates allow a mixing of literal text with one or more evaluation blocks. |
|
Each evaluation block is delimited with prefix and suffix characters that you can |
|
define, a common choice is to use `#{ }` as the delimiters. For example, |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
String randomPhrase = parser.parseExpression( |
|
"random number is #{T(java.lang.Math).random()}", |
|
new TemplateParserContext()).getValue(String.class); |
|
|
|
// evaluates to "random number is 0.7038186818312008" |
|
---- |
|
|
|
The string is evaluated by concatenating the literal text 'random number is ' with the |
|
result of evaluating the expression inside the #{ } delimiter, in this case the result |
|
of calling that random() method. The second argument to the method `parseExpression()` |
|
is of the type `ParserContext`. The `ParserContext` interface is used to influence how |
|
the expression is parsed in order to support the expression templating functionality. |
|
The definition of `TemplateParserContext` is shown below. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class TemplateParserContext implements ParserContext { |
|
|
|
public String getExpressionPrefix() { |
|
return "#{"; |
|
} |
|
|
|
public String getExpressionSuffix() { |
|
return "}"; |
|
} |
|
|
|
public boolean isTemplate() { |
|
return true; |
|
} |
|
} |
|
---- |
|
|
|
|
|
|
|
|
|
[[expressions-example-classes]] |
|
=== Classes used in the examples |
|
Inventor.java |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.spring.samples.spel.inventor; |
|
|
|
import java.util.Date; |
|
import java.util.GregorianCalendar; |
|
|
|
public class Inventor { |
|
|
|
private String name; |
|
private String nationality; |
|
private String[] inventions; |
|
private Date birthdate; |
|
private PlaceOfBirth placeOfBirth; |
|
|
|
public Inventor(String name, String nationality) { |
|
GregorianCalendar c= new GregorianCalendar(); |
|
this.name = name; |
|
this.nationality = nationality; |
|
this.birthdate = c.getTime(); |
|
} |
|
|
|
public Inventor(String name, Date birthdate, String nationality) { |
|
this.name = name; |
|
this.nationality = nationality; |
|
this.birthdate = birthdate; |
|
} |
|
|
|
public Inventor() { |
|
} |
|
|
|
public String getName() { |
|
return name; |
|
} |
|
|
|
public void setName(String name) { |
|
this.name = name; |
|
} |
|
|
|
public String getNationality() { |
|
return nationality; |
|
} |
|
|
|
public void setNationality(String nationality) { |
|
this.nationality = nationality; |
|
} |
|
|
|
public Date getBirthdate() { |
|
return birthdate; |
|
} |
|
|
|
public void setBirthdate(Date birthdate) { |
|
this.birthdate = birthdate; |
|
} |
|
|
|
public PlaceOfBirth getPlaceOfBirth() { |
|
return placeOfBirth; |
|
} |
|
|
|
public void setPlaceOfBirth(PlaceOfBirth placeOfBirth) { |
|
this.placeOfBirth = placeOfBirth; |
|
} |
|
|
|
public void setInventions(String[] inventions) { |
|
this.inventions = inventions; |
|
} |
|
|
|
public String[] getInventions() { |
|
return inventions; |
|
} |
|
} |
|
---- |
|
|
|
PlaceOfBirth.java |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.spring.samples.spel.inventor; |
|
|
|
public class PlaceOfBirth { |
|
|
|
private String city; |
|
private String country; |
|
|
|
public PlaceOfBirth(String city) { |
|
this.city=city; |
|
} |
|
|
|
public PlaceOfBirth(String city, String country) { |
|
this(city); |
|
this.country = country; |
|
} |
|
|
|
public String getCity() { |
|
return city; |
|
} |
|
|
|
public void setCity(String s) { |
|
this.city = s; |
|
} |
|
|
|
public String getCountry() { |
|
return country; |
|
} |
|
|
|
public void setCountry(String country) { |
|
this.country = country; |
|
} |
|
|
|
} |
|
---- |
|
|
|
Society.java |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.spring.samples.spel.inventor; |
|
|
|
import java.util.*; |
|
|
|
public class Society { |
|
|
|
private String name; |
|
|
|
public static String Advisors = "advisors"; |
|
public static String President = "president"; |
|
|
|
private List<Inventor> members = new ArrayList<Inventor>(); |
|
private Map officers = new HashMap(); |
|
|
|
public List getMembers() { |
|
return members; |
|
} |
|
|
|
public Map getOfficers() { |
|
return officers; |
|
} |
|
|
|
public String getName() { |
|
return name; |
|
} |
|
|
|
public void setName(String name) { |
|
this.name = name; |
|
} |
|
|
|
public boolean isMember(String name) { |
|
for (Inventor inventor : members) { |
|
if (inventor.getName().equals(name)) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
} |
|
---- |
|
|
|
|
|
|
|
|
|
|
|
[[aop]] |
|
== Aspect Oriented Programming with Spring |
|
|
|
|
|
|
|
|
|
[[aop-introduction]] |
|
=== Introduction |
|
__Aspect-Oriented Programming__ (AOP) complements Object-Oriented Programming (OOP) by |
|
providing another way of thinking about program structure. The key unit of modularity in |
|
OOP is the class, whereas in AOP the unit of modularity is the __aspect__. Aspects |
|
enable the modularization of concerns such as transaction management that cut across |
|
multiple types and objects. (Such concerns are often termed __crosscutting__ concerns in |
|
AOP literature.) |
|
|
|
One of the key components of Spring is the __AOP framework__. While the Spring IoC |
|
container does not depend on AOP, meaning you do not need to use AOP if you don't want |
|
to, AOP complements Spring IoC to provide a very capable middleware solution. |
|
|
|
.Spring 2.0 AOP |
|
**** |
|
Spring 2.0 introduces a simpler and more powerful way of writing custom aspects using |
|
either a <<aop-schema,schema-based approach>> or the <<aop-ataspectj,@AspectJ annotation |
|
style>>. Both of these styles offer fully typed advice and use of the AspectJ pointcut |
|
language, while still using Spring AOP for weaving. |
|
|
|
The Spring 2.0 schema- and @AspectJ-based AOP support is discussed in this chapter. |
|
Spring 2.0 AOP remains fully backwards compatible with Spring 1.2 AOP, and the |
|
lower-level AOP support offered by the Spring 1.2 APIs is discussed in <<aop-api,the |
|
following chapter>>. |
|
**** |
|
|
|
AOP is used in the Spring Framework to... |
|
|
|
* ... provide declarative enterprise services, especially as a replacement for EJB |
|
declarative services. The most important such service is |
|
<<transaction-declarative,__declarative transaction management__>>. |
|
* ... allow users to implement custom aspects, complementing their use of OOP with AOP. |
|
|
|
[NOTE] |
|
==== |
|
If you are interested only in generic declarative services or other pre-packaged |
|
declarative middleware services such as pooling, you do not need to work directly with |
|
Spring AOP, and can skip most of this chapter. |
|
==== |
|
|
|
|
|
|
|
[[aop-introduction-defn]] |
|
==== AOP concepts |
|
Let us begin by defining some central AOP concepts and terminology. These terms are not |
|
Spring-specific... unfortunately, AOP terminology is not particularly intuitive; |
|
however, it would be even more confusing if Spring used its own terminology. |
|
|
|
* __Aspect__: a modularization of a concern that cuts across multiple classes. |
|
Transaction management is a good example of a crosscutting concern in enterprise Java |
|
applications. In Spring AOP, aspects are implemented using regular classes |
|
(the <<aop-schema,schema-based approach>>) or regular classes annotated with the |
|
`@Aspect` annotation (the <<aop-ataspectj, `@AspectJ` style>>). |
|
* __Join point__: a point during the execution of a program, such as the execution of a |
|
method or the handling of an exception. In Spring AOP, a join point __always__ |
|
represents a method execution. |
|
* __Advice__: action taken by an aspect at a particular join point. Different types of |
|
advice include "around," "before" and "after" advice. (Advice types are discussed |
|
below.) Many AOP frameworks, including Spring, model an advice as an __interceptor__, |
|
maintaining a chain of interceptors __around__ the join point. |
|
* __Pointcut__: a predicate that matches join points. Advice is associated with a |
|
pointcut expression and runs at any join point matched by the pointcut (for example, |
|
the execution of a method with a certain name). The concept of join points as matched |
|
by pointcut expressions is central to AOP, and Spring uses the AspectJ pointcut |
|
expression language by default. |
|
* __Introduction__: declaring additional methods or fields on behalf of a type. Spring |
|
AOP allows you to introduce new interfaces (and a corresponding implementation) to any |
|
advised object. For example, you could use an introduction to make a bean implement an |
|
`IsModified` interface, to simplify caching. (An introduction is known as an |
|
inter-type declaration in the AspectJ community.) |
|
* __Target object__: object being advised by one or more aspects. Also referred to as |
|
the __advised__ object. Since Spring AOP is implemented using runtime proxies, this |
|
object will always be a __proxied__ object. |
|
* __AOP proxy__: an object created by the AOP framework in order to implement the aspect |
|
contracts (advise method executions and so on). In the Spring Framework, an AOP proxy |
|
will be a JDK dynamic proxy or a CGLIB proxy. |
|
* __Weaving__: linking aspects with other application types or objects to create an |
|
advised object. This can be done at compile time (using the AspectJ compiler, for |
|
example), load time, or at runtime. Spring AOP, like other pure Java AOP frameworks, |
|
performs weaving at runtime. |
|
|
|
Types of advice: |
|
|
|
* __Before advice__: Advice that executes before a join point, but which does not have |
|
the ability to prevent execution flow proceeding to the join point (unless it throws |
|
an exception). |
|
* __After returning advice__: Advice to be executed after a join point completes |
|
normally: for example, if a method returns without throwing an exception. |
|
* __After throwing advice__: Advice to be executed if a method exits by throwing an |
|
exception. |
|
* __After (finally) advice__: Advice to be executed regardless of the means by which a |
|
join point exits (normal or exceptional return). |
|
* __Around advice__: Advice that surrounds a join point such as a method invocation. |
|
This is the most powerful kind of advice. Around advice can perform custom behavior |
|
before and after the method invocation. It is also responsible for choosing whether to |
|
proceed to the join point or to shortcut the advised method execution by returning its |
|
own return value or throwing an exception. |
|
|
|
Around advice is the most general kind of advice. Since Spring AOP, like AspectJ, |
|
provides a full range of advice types, we recommend that you use the least powerful |
|
advice type that can implement the required behavior. For example, if you need only to |
|
update a cache with the return value of a method, you are better off implementing an |
|
after returning advice than an around advice, although an around advice can accomplish |
|
the same thing. Using the most specific advice type provides a simpler programming model |
|
with less potential for errors. For example, you do not need to invoke the `proceed()` |
|
method on the `JoinPoint` used for around advice, and hence cannot fail to invoke it. |
|
|
|
In Spring 2.0, all advice parameters are statically typed, so that you work with advice |
|
parameters of the appropriate type (the type of the return value from a method execution |
|
for example) rather than `Object` arrays. |
|
|
|
The concept of join points, matched by pointcuts, is the key to AOP which distinguishes |
|
it from older technologies offering only interception. Pointcuts enable advice to be |
|
targeted independently of the Object-Oriented hierarchy. For example, an around advice |
|
providing declarative transaction management can be applied to a set of methods spanning |
|
multiple objects (such as all business operations in the service layer). |
|
|
|
|
|
|
|
[[aop-introduction-spring-defn]] |
|
==== Spring AOP capabilities and goals |
|
Spring AOP is implemented in pure Java. There is no need for a special compilation |
|
process. Spring AOP does not need to control the class loader hierarchy, and is thus |
|
suitable for use in a Servlet container or application server. |
|
|
|
Spring AOP currently supports only method execution join points (advising the execution |
|
of methods on Spring beans). Field interception is not implemented, although support for |
|
field interception could be added without breaking the core Spring AOP APIs. If you need |
|
to advise field access and update join points, consider a language such as AspectJ. |
|
|
|
Spring AOP's approach to AOP differs from that of most other AOP frameworks. The aim is |
|
not to provide the most complete AOP implementation (although Spring AOP is quite |
|
capable); it is rather to provide a close integration between AOP implementation and |
|
Spring IoC to help solve common problems in enterprise applications. |
|
|
|
Thus, for example, the Spring Framework's AOP functionality is normally used in |
|
conjunction with the Spring IoC container. Aspects are configured using normal bean |
|
definition syntax (although this allows powerful "autoproxying" capabilities): this is a |
|
crucial difference from other AOP implementations. There are some things you cannot do |
|
easily or efficiently with Spring AOP, such as advise very fine-grained objects (such as |
|
domain objects typically): AspectJ is the best choice in such cases. However, our |
|
experience is that Spring AOP provides an excellent solution to most problems in |
|
enterprise Java applications that are amenable to AOP. |
|
|
|
Spring AOP will never strive to compete with AspectJ to provide a comprehensive AOP |
|
solution. We believe that both proxy-based frameworks like Spring AOP and full-blown |
|
frameworks such as AspectJ are valuable, and that they are complementary, rather than in |
|
competition. Spring seamlessly integrates Spring AOP and IoC with AspectJ, to enable |
|
all uses of AOP to be catered for within a consistent Spring-based application |
|
architecture. This integration does not affect the Spring AOP API or the AOP Alliance |
|
API: Spring AOP remains backward-compatible. See <<aop-api,the following chapter>> for a |
|
discussion of the Spring AOP APIs. |
|
|
|
[NOTE] |
|
==== |
|
One of the central tenets of the Spring Framework is that of __non-invasiveness__; this |
|
is the idea that you should not be forced to introduce framework-specific classes and |
|
interfaces into your business/domain model. However, in some places the Spring Framework |
|
does give you the option to introduce Spring Framework-specific dependencies into your |
|
codebase: the rationale in giving you such options is because in certain scenarios it |
|
might be just plain easier to read or code some specific piece of functionality in such |
|
a way. The Spring Framework (almost) always offers you the choice though: you have the |
|
freedom to make an informed decision as to which option best suits your particular use |
|
case or scenario. |
|
|
|
One such choice that is relevant to this chapter is that of which AOP framework (and |
|
which AOP style) to choose. You have the choice of AspectJ and/or Spring AOP, and you |
|
also have the choice of either the @AspectJ annotation-style approach or the Spring XML |
|
configuration-style approach. The fact that this chapter chooses to introduce the |
|
@AspectJ-style approach first should not be taken as an indication that the Spring team |
|
favors the @AspectJ annotation-style approach over the Spring XML configuration-style. |
|
|
|
See <<aop-choosing>> for a more complete discussion of the whys and wherefores of each |
|
style. |
|
==== |
|
|
|
|
|
|
|
[[aop-introduction-proxies]] |
|
==== AOP Proxies |
|
Spring AOP defaults to using standard JDK __dynamic proxies__ for AOP proxies. This |
|
enables any interface (or set of interfaces) to be proxied. |
|
|
|
Spring AOP can also use CGLIB proxies. This is necessary to proxy classes, rather than |
|
interfaces. CGLIB is used by default if a business object does not implement an |
|
interface. As it is good practice to program to interfaces rather than classes, business |
|
classes normally will implement one or more business interfaces. It is possible to |
|
<<aop-autoproxy-force-CGLIB,force the use of CGLIB>>, in those (hopefully rare) cases |
|
where you need to advise a method that is not declared on an interface, or where you |
|
need to pass a proxied object to a method as a concrete type. |
|
|
|
It is important to grasp the fact that Spring AOP is __proxy-based__. See |
|
<<aop-understanding-aop-proxies>> for a thorough examination of exactly what this |
|
implementation detail actually means. |
|
|
|
|
|
|
|
|
|
[[aop-ataspectj]] |
|
=== @AspectJ support |
|
@AspectJ refers to a style of declaring aspects as regular Java classes annotated with |
|
annotations. The @AspectJ style was introduced by the |
|
http://www.eclipse.org/aspectj[AspectJ project] as part of the AspectJ 5 release. Spring |
|
interprets the same annotations as AspectJ 5, using a library supplied by AspectJ |
|
for pointcut parsing and matching. The AOP runtime is still pure Spring AOP though, and |
|
there is no dependency on the AspectJ compiler or weaver. |
|
|
|
[NOTE] |
|
==== |
|
Using the AspectJ compiler and weaver enables use of the full AspectJ language, and is |
|
discussed in <<aop-using-aspectj>>. |
|
==== |
|
|
|
|
|
|
|
[[aop-aspectj-support]] |
|
==== Enabling @AspectJ Support |
|
To use @AspectJ aspects in a Spring configuration you need to enable Spring support for |
|
configuring Spring AOP based on @AspectJ aspects, and __autoproxying__ beans based on |
|
whether or not they are advised by those aspects. By autoproxying we mean that if Spring |
|
determines that a bean is advised by one or more aspects, it will automatically generate |
|
a proxy for that bean to intercept method invocations and ensure that advice is executed |
|
as needed. |
|
|
|
The @AspectJ support can be enabled with XML or Java style configuration. In either |
|
case you will also need to ensure that AspectJ's `aspectjweaver.jar` library is on the |
|
classpath of your application (version 1.6.8 or later). This library is available in the |
|
`'lib'` directory of an AspectJ distribution or via the Maven Central repository. |
|
|
|
|
|
[[aop-enable-aspectj-java]] |
|
===== Enabling @AspectJ Support with Java configuration |
|
To enable @AspectJ support with Java `@Configuration` add the `@EnableAspectJAutoProxy` |
|
annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableAspectJAutoProxy |
|
public class AppConfig { |
|
|
|
} |
|
---- |
|
|
|
|
|
[[aop-enable-aspectj-xml]] |
|
===== Enabling @AspectJ Support with XML configuration |
|
To enable @AspectJ support with XML based configuration use the `aop:aspectj-autoproxy` |
|
element: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<aop:aspectj-autoproxy/> |
|
---- |
|
|
|
This assumes that you are using schema support as described in <<xsd-config>>. See |
|
<<xsd-config-body-schemas-aop>> for how to import the tags in the aop namespace. |
|
|
|
|
|
|
|
[[aop-at-aspectj]] |
|
==== Declaring an aspect |
|
|
|
With the @AspectJ support enabled, any bean defined in your application context with a |
|
class that is an @AspectJ aspect (has the `@Aspect` annotation) will be automatically |
|
detected by Spring and used to configure Spring AOP. The following example shows the |
|
minimal definition required for a not-very-useful aspect: |
|
|
|
A regular bean definition in the application context, pointing to a bean class that has |
|
the `@Aspect` annotation: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="myAspect" class="org.xyz.NotVeryUsefulAspect"> |
|
<!-- configure properties of aspect here as normal --> |
|
</bean> |
|
---- |
|
|
|
And the `NotVeryUsefulAspect` class definition, annotated with |
|
`org.aspectj.lang.annotation.Aspect` annotation; |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.xyz; |
|
import org.aspectj.lang.annotation.Aspect; |
|
|
|
@Aspect |
|
public class NotVeryUsefulAspect { |
|
|
|
} |
|
---- |
|
|
|
Aspects (classes annotated with `@Aspect`) may have methods and fields just like any |
|
other class. They may also contain pointcut, advice, and introduction (inter-type) |
|
declarations. |
|
|
|
.Autodetecting aspects through component scanning |
|
[NOTE] |
|
==== |
|
You may register aspect classes as regular beans in your Spring XML configuration, or |
|
autodetect them through classpath scanning - just like any other Spring-managed bean. |
|
However, note that the __@Aspect__ annotation is __not__ sufficient for autodetection in |
|
the classpath: For that purpose, you need to add a separate __@Component__ annotation |
|
(or alternatively a custom stereotype annotation that qualifies, as per the rules of |
|
Spring's component scanner). |
|
==== |
|
|
|
.Advising aspects with other aspects? |
|
[NOTE] |
|
==== |
|
In Spring AOP, it is __not__ possible to have aspects themselves be the target of advice |
|
from other aspects. The __@Aspect__ annotation on a class marks it as an aspect, and |
|
hence excludes it from auto-proxying. |
|
==== |
|
|
|
|
|
|
|
[[aop-pointcuts]] |
|
==== Declaring a pointcut |
|
Recall that pointcuts determine join points of interest, and thus enable us to control |
|
when advice executes. __Spring AOP only supports method execution join points for Spring |
|
beans__, so you can think of a pointcut as matching the execution of methods on Spring |
|
beans. A pointcut declaration has two parts: a signature comprising a name and any |
|
parameters, and a pointcut expression that determines __exactly__ which method |
|
executions we are interested in. In the @AspectJ annotation-style of AOP, a pointcut |
|
signature is provided by a regular method definition, and the pointcut expression is |
|
indicated using the `@Pointcut` annotation (the method serving as the pointcut signature |
|
__must__ have a `void` return type). |
|
|
|
An example will help make this distinction between a pointcut signature and a pointcut |
|
expression clear. The following example defines a pointcut named `'anyOldTransfer'` that |
|
will match the execution of any method named `'transfer'`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Pointcut("execution(* transfer(..))")// the pointcut expression |
|
private void anyOldTransfer() {}// the pointcut signature |
|
---- |
|
|
|
The pointcut expression that forms the value of the `@Pointcut` annotation is a regular |
|
AspectJ 5 pointcut expression. For a full discussion of AspectJ's pointcut language, see |
|
the http://www.eclipse.org/aspectj/doc/released/progguide/index.html[AspectJ |
|
Programming Guide] (and for extensions, the |
|
http://www.eclipse.org/aspectj/doc/released/adk15notebook/index.html[AspectJ 5 |
|
Developers Notebook]) or one of the books on AspectJ such as "Eclipse AspectJ" by Colyer |
|
et. al. or "AspectJ in Action" by Ramnivas Laddad. |
|
|
|
|
|
[[aop-pointcuts-designators]] |
|
===== Supported Pointcut Designators |
|
Spring AOP supports the following AspectJ pointcut designators (PCD) for use in pointcut |
|
expressions: |
|
|
|
.Other pointcut types |
|
**** |
|
The full AspectJ pointcut language supports additional pointcut designators that are not |
|
supported in Spring. These are: `call, get, set, preinitialization, |
|
staticinitialization, initialization, handler, adviceexecution, withincode, cflow, |
|
cflowbelow, if, @this`, and `@withincode`. Use of these pointcut designators in pointcut |
|
expressions interpreted by Spring AOP will result in an `IllegalArgumentException` being |
|
thrown. |
|
|
|
The set of pointcut designators supported by Spring AOP may be extended in future |
|
releases to support more of the AspectJ pointcut designators. |
|
**** |
|
|
|
* __execution__ - for matching method execution join points, this is the primary |
|
pointcut designator you will use when working with Spring AOP |
|
* __within__ - limits matching to join points within certain types (simply the execution |
|
of a method declared within a matching type when using Spring AOP) |
|
* __this__ - limits matching to join points (the execution of methods when using Spring |
|
AOP) where the bean reference (Spring AOP proxy) is an instance of the given type |
|
* __target__ - limits matching to join points (the execution of methods when using |
|
Spring AOP) where the target object (application object being proxied) is an instance |
|
of the given type |
|
* __args__ - limits matching to join points (the execution of methods when using Spring |
|
AOP) where the arguments are instances of the given types |
|
* __@target__ - limits matching to join points (the execution of methods when using |
|
Spring AOP) where the class of the executing object has an annotation of the given type |
|
* __@args__ - limits matching to join points (the execution of methods when using Spring |
|
AOP) where the runtime type of the actual arguments passed have annotations of the |
|
given type(s) |
|
* __@within__ - limits matching to join points within types that have the given |
|
annotation (the execution of methods declared in types with the given annotation when |
|
using Spring AOP) |
|
* __@annotation__ - limits matching to join points where the subject of the join point |
|
(method being executed in Spring AOP) has the given annotation |
|
|
|
Because Spring AOP limits matching to only method execution join points, the discussion |
|
of the pointcut designators above gives a narrower definition than you will find in the |
|
AspectJ programming guide. In addition, AspectJ itself has type-based semantics and at |
|
an execution join point both '++this++' and '++target++' refer to the same object - the |
|
object executing the method. Spring AOP is a proxy-based system and differentiates |
|
between the proxy object itself (bound to '++this++') and the target object behind the |
|
proxy (bound to '++target++'). |
|
|
|
[NOTE] |
|
==== |
|
Due to the proxy-based nature of Spring's AOP framework, protected methods are by |
|
definition __not__ intercepted, neither for JDK proxies (where this isn't applicable) |
|
nor for CGLIB proxies (where this is technically possible but not recommendable for AOP |
|
purposes). As a consequence, any given pointcut will be matched against __public methods |
|
only__! |
|
|
|
If your interception needs include protected/private methods or even constructors, |
|
consider the use of Spring-driven <<aop-aj-ltw,native AspectJ weaving>> instead of |
|
Spring's proxy-based AOP framework. This constitutes a different mode of AOP usage with |
|
different characteristics, so be sure to make yourself familiar with weaving first |
|
before making a decision. |
|
==== |
|
|
|
Spring AOP also supports an additional PCD named '++bean++'. This PCD allows you to limit |
|
the matching of join points to a particular named Spring bean, or to a set of named |
|
Spring beans (when using wildcards). The '++bean++' PCD has the following form: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
bean(idOrNameOfBean) |
|
---- |
|
|
|
The '++idOrNameOfBean++' token can be the name of any Spring bean: limited wildcard |
|
support using the '++*++' character is provided, so if you establish some naming |
|
conventions for your Spring beans you can quite easily write a '++bean++' PCD expression |
|
to pick them out. As is the case with other pointcut designators, the '++bean++' PCD can |
|
be &&'ed, ||'ed, and ! (negated) too. |
|
|
|
[NOTE] |
|
==== |
|
Please note that the '++bean++' PCD is __only__ supported in Spring AOP - and __not__ in |
|
native AspectJ weaving. It is a Spring-specific extension to the standard PCDs that |
|
AspectJ defines. |
|
|
|
The '++bean++' PCD operates at the __instance__ level (building on the Spring bean name |
|
concept) rather than at the type level only (which is what weaving-based AOP is limited |
|
to). Instance-based pointcut designators are a special capability of Spring's |
|
proxy-based AOP framework and its close integration with the Spring bean factory, where |
|
it is natural and straightforward to identify specific beans by name. |
|
==== |
|
|
|
|
|
[[aop-pointcuts-combining]] |
|
===== Combining pointcut expressions |
|
Pointcut expressions can be combined using '&&', '||' and '!'. It is also possible to |
|
refer to pointcut expressions by name. The following example shows three pointcut |
|
expressions: `anyPublicOperation` (which matches if a method execution join point |
|
represents the execution of any public method); `inTrading` (which matches if a method |
|
execution is in the trading module), and `tradingOperation` (which matches if a method |
|
execution represents any public method in the trading module). |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
@Pointcut("execution(public * *(..))") |
|
private void anyPublicOperation() {} |
|
|
|
@Pointcut("within(com.xyz.someapp.trading..*)") |
|
private void inTrading() {} |
|
|
|
@Pointcut("anyPublicOperation() && inTrading()") |
|
private void tradingOperation() {} |
|
---- |
|
|
|
It is a best practice to build more complex pointcut expressions out of smaller named |
|
components as shown above. When referring to pointcuts by name, normal Java visibility |
|
rules apply (you can see private pointcuts in the same type, protected pointcuts in the |
|
hierarchy, public pointcuts anywhere and so on). Visibility does not affect pointcut |
|
__matching__. |
|
|
|
|
|
[[aop-common-pointcuts]] |
|
===== Sharing common pointcut definitions |
|
When working with enterprise applications, you often want to refer to modules of the |
|
application and particular sets of operations from within several aspects. We recommend |
|
defining a "SystemArchitecture" aspect that captures common pointcut expressions for |
|
this purpose. A typical such aspect would look as follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
package com.xyz.someapp; |
|
|
|
import org.aspectj.lang.annotation.Aspect; |
|
import org.aspectj.lang.annotation.Pointcut; |
|
|
|
@Aspect |
|
public class SystemArchitecture { |
|
|
|
/** |
|
* A join point is in the web layer if the method is defined |
|
* in a type in the com.xyz.someapp.web package or any sub-package |
|
* under that. |
|
*/ |
|
@Pointcut("within(com.xyz.someapp.web..*)") |
|
public void inWebLayer() {} |
|
|
|
/** |
|
* A join point is in the service layer if the method is defined |
|
* in a type in the com.xyz.someapp.service package or any sub-package |
|
* under that. |
|
*/ |
|
@Pointcut("within(com.xyz.someapp.service..*)") |
|
public void inServiceLayer() {} |
|
|
|
/** |
|
* A join point is in the data access layer if the method is defined |
|
* in a type in the com.xyz.someapp.dao package or any sub-package |
|
* under that. |
|
*/ |
|
@Pointcut("within(com.xyz.someapp.dao..*)") |
|
public void inDataAccessLayer() {} |
|
|
|
/** |
|
* A business service is the execution of any method defined on a service |
|
* interface. This definition assumes that interfaces are placed in the |
|
* "service" package, and that implementation types are in sub-packages. |
|
* |
|
* If you group service interfaces by functional area (for example, |
|
* in packages com.xyz.someapp.abc.service and com.xyz.someapp.def.service) then |
|
* the pointcut expression "execution(* com.xyz.someapp..service.*.*(..))" |
|
* could be used instead. |
|
* |
|
* Alternatively, you can write the expression using the 'bean' |
|
* PCD, like so "bean(*Service)". (This assumes that you have |
|
* named your Spring service beans in a consistent fashion.) |
|
*/ |
|
@Pointcut("execution(* com.xyz.someapp..service.*.*(..))") |
|
public void businessService() {} |
|
|
|
/** |
|
* A data access operation is the execution of any method defined on a |
|
* dao interface. This definition assumes that interfaces are placed in the |
|
* "dao" package, and that implementation types are in sub-packages. |
|
*/ |
|
@Pointcut("execution(* com.xyz.someapp.dao.*.*(..))") |
|
public void dataAccessOperation() {} |
|
|
|
} |
|
---- |
|
|
|
The pointcuts defined in such an aspect can be referred to anywhere that you need a |
|
pointcut expression. For example, to make the service layer transactional, you could |
|
write: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<aop:config> |
|
<aop:advisor |
|
pointcut="com.xyz.someapp.SystemArchitecture.businessService()" |
|
advice-ref="tx-advice"/> |
|
</aop:config> |
|
|
|
<tx:advice id="tx-advice"> |
|
<tx:attributes> |
|
<tx:method name="*" propagation="REQUIRED"/> |
|
</tx:attributes> |
|
</tx:advice> |
|
---- |
|
|
|
The `<aop:config>` and `<aop:advisor>` elements are discussed in <<aop-schema>>. The |
|
transaction elements are discussed in <<transaction>>. |
|
|
|
|
|
[[aop-pointcuts-examples]] |
|
===== Examples |
|
Spring AOP users are likely to use the `execution` pointcut designator the most often. |
|
The format of an execution expression is: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) |
|
throws-pattern?) |
|
---- |
|
|
|
All parts except the returning type pattern (ret-type-pattern in the snippet above), |
|
name pattern, and parameters pattern are optional. The returning type pattern determines |
|
what the return type of the method must be in order for a join point to be matched. Most |
|
frequently you will use `*` as the returning type pattern, which matches any return |
|
type. A fully-qualified type name will match only when the method returns the given |
|
type. The name pattern matches the method name. You can use the `*` wildcard as all or |
|
part of a name pattern. The parameters pattern is slightly more complex: `()` matches a |
|
method that takes no parameters, whereas `(..)` matches any number of parameters (zero |
|
or more). The pattern `(*)` matches a method taking one parameter of any type, |
|
`(*,String)` matches a method taking two parameters, the first can be of any type, the |
|
second must be a String. Consult the |
|
http://www.eclipse.org/aspectj/doc/released/progguide/semantics-pointcuts.html[Language |
|
Semantics] section of the AspectJ Programming Guide for more information. |
|
|
|
Some examples of common pointcut expressions are given below. |
|
|
|
* the execution of any public method: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
execution(public * *(..)) |
|
---- |
|
|
|
* the execution of any method with a name beginning with "set": |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
execution(* set*(..)) |
|
---- |
|
|
|
* the execution of any method defined by the `AccountService` interface: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
execution(* com.xyz.service.AccountService.*(..)) |
|
---- |
|
|
|
* the execution of any method defined in the service package: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
execution(* com.xyz.service.*.*(..)) |
|
---- |
|
|
|
* the execution of any method defined in the service package or a sub-package: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
execution(* com.xyz.service..*.*(..)) |
|
---- |
|
|
|
* any join point (method execution only in Spring AOP) within the service package: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
within(com.xyz.service.*) |
|
---- |
|
|
|
* any join point (method execution only in Spring AOP) within the service package or a |
|
sub-package: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
within(com.xyz.service..*) |
|
---- |
|
|
|
* any join point (method execution only in Spring AOP) where the proxy implements the |
|
`AccountService` interface: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
this(com.xyz.service.AccountService) |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
'this' is more commonly used in a binding form :- see the following section on advice |
|
for how to make the proxy object available in the advice body. |
|
==== |
|
|
|
* any join point (method execution only in Spring AOP) where the target object |
|
implements the `AccountService` interface: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
target(com.xyz.service.AccountService) |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
'target' is more commonly used in a binding form :- see the following section on advice |
|
for how to make the target object available in the advice body. |
|
==== |
|
|
|
* any join point (method execution only in Spring AOP) which takes a single parameter, |
|
and where the argument passed at runtime is `Serializable`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
args(java.io.Serializable) |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
'args' is more commonly used in a binding form :- see the following section on advice |
|
for how to make the method arguments available in the advice body. |
|
==== |
|
|
|
Note that the pointcut given in this example is different to `execution(* |
|
*(java.io.Serializable))`: the args version matches if the argument passed at runtime is |
|
Serializable, the execution version matches if the method signature declares a single |
|
parameter of type `Serializable`. |
|
|
|
* any join point (method execution only in Spring AOP) where the target object has an |
|
`@Transactional` annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@target(org.springframework.transaction.annotation.Transactional) |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
'@target' can also be used in a binding form :- see the following section on advice for |
|
how to make the annotation object available in the advice body. |
|
==== |
|
|
|
* any join point (method execution only in Spring AOP) where the declared type of the |
|
target object has an `@Transactional` annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@within(org.springframework.transaction.annotation.Transactional) |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
'@within' can also be used in a binding form :- see the following section on advice for |
|
how to make the annotation object available in the advice body. |
|
==== |
|
|
|
* any join point (method execution only in Spring AOP) where the executing method has an |
|
`@Transactional` annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@annotation(org.springframework.transaction.annotation.Transactional) |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
'@annotation' can also be used in a binding form :- see the following section on advice |
|
for how to make the annotation object available in the advice body. |
|
==== |
|
|
|
* any join point (method execution only in Spring AOP) which takes a single parameter, |
|
and where the runtime type of the argument passed has the `@Classified` annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@args(com.xyz.security.Classified) |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
'@args' can also be used in a binding form :- see the following section on advice for |
|
how to make the annotation object(s) available in the advice body. |
|
==== |
|
|
|
* any join point (method execution only in Spring AOP) on a Spring bean named |
|
'++tradeService++': |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
bean(tradeService) |
|
---- |
|
|
|
* any join point (method execution only in Spring AOP) on Spring beans having names that |
|
match the wildcard expression '++*Service++': |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
bean(*Service) |
|
---- |
|
|
|
|
|
[[writing-good-pointcuts]] |
|
===== Writing good pointcuts |
|
During compilation, AspectJ processes pointcuts in order to try and optimize matching |
|
performance. Examining code and determining if each join point matches (statically or |
|
dynamically) a given pointcut is a costly process. (A dynamic match means the match |
|
cannot be fully determined from static analysis and a test will be placed in the code to |
|
determine if there is an actual match when the code is running). On first encountering a |
|
pointcut declaration, AspectJ will rewrite it into an optimal form for the matching |
|
process. What does this mean? Basically pointcuts are rewritten in DNF (Disjunctive |
|
Normal Form) and the components of the pointcut are sorted such that those components |
|
that are cheaper to evaluate are checked first. This means you do not have to worry |
|
about understanding the performance of various pointcut designators and may supply them |
|
in any order in a pointcut declaration. |
|
|
|
However, AspectJ can only work with what it is told, and for optimal performance of |
|
matching you should think about what they are trying to achieve and narrow the search |
|
space for matches as much as possible in the definition. The existing designators |
|
naturally fall into one of three groups: kinded, scoping and context: |
|
|
|
* Kinded designators are those which select a particular kind of join point. For |
|
example: execution, get, set, call, handler |
|
* Scoping designators are those which select a group of join points of interest (of |
|
probably many kinds). For example: within, withincode |
|
* Contextual designators are those that match (and optionally bind) based on context. |
|
For example: this, target, @annotation |
|
|
|
A well written pointcut should try and include at least the first two types (kinded and |
|
scoping), whilst the contextual designators may be included if wishing to match based on |
|
join point context, or bind that context for use in the advice. Supplying either just a |
|
kinded designator or just a contextual designator will work but could affect weaving |
|
performance (time and memory used) due to all the extra processing and analysis. Scoping |
|
designators are very fast to match and their usage means AspectJ can very quickly |
|
dismiss groups of join points that should not be further processed - that is why a good |
|
pointcut should always include one if possible. |
|
|
|
|
|
|
|
[[aop-advice]] |
|
==== Declaring advice |
|
Advice is associated with a pointcut expression, and runs before, after, or around |
|
method executions matched by the pointcut. The pointcut expression may be either a |
|
simple reference to a named pointcut, or a pointcut expression declared in place. |
|
|
|
|
|
[[aop-advice-before]] |
|
===== Before advice |
|
Before advice is declared in an aspect using the `@Before` annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import org.aspectj.lang.annotation.Aspect; |
|
import org.aspectj.lang.annotation.Before; |
|
|
|
@Aspect |
|
public class BeforeExample { |
|
|
|
@Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation()") |
|
public void doAccessCheck() { |
|
// ... |
|
} |
|
|
|
} |
|
---- |
|
|
|
If using an in-place pointcut expression we could rewrite the above example as: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
import org.aspectj.lang.annotation.Aspect; |
|
import org.aspectj.lang.annotation.Before; |
|
|
|
@Aspect |
|
public class BeforeExample { |
|
|
|
@Before("execution(* com.xyz.myapp.dao.*.*(..))") |
|
public void doAccessCheck() { |
|
// ... |
|
} |
|
|
|
} |
|
---- |
|
|
|
|
|
[[aop-advice-after-returning]] |
|
===== After returning advice |
|
After returning advice runs when a matched method execution returns normally. It is |
|
declared using the `@AfterReturning` annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import org.aspectj.lang.annotation.Aspect; |
|
import org.aspectj.lang.annotation.AfterReturning; |
|
|
|
@Aspect |
|
public class AfterReturningExample { |
|
|
|
@AfterReturning("com.xyz.myapp.SystemArchitecture.dataAccessOperation()") |
|
public void doAccessCheck() { |
|
// ... |
|
} |
|
|
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
Note: it is of course possible to have multiple advice declarations, and other members |
|
as well, all inside the same aspect. We're just showing a single advice declaration in |
|
these examples to focus on the issue under discussion at the time. |
|
==== |
|
|
|
Sometimes you need access in the advice body to the actual value that was returned. You |
|
can use the form of `@AfterReturning` that binds the return value for this: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import org.aspectj.lang.annotation.Aspect; |
|
import org.aspectj.lang.annotation.AfterReturning; |
|
|
|
@Aspect |
|
public class AfterReturningExample { |
|
|
|
@AfterReturning( |
|
pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()", |
|
returning="retVal") |
|
public void doAccessCheck(Object retVal) { |
|
// ... |
|
} |
|
|
|
} |
|
---- |
|
|
|
The name used in the `returning` attribute must correspond to the name of a parameter in |
|
the advice method. When a method execution returns, the return value will be passed to |
|
the advice method as the corresponding argument value. A `returning` clause also |
|
restricts matching to only those method executions that return a value of the specified |
|
type ( `Object` in this case, which will match any return value). |
|
|
|
Please note that it is __not__ possible to return a totally different reference when |
|
using after-returning advice. |
|
|
|
|
|
[[aop-advice-after-throwing]] |
|
===== After throwing advice |
|
After throwing advice runs when a matched method execution exits by throwing an |
|
exception. It is declared using the `@AfterThrowing` annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import org.aspectj.lang.annotation.Aspect; |
|
import org.aspectj.lang.annotation.AfterThrowing; |
|
|
|
@Aspect |
|
public class AfterThrowingExample { |
|
|
|
@AfterThrowing("com.xyz.myapp.SystemArchitecture.dataAccessOperation()") |
|
public void doRecoveryActions() { |
|
// ... |
|
} |
|
|
|
} |
|
---- |
|
|
|
Often you want the advice to run only when exceptions of a given type are thrown, and |
|
you also often need access to the thrown exception in the advice body. Use the |
|
`throwing` attribute to both restrict matching (if desired, use `Throwable` as the |
|
exception type otherwise) and bind the thrown exception to an advice parameter. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import org.aspectj.lang.annotation.Aspect; |
|
import org.aspectj.lang.annotation.AfterThrowing; |
|
|
|
@Aspect |
|
public class AfterThrowingExample { |
|
|
|
@AfterThrowing( |
|
pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()", |
|
throwing="ex") |
|
public void doRecoveryActions(DataAccessException ex) { |
|
// ... |
|
} |
|
|
|
} |
|
---- |
|
|
|
The name used in the `throwing` attribute must correspond to the name of a parameter in |
|
the advice method. When a method execution exits by throwing an exception, the exception |
|
will be passed to the advice method as the corresponding argument value. A `throwing` |
|
clause also restricts matching to only those method executions that throw an exception |
|
of the specified type ( `DataAccessException` in this case). |
|
|
|
|
|
[[aop-advice-after-finally]] |
|
===== After (finally) advice |
|
After (finally) advice runs however a matched method execution exits. It is declared |
|
using the `@After` annotation. After advice must be prepared to handle both normal and |
|
exception return conditions. It is typically used for releasing resources, etc. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import org.aspectj.lang.annotation.Aspect; |
|
import org.aspectj.lang.annotation.After; |
|
|
|
@Aspect |
|
public class AfterFinallyExample { |
|
|
|
@After("com.xyz.myapp.SystemArchitecture.dataAccessOperation()") |
|
public void doReleaseLock() { |
|
// ... |
|
} |
|
|
|
} |
|
---- |
|
|
|
|
|
[[aop-ataspectj-around-advice]] |
|
===== Around advice |
|
The final kind of advice is around advice. Around advice runs "around" a matched method |
|
execution. It has the opportunity to do work both before and after the method executes, |
|
and to determine when, how, and even if, the method actually gets to execute at all. |
|
Around advice is often used if you need to share state before and after a method |
|
execution in a thread-safe manner (starting and stopping a timer for example). Always |
|
use the least powerful form of advice that meets your requirements (i.e. don't use |
|
around advice if simple before advice would do). |
|
|
|
Around advice is declared using the `@Around` annotation. The first parameter of the |
|
advice method must be of type `ProceedingJoinPoint`. Within the body of the advice, |
|
calling `proceed()` on the `ProceedingJoinPoint` causes the underlying method to |
|
execute. The `proceed` method may also be called passing in an `Object[]` - the values |
|
in the array will be used as the arguments to the method execution when it proceeds. |
|
|
|
[NOTE] |
|
==== |
|
The behavior of proceed when called with an Object[] is a little different than the |
|
behavior of proceed for around advice compiled by the AspectJ compiler. For around |
|
advice written using the traditional AspectJ language, the number of arguments passed to |
|
proceed must match the number of arguments passed to the around advice (not the number |
|
of arguments taken by the underlying join point), and the value passed to proceed in a |
|
given argument position supplants the original value at the join point for the entity |
|
the value was bound to (Don't worry if this doesn't make sense right now!). The approach |
|
taken by Spring is simpler and a better match to its proxy-based, execution only |
|
semantics. You only need to be aware of this difference if you are compiling @AspectJ |
|
aspects written for Spring and using proceed with arguments with the AspectJ compiler |
|
and weaver. There is a way to write such aspects that is 100% compatible across both |
|
Spring AOP and AspectJ, and this is discussed in the following section on advice |
|
parameters. |
|
==== |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import org.aspectj.lang.annotation.Aspect; |
|
import org.aspectj.lang.annotation.Around; |
|
import org.aspectj.lang.ProceedingJoinPoint; |
|
|
|
@Aspect |
|
public class AroundExample { |
|
|
|
@Around("com.xyz.myapp.SystemArchitecture.businessService()") |
|
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { |
|
// start stopwatch |
|
Object retVal = pjp.proceed(); |
|
// stop stopwatch |
|
return retVal; |
|
} |
|
|
|
} |
|
---- |
|
|
|
The value returned by the around advice will be the return value seen by the caller of |
|
the method. A simple caching aspect for example could return a value from a cache if it |
|
has one, and invoke proceed() if it does not. Note that proceed may be invoked once, |
|
many times, or not at all within the body of the around advice, all of these are quite |
|
legal. |
|
|
|
|
|
[[aop-ataspectj-advice-params]] |
|
===== Advice parameters |
|
Spring offers fully typed advice - meaning that you declare the parameters you need |
|
in the advice signature (as we saw for the returning and throwing examples above) rather |
|
than work with `Object[]` arrays all the time. We'll see how to make argument and other |
|
contextual values available to the advice body in a moment. First let's take a look at |
|
how to write generic advice that can find out about the method the advice is currently |
|
advising. |
|
|
|
[[aop-ataspectj-advice-params-the-joinpoint]] |
|
====== Access to the current JoinPoint |
|
|
|
Any advice method may declare as its first parameter, a parameter of type |
|
`org.aspectj.lang.JoinPoint` (please note that around advice is __required__ to declare |
|
a first parameter of type `ProceedingJoinPoint`, which is a subclass of `JoinPoint`. The |
|
`JoinPoint` interface provides a number of useful methods such as `getArgs()` (returns |
|
the method arguments), `getThis()` (returns the proxy object), `getTarget()` (returns |
|
the target object), `getSignature()` (returns a description of the method that is being |
|
advised) and `toString()` (prints a useful description of the method being advised). |
|
Please do consult the javadocs for full details. |
|
|
|
[[aop-ataspectj-advice-params-passing]] |
|
====== Passing parameters to advice |
|
We've already seen how to bind the returned value or exception value (using after |
|
returning and after throwing advice). To make argument values available to the advice |
|
body, you can use the binding form of `args`. If a parameter name is used in place of a |
|
type name in an args expression, then the value of the corresponding argument will be |
|
passed as the parameter value when the advice is invoked. An example should make this |
|
clearer. Suppose you want to advise the execution of dao operations that take an Account |
|
object as the first parameter, and you need access to the account in the advice body. |
|
You could write the following: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation() && args(account,..)") |
|
public void validateAccount(Account account) { |
|
// ... |
|
} |
|
---- |
|
|
|
The `args(account,..)` part of the pointcut expression serves two purposes: firstly, it |
|
restricts matching to only those method executions where the method takes at least one |
|
parameter, and the argument passed to that parameter is an instance of `Account`; |
|
secondly, it makes the actual `Account` object available to the advice via the `account` |
|
parameter. |
|
|
|
Another way of writing this is to declare a pointcut that "provides" the `Account` |
|
object value when it matches a join point, and then just refer to the named pointcut |
|
from the advice. This would look as follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Pointcut("com.xyz.myapp.SystemArchitecture.dataAccessOperation() && args(account,..)") |
|
private void accountDataAccessOperation(Account account) {} |
|
|
|
@Before("accountDataAccessOperation(account)") |
|
public void validateAccount(Account account) { |
|
// ... |
|
} |
|
---- |
|
|
|
The interested reader is once more referred to the AspectJ programming guide for more |
|
details. |
|
|
|
The proxy object ( `this`), target object ( `target`), and annotations ( `@within, |
|
@target, @annotation, @args`) can all be bound in a similar fashion. The following |
|
example shows how you could match the execution of methods annotated with an |
|
`@Auditable` annotation, and extract the audit code. |
|
|
|
First the definition of the `@Auditable` annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Retention(RetentionPolicy.RUNTIME) |
|
@Target(ElementType.METHOD) |
|
public @interface Auditable { |
|
AuditCode value(); |
|
} |
|
---- |
|
|
|
And then the advice that matches the execution of `@Auditable` methods: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Before("com.xyz.lib.Pointcuts.anyPublicMethod() && @annotation(auditable)") |
|
public void audit(Auditable auditable) { |
|
AuditCode code = auditable.value(); |
|
// ... |
|
} |
|
---- |
|
|
|
[[aop-ataspectj-advice-params-generics]] |
|
====== Advice parameters and generics |
|
Spring AOP can handle generics used in class declarations and method parameters. Suppose |
|
you have a generic type like this: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface Sample<T> { |
|
void sampleGenericMethod(T param); |
|
void sampleGenericCollectionMethod(Collection>T> param); |
|
} |
|
---- |
|
|
|
You can restrict interception of method types to certain parameter types by simply |
|
typing the advice parameter to the parameter type you want to intercept the method for: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
@Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)") |
|
public void beforeSampleMethod(MyType param) { |
|
// Advice implementation |
|
} |
|
---- |
|
|
|
That this works is pretty obvious as we already discussed above. However, it's worth |
|
pointing out that this won't work for generic collections. So you cannot define a |
|
pointcut like this: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
@Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)") |
|
public void beforeSampleMethod(Collection<MyType> param) { |
|
// Advice implementation |
|
} |
|
---- |
|
|
|
To make this work we would have to inspect every element of the collection, which is not |
|
reasonable as we also cannot decide how to treat `null` values in general. To achieve |
|
something similar to this you have to type the parameter to `Collection<?>` and manually |
|
check the type of the elements. |
|
|
|
[[aop-ataspectj-advice-params-names]] |
|
====== Determining argument names |
|
The parameter binding in advice invocations relies on matching names used in pointcut |
|
expressions to declared parameter names in (advice and pointcut) method signatures. |
|
Parameter names are __not__ available through Java reflection, so Spring AOP uses the |
|
following strategies to determine parameter names: |
|
|
|
* If the parameter names have been specified by the user explicitly, then the specified |
|
parameter names are used: both the advice and the pointcut annotations have |
|
an optional "argNames" attribute which can be used to specify the argument names of |
|
the annotated method - these argument names __are__ available at runtime. For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Before(value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)", |
|
argNames="bean,auditable") |
|
public void audit(Object bean, Auditable auditable) { |
|
AuditCode code = auditable.value(); |
|
// ... use code and bean |
|
} |
|
---- |
|
|
|
If the first parameter is of the `JoinPoint`, `ProceedingJoinPoint`, or |
|
`JoinPoint.StaticPart` type, you may leave out the name of the parameter from the value |
|
of the "argNames" attribute. For example, if you modify the preceding advice to receive |
|
the join point object, the "argNames" attribute need not include it: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Before(value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)", |
|
argNames="bean,auditable") |
|
public void audit(JoinPoint jp, Object bean, Auditable auditable) { |
|
AuditCode code = auditable.value(); |
|
// ... use code, bean, and jp |
|
} |
|
---- |
|
|
|
The special treatment given to the first parameter of the `JoinPoint`, |
|
`ProceedingJoinPoint`, and `JoinPoint.StaticPart` types is particularly convenient for |
|
advice that do not collect any other join point context. In such situations, you may |
|
simply omit the "argNames" attribute. For example, the following advice need not declare |
|
the "argNames" attribute: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Before("com.xyz.lib.Pointcuts.anyPublicMethod()") |
|
public void audit(JoinPoint jp) { |
|
// ... use jp |
|
} |
|
---- |
|
|
|
* Using the `'argNames'` attribute is a little clumsy, so if the `'argNames'` attribute |
|
has not been specified, then Spring AOP will look at the debug information for the |
|
class and try to determine the parameter names from the local variable table. This |
|
information will be present as long as the classes have been compiled with debug |
|
information ( `'-g:vars'` at a minimum). The consequences of compiling with this flag |
|
on are: (1) your code will be slightly easier to understand (reverse engineer), (2) |
|
the class file sizes will be very slightly bigger (typically inconsequential), (3) the |
|
optimization to remove unused local variables will not be applied by your compiler. In |
|
other words, you should encounter no difficulties building with this flag on. |
|
|
|
[NOTE] |
|
==== |
|
If an @AspectJ aspect has been compiled by the AspectJ compiler (ajc) even without the |
|
debug information then there is no need to add the argNames attribute as the compiler |
|
will retain the needed information. |
|
==== |
|
|
|
* If the code has been compiled without the necessary debug information, then Spring AOP |
|
will attempt to deduce the pairing of binding variables to parameters (for example, if |
|
only one variable is bound in the pointcut expression, and the advice method only |
|
takes one parameter, the pairing is obvious!). If the binding of variables is |
|
ambiguous given the available information, then an `AmbiguousBindingException` will be |
|
thrown. |
|
* If all of the above strategies fail then an `IllegalArgumentException` will be thrown. |
|
|
|
[[aop-ataspectj-advice-proceeding-with-the-call]] |
|
====== Proceeding with arguments |
|
We remarked earlier that we would describe how to write a proceed call __with |
|
arguments__ that works consistently across Spring AOP and AspectJ. The solution is |
|
simply to ensure that the advice signature binds each of the method parameters in order. |
|
For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Around("execution(List<Account> find*(..)) && " + |
|
"com.xyz.myapp.SystemArchitecture.inDataAccessLayer() && " + |
|
"args(accountHolderNamePattern)") |
|
public Object preProcessQueryPattern(ProceedingJoinPoint pjp, |
|
String accountHolderNamePattern) throws Throwable { |
|
String newPattern = preProcess(accountHolderNamePattern); |
|
return pjp.proceed(new Object[] {newPattern}); |
|
} |
|
---- |
|
|
|
In many cases you will be doing this binding anyway (as in the example above). |
|
|
|
|
|
[[aop-ataspectj-advice-ordering]] |
|
===== Advice ordering |
|
What happens when multiple pieces of advice all want to run at the same join point? |
|
Spring AOP follows the same precedence rules as AspectJ to determine the order of advice |
|
execution. The highest precedence advice runs first "on the way in" (so given two pieces |
|
of before advice, the one with highest precedence runs first). "On the way out" from a |
|
join point, the highest precedence advice runs last (so given two pieces of after |
|
advice, the one with the highest precedence will run second). |
|
|
|
When two pieces of advice defined in __different__ aspects both need to run at the same |
|
join point, unless you specify otherwise the order of execution is undefined. You can |
|
control the order of execution by specifying precedence. This is done in the normal |
|
Spring way by either implementing the `org.springframework.core.Ordered` interface in |
|
the aspect class or annotating it with the `Order` annotation. Given two aspects, the |
|
aspect returning the lower value from `Ordered.getValue()` (or the annotation value) has |
|
the higher precedence. |
|
|
|
When two pieces of advice defined in __the same__ aspect both need to run at the same |
|
join point, the ordering is undefined (since there is no way to retrieve the declaration |
|
order via reflection for javac-compiled classes). Consider collapsing such advice |
|
methods into one advice method per join point in each aspect class, or refactor the |
|
pieces of advice into separate aspect classes - which can be ordered at the aspect level. |
|
|
|
|
|
|
|
[[aop-introductions]] |
|
==== Introductions |
|
Introductions (known as inter-type declarations in AspectJ) enable an aspect to declare |
|
that advised objects implement a given interface, and to provide an implementation of |
|
that interface on behalf of those objects. |
|
|
|
An introduction is made using the `@DeclareParents` annotation. This annotation is used |
|
to declare that matching types have a new parent (hence the name). For example, given an |
|
interface `UsageTracked`, and an implementation of that interface `DefaultUsageTracked`, |
|
the following aspect declares that all implementors of service interfaces also implement |
|
the `UsageTracked` interface. (In order to expose statistics via JMX for example.) |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Aspect |
|
public class UsageTracking { |
|
|
|
@DeclareParents(value="com.xzy.myapp.service.*+", defaultImpl=DefaultUsageTracked.class) |
|
public static UsageTracked mixin; |
|
|
|
@Before("com.xyz.myapp.SystemArchitecture.businessService() && this(usageTracked)") |
|
public void recordUsage(UsageTracked usageTracked) { |
|
usageTracked.incrementUseCount(); |
|
} |
|
|
|
} |
|
---- |
|
|
|
The interface to be implemented is determined by the type of the annotated field. The |
|
`value` attribute of the `@DeclareParents` annotation is an AspectJ type pattern :- any |
|
bean of a matching type will implement the UsageTracked interface. Note that in the |
|
before advice of the above example, service beans can be directly used as |
|
implementations of the `UsageTracked` interface. If accessing a bean programmatically |
|
you would write the following: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
UsageTracked usageTracked = (UsageTracked) context.getBean("myService"); |
|
---- |
|
|
|
|
|
|
|
[[aop-instantiation-models]] |
|
==== Aspect instantiation models |
|
[NOTE] |
|
==== |
|
(This is an advanced topic, so if you are just starting out with AOP you can safely skip |
|
it until later.) |
|
==== |
|
|
|
By default there will be a single instance of each aspect within the application |
|
context. AspectJ calls this the singleton instantiation model. It is possible to define |
|
aspects with alternate lifecycles :- Spring supports AspectJ's `perthis` and `pertarget` |
|
instantiation models ( `percflow, percflowbelow,` and `pertypewithin` are not currently |
|
supported). |
|
|
|
A "perthis" aspect is declared by specifying a `perthis` clause in the `@Aspect` |
|
annotation. Let's look at an example, and then we'll explain how it works. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Aspect("perthis(com.xyz.myapp.SystemArchitecture.businessService())") |
|
public class MyAspect { |
|
|
|
private int someState; |
|
|
|
@Before(com.xyz.myapp.SystemArchitecture.businessService()) |
|
public void recordServiceUsage() { |
|
// ... |
|
} |
|
|
|
} |
|
---- |
|
|
|
The effect of the `'perthis'` clause is that one aspect instance will be created for |
|
each unique service object executing a business service (each unique object bound to |
|
'this' at join points matched by the pointcut expression). The aspect instance is |
|
created the first time that a method is invoked on the service object. The aspect goes |
|
out of scope when the service object goes out of scope. Before the aspect instance is |
|
created, none of the advice within it executes. As soon as the aspect instance has been |
|
created, the advice declared within it will execute at matched join points, but only |
|
when the service object is the one this aspect is associated with. See the AspectJ |
|
programming guide for more information on per-clauses. |
|
|
|
The `'pertarget'` instantiation model works in exactly the same way as perthis, but |
|
creates one aspect instance for each unique target object at matched join points. |
|
|
|
|
|
|
|
[[aop-ataspectj-example]] |
|
==== Example |
|
Now that you have seen how all the constituent parts work, let's put them together to do |
|
something useful! |
|
|
|
The execution of business services can sometimes fail due to concurrency issues (for |
|
example, deadlock loser). If the operation is retried, it is quite likely to succeed |
|
next time round. For business services where it is appropriate to retry in such |
|
conditions (idempotent operations that don't need to go back to the user for conflict |
|
resolution), we'd like to transparently retry the operation to avoid the client seeing a |
|
`PessimisticLockingFailureException`. This is a requirement that clearly cuts across |
|
multiple services in the service layer, and hence is ideal for implementing via an |
|
aspect. |
|
|
|
Because we want to retry the operation, we will need to use around advice so that we can |
|
call proceed multiple times. Here's how the basic aspect implementation looks: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Aspect |
|
public class ConcurrentOperationExecutor implements Ordered { |
|
|
|
private static final int DEFAULT_MAX_RETRIES = 2; |
|
|
|
private int maxRetries = DEFAULT_MAX_RETRIES; |
|
private int order = 1; |
|
|
|
public void setMaxRetries(int maxRetries) { |
|
this.maxRetries = maxRetries; |
|
} |
|
|
|
public int getOrder() { |
|
return this.order; |
|
} |
|
|
|
public void setOrder(int order) { |
|
this.order = order; |
|
} |
|
|
|
@Around("com.xyz.myapp.SystemArchitecture.businessService()") |
|
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable { |
|
int numAttempts = 0; |
|
PessimisticLockingFailureException lockFailureException; |
|
do { |
|
numAttempts++; |
|
try { |
|
return pjp.proceed(); |
|
} |
|
catch(PessimisticLockingFailureException ex) { |
|
lockFailureException = ex; |
|
} |
|
} while(numAttempts <= this.maxRetries); |
|
throw lockFailureException; |
|
} |
|
|
|
} |
|
---- |
|
|
|
Note that the aspect implements the `Ordered` interface so we can set the precedence of |
|
the aspect higher than the transaction advice (we want a fresh transaction each time we |
|
retry). The `maxRetries` and `order` properties will both be configured by Spring. The |
|
main action happens in the `doConcurrentOperation` around advice. Notice that for the |
|
moment we're applying the retry logic to all `businessService()s`. We try to proceed, |
|
and if we fail with an `PessimisticLockingFailureException` we simply try again unless |
|
we have exhausted all of our retry attempts. |
|
|
|
The corresponding Spring configuration is: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<aop:aspectj-autoproxy/> |
|
|
|
<bean id="concurrentOperationExecutor" class="com.xyz.myapp.service.impl.ConcurrentOperationExecutor"> |
|
<property name="maxRetries" value="3"/> |
|
<property name="order" value="100"/> |
|
</bean> |
|
---- |
|
|
|
To refine the aspect so that it only retries idempotent operations, we might define an |
|
`Idempotent` annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Retention(RetentionPolicy.RUNTIME) |
|
public @interface Idempotent { |
|
// marker annotation |
|
} |
|
---- |
|
|
|
and use the annotation to annotate the implementation of service operations. The change |
|
to the aspect to only retry idempotent operations simply involves refining the pointcut |
|
expression so that only `@Idempotent` operations match: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Around("com.xyz.myapp.SystemArchitecture.businessService() && " + |
|
"@annotation(com.xyz.myapp.service.Idempotent)") |
|
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable { |
|
... |
|
} |
|
---- |
|
|
|
|
|
|
|
|
|
[[aop-schema]] |
|
=== Schema-based AOP support |
|
If you prefer an XML-based format, then Spring also offers support for defining aspects |
|
using the new "aop" namespace tags. The exact same pointcut expressions and advice kinds |
|
are supported as when using the @AspectJ style, hence in this section we will focus on |
|
the new __syntax__ and refer the reader to the discussion in the previous section |
|
(<<aop-ataspectj>>) for an understanding of writing pointcut expressions and the binding |
|
of advice parameters. |
|
|
|
To use the aop namespace tags described in this section, you need to import the |
|
spring-aop schema as described in <<xsd-config>>. See <<xsd-config-body-schemas-aop>> |
|
for how to import the tags in the aop namespace. |
|
|
|
Within your Spring configurations, all aspect and advisor elements must be placed within |
|
an `<aop:config>` element (you can have more than one `<aop:config>` element in an |
|
application context configuration). An `<aop:config>` element can contain pointcut, |
|
advisor, and aspect elements (note these must be declared in that order). |
|
|
|
[WARNING] |
|
==== |
|
|
|
The `<aop:config>` style of configuration makes heavy use of Spring's |
|
<<aop-autoproxy,auto-proxying>> mechanism. This can cause issues (such as advice not |
|
being woven) if you are already using explicit auto-proxying via the use of |
|
`BeanNameAutoProxyCreator` or suchlike. The recommended usage pattern is to use either |
|
just the `<aop:config>` style, or just the `AutoProxyCreator` style. |
|
==== |
|
|
|
|
|
|
|
[[aop-schema-declaring-an-aspect]] |
|
==== Declaring an aspect |
|
Using the schema support, an aspect is simply a regular Java object defined as a bean in |
|
your Spring application context. The state and behavior is captured in the fields and |
|
methods of the object, and the pointcut and advice information is captured in the XML. |
|
|
|
An aspect is declared using the <aop:aspect> element, and the backing bean is referenced |
|
using the `ref` attribute: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<aop:config> |
|
<aop:aspect id="myAspect" ref="aBean"> |
|
... |
|
</aop:aspect> |
|
</aop:config> |
|
|
|
<bean id="aBean" class="..."> |
|
... |
|
</bean> |
|
---- |
|
|
|
The bean backing the aspect (" `aBean`" in this case) can of course be configured and |
|
dependency injected just like any other Spring bean. |
|
|
|
|
|
|
|
[[aop-schema-pointcuts]] |
|
==== Declaring a pointcut |
|
A named pointcut can be declared inside an <aop:config> element, enabling the pointcut |
|
definition to be shared across several aspects and advisors. |
|
|
|
A pointcut representing the execution of any business service in the service layer could |
|
be defined as follows: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<aop:config> |
|
|
|
<aop:pointcut id="businessService" |
|
expression="execution(* com.xyz.myapp.service.*.*(..))"/> |
|
|
|
</aop:config> |
|
---- |
|
|
|
Note that the pointcut expression itself is using the same AspectJ pointcut expression |
|
language as described in <<aop-ataspectj>>. If you are using the schema based |
|
declaration style, you can refer to named pointcuts defined in types |
|
(@Aspects) within the pointcut expression. Another way of defining the above pointcut |
|
would be: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<aop:config> |
|
|
|
<aop:pointcut id="businessService" |
|
expression="com.xyz.myapp.SystemArchitecture.businessService()"/> |
|
|
|
</aop:config> |
|
---- |
|
|
|
Assuming you have a `SystemArchitecture` aspect as described in <<aop-common-pointcuts>>. |
|
|
|
Declaring a pointcut inside an aspect is very similar to declaring a top-level pointcut: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<aop:config> |
|
|
|
<aop:aspect id="myAspect" ref="aBean"> |
|
|
|
<aop:pointcut id="businessService" |
|
expression="execution(* com.xyz.myapp.service.*.*(..))"/> |
|
|
|
... |
|
|
|
</aop:aspect> |
|
|
|
</aop:config> |
|
---- |
|
|
|
Much the same way in an @AspectJ aspect, pointcuts declared using the schema based |
|
definition style may collect join point context. For example, the following pointcut |
|
collects the 'this' object as the join point context and passes it to advice: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<aop:config> |
|
|
|
<aop:aspect id="myAspect" ref="aBean"> |
|
|
|
<aop:pointcut id="businessService" |
|
expression="execution(* com.xyz.myapp.service.*.*(..)) && this(service)"/> |
|
|
|
<aop:before pointcut-ref="businessService" method="monitor"/> |
|
|
|
... |
|
|
|
</aop:aspect> |
|
|
|
</aop:config> |
|
---- |
|
|
|
The advice must be declared to receive the collected join point context by including |
|
parameters of the matching names: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public void monitor(Object service) { |
|
... |
|
} |
|
---- |
|
|
|
When combining pointcut sub-expressions, '&&' is awkward within an XML document, and so |
|
the keywords 'and', 'or' and 'not' can be used in place of '&&', '||' and '!' |
|
respectively. For example, the previous pointcut may be better written as: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<aop:config> |
|
|
|
<aop:aspect id="myAspect" ref="aBean"> |
|
|
|
<aop:pointcut id="businessService" |
|
expression="execution(* com.xyz.myapp.service.*.*(..)) **and** this(service)"/> |
|
|
|
<aop:before pointcut-ref="businessService" method="monitor"/> |
|
|
|
... |
|
</aop:aspect> |
|
</aop:config> |
|
---- |
|
|
|
Note that pointcuts defined in this way are referred to by their XML id and cannot be |
|
used as named pointcuts to form composite pointcuts. The named pointcut support in the |
|
schema based definition style is thus more limited than that offered by the @AspectJ |
|
style. |
|
|
|
|
|
|
|
[[aop-schema-advice]] |
|
==== Declaring advice |
|
The same five advice kinds are supported as for the @AspectJ style, and they have |
|
exactly the same semantics. |
|
|
|
|
|
[[aop-schema-advice-before]] |
|
===== Before advice |
|
Before advice runs before a matched method execution. It is declared inside an |
|
`<aop:aspect>` using the <aop:before> element. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<aop:aspect id="beforeExample" ref="aBean"> |
|
|
|
<aop:before |
|
pointcut-ref="dataAccessOperation" |
|
method="doAccessCheck"/> |
|
|
|
... |
|
|
|
</aop:aspect> |
|
---- |
|
|
|
Here `dataAccessOperation` is the id of a pointcut defined at the top ( `<aop:config>`) |
|
level. To define the pointcut inline instead, replace the `pointcut-ref` attribute with |
|
a `pointcut` attribute: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<aop:aspect id="beforeExample" ref="aBean"> |
|
|
|
<aop:before |
|
pointcut="execution(* com.xyz.myapp.dao.*.*(..))" |
|
method="doAccessCheck"/> |
|
|
|
... |
|
|
|
</aop:aspect> |
|
---- |
|
|
|
As we noted in the discussion of the @AspectJ style, using named pointcuts can |
|
significantly improve the readability of your code. |
|
|
|
The method attribute identifies a method ( `doAccessCheck`) that provides the body of |
|
the advice. This method must be defined for the bean referenced by the aspect element |
|
containing the advice. Before a data access operation is executed (a method execution |
|
join point matched by the pointcut expression), the "doAccessCheck" method on the aspect |
|
bean will be invoked. |
|
|
|
|
|
[[aop-schema-advice-after-returning]] |
|
===== After returning advice |
|
After returning advice runs when a matched method execution completes normally. It is |
|
declared inside an `<aop:aspect>` in the same way as before advice. For example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<aop:aspect id="afterReturningExample" ref="aBean"> |
|
|
|
<aop:after-returning |
|
pointcut-ref="dataAccessOperation" |
|
method="doAccessCheck"/> |
|
|
|
... |
|
|
|
</aop:aspect> |
|
---- |
|
|
|
Just as in the @AspectJ style, it is possible to get hold of the return value within the |
|
advice body. Use the returning attribute to specify the name of the parameter to which |
|
the return value should be passed: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<aop:aspect id="afterReturningExample" ref="aBean"> |
|
|
|
<aop:after-returning |
|
pointcut-ref="dataAccessOperation" |
|
returning="retVal" |
|
method="doAccessCheck"/> |
|
|
|
... |
|
|
|
</aop:aspect> |
|
---- |
|
|
|
The doAccessCheck method must declare a parameter named `retVal`. The type of this |
|
parameter constrains matching in the same way as described for @AfterReturning. For |
|
example, the method signature may be declared as: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public void doAccessCheck(Object retVal) {... |
|
---- |
|
|
|
|
|
[[aop-schema-advice-after-throwing]] |
|
===== After throwing advice |
|
After throwing advice executes when a matched method execution exits by throwing an |
|
exception. It is declared inside an `<aop:aspect>` using the after-throwing element: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<aop:aspect id="afterThrowingExample" ref="aBean"> |
|
|
|
<aop:after-throwing |
|
pointcut-ref="dataAccessOperation" |
|
method="doRecoveryActions"/> |
|
|
|
... |
|
|
|
</aop:aspect> |
|
---- |
|
|
|
Just as in the @AspectJ style, it is possible to get hold of the thrown exception within |
|
the advice body. Use the throwing attribute to specify the name of the parameter to |
|
which the exception should be passed: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<aop:aspect id="afterThrowingExample" ref="aBean"> |
|
|
|
<aop:after-throwing |
|
pointcut-ref="dataAccessOperation" |
|
throwing="dataAccessEx" |
|
method="doRecoveryActions"/> |
|
|
|
... |
|
|
|
</aop:aspect> |
|
---- |
|
|
|
The doRecoveryActions method must declare a parameter named `dataAccessEx`. The type of |
|
this parameter constrains matching in the same way as described for @AfterThrowing. For |
|
example, the method signature may be declared as: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public void doRecoveryActions(DataAccessException dataAccessEx) {... |
|
---- |
|
|
|
|
|
[[aop-schema-advice-after-finally]] |
|
===== After (finally) advice |
|
After (finally) advice runs however a matched method execution exits. It is declared |
|
using the `after` element: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<aop:aspect id="afterFinallyExample" ref="aBean"> |
|
|
|
<aop:after |
|
pointcut-ref="dataAccessOperation" |
|
method="doReleaseLock"/> |
|
|
|
... |
|
|
|
</aop:aspect> |
|
---- |
|
|
|
|
|
[[aop-schema-advice-around]] |
|
===== Around advice |
|
The final kind of advice is around advice. Around advice runs "around" a matched method |
|
execution. It has the opportunity to do work both before and after the method executes, |
|
and to determine when, how, and even if, the method actually gets to execute at all. |
|
Around advice is often used if you need to share state before and after a method |
|
execution in a thread-safe manner (starting and stopping a timer for example). Always |
|
use the least powerful form of advice that meets your requirements; don't use around |
|
advice if simple before advice would do. |
|
|
|
Around advice is declared using the `aop:around` element. The first parameter of the |
|
advice method must be of type `ProceedingJoinPoint`. Within the body of the advice, |
|
calling `proceed()` on the `ProceedingJoinPoint` causes the underlying method to |
|
execute. The `proceed` method may also be calling passing in an `Object[]` - the values |
|
in the array will be used as the arguments to the method execution when it proceeds. See |
|
<<aop-ataspectj-around-advice>> for notes on calling proceed with an `Object[]`. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<aop:aspect id="aroundExample" ref="aBean"> |
|
|
|
<aop:around |
|
pointcut-ref="businessService" |
|
method="doBasicProfiling"/> |
|
|
|
... |
|
|
|
</aop:aspect> |
|
---- |
|
|
|
The implementation of the `doBasicProfiling` advice would be exactly the same as in the |
|
@AspectJ example (minus the annotation of course): |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { |
|
// start stopwatch |
|
Object retVal = pjp.proceed(); |
|
// stop stopwatch |
|
return retVal; |
|
} |
|
---- |
|
|
|
|
|
[[aop-schema-params]] |
|
===== Advice parameters |
|
The schema based declaration style supports fully typed advice in the same way as |
|
described for the @AspectJ support - by matching pointcut parameters by name against |
|
advice method parameters. See <<aop-ataspectj-advice-params>> for details. If you wish |
|
to explicitly specify argument names for the advice methods (not relying on the |
|
detection strategies previously described) then this is done using the `arg-names` |
|
attribute of the advice element, which is treated in the same manner to the "argNames" |
|
attribute in an advice annotation as described in <<aop-ataspectj-advice-params-names>>. |
|
For example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<aop:before |
|
pointcut="com.xyz.lib.Pointcuts.anyPublicMethod() and @annotation(auditable)" |
|
method="audit" |
|
arg-names="auditable"/> |
|
---- |
|
|
|
The `arg-names` attribute accepts a comma-delimited list of parameter names. |
|
|
|
Find below a slightly more involved example of the XSD-based approach that illustrates |
|
some around advice used in conjunction with a number of strongly typed parameters. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package x.y.service; |
|
|
|
public interface FooService { |
|
|
|
Foo getFoo(String fooName, int age); |
|
} |
|
|
|
public class DefaultFooService implements FooService { |
|
|
|
public Foo getFoo(String name, int age) { |
|
return new Foo(name, age); |
|
} |
|
} |
|
---- |
|
|
|
Next up is the aspect. Notice the fact that the `profile(..)` method accepts a number of |
|
strongly-typed parameters, the first of which happens to be the join point used to |
|
proceed with the method call: the presence of this parameter is an indication that the |
|
`profile(..)` is to be used as `around` advice: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package x.y; |
|
|
|
import org.aspectj.lang.ProceedingJoinPoint; |
|
import org.springframework.util.StopWatch; |
|
|
|
public class SimpleProfiler { |
|
|
|
public Object profile(ProceedingJoinPoint call, String name, int age) throws Throwable { |
|
StopWatch clock = new StopWatch("Profiling for ''" + name + "'' and ''" + age + "''"); |
|
try { |
|
clock.start(call.toShortString()); |
|
return call.proceed(); |
|
} finally { |
|
clock.stop(); |
|
System.out.println(clock.prettyPrint()); |
|
} |
|
} |
|
} |
|
---- |
|
|
|
Finally, here is the XML configuration that is required to effect the execution of the |
|
above advice for a particular join point: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:aop="http://www.springframework.org/schema/aop" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> |
|
|
|
<!-- this is the object that will be proxied by Spring's AOP infrastructure --> |
|
<bean id="fooService" class="x.y.service.DefaultFooService"/> |
|
|
|
<!-- this is the actual advice itself --> |
|
<bean id="profiler" class="x.y.SimpleProfiler"/> |
|
|
|
<aop:config> |
|
<aop:aspect ref="profiler"> |
|
|
|
<aop:pointcut id="theExecutionOfSomeFooServiceMethod" |
|
expression="execution(* x.y.service.FooService.getFoo(String,int)) |
|
and args(name, age)"/> |
|
|
|
<aop:around pointcut-ref="theExecutionOfSomeFooServiceMethod" |
|
method="profile"/> |
|
|
|
</aop:aspect> |
|
</aop:config> |
|
|
|
</beans> |
|
---- |
|
|
|
If we had the following driver script, we would get output something like this on |
|
standard output: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import org.springframework.beans.factory.BeanFactory; |
|
import org.springframework.context.support.ClassPathXmlApplicationContext; |
|
import x.y.service.FooService; |
|
|
|
public final class Boot { |
|
|
|
public static void main(final String[] args) throws Exception { |
|
BeanFactory ctx = new ClassPathXmlApplicationContext("x/y/plain.xml"); |
|
FooService foo = (FooService) ctx.getBean("fooService"); |
|
foo.getFoo("Pengo", 12); |
|
} |
|
} |
|
---- |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
StopWatch 'Profiling for 'Pengo' and '12'': running time (millis) = 0 |
|
----------------------------------------- |
|
ms % Task name |
|
----------------------------------------- |
|
00000 ? execution(getFoo) |
|
---- |
|
|
|
|
|
[[aop-ordering]] |
|
===== Advice ordering |
|
When multiple advice needs to execute at the same join point (executing method) the |
|
ordering rules are as described in <<aop-ataspectj-advice-ordering>>. The precedence |
|
between aspects is determined by either adding the `Order` annotation to the bean |
|
backing the aspect or by having the bean implement the `Ordered` interface. |
|
|
|
|
|
|
|
[[aop-schema-introductions]] |
|
==== Introductions |
|
Introductions (known as inter-type declarations in AspectJ) enable an aspect to declare |
|
that advised objects implement a given interface, and to provide an implementation of |
|
that interface on behalf of those objects. |
|
|
|
An introduction is made using the `aop:declare-parents` element inside an `aop:aspect` |
|
This element is used to declare that matching types have a new parent (hence the name). |
|
For example, given an interface `UsageTracked`, and an implementation of that interface |
|
`DefaultUsageTracked`, the following aspect declares that all implementors of service |
|
interfaces also implement the `UsageTracked` interface. (In order to expose statistics |
|
via JMX for example.) |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<aop:aspect id="usageTrackerAspect" ref="usageTracking"> |
|
|
|
<aop:declare-parents |
|
types-matching="com.xzy.myapp.service.*+" |
|
implement-interface="com.xyz.myapp.service.tracking.UsageTracked" |
|
default-impl="com.xyz.myapp.service.tracking.DefaultUsageTracked"/> |
|
|
|
<aop:before |
|
pointcut="com.xyz.myapp.SystemArchitecture.businessService() |
|
and this(usageTracked)" |
|
method="recordUsage"/> |
|
|
|
</aop:aspect> |
|
---- |
|
|
|
The class backing the `usageTracking` bean would contain the method: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public void recordUsage(UsageTracked usageTracked) { |
|
usageTracked.incrementUseCount(); |
|
} |
|
---- |
|
|
|
The interface to be implemented is determined by `implement-interface` attribute. The |
|
value of the `types-matching` attribute is an AspectJ type pattern :- any bean of a |
|
matching type will implement the `UsageTracked` interface. Note that in the before |
|
advice of the above example, service beans can be directly used as implementations of |
|
the `UsageTracked` interface. If accessing a bean programmatically you would write the |
|
following: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
UsageTracked usageTracked = (UsageTracked) context.getBean("myService"); |
|
---- |
|
|
|
|
|
|
|
[[aop-schema-instatiation-models]] |
|
==== Aspect instantiation models |
|
The only supported instantiation model for schema-defined aspects is the singleton |
|
model. Other instantiation models may be supported in future releases. |
|
|
|
|
|
|
|
[[aop-schema-advisors]] |
|
==== Advisors |
|
The concept of "advisors" is brought forward from the AOP support defined in Spring 1.2 |
|
and does not have a direct equivalent in AspectJ. An advisor is like a small |
|
self-contained aspect that has a single piece of advice. The advice itself is |
|
represented by a bean, and must implement one of the advice interfaces described in |
|
<<aop-api-advice-types>>. Advisors can take advantage of AspectJ pointcut expressions |
|
though. |
|
|
|
Spring supports the advisor concept with the `<aop:advisor>` element. You will most |
|
commonly see it used in conjunction with transactional advice, which also has its own |
|
namespace support in Spring. Here's how it looks: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<aop:config> |
|
|
|
<aop:pointcut id="businessService" |
|
expression="execution(* com.xyz.myapp.service.*.*(..))"/> |
|
|
|
<aop:advisor |
|
pointcut-ref="businessService" |
|
advice-ref="tx-advice"/> |
|
|
|
</aop:config> |
|
|
|
<tx:advice id="tx-advice"> |
|
<tx:attributes> |
|
<tx:method name="*" propagation="REQUIRED"/> |
|
</tx:attributes> |
|
</tx:advice> |
|
---- |
|
|
|
As well as the `pointcut-ref` attribute used in the above example, you can also use the |
|
`pointcut` attribute to define a pointcut expression inline. |
|
|
|
To define the precedence of an advisor so that the advice can participate in ordering, |
|
use the `order` attribute to define the `Ordered` value of the advisor. |
|
|
|
|
|
|
|
[[aop-schema-example]] |
|
==== Example |
|
Let's see how the concurrent locking failure retry example from |
|
<<aop-ataspectj-example>> looks when rewritten using the schema support. |
|
|
|
The execution of business services can sometimes fail due to concurrency issues (for |
|
example, deadlock loser). If the operation is retried, it is quite likely it will |
|
succeed next time round. For business services where it is appropriate to retry in such |
|
conditions (idempotent operations that don't need to go back to the user for conflict |
|
resolution), we'd like to transparently retry the operation to avoid the client seeing a |
|
`PessimisticLockingFailureException`. This is a requirement that clearly cuts across |
|
multiple services in the service layer, and hence is ideal for implementing via an |
|
aspect. |
|
|
|
Because we want to retry the operation, we'll need to use around advice so that we can |
|
call proceed multiple times. Here's how the basic aspect implementation looks (it's just |
|
a regular Java class using the schema support): |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class ConcurrentOperationExecutor implements Ordered { |
|
|
|
private static final int DEFAULT_MAX_RETRIES = 2; |
|
|
|
private int maxRetries = DEFAULT_MAX_RETRIES; |
|
private int order = 1; |
|
|
|
public void setMaxRetries(int maxRetries) { |
|
this.maxRetries = maxRetries; |
|
} |
|
|
|
public int getOrder() { |
|
return this.order; |
|
} |
|
|
|
public void setOrder(int order) { |
|
this.order = order; |
|
} |
|
|
|
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable { |
|
int numAttempts = 0; |
|
PessimisticLockingFailureException lockFailureException; |
|
do { |
|
numAttempts++; |
|
try { |
|
return pjp.proceed(); |
|
} |
|
catch(PessimisticLockingFailureException ex) { |
|
lockFailureException = ex; |
|
} |
|
} while(numAttempts <= this.maxRetries); |
|
throw lockFailureException; |
|
} |
|
|
|
} |
|
---- |
|
|
|
Note that the aspect implements the `Ordered` interface so we can set the precedence of |
|
the aspect higher than the transaction advice (we want a fresh transaction each time we |
|
retry). The `maxRetries` and `order` properties will both be configured by Spring. The |
|
main action happens in the `doConcurrentOperation` around advice method. We try to |
|
proceed, and if we fail with a `PessimisticLockingFailureException` we simply try again |
|
unless we have exhausted all of our retry attempts. |
|
|
|
[NOTE] |
|
==== |
|
This class is identical to the one used in the @AspectJ example, but with the |
|
annotations removed. |
|
==== |
|
|
|
The corresponding Spring configuration is: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<aop:config> |
|
|
|
<aop:aspect id="concurrentOperationRetry" ref="concurrentOperationExecutor"> |
|
|
|
<aop:pointcut id="idempotentOperation" |
|
expression="execution(* com.xyz.myapp.service.*.*(..))"/> |
|
|
|
<aop:around |
|
pointcut-ref="idempotentOperation" |
|
method="doConcurrentOperation"/> |
|
|
|
</aop:aspect> |
|
|
|
</aop:config> |
|
|
|
<bean id="concurrentOperationExecutor" |
|
class="com.xyz.myapp.service.impl.ConcurrentOperationExecutor"> |
|
<property name="maxRetries" value="3"/> |
|
<property name="order" value="100"/> |
|
</bean> |
|
---- |
|
|
|
Notice that for the time being we assume that all business services are idempotent. If |
|
this is not the case we can refine the aspect so that it only retries genuinely |
|
idempotent operations, by introducing an `Idempotent` annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Retention(RetentionPolicy.RUNTIME) |
|
public @interface Idempotent { |
|
// marker annotation |
|
} |
|
---- |
|
|
|
and using the annotation to annotate the implementation of service operations. The |
|
change to the aspect to retry only idempotent operations simply involves refining the |
|
pointcut expression so that only `@Idempotent` operations match: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<aop:pointcut id="idempotentOperation" |
|
expression="execution(* com.xyz.myapp.service.*.*(..)) and |
|
@annotation(com.xyz.myapp.service.Idempotent)"/> |
|
---- |
|
|
|
|
|
|
|
|
|
[[aop-choosing]] |
|
=== Choosing which AOP declaration style to use |
|
Once you have decided that an aspect is the best approach for implementing a given |
|
requirement, how do you decide between using Spring AOP or AspectJ, and between the |
|
Aspect language (code) style, @AspectJ annotation style, or the Spring XML style? These |
|
decisions are influenced by a number of factors including application requirements, |
|
development tools, and team familiarity with AOP. |
|
|
|
|
|
|
|
[[aop-spring-or-aspectj]] |
|
==== Spring AOP or full AspectJ? |
|
Use the simplest thing that can work. Spring AOP is simpler than using full AspectJ as |
|
there is no requirement to introduce the AspectJ compiler / weaver into your development |
|
and build processes. If you only need to advise the execution of operations on Spring |
|
beans, then Spring AOP is the right choice. If you need to advise objects not managed by |
|
the Spring container (such as domain objects typically), then you will need to use |
|
AspectJ. You will also need to use AspectJ if you wish to advise join points other than |
|
simple method executions (for example, field get or set join points, and so on). |
|
|
|
When using AspectJ, you have the choice of the AspectJ language syntax (also known as |
|
the "code style") or the @AspectJ annotation style. Clearly, if you are not using Java |
|
5+ then the choice has been made for you... use the code style. If aspects play a large |
|
role in your design, and you are able to use the http://www.eclipse.org/ajdt/[AspectJ |
|
Development Tools (AJDT)] plugin for Eclipse, then the AspectJ language syntax is the |
|
preferred option: it is cleaner and simpler because the language was purposefully |
|
designed for writing aspects. If you are not using Eclipse, or have only a few aspects |
|
that do not play a major role in your application, then you may want to consider using |
|
the @AspectJ style and sticking with a regular Java compilation in your IDE, and adding |
|
an aspect weaving phase to your build script. |
|
|
|
|
|
|
|
[[aop-ataspectj-or-xml]] |
|
==== @AspectJ or XML for Spring AOP? |
|
If you have chosen to use Spring AOP, then you have a choice of @AspectJ or XML style. |
|
There are various tradeoffs to consider. |
|
|
|
The XML style will be most familiar to existing Spring users and it is backed by genuine |
|
POJOs. When using AOP as a tool to configure enterprise services then XML can be a good |
|
choice (a good test is whether you consider the pointcut expression to be a part of your |
|
configuration you might want to change independently). With the XML style arguably it is |
|
clearer from your configuration what aspects are present in the system. |
|
|
|
The XML style has two disadvantages. Firstly it does not fully encapsulate the |
|
implementation of the requirement it addresses in a single place. The DRY principle says |
|
that there should be a single, unambiguous, authoritative representation of any piece of |
|
knowledge within a system. When using the XML style, the knowledge of __how__ a |
|
requirement is implemented is split across the declaration of the backing bean class, |
|
and the XML in the configuration file. When using the @AspectJ style there is a single |
|
module - the aspect - in which this information is encapsulated. Secondly, the XML style |
|
is slightly more limited in what it can express than the @AspectJ style: only the |
|
"singleton" aspect instantiation model is supported, and it is not possible to combine |
|
named pointcuts declared in XML. For example, in the @AspectJ style you can write |
|
something like: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
@Pointcut(execution(* get*())) |
|
public void propertyAccess() {} |
|
|
|
@Pointcut(execution(org.xyz.Account+ *(..)) |
|
public void operationReturningAnAccount() {} |
|
|
|
@Pointcut(propertyAccess() && operationReturningAnAccount()) |
|
public void accountPropertyAccess() {} |
|
---- |
|
|
|
In the XML style I can declare the first two pointcuts: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<aop:pointcut id="propertyAccess" |
|
expression="execution(* get*())"/> |
|
|
|
<aop:pointcut id="operationReturningAnAccount" |
|
expression="execution(org.xyz.Account+ *(..))"/> |
|
---- |
|
|
|
The downside of the XML approach is that you cannot define the |
|
'++accountPropertyAccess++' pointcut by combining these definitions. |
|
|
|
The @AspectJ style supports additional instantiation models, and richer pointcut |
|
composition. It has the advantage of keeping the aspect as a modular unit. It also has |
|
the advantage the @AspectJ aspects can be understood (and thus consumed) both by Spring |
|
AOP and by AspectJ - so if you later decide you need the capabilities of AspectJ to |
|
implement additional requirements then it is very easy to migrate to an AspectJ-based |
|
approach. On balance the Spring team prefer the @AspectJ style whenever you have aspects |
|
that do more than simple "configuration" of enterprise services. |
|
|
|
|
|
|
|
|
|
[[aop-mixing-styles]] |
|
=== Mixing aspect types |
|
It is perfectly possible to mix @AspectJ style aspects using the autoproxying support, |
|
schema-defined `<aop:aspect>` aspects, `<aop:advisor>` declared advisors and even |
|
proxies and interceptors defined using the Spring 1.2 style in the same configuration. |
|
All of these are implemented using the same underlying support mechanism and will |
|
co-exist without any difficulty. |
|
|
|
|
|
|
|
|
|
[[aop-proxying]] |
|
=== Proxying mechanisms |
|
Spring AOP uses either JDK dynamic proxies or CGLIB to create the proxy for a given |
|
target object. (JDK dynamic proxies are preferred whenever you have a choice). |
|
|
|
If the target object to be proxied implements at least one interface then a JDK dynamic |
|
proxy will be used. All of the interfaces implemented by the target type will be |
|
proxied. If the target object does not implement any interfaces then a CGLIB proxy will |
|
be created. |
|
|
|
If you want to force the use of CGLIB proxying (for example, to proxy every method |
|
defined for the target object, not just those implemented by its interfaces) you can do |
|
so. However, there are some issues to consider: |
|
|
|
* `final` methods cannot be advised, as they cannot be overridden. |
|
* As of Spring 3.2, it is no longer necessary to add CGLIB to your project classpath, as |
|
CGLIB classes are repackaged under org.springframework and included directly in the |
|
spring-core JAR. This means that CGLIB-based proxy support 'just works' in the same |
|
way that JDK dynamic proxies always have. |
|
* As of Spring 4.0, the constructor of your proxied object will NOT be called twice |
|
anymore since the CGLIB proxy instance will be created via Objenesis. Only if your |
|
JVM does not allow for constructor bypassing, you might see double invocations and |
|
corresponding debug log entries from Spring's AOP support. |
|
|
|
To force the use of CGLIB proxies set the value of the `proxy-target-class` attribute of |
|
the `<aop:config>` element to true: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<aop:config proxy-target-class="true"> |
|
<!-- other beans defined here... --> |
|
</aop:config> |
|
---- |
|
|
|
To force CGLIB proxying when using the @AspectJ autoproxy support, set the |
|
`'proxy-target-class'` attribute of the `<aop:aspectj-autoproxy>` element to `true`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<aop:aspectj-autoproxy proxy-target-class="true"/> |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
Multiple `<aop:config/>` sections are collapsed into a single unified auto-proxy creator |
|
at runtime, which applies the __strongest__ proxy settings that any of the |
|
`<aop:config/>` sections (typically from different XML bean definition files) specified. |
|
This also applies to the `<tx:annotation-driven/>` and `<aop:aspectj-autoproxy/>` |
|
elements. |
|
|
|
To be clear: using '++proxy-target-class="true"++' on `<tx:annotation-driven/>`, |
|
`<aop:aspectj-autoproxy/>` or `<aop:config/>` elements will force the use of CGLIB |
|
proxies __for all three of them__. |
|
==== |
|
|
|
|
|
|
|
[[aop-understanding-aop-proxies]] |
|
==== Understanding AOP proxies |
|
Spring AOP is __proxy-based__. It is vitally important that you grasp the semantics of |
|
what that last statement actually means before you write your own aspects or use any of |
|
the Spring AOP-based aspects supplied with the Spring Framework. |
|
|
|
Consider first the scenario where you have a plain-vanilla, un-proxied, |
|
nothing-special-about-it, straight object reference, as illustrated by the following |
|
code snippet. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class SimplePojo implements Pojo { |
|
|
|
public void foo() { |
|
// this next method invocation is a direct call on the 'this' reference |
|
this.bar(); |
|
} |
|
|
|
public void bar() { |
|
// some logic... |
|
} |
|
} |
|
---- |
|
|
|
If you invoke a method on an object reference, the method is invoked __directly__ on |
|
that object reference, as can be seen below. |
|
|
|
image::images/aop-proxy-plain-pojo-call.png[width=400] |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class Main { |
|
|
|
public static void main(String[] args) { |
|
|
|
Pojo pojo = new SimplePojo(); |
|
|
|
// this is a direct method call on the 'pojo' reference |
|
pojo.foo(); |
|
} |
|
} |
|
---- |
|
|
|
Things change slightly when the reference that client code has is a proxy. Consider the |
|
following diagram and code snippet. |
|
|
|
image::images/aop-proxy-call.png[width=400] |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class Main { |
|
|
|
public static void main(String[] args) { |
|
|
|
ProxyFactory factory = new ProxyFactory(new SimplePojo()); |
|
factory.addInterface(Pojo.class); |
|
factory.addAdvice(new RetryAdvice()); |
|
|
|
Pojo pojo = (Pojo) factory.getProxy(); |
|
|
|
// this is a method call on the proxy! |
|
pojo.foo(); |
|
} |
|
} |
|
---- |
|
|
|
The key thing to understand here is that the client code inside the `main(..)` of the |
|
`Main` class __has a reference to the proxy__. This means that method calls on that |
|
object reference will be calls on the proxy, and as such the proxy will be able to |
|
delegate to all of the interceptors (advice) that are relevant to that particular method |
|
call. However, once the call has finally reached the target object, the `SimplePojo` |
|
reference in this case, any method calls that it may make on itself, such as |
|
`this.bar()` or `this.foo()`, are going to be invoked against the __this__ reference, |
|
and __not__ the proxy. This has important implications. It means that self-invocation is |
|
__not__ going to result in the advice associated with a method invocation getting a |
|
chance to execute. |
|
|
|
Okay, so what is to be done about this? The best approach (the term best is used loosely |
|
here) is to refactor your code such that the self-invocation does not happen. For sure, |
|
this does entail some work on your part, but it is the best, least-invasive approach. |
|
The next approach is absolutely horrendous, and I am almost reticent to point it out |
|
precisely because it is so horrendous. You can (choke!) totally tie the logic within |
|
your class to Spring AOP by doing this: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class SimplePojo implements Pojo { |
|
|
|
public void foo() { |
|
// this works, but... gah! |
|
((Pojo) AopContext.currentProxy()).bar(); |
|
} |
|
|
|
public void bar() { |
|
// some logic... |
|
} |
|
} |
|
---- |
|
|
|
This totally couples your code to Spring AOP, __and__ it makes the class itself aware of |
|
the fact that it is being used in an AOP context, which flies in the face of AOP. It |
|
also requires some additional configuration when the proxy is being created: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class Main { |
|
|
|
public static void main(String[] args) { |
|
|
|
ProxyFactory factory = new ProxyFactory(new SimplePojo()); |
|
factory.adddInterface(Pojo.class); |
|
factory.addAdvice(new RetryAdvice()); |
|
factory.setExposeProxy(true); |
|
|
|
Pojo pojo = (Pojo) factory.getProxy(); |
|
|
|
// this is a method call on the proxy! |
|
pojo.foo(); |
|
} |
|
} |
|
---- |
|
|
|
Finally, it must be noted that AspectJ does not have this self-invocation issue because |
|
it is not a proxy-based AOP framework. |
|
|
|
|
|
|
|
|
|
[[aop-aspectj-programmatic]] |
|
=== Programmatic creation of @AspectJ Proxies |
|
In addition to declaring aspects in your configuration using either `<aop:config>` or |
|
`<aop:aspectj-autoproxy>`, it is also possible programmatically to create proxies that |
|
advise target objects. For the full details of Spring's AOP API, see the next chapter. |
|
Here we want to focus on the ability to automatically create proxies using @AspectJ |
|
aspects. |
|
|
|
The class `org.springframework.aop.aspectj.annotation.AspectJProxyFactory` can be used |
|
to create a proxy for a target object that is advised by one or more @AspectJ aspects. |
|
Basic usage for this class is very simple, as illustrated below. See the javadocs for |
|
full information. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// create a factory that can generate a proxy for the given target object |
|
AspectJProxyFactory factory = new AspectJProxyFactory(targetObject); |
|
|
|
// add an aspect, the class must be an @AspectJ aspect |
|
// you can call this as many times as you need with different aspects |
|
factory.addAspect(SecurityManager.class); |
|
|
|
// you can also add existing aspect instances, the type of the object supplied must be an @AspectJ aspect |
|
factory.addAspect(usageTracker); |
|
|
|
// now get the proxy object... |
|
MyInterfaceType proxy = factory.getProxy(); |
|
---- |
|
|
|
|
|
|
|
|
|
[[aop-using-aspectj]] |
|
=== Using AspectJ with Spring applications |
|
Everything we've covered so far in this chapter is pure Spring AOP. In this section, |
|
we're going to look at how you can use the AspectJ compiler/weaver instead of, or in |
|
addition to, Spring AOP if your needs go beyond the facilities offered by Spring AOP |
|
alone. |
|
|
|
Spring ships with a small AspectJ aspect library, which is available standalone in your |
|
distribution as `spring-aspects.jar`; you'll need to add this to your classpath in order |
|
to use the aspects in it. <<aop-atconfigurable>> and <<aop-ajlib-other>> discuss the |
|
content of this library and how you can use it. <<aop-aj-configure>> discusses how to |
|
dependency inject AspectJ aspects that are woven using the AspectJ compiler. Finally, |
|
<<aop-aj-ltw>> provides an introduction to load-time weaving for Spring applications |
|
using AspectJ. |
|
|
|
|
|
|
|
[[aop-atconfigurable]] |
|
==== Using AspectJ to dependency inject domain objects with Spring |
|
The Spring container instantiates and configures beans defined in your application |
|
context. It is also possible to ask a bean factory to configure a __pre-existing__ |
|
object given the name of a bean definition containing the configuration to be applied. |
|
The `spring-aspects.jar` contains an annotation-driven aspect that exploits this |
|
capability to allow dependency injection of __any object__. The support is intended to |
|
be used for objects created __outside of the control of any container__. Domain objects |
|
often fall into this category because they are often created programmatically using the |
|
`new` operator, or by an ORM tool as a result of a database query. |
|
|
|
The `@Configurable` annotation marks a class as eligible for Spring-driven |
|
configuration. In the simplest case it can be used just as a marker annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package com.xyz.myapp.domain; |
|
|
|
import org.springframework.beans.factory.annotation.Configurable; |
|
|
|
@Configurable |
|
public class Account { |
|
// ... |
|
} |
|
---- |
|
|
|
When used as a marker interface in this way, Spring will configure new instances of the |
|
annotated type ( `Account` in this case) using a bean definition (typically |
|
prototype-scoped) with the same name as the fully-qualified type name ( |
|
`com.xyz.myapp.domain.Account`). Since the default name for a bean is the |
|
fully-qualified name of its type, a convenient way to declare the prototype definition |
|
is simply to omit the `id` attribute: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="com.xyz.myapp.domain.Account" scope="prototype"> |
|
<property name="fundsTransferService" ref="fundsTransferService"/> |
|
</bean> |
|
---- |
|
|
|
If you want to explicitly specify the name of the prototype bean definition to use, you |
|
can do so directly in the annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package com.xyz.myapp.domain; |
|
|
|
import org.springframework.beans.factory.annotation.Configurable; |
|
|
|
@Configurable("account") |
|
public class Account { |
|
// ... |
|
} |
|
---- |
|
|
|
Spring will now look for a bean definition named " `account`" and use that as the |
|
definition to configure new `Account` instances. |
|
|
|
You can also use autowiring to avoid having to specify a dedicated bean definition at |
|
all. To have Spring apply autowiring use the '++autowire++' property of the |
|
`@Configurable` annotation: specify either `@Configurable(autowire=Autowire.BY_TYPE)` or |
|
`@Configurable(autowire=Autowire.BY_NAME` for autowiring by type or by name |
|
respectively. As an alternative, as of Spring 2.5 it is preferable to specify explicit, |
|
annotation-driven dependency injection for your `@Configurable` beans by using |
|
`@Autowired` or `@Inject` at the field or method level (see <<beans-annotation-config>> |
|
for further details). |
|
|
|
Finally you can enable Spring dependency checking for the object references in the newly |
|
created and configured object by using the `dependencyCheck` attribute (for example: |
|
`@Configurable(autowire=Autowire.BY_NAME,dependencyCheck=true)`). If this attribute is |
|
set to true, then Spring will validate after configuration that all properties (__which |
|
are not primitives or collections__) have been set. |
|
|
|
Using the annotation on its own does nothing of course. It is the |
|
`AnnotationBeanConfigurerAspect` in `spring-aspects.jar` that acts on the presence of |
|
the annotation. In essence the aspect says "after returning from the initialization of a |
|
new object of a type annotated with `@Configurable`, configure the newly created object |
|
using Spring in accordance with the properties of the annotation". In this context, |
|
__initialization__ refers to newly instantiated objects (e.g., objects instantiated with |
|
the '++new++' operator) as well as to `Serializable` objects that are undergoing |
|
deserialization (e.g., via |
|
http://docs.oracle.com/javase/6/docs/api/java/io/Serializable.html[readResolve()]). |
|
|
|
[NOTE] |
|
==== |
|
One of the key phrases in the above paragraph is '__in essence__'. For most cases, the |
|
exact semantics of '__after returning from the initialization of a new object__' will be |
|
fine... in this context, '__after initialization__' means that the dependencies will be |
|
injected __after__ the object has been constructed - this means that the dependencies |
|
will not be available for use in the constructor bodies of the class. If you want the |
|
dependencies to be injected __before__ the constructor bodies execute, and thus be |
|
available for use in the body of the constructors, then you need to define this on the |
|
`@Configurable` declaration like so: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configurable(preConstruction=true) |
|
---- |
|
|
|
You can find out more information about the language semantics of the various pointcut |
|
types in AspectJ |
|
http://www.eclipse.org/aspectj/doc/next/progguide/semantics-joinPoints.html[in this |
|
appendix] of the http://www.eclipse.org/aspectj/doc/next/progguide/index.html[AspectJ |
|
Programming Guide]. |
|
==== |
|
|
|
For this to work the annotated types must be woven with the AspectJ weaver - you can |
|
either use a build-time Ant or Maven task to do this (see for example the |
|
http://www.eclipse.org/aspectj/doc/released/devguide/antTasks.html[AspectJ Development |
|
Environment Guide]) or load-time weaving (see <<aop-aj-ltw>>). The |
|
`AnnotationBeanConfigurerAspect` itself needs configuring by Spring (in order to obtain |
|
a reference to the bean factory that is to be used to configure new objects). If you are |
|
using Java based configuration simply add `@EnableSpringConfigured` to any |
|
`@Configuration` class. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableSpringConfigured |
|
public class AppConfig { |
|
|
|
} |
|
---- |
|
|
|
If you prefer XML based configuration, the Spring <<xsd-config-body-schemas-context, |
|
`context` namespace>> defines a convenient `context:spring-configured` element: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<context:spring-configured/> |
|
---- |
|
|
|
Instances of `@Configurable` objects created __before__ the aspect has been configured |
|
will result in a message being issued to the debug log and no configuration of the |
|
object taking place. An example might be a bean in the Spring configuration that creates |
|
domain objects when it is initialized by Spring. In this case you can use the |
|
"depends-on" bean attribute to manually specify that the bean depends on the |
|
configuration aspect. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="myService" |
|
class="com.xzy.myapp.service.MyService" |
|
depends-on="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect"> |
|
|
|
<!-- ... --> |
|
|
|
</bean> |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
Do not activate `@Configurable` processing through the bean configurer aspect unless you |
|
really mean to rely on its semantics at runtime. In particular, make sure that you do |
|
not use `@Configurable` on bean classes which are registered as regular Spring beans |
|
with the container: You would get double initialization otherwise, once through the |
|
container and once through the aspect. |
|
==== |
|
|
|
|
|
[[aop-configurable-testing]] |
|
===== Unit testing @Configurable objects |
|
|
|
One of the goals of the `@Configurable` support is to enable independent unit testing of |
|
domain objects without the difficulties associated with hard-coded lookups. If |
|
`@Configurable` types have not been woven by AspectJ then the annotation has no affect |
|
during unit testing, and you can simply set mock or stub property references in the |
|
object under test and proceed as normal. If `@Configurable` types __have__ been woven by |
|
AspectJ then you can still unit test outside of the container as normal, but you will |
|
see a warning message each time that you construct an `@Configurable` object indicating |
|
that it has not been configured by Spring. |
|
|
|
|
|
[[aop-configurable-container]] |
|
===== Working with multiple application contexts |
|
The `AnnotationBeanConfigurerAspect` used to implement the `@Configurable` support is an |
|
AspectJ singleton aspect. The scope of a singleton aspect is the same as the scope of |
|
`static` members, that is to say there is one aspect instance per classloader that |
|
defines the type. This means that if you define multiple application contexts within the |
|
same classloader hierarchy you need to consider where to define the |
|
`@EnableSpringConfigured` bean and where to place `spring-aspects.jar` on the classpath. |
|
|
|
Consider a typical Spring web-app configuration with a shared parent application context |
|
defining common business services and everything needed to support them, and one child |
|
application context per servlet containing definitions particular to that servlet. All |
|
of these contexts will co-exist within the same classloader hierarchy, and so the |
|
`AnnotationBeanConfigurerAspect` can only hold a reference to one of them. In this case |
|
we recommend defining the `@EnableSpringConfigured` bean in the shared (parent) |
|
application context: this defines the services that you are likely to want to inject |
|
into domain objects. A consequence is that you cannot configure domain objects with |
|
references to beans defined in the child (servlet-specific) contexts using the |
|
@Configurable mechanism (probably not something you want to do anyway!). |
|
|
|
When deploying multiple web-apps within the same container, ensure that each |
|
web-application loads the types in `spring-aspects.jar` using its own classloader (for |
|
example, by placing `spring-aspects.jar` in `'WEB-INF/lib'`). If `spring-aspects.jar` is |
|
only added to the container wide classpath (and hence loaded by the shared parent |
|
classloader), all web applications will share the same aspect instance which is probably |
|
not what you want. |
|
|
|
|
|
|
|
[[aop-ajlib-other]] |
|
==== Other Spring aspects for AspectJ |
|
In addition to the `@Configurable` aspect, `spring-aspects.jar` contains an AspectJ |
|
aspect that can be used to drive Spring's transaction management for types and methods |
|
annotated with the `@Transactional` annotation. This is primarily intended for users who |
|
want to use the Spring Framework's transaction support outside of the Spring container. |
|
|
|
The aspect that interprets `@Transactional` annotations is the |
|
`AnnotationTransactionAspect`. When using this aspect, you must annotate the |
|
__implementation__ class (and/or methods within that class), __not__ the interface (if |
|
any) that the class implements. AspectJ follows Java's rule that annotations on |
|
interfaces are __not inherited__. |
|
|
|
A `@Transactional` annotation on a class specifies the default transaction semantics for |
|
the execution of any __public__ operation in the class. |
|
|
|
A `@Transactional` annotation on a method within the class overrides the default |
|
transaction semantics given by the class annotation (if present). Methods with `public`, |
|
`protected`, and default visibility may all be annotated. Annotating `protected` and |
|
default visibility methods directly is the only way to get transaction demarcation for |
|
the execution of such methods. |
|
|
|
For AspectJ programmers that want to use the Spring configuration and transaction |
|
management support but don't want to (or cannot) use annotations, `spring-aspects.jar` |
|
also contains `abstract` aspects you can extend to provide your own pointcut |
|
definitions. See the sources for the `AbstractBeanConfigurerAspect` and |
|
`AbstractTransactionAspect` aspects for more information. As an example, the following |
|
excerpt shows how you could write an aspect to configure all instances of objects |
|
defined in the domain model using prototype bean definitions that match the |
|
fully-qualified class names: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public aspect DomainObjectConfiguration extends AbstractBeanConfigurerAspect { |
|
|
|
public DomainObjectConfiguration() { |
|
setBeanWiringInfoResolver(new ClassNameBeanWiringInfoResolver()); |
|
} |
|
|
|
// the creation of a new bean (any object in the domain model) |
|
protected pointcut beanCreation(Object beanInstance) : |
|
initialization(new(..)) && |
|
SystemArchitecture.inDomainModel() && |
|
this(beanInstance); |
|
|
|
} |
|
---- |
|
|
|
|
|
|
|
[[aop-aj-configure]] |
|
==== Configuring AspectJ aspects using Spring IoC |
|
When using AspectJ aspects with Spring applications, it is natural to both want and |
|
expect to be able to configure such aspects using Spring. The AspectJ runtime itself is |
|
responsible for aspect creation, and the means of configuring the AspectJ created |
|
aspects via Spring depends on the AspectJ instantiation model (the '++per-xxx++' clause) |
|
used by the aspect. |
|
|
|
The majority of AspectJ aspects are __singleton__ aspects. Configuration of these |
|
aspects is very easy: simply create a bean definition referencing the aspect type as |
|
normal, and include the bean attribute `'factory-method="aspectOf"'`. This ensures that |
|
Spring obtains the aspect instance by asking AspectJ for it rather than trying to create |
|
an instance itself. For example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="profiler" class="com.xyz.profiler.Profiler" |
|
**factory-method="aspectOf"**> |
|
|
|
<property name="profilingStrategy" ref="jamonProfilingStrategy"/> |
|
</bean> |
|
---- |
|
|
|
Non-singleton aspects are harder to configure: however it is possible to do so by |
|
creating prototype bean definitions and using the `@Configurable` support from |
|
`spring-aspects.jar` to configure the aspect instances once they have bean created by |
|
the AspectJ runtime. |
|
|
|
If you have some @AspectJ aspects that you want to weave with AspectJ (for example, |
|
using load-time weaving for domain model types) and other @AspectJ aspects that you want |
|
to use with Spring AOP, and these aspects are all configured using Spring, then you will |
|
need to tell the Spring AOP @AspectJ autoproxying support which exact subset of the |
|
@AspectJ aspects defined in the configuration should be used for autoproxying. You can |
|
do this by using one or more `<include/>` elements inside the `<aop:aspectj-autoproxy/>` |
|
declaration. Each `<include/>` element specifies a name pattern, and only beans with |
|
names matched by at least one of the patterns will be used for Spring AOP autoproxy |
|
configuration: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<aop:aspectj-autoproxy> |
|
<aop:include name="thisBean"/> |
|
<aop:include name="thatBean"/> |
|
</aop:aspectj-autoproxy> |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
Do not be misled by the name of the `<aop:aspectj-autoproxy/>` element: using it will |
|
result in the creation of __Spring AOP proxies__. The @AspectJ style of aspect |
|
declaration is just being used here, but the AspectJ runtime is __not__ involved. |
|
==== |
|
|
|
|
|
|
|
[[aop-aj-ltw]] |
|
==== Load-time weaving with AspectJ in the Spring Framework |
|
Load-time weaving (LTW) refers to the process of weaving AspectJ aspects into an |
|
application's class files as they are being loaded into the Java virtual machine (JVM). |
|
The focus of this section is on configuring and using LTW in the specific context of the |
|
Spring Framework: this section is not an introduction to LTW though. For full details on |
|
the specifics of LTW and configuring LTW with just AspectJ (with Spring not being |
|
involved at all), see the |
|
http://www.eclipse.org/aspectj/doc/released/devguide/ltw.html[LTW section of the AspectJ |
|
Development Environment Guide]. |
|
|
|
The value-add that the Spring Framework brings to AspectJ LTW is in enabling much |
|
finer-grained control over the weaving process. 'Vanilla' AspectJ LTW is effected using |
|
a Java (5+) agent, which is switched on by specifying a VM argument when starting up a |
|
JVM. It is thus a JVM-wide setting, which may be fine in some situations, but often is a |
|
little too coarse. Spring-enabled LTW enables you to switch on LTW on a |
|
__per-ClassLoader__ basis, which obviously is more fine-grained and which can make more |
|
sense in a 'single-JVM-multiple-application' environment (such as is found in a typical |
|
application server environment). |
|
|
|
Further, <<aop-aj-ltw-environments,in certain environments>>, this support enables |
|
load-time weaving __without making any modifications to the application server's launch |
|
script__ that will be needed to add `-javaagent:path/to/aspectjweaver.jar` or (as we |
|
describe later in this section) |
|
`-javaagent:path/to/org.springframework.instrument-{version}.jar` (previously named |
|
`spring-agent.jar`). Developers simply modify one or more files that form the |
|
application context to enable load-time weaving instead of relying on administrators who |
|
typically are in charge of the deployment configuration such as the launch script. |
|
|
|
Now that the sales pitch is over, let us first walk through a quick example of AspectJ |
|
LTW using Spring, followed by detailed specifics about elements introduced in the |
|
following example. For a complete example, please see the |
|
https://github.com/spring-projects/spring-petclinic[Petclinic sample application]. |
|
|
|
|
|
[[aop-aj-ltw-first-example]] |
|
===== A first example |
|
Let us assume that you are an application developer who has been tasked with diagnosing |
|
the cause of some performance problems in a system. Rather than break out a profiling |
|
tool, what we are going to do is switch on a simple profiling aspect that will enable us |
|
to very quickly get some performance metrics, so that we can then apply a finer-grained |
|
profiling tool to that specific area immediately afterwards. |
|
|
|
[NOTE] |
|
==== |
|
The example presented here uses XML style configuration, it is also possible to |
|
configure and use @AspectJ with <<beans-java,Java Configuration>>. Specifically the |
|
`@EnableLoadTimeWeaving` annotation can be used as an alternative to |
|
`<context:load-time-weaver/>` (see <<aop-aj-ltw-spring,below>> for details). |
|
==== |
|
|
|
Here is the profiling aspect. Nothing too fancy, just a quick-and-dirty time-based |
|
profiler, using the @AspectJ-style of aspect declaration. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
package foo; |
|
|
|
import org.aspectj.lang.ProceedingJoinPoint; |
|
import org.aspectj.lang.annotation.Aspect; |
|
import org.aspectj.lang.annotation.Around; |
|
import org.aspectj.lang.annotation.Pointcut; |
|
import org.springframework.util.StopWatch; |
|
import org.springframework.core.annotation.Order; |
|
|
|
@Aspect |
|
public class ProfilingAspect { |
|
|
|
@Around("methodsToBeProfiled()") |
|
public Object profile(ProceedingJoinPoint pjp) throws Throwable { |
|
StopWatch sw = new StopWatch(getClass().getSimpleName()); |
|
try { |
|
sw.start(pjp.getSignature().getName()); |
|
return pjp.proceed(); |
|
} finally { |
|
sw.stop(); |
|
System.out.println(sw.prettyPrint()); |
|
} |
|
} |
|
|
|
@Pointcut("execution(public * foo..*.*(..))") |
|
public void methodsToBeProfiled(){} |
|
} |
|
---- |
|
|
|
We will also need to create an '++META-INF/aop.xml++' file, to inform the AspectJ weaver |
|
that we want to weave our `ProfilingAspect` into our classes. This file convention, |
|
namely the presence of a file (or files) on the Java classpath called |
|
'++META-INF/aop.xml++' is standard AspectJ. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd"> |
|
<aspectj> |
|
|
|
<weaver> |
|
<!-- only weave classes in our application-specific packages --> |
|
<include within="foo.*"/> |
|
</weaver> |
|
|
|
<aspects> |
|
<!-- weave in just this aspect --> |
|
<aspect name="foo.ProfilingAspect"/> |
|
</aspects> |
|
|
|
</aspectj> |
|
---- |
|
|
|
Now to the Spring-specific portion of the configuration. We need to configure a |
|
`LoadTimeWeaver` (all explained later, just take it on trust for now). This load-time |
|
weaver is the essential component responsible for weaving the aspect configuration in |
|
one or more '++META-INF/aop.xml++' files into the classes in your application. The good |
|
thing is that it does not require a lot of configuration, as can be seen below (there |
|
are some more options that you can specify, but these are detailed later). |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:context="http://www.springframework.org/schema/context" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/context |
|
http://www.springframework.org/schema/context/spring-context.xsd"> |
|
|
|
<!-- a service object; we will be profiling its methods --> |
|
<bean id="entitlementCalculationService" |
|
class="foo.StubEntitlementCalculationService"/> |
|
|
|
<!-- this switches on the load-time weaving --> |
|
**<context:load-time-weaver/>** |
|
</beans> |
|
---- |
|
|
|
Now that all the required artifacts are in place - the aspect, the '++META-INF/aop.xml++' |
|
file, and the Spring configuration -, let us create a simple driver class with a |
|
`main(..)` method to demonstrate the LTW in action. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package foo; |
|
|
|
import org.springframework.context.support.ClassPathXmlApplicationContext; |
|
|
|
public final class Main { |
|
|
|
public static void main(String[] args) { |
|
|
|
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml", Main.class); |
|
|
|
EntitlementCalculationService entitlementCalculationService |
|
= (EntitlementCalculationService) ctx.getBean("entitlementCalculationService"); |
|
|
|
// the profiling aspect is 'woven' around this method execution |
|
entitlementCalculationService.calculateEntitlement(); |
|
} |
|
} |
|
---- |
|
|
|
There is one last thing to do. The introduction to this section did say that one could |
|
switch on LTW selectively on a per- `ClassLoader` basis with Spring, and this is true. |
|
However, just for this example, we are going to use a Java agent (supplied with Spring) |
|
to switch on the LTW. This is the command line we will use to run the above `Main` class: |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
java -javaagent:C:/projects/foo/lib/global/spring-instrument.jar foo.Main |
|
---- |
|
|
|
The '++-javaagent++' is a flag for specifying and enabling |
|
http://docs.oracle.com/javase/6/docs/api/java/lang/instrument/package-summary.html[agents |
|
to instrument programs running on the JVM]. The Spring Framework ships with such an |
|
agent, the `InstrumentationSavingAgent`, which is packaged in the |
|
`spring-instrument.jar` that was supplied as the value of the `-javaagent` argument in |
|
the above example. |
|
|
|
The output from the execution of the `Main` program will look something like that below. |
|
(I have introduced a `Thread.sleep(..)` statement into the `calculateEntitlement()` |
|
implementation so that the profiler actually captures something other than 0 |
|
milliseconds - the `01234` milliseconds is __not__ an overhead introduced by the AOP :) ) |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Calculating entitlement |
|
|
|
StopWatch 'ProfilingAspect': running time (millis) = 1234 |
|
------ ----- ---------------------------- |
|
ms % Task name |
|
------ ----- ---------------------------- |
|
01234 100% calculateEntitlement |
|
---- |
|
|
|
Since this LTW is effected using full-blown AspectJ, we are not just limited to advising |
|
Spring beans; the following slight variation on the `Main` program will yield the same |
|
result. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package foo; |
|
|
|
import org.springframework.context.support.ClassPathXmlApplicationContext; |
|
|
|
public final class Main { |
|
|
|
public static void main(String[] args) { |
|
|
|
new ClassPathXmlApplicationContext("beans.xml", Main.class); |
|
|
|
EntitlementCalculationService entitlementCalculationService = |
|
new StubEntitlementCalculationService(); |
|
|
|
// the profiling aspect will be 'woven' around this method execution |
|
entitlementCalculationService.calculateEntitlement(); |
|
} |
|
} |
|
---- |
|
|
|
Notice how in the above program we are simply bootstrapping the Spring container, and |
|
then creating a new instance of the `StubEntitlementCalculationService` totally outside |
|
the context of Spring... the profiling advice still gets woven in. |
|
|
|
The example admittedly is simplistic... however the basics of the LTW support in Spring |
|
have all been introduced in the above example, and the rest of this section will explain |
|
the 'why' behind each bit of configuration and usage in detail. |
|
|
|
[NOTE] |
|
==== |
|
The `ProfilingAspect` used in this example may be basic, but it is quite useful. It is a |
|
nice example of a development-time aspect that developers can use during development (of |
|
course), and then quite easily exclude from builds of the application being deployed |
|
into UAT or production. |
|
==== |
|
|
|
|
|
[[aop-aj-ltw-the-aspects]] |
|
===== Aspects |
|
The aspects that you use in LTW have to be AspectJ aspects. They can be written in |
|
either the AspectJ language itself or you can write your aspects in the @AspectJ-style. |
|
It means that your aspects are then both valid AspectJ __and__ Spring AOP aspects. |
|
Furthermore, the compiled aspect classes need to be available on the classpath. |
|
|
|
|
|
[[aop-aj-ltw-aop_dot_xml]] |
|
===== 'META-INF/aop.xml' |
|
|
|
The AspectJ LTW infrastructure is configured using one or more '++META-INF/aop.xml++' |
|
files, that are on the Java classpath (either directly, or more typically in jar files). |
|
|
|
The structure and contents of this file is detailed in the main AspectJ reference |
|
documentation, and the interested reader is |
|
http://www.eclipse.org/aspectj/doc/released/devguide/ltw-configuration.html[referred to |
|
that resource]. (I appreciate that this section is brief, but the '++aop.xml++' file is |
|
100% AspectJ - there is no Spring-specific information or semantics that apply to it, |
|
and so there is no extra value that I can contribute either as a result), so rather than |
|
rehash the quite satisfactory section that the AspectJ developers wrote, I am just |
|
directing you there.) |
|
|
|
|
|
[[aop-aj-ltw-libraries]] |
|
===== Required libraries (JARS) |
|
At a minimum you will need the following libraries to use the Spring Framework's support |
|
for AspectJ LTW: |
|
|
|
* `spring-aop.jar` (version 2.5 or later, plus all mandatory dependencies) |
|
* `aspectjweaver.jar` (version 1.6.8 or later) |
|
|
|
If you are using the <<aop-aj-ltw-environment-generic,Spring-provided agent to enable |
|
instrumentation>>, you will also need: |
|
|
|
* `spring-instrument.jar` |
|
|
|
|
|
[[aop-aj-ltw-spring]] |
|
===== Spring configuration |
|
The key component in Spring's LTW support is the `LoadTimeWeaver` interface (in the |
|
`org.springframework.instrument.classloading` package), and the numerous implementations |
|
of it that ship with the Spring distribution. A `LoadTimeWeaver` is responsible for |
|
adding one or more `java.lang.instrument.ClassFileTransformers` to a `ClassLoader` at |
|
runtime, which opens the door to all manner of interesting applications, one of which |
|
happens to be the LTW of aspects. |
|
|
|
[TIP] |
|
==== |
|
|
|
If you are unfamiliar with the idea of runtime class file transformation, you are |
|
encouraged to read the javadoc API documentation for the `java.lang.instrument` package |
|
before continuing. This is not a huge chore because there is - rather annoyingly - |
|
precious little documentation there... the key interfaces and classes will at least be |
|
laid out in front of you for reference as you read through this section. |
|
==== |
|
|
|
Configuring a `LoadTimeWeaver` for a particular `ApplicationContext` can be as easy as |
|
adding one line. (Please note that you almost certainly will need to be using an |
|
`ApplicationContext` as your Spring container - typically a `BeanFactory` will not be |
|
enough because the LTW support makes use of `BeanFactoryPostProcessors`.) |
|
|
|
To enable the Spring Framework's LTW support, you need to configure a `LoadTimeWeaver`, |
|
which typically is done using the `@EnableLoadTimeWeaving` annotation. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableLoadTimeWeaving |
|
public class AppConfig { |
|
|
|
} |
|
---- |
|
|
|
Alternatively, if you prefer XML based configuration, use the |
|
`<context:load-time-weaver/>` element. Note that the element is defined in the |
|
'++context++' namespace. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:context="http://www.springframework.org/schema/context" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/context |
|
http://www.springframework.org/schema/context/spring-context.xsd"> |
|
|
|
<context:load-time-weaver/> |
|
|
|
</beans> |
|
---- |
|
|
|
The above configuration will define and register a number of LTW-specific infrastructure |
|
beans for you automatically, such as a `LoadTimeWeaver` and an `AspectJWeavingEnabler`. |
|
The default `LoadTimeWeaver` is the `DefaultContextLoadTimeWeaver` class, which attempts |
|
to decorate an automatically detected `LoadTimeWeaver`: the exact type of |
|
`LoadTimeWeaver` that will be 'automatically detected' is dependent upon your runtime |
|
environment (summarized in the following table). |
|
|
|
[[aop-aj-ltw-spring-env-impls]] |
|
.DefaultContextLoadTimeWeaver LoadTimeWeavers |
|
|=== |
|
| Runtime Environment| `LoadTimeWeaver` implementation |
|
|
|
| Running in |
|
http://www.bea.com/framework.jsp?CNT=index.htm&FP=/content/products/weblogic/server[BEA's |
|
Weblogic 10] |
|
| `WebLogicLoadTimeWeaver` |
|
|
|
| Running in http://www-01.ibm.com/software/webservers/appserv/was/[IBM WebSphere |
|
Application Server 7] |
|
| `WebSphereLoadTimeWeaver` |
|
|
|
| Running in http://glassfish.dev.java.net/[GlassFish] |
|
| `GlassFishLoadTimeWeaver` |
|
|
|
| Running in http://www.jboss.org/jbossas/[JBoss AS] |
|
| `JBossLoadTimeWeaver` |
|
|
|
| JVM started with Spring `InstrumentationSavingAgent` __(java |
|
-javaagent:path/to/spring-instrument.jar)__ |
|
| `InstrumentationLoadTimeWeaver` |
|
|
|
| Fallback, expecting the underlying ClassLoader to follow common conventions (e.g. |
|
applicable to `TomcatInstrumentableClassLoader` and http://www.caucho.com/[Resin]) |
|
| `ReflectiveLoadTimeWeaver` |
|
|=== |
|
|
|
Note that these are just the `LoadTimeWeavers` that are autodetected when using the |
|
`DefaultContextLoadTimeWeaver`: it is of course possible to specify exactly which |
|
`LoadTimeWeaver` implementation that you wish to use. |
|
|
|
To specify a specific `LoadTimeWeaver` with Java configuration implement the |
|
`LoadTimeWeavingConfigurer` interface and override the `getLoadTimeWeaver()` method: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableLoadTimeWeaving |
|
public class AppConfig implements LoadTimeWeavingConfigurer { |
|
|
|
@Override |
|
public LoadTimeWeaver getLoadTimeWeaver() { |
|
return new ReflectiveLoadTimeWeaver(); |
|
} |
|
} |
|
---- |
|
|
|
If you are using XML based configuration you can specify the fully-qualified classname |
|
as the value of the '++weaver-class++' attribute on the `<context:load-time-weaver/>` |
|
element: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:context="http://www.springframework.org/schema/context" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/context |
|
http://www.springframework.org/schema/context/spring-context.xsd"> |
|
|
|
<context:load-time-weaver |
|
weaver-class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/> |
|
|
|
</beans> |
|
---- |
|
|
|
The `LoadTimeWeaver` that is defined and registered by the configuration can be later |
|
retrieved from the Spring container using the well-known name '++loadTimeWeaver++'. |
|
Remember that the `LoadTimeWeaver` exists just as a mechanism for Spring's LTW |
|
infrastructure to add one or more `ClassFileTransformers`. The actual |
|
`ClassFileTransformer` that does the LTW is the `ClassPreProcessorAgentAdapter` (from |
|
the `org.aspectj.weaver.loadtime` package) class. See the class-level javadocs of the |
|
`ClassPreProcessorAgentAdapter` class for further details, because the specifics of how |
|
the weaving is actually effected is beyond the scope of this section. |
|
|
|
There is one final attribute of the configuration left to discuss: the |
|
'++aspectjWeaving++' attribute (or '++aspectj-weaving++' if you are using XML). This is a |
|
simple attribute that controls whether LTW is enabled or not; it is as simple as that. |
|
It accepts one of three possible values, summarized below, with the default value being |
|
'++autodetect++' if the attribute is not present. |
|
|
|
[[aop-aj-ltw-ltw-tag-attrs]] |
|
.AspectJ weaving attribute values |
|
|=== |
|
| Annotation Value| XML Value| Explanation |
|
|
|
| `ENABLED` |
|
| `on` |
|
| AspectJ weaving is on, and aspects will be woven at load-time as appropriate. |
|
|
|
| `DISABLED` |
|
| `off` |
|
| LTW is off... no aspect will be woven at load-time. |
|
|
|
| `AUTODETECT` |
|
| `autodetect` |
|
| If the Spring LTW infrastructure can find at least one '++META-INF/aop.xml++' file, |
|
then AspectJ weaving is on, else it is off. This is the default value. |
|
|=== |
|
|
|
|
|
[[aop-aj-ltw-environments]] |
|
===== Environment-specific configuration |
|
This last section contains any additional settings and configuration that you will need |
|
when using Spring's LTW support in environments such as application servers and web |
|
containers. |
|
|
|
[[aop-aj-ltw-environment-tomcat]] |
|
====== Tomcat |
|
http://tomcat.apache.org/[Apache Tomcat]'s default class loader does not support class |
|
transformation which is why Spring provides an enhanced implementation that addresses |
|
this need. Named `TomcatInstrumentableClassLoader`, the loader works on Tomcat 5.0 and |
|
above and can be registered individually for __each__ web application as follows: |
|
|
|
* Tomcat 6.0.x or higher |
|
* Copy `org.springframework.instrument.tomcat.jar` into __$CATALINA_HOME__/lib, where |
|
__$CATALINA_HOME__ represents the root of the Tomcat installation) |
|
* Instruct Tomcat to use the custom class loader (instead of the default) by editing the |
|
web application context file: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<Context path="/myWebApp" docBase="/my/webApp/location"> |
|
<Loader |
|
loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/> |
|
</Context> |
|
---- |
|
|
|
Apache Tomcat (6.0+) supports several context locations: |
|
|
|
* server configuration file - __$CATALINA_HOME/conf/server.xml__ |
|
* default context configuration - __$CATALINA_HOME/conf/context.xml__ - that affects all |
|
deployed web applications |
|
* per-web application configuration which can be deployed either on the server-side at |
|
__$CATALINA_HOME/conf/[enginename]/[hostname]/[webapp]-context.xml__ or embedded |
|
inside the web-app archive at __META-INF/context.xml__ |
|
|
|
For efficiency, the embedded per-web-app configuration style is recommended because it |
|
will impact only applications that use the custom class loader and does not require any |
|
changes to the server configuration. See the Tomcat 6.0.x |
|
http://tomcat.apache.org/tomcat-6.0-doc/config/context.html[documentation] for more |
|
details about available context locations. |
|
|
|
Alternatively, consider the use of the Spring-provided generic VM agent, to be specified |
|
in Tomcat's launch script (see above). This will make instrumentation available to all |
|
deployed web applications, no matter what ClassLoader they happen to run on. |
|
|
|
[[aop-aj-ltw-environments-weblogic-oc4j-resin-glassfish-jboss]] |
|
====== WebLogic, WebSphere, Resin, GlassFish, JBoss |
|
Recent versions of WebLogic Server (version 10 and above), IBM WebSphere Application |
|
Server (version 7 and above), Resin (3.1 and above) and JBoss (6.x or above) provide a |
|
ClassLoader that is capable of local instrumentation. Spring's native LTW leverages such |
|
ClassLoaders to enable AspectJ weaving. You can enable LTW by simply activating |
|
load-time weaving as described earlier. Specifically, you do __not__ need to modify the |
|
launch script to add `-javaagent:path/to/spring-instrument.jar`. |
|
|
|
Note that GlassFish instrumentation-capable ClassLoader is available only in its EAR |
|
environment. For GlassFish web applications, follow the Tomcat setup instructions as |
|
outlined above. |
|
|
|
Note that on JBoss 6.x, the app server scanning needs to be disabled to prevent it from |
|
loading the classes before the application actually starts. A quick workaround is to add |
|
to your artifact a file named `WEB-INF/jboss-scanning.xml` with the following content: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<scanning xmlns="urn:jboss:scanning:1.0"/> |
|
---- |
|
|
|
[[aop-aj-ltw-environment-generic]] |
|
====== Generic Java applications |
|
When class instrumentation is required in environments that do not support or are not |
|
supported by the existing `LoadTimeWeaver` implementations, a JDK agent can be the only |
|
solution. For such cases, Spring provides `InstrumentationLoadTimeWeaver`, which |
|
requires a Spring-specific (but very general) VM agent, |
|
`org.springframework.instrument-{version}.jar` (previously named `spring-agent.jar`). |
|
|
|
To use it, you must start the virtual machine with the Spring agent, by supplying the |
|
following JVM options: |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
-javaagent:/path/to/org.springframework.instrument-{version}.jar |
|
---- |
|
|
|
Note that this requires modification of the VM launch script which may prevent you from |
|
using this in application server environments (depending on your operation policies). |
|
Additionally, the JDK agent will instrument the __entire__ VM which can prove expensive. |
|
|
|
For performance reasons, it is recommended to use this configuration only if your target |
|
environment (such as http://www.eclipse.org/jetty/[Jetty]) does not have (or does not |
|
support) a dedicated LTW. |
|
|
|
|
|
|
|
|
|
[[aop-resources]] |
|
=== Further Resources |
|
More information on AspectJ can be found on the http://www.eclipse.org/aspectj[AspectJ |
|
website]. |
|
|
|
The book __Eclipse AspectJ__ by Adrian Colyer et. al. (Addison-Wesley, 2005) provides a |
|
comprehensive introduction and reference for the AspectJ language. |
|
|
|
The book __AspectJ in Action__ by Ramnivas Laddad (Manning, 2003) comes highly |
|
recommended; the focus of the book is on AspectJ, but a lot of general AOP themes are |
|
explored (in some depth). |
|
|
|
|
|
|
|
|
|
|
|
[[aop-api]] |
|
== Spring AOP APIs |
|
|
|
|
|
|
|
|
|
[[aop-api-introduction]] |
|
=== Introduction |
|
The previous chapter described the Spring's support for AOP using |
|
@AspectJ and schema-based aspect definitions. In this chapter we discuss the lower-level |
|
Spring AOP APIs and the AOP support used in Spring 1.2 applications. For new |
|
applications, we recommend the use of the Spring 2.0 and later AOP support described in |
|
the previous chapter, but when working with existing applications, or when reading books |
|
and articles, you may come across Spring 1.2 style examples. Spring 4.0 is backwards |
|
compatible with Spring 1.2 and everything described in this chapter is fully supported |
|
in Spring 4.0. |
|
|
|
|
|
|
|
|
|
[[aop-api-pointcuts]] |
|
=== Pointcut API in Spring |
|
Let's look at how Spring handles the crucial pointcut concept. |
|
|
|
|
|
|
|
[[aop-api-concepts]] |
|
==== Concepts |
|
Spring's pointcut model enables pointcut reuse independent of advice types. It's |
|
possible to target different advice using the same pointcut. |
|
|
|
The `org.springframework.aop.Pointcut` interface is the central interface, used to |
|
target advices to particular classes and methods. The complete interface is shown below: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface Pointcut { |
|
|
|
ClassFilter getClassFilter(); |
|
|
|
MethodMatcher getMethodMatcher(); |
|
|
|
} |
|
---- |
|
|
|
Splitting the `Pointcut` interface into two parts allows reuse of class and method |
|
matching parts, and fine-grained composition operations (such as performing a "union" |
|
with another method matcher). |
|
|
|
The `ClassFilter` interface is used to restrict the pointcut to a given set of target |
|
classes. If the `matches()` method always returns true, all target classes will be |
|
matched: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface ClassFilter { |
|
|
|
boolean matches(Class clazz); |
|
} |
|
---- |
|
|
|
The `MethodMatcher` interface is normally more important. The complete interface is |
|
shown below: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface MethodMatcher { |
|
|
|
boolean matches(Method m, Class targetClass); |
|
|
|
boolean isRuntime(); |
|
|
|
boolean matches(Method m, Class targetClass, Object[] args); |
|
} |
|
---- |
|
|
|
The `matches(Method, Class)` method is used to test whether this pointcut will ever |
|
match a given method on a target class. This evaluation can be performed when an AOP |
|
proxy is created, to avoid the need for a test on every method invocation. If the |
|
2-argument matches method returns true for a given method, and the `isRuntime()` method |
|
for the MethodMatcher returns true, the 3-argument matches method will be invoked on |
|
every method invocation. This enables a pointcut to look at the arguments passed to the |
|
method invocation immediately before the target advice is to execute. |
|
|
|
Most MethodMatchers are static, meaning that their `isRuntime()` method returns false. |
|
In this case, the 3-argument matches method will never be invoked. |
|
|
|
[TIP] |
|
==== |
|
|
|
If possible, try to make pointcuts static, allowing the AOP framework to cache the |
|
results of pointcut evaluation when an AOP proxy is created. |
|
==== |
|
|
|
|
|
|
|
[[aop-api-pointcut-ops]] |
|
==== Operations on pointcuts |
|
Spring supports operations on pointcuts: notably, __union__ and __intersection__. |
|
|
|
* Union means the methods that either pointcut matches. |
|
* Intersection means the methods that both pointcuts match. |
|
* Union is usually more useful. |
|
* Pointcuts can be composed using the static methods in the |
|
__org.springframework.aop.support.Pointcuts__ class, or using the |
|
__ComposablePointcut__ class in the same package. However, using AspectJ pointcut |
|
expressions is usually a simpler approach. |
|
|
|
|
|
|
|
[[aop-api-pointcuts-aspectj]] |
|
==== AspectJ expression pointcuts |
|
Since 2.0, the most important type of pointcut used by Spring is |
|
`org.springframework.aop.aspectj.AspectJExpressionPointcut`. This is a pointcut that |
|
uses an AspectJ supplied library to parse an AspectJ pointcut expression string. |
|
|
|
See the previous chapter for a discussion of supported AspectJ pointcut primitives. |
|
|
|
|
|
|
|
[[aop-api-pointcuts-impls]] |
|
==== Convenience pointcut implementations |
|
Spring provides several convenient pointcut implementations. Some can be used out of the |
|
box; others are intended to be subclassed in application-specific pointcuts. |
|
|
|
|
|
[[aop-api-pointcuts-static]] |
|
===== Static pointcuts |
|
Static pointcuts are based on method and target class, and cannot take into account the |
|
method's arguments. Static pointcuts are sufficient - __and best__ - for most usages. |
|
It's possible for Spring to evaluate a static pointcut only once, when a method is first |
|
invoked: after that, there is no need to evaluate the pointcut again with each method |
|
invocation. |
|
|
|
Let's consider some static pointcut implementations included with Spring. |
|
|
|
[[aop-api-pointcuts-regex]] |
|
====== Regular expression pointcuts |
|
One obvious way to specify static pointcuts is regular expressions. Several AOP |
|
frameworks besides Spring make this possible. |
|
`org.springframework.aop.support.JdkRegexpMethodPointcut` is a generic regular |
|
expression pointcut, using the regular expression support in JDK 1.4+. |
|
|
|
Using the `JdkRegexpMethodPointcut` class, you can provide a list of pattern Strings. If |
|
any of these is a match, the pointcut will evaluate to true. (So the result is |
|
effectively the union of these pointcuts.) |
|
|
|
The usage is shown below: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<bean id="settersAndAbsquatulatePointcut" |
|
class="org.springframework.aop.support.JdkRegexpMethodPointcut"> |
|
<property name="patterns"> |
|
<list> |
|
<value>.*set.*</value> |
|
<value>.*absquatulate</value> |
|
</list> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
Spring provides a convenience class, `RegexpMethodPointcutAdvisor`, that allows us to |
|
also reference an Advice (remember that an Advice can be an interceptor, before advice, |
|
throws advice etc.). Behind the scenes, Spring will use a `JdkRegexpMethodPointcut`. |
|
Using `RegexpMethodPointcutAdvisor` simplifies wiring, as the one bean encapsulates both |
|
pointcut and advice, as shown below: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<bean id="settersAndAbsquatulateAdvisor" |
|
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> |
|
<property name="advice"> |
|
<ref bean="beanNameOfAopAllianceInterceptor"/> |
|
</property> |
|
<property name="patterns"> |
|
<list> |
|
<value>.*set.*</value> |
|
<value>.*absquatulate</value> |
|
</list> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
__RegexpMethodPointcutAdvisor__ can be used with any Advice type. |
|
|
|
[[aop-api-pointcuts-attribute-driven]] |
|
====== Attribute-driven pointcuts |
|
An important type of static pointcut is a __metadata-driven__ pointcut. This uses the |
|
values of metadata attributes: typically, source-level metadata. |
|
|
|
|
|
[[aop-api-pointcuts-dynamic]] |
|
===== Dynamic pointcuts |
|
Dynamic pointcuts are costlier to evaluate than static pointcuts. They take into account |
|
method __arguments__, as well as static information. This means that they must be |
|
evaluated with every method invocation; the result cannot be cached, as arguments will |
|
vary. |
|
|
|
The main example is the `control flow` pointcut. |
|
|
|
[[aop-api-pointcuts-cflow]] |
|
====== Control flow pointcuts |
|
Spring control flow pointcuts are conceptually similar to AspectJ __cflow__ pointcuts, |
|
although less powerful. (There is currently no way to specify that a pointcut executes |
|
below a join point matched by another pointcut.) A control flow pointcut matches the |
|
current call stack. For example, it might fire if the join point was invoked by a method |
|
in the `com.mycompany.web` package, or by the `SomeCaller` class. Control flow pointcuts |
|
are specified using the `org.springframework.aop.support.ControlFlowPointcut` class. |
|
[NOTE] |
|
==== |
|
Control flow pointcuts are significantly more expensive to evaluate at runtime than even |
|
other dynamic pointcuts. In Java 1.4, the cost is about 5 times that of other dynamic |
|
pointcuts. |
|
==== |
|
|
|
|
|
|
|
[[aop-api-pointcuts-superclasses]] |
|
==== Pointcut superclasses |
|
Spring provides useful pointcut superclasses to help you to implement your own pointcuts. |
|
|
|
Because static pointcuts are most useful, you'll probably subclass |
|
StaticMethodMatcherPointcut, as shown below. This requires implementing just one |
|
abstract method (although it's possible to override other methods to customize behavior): |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
class TestStaticPointcut extends StaticMethodMatcherPointcut { |
|
|
|
public boolean matches(Method m, Class targetClass) { |
|
// return true if custom criteria match |
|
} |
|
} |
|
---- |
|
|
|
There are also superclasses for dynamic pointcuts. |
|
|
|
You can use custom pointcuts with any advice type in Spring 1.0 RC2 and above. |
|
|
|
|
|
|
|
[[aop-api-pointcuts-custom]] |
|
==== Custom pointcuts |
|
Because pointcuts in Spring AOP are Java classes, rather than language features (as in |
|
AspectJ) it's possible to declare custom pointcuts, whether static or dynamic. Custom |
|
pointcuts in Spring can be arbitrarily complex. However, using the AspectJ pointcut |
|
expression language is recommended if possible. |
|
|
|
[NOTE] |
|
==== |
|
Later versions of Spring may offer support for "semantic pointcuts" as offered by JAC: |
|
for example, "all methods that change instance variables in the target object." |
|
==== |
|
|
|
|
|
|
|
|
|
[[aop-api-advice]] |
|
=== Advice API in Spring |
|
Let's now look at how Spring AOP handles advice. |
|
|
|
|
|
|
|
[[aop-api-advice-lifecycle]] |
|
==== Advice lifecycles |
|
Each advice is a Spring bean. An advice instance can be shared across all advised |
|
objects, or unique to each advised object. This corresponds to __per-class__ or |
|
__per-instance__ advice. |
|
|
|
Per-class advice is used most often. It is appropriate for generic advice such as |
|
transaction advisors. These do not depend on the state of the proxied object or add new |
|
state; they merely act on the method and arguments. |
|
|
|
Per-instance advice is appropriate for introductions, to support mixins. In this case, |
|
the advice adds state to the proxied object. |
|
|
|
It's possible to use a mix of shared and per-instance advice in the same AOP proxy. |
|
|
|
|
|
|
|
[[aop-api-advice-types]] |
|
==== Advice types in Spring |
|
Spring provides several advice types out of the box, and is extensible to support |
|
arbitrary advice types. Let us look at the basic concepts and standard advice types. |
|
|
|
|
|
[[aop-api-advice-around]] |
|
===== Interception around advice |
|
The most fundamental advice type in Spring is __interception around advice__. |
|
|
|
Spring is compliant with the AOP Alliance interface for around advice using method |
|
interception. MethodInterceptors implementing around advice should implement the |
|
following interface: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface MethodInterceptor extends Interceptor { |
|
|
|
Object invoke(MethodInvocation invocation) throws Throwable; |
|
} |
|
---- |
|
|
|
The `MethodInvocation` argument to the `invoke()` method exposes the method being |
|
invoked; the target join point; the AOP proxy; and the arguments to the method. The |
|
`invoke()` method should return the invocation's result: the return value of the join |
|
point. |
|
|
|
A simple `MethodInterceptor` implementation looks as follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class DebugInterceptor implements MethodInterceptor { |
|
|
|
public Object invoke(MethodInvocation invocation) throws Throwable { |
|
System.out.println("Before: invocation=[" + invocation + "]"); |
|
Object rval = invocation.proceed(); |
|
System.out.println("Invocation returned"); |
|
return rval; |
|
} |
|
} |
|
---- |
|
|
|
Note the call to the MethodInvocation's `proceed()` method. This proceeds down the |
|
interceptor chain towards the join point. Most interceptors will invoke this method, and |
|
return its return value. However, a MethodInterceptor, like any around advice, can |
|
return a different value or throw an exception rather than invoke the proceed method. |
|
However, you don't want to do this without good reason! |
|
|
|
[NOTE] |
|
==== |
|
MethodInterceptors offer interoperability with other AOP Alliance-compliant AOP |
|
implementations. The other advice types discussed in the remainder of this section |
|
implement common AOP concepts, but in a Spring-specific way. While there is an advantage |
|
in using the most specific advice type, stick with MethodInterceptor around advice if |
|
you are likely to want to run the aspect in another AOP framework. Note that pointcuts |
|
are not currently interoperable between frameworks, and the AOP Alliance does not |
|
currently define pointcut interfaces. |
|
==== |
|
|
|
|
|
[[aop-api-advice-before]] |
|
===== Before advice |
|
A simpler advice type is a __before advice__. This does not need a `MethodInvocation` |
|
object, since it will only be called before entering the method. |
|
|
|
The main advantage of a before advice is that there is no need to invoke the `proceed()` |
|
method, and therefore no possibility of inadvertently failing to proceed down the |
|
interceptor chain. |
|
|
|
The `MethodBeforeAdvice` interface is shown below. (Spring's API design would allow for |
|
field before advice, although the usual objects apply to field interception and it's |
|
unlikely that Spring will ever implement it). |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface MethodBeforeAdvice extends BeforeAdvice { |
|
|
|
void before(Method m, Object[] args, Object target) throws Throwable; |
|
} |
|
---- |
|
|
|
Note the return type is `void`. Before advice can insert custom behavior before the join |
|
point executes, but cannot change the return value. If a before advice throws an |
|
exception, this will abort further execution of the interceptor chain. The exception |
|
will propagate back up the interceptor chain. If it is unchecked, or on the signature of |
|
the invoked method, it will be passed directly to the client; otherwise it will be |
|
wrapped in an unchecked exception by the AOP proxy. |
|
|
|
An example of a before advice in Spring, which counts all method invocations: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class CountingBeforeAdvice implements MethodBeforeAdvice { |
|
|
|
private int count; |
|
|
|
public void before(Method m, Object[] args, Object target) throws Throwable { |
|
++count; |
|
} |
|
|
|
public int getCount() { |
|
return count; |
|
} |
|
} |
|
---- |
|
|
|
[TIP] |
|
==== |
|
|
|
Before advice can be used with any pointcut. |
|
==== |
|
|
|
|
|
[[aop-api-advice-throws]] |
|
===== Throws advice |
|
__Throws advice__ is invoked after the return of the join point if the join point threw |
|
an exception. Spring offers typed throws advice. Note that this means that the |
|
`org.springframework.aop.ThrowsAdvice` interface does not contain any methods: It is a |
|
tag interface identifying that the given object implements one or more typed throws |
|
advice methods. These should be in the form of: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
afterThrowing([Method, args, target], subclassOfThrowable) |
|
---- |
|
|
|
Only the last argument is required. The method signatures may have either one or four |
|
arguments, depending on whether the advice method is interested in the method and |
|
arguments. The following classes are examples of throws advice. |
|
|
|
The advice below is invoked if a `RemoteException` is thrown (including subclasses): |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class RemoteThrowsAdvice implements ThrowsAdvice { |
|
|
|
public void afterThrowing(RemoteException ex) throws Throwable { |
|
// Do something with remote exception |
|
} |
|
} |
|
---- |
|
|
|
The following advice is invoked if a `ServletException` is thrown. Unlike the above |
|
advice, it declares 4 arguments, so that it has access to the invoked method, method |
|
arguments and target object: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class ServletThrowsAdviceWithArguments implements ThrowsAdvice { |
|
|
|
public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) { |
|
// Do something with all arguments |
|
} |
|
} |
|
---- |
|
|
|
The final example illustrates how these two methods could be used in a single class, |
|
which handles both `RemoteException` and `ServletException`. Any number of throws advice |
|
methods can be combined in a single class. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public static class CombinedThrowsAdvice implements ThrowsAdvice { |
|
|
|
public void afterThrowing(RemoteException ex) throws Throwable { |
|
// Do something with remote exception |
|
} |
|
|
|
public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) { |
|
// Do something with all arguments |
|
} |
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
If a throws-advice method throws an exception itself, it will override the |
|
original exception (i.e. change the exception thrown to the user). The overriding |
|
exception will typically be a RuntimeException; this is compatible with any method |
|
signature. However, if a throws-advice method throws a checked exception, it will have |
|
to match the declared exceptions of the target method and is hence to some degree |
|
coupled to specific target method signatures. __Do not throw an undeclared checked |
|
exception that is incompatible with the target method's signature!__ |
|
==== |
|
|
|
[TIP] |
|
==== |
|
|
|
Throws advice can be used with any pointcut. |
|
==== |
|
|
|
|
|
[[aop-api-advice-after-returning]] |
|
===== After Returning advice |
|
An after returning advice in Spring must implement the |
|
__org.springframework.aop.AfterReturningAdvice__ interface, shown below: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface AfterReturningAdvice extends Advice { |
|
|
|
void afterReturning(Object returnValue, Method m, Object[] args, Object target) |
|
throws Throwable; |
|
} |
|
---- |
|
|
|
An after returning advice has access to the return value (which it cannot modify), |
|
invoked method, methods arguments and target. |
|
|
|
The following after returning advice counts all successful method invocations that have |
|
not thrown exceptions: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class CountingAfterReturningAdvice implements AfterReturningAdvice { |
|
|
|
private int count; |
|
|
|
public void afterReturning(Object returnValue, Method m, Object[] args, Object target) |
|
throws Throwable { |
|
++count; |
|
} |
|
|
|
public int getCount() { |
|
return count; |
|
} |
|
} |
|
---- |
|
|
|
This advice doesn't change the execution path. If it throws an exception, this will be |
|
thrown up the interceptor chain instead of the return value. |
|
|
|
[TIP] |
|
==== |
|
|
|
After returning advice can be used with any pointcut. |
|
==== |
|
|
|
|
|
[[aop-api-advice-introduction]] |
|
===== Introduction advice |
|
Spring treats introduction advice as a special kind of interception advice. |
|
|
|
Introduction requires an `IntroductionAdvisor`, and an `IntroductionInterceptor`, |
|
implementing the following interface: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface IntroductionInterceptor extends MethodInterceptor { |
|
|
|
boolean implementsInterface(Class intf); |
|
} |
|
---- |
|
|
|
The `invoke()` method inherited from the AOP Alliance `MethodInterceptor` interface must |
|
implement the introduction: that is, if the invoked method is on an introduced |
|
interface, the introduction interceptor is responsible for handling the method call - it |
|
cannot invoke `proceed()`. |
|
|
|
Introduction advice cannot be used with any pointcut, as it applies only at class, |
|
rather than method, level. You can only use introduction advice with the |
|
`IntroductionAdvisor`, which has the following methods: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface IntroductionAdvisor extends Advisor, IntroductionInfo { |
|
|
|
ClassFilter getClassFilter(); |
|
|
|
void validateInterfaces() throws IllegalArgumentException; |
|
} |
|
|
|
public interface IntroductionInfo { |
|
|
|
Class[] getInterfaces(); |
|
} |
|
---- |
|
|
|
There is no `MethodMatcher`, and hence no `Pointcut`, associated with introduction |
|
advice. Only class filtering is logical. |
|
|
|
The `getInterfaces()` method returns the interfaces introduced by this advisor. |
|
|
|
The `validateInterfaces()` method is used internally to see whether or not the |
|
introduced interfaces can be implemented by the configured `IntroductionInterceptor`. |
|
|
|
Let's look at a simple example from the Spring test suite. Let's suppose we want to |
|
introduce the following interface to one or more objects: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface Lockable { |
|
void lock(); |
|
void unlock(); |
|
boolean locked(); |
|
} |
|
---- |
|
|
|
This illustrates a __mixin__. We want to be able to cast advised objects to Lockable, |
|
whatever their type, and call lock and unlock methods. If we call the lock() method, we |
|
want all setter methods to throw a `LockedException`. Thus we can add an aspect that |
|
provides the ability to make objects immutable, without them having any knowledge of it: |
|
a good example of AOP. |
|
|
|
Firstly, we'll need an `IntroductionInterceptor` that does the heavy lifting. In this |
|
case, we extend the `org.springframework.aop.support.DelegatingIntroductionInterceptor` |
|
convenience class. We could implement IntroductionInterceptor directly, but using |
|
`DelegatingIntroductionInterceptor` is best for most cases. |
|
|
|
The `DelegatingIntroductionInterceptor` is designed to delegate an introduction to an |
|
actual implementation of the introduced interface(s), concealing the use of interception |
|
to do so. The delegate can be set to any object using a constructor argument; the |
|
default delegate (when the no-arg constructor is used) is this. Thus in the example |
|
below, the delegate is the `LockMixin` subclass of `DelegatingIntroductionInterceptor`. |
|
Given a delegate (by default itself), a `DelegatingIntroductionInterceptor` instance |
|
looks for all interfaces implemented by the delegate (other than |
|
IntroductionInterceptor), and will support introductions against any of them. It's |
|
possible for subclasses such as `LockMixin` to call the `suppressInterface(Class intf)` |
|
method to suppress interfaces that should not be exposed. However, no matter how many |
|
interfaces an `IntroductionInterceptor` is prepared to support, the |
|
`IntroductionAdvisor` used will control which interfaces are actually exposed. An |
|
introduced interface will conceal any implementation of the same interface by the target. |
|
|
|
Thus `LockMixin` extends `DelegatingIntroductionInterceptor` and implements `Lockable` |
|
itself. The superclass automatically picks up that Lockable can be supported for |
|
introduction, so we don't need to specify that. We could introduce any number of |
|
interfaces in this way. |
|
|
|
Note the use of the `locked` instance variable. This effectively adds additional state |
|
to that held in the target object. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable { |
|
|
|
private boolean locked; |
|
|
|
public void lock() { |
|
this.locked = true; |
|
} |
|
|
|
public void unlock() { |
|
this.locked = false; |
|
} |
|
|
|
public boolean locked() { |
|
return this.locked; |
|
} |
|
|
|
public Object invoke(MethodInvocation invocation) throws Throwable { |
|
if (locked() && invocation.getMethod().getName().indexOf("set") == 0) { |
|
throw new LockedException(); |
|
} |
|
return super.invoke(invocation); |
|
} |
|
|
|
} |
|
---- |
|
|
|
Often it isn't necessary to override the `invoke()` method: the |
|
`DelegatingIntroductionInterceptor` implementation - which calls the delegate method if |
|
the method is introduced, otherwise proceeds towards the join point - is usually |
|
sufficient. In the present case, we need to add a check: no setter method can be invoked |
|
if in locked mode. |
|
|
|
The introduction advisor required is simple. All it needs to do is hold a distinct |
|
`LockMixin` instance, and specify the introduced interfaces - in this case, just |
|
`Lockable`. A more complex example might take a reference to the introduction |
|
interceptor (which would be defined as a prototype): in this case, there's no |
|
configuration relevant for a `LockMixin`, so we simply create it using `new`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class LockMixinAdvisor extends DefaultIntroductionAdvisor { |
|
|
|
public LockMixinAdvisor() { |
|
super(new LockMixin(), Lockable.class); |
|
} |
|
} |
|
---- |
|
|
|
We can apply this advisor very simply: it requires no configuration. (However, it __is__ |
|
necessary: It's impossible to use an `IntroductionInterceptor` without an |
|
__IntroductionAdvisor__.) As usual with introductions, the advisor must be per-instance, |
|
as it is stateful. We need a different instance of `LockMixinAdvisor`, and hence |
|
`LockMixin`, for each advised object. The advisor comprises part of the advised object's |
|
state. |
|
|
|
We can apply this advisor programmatically, using the `Advised.addAdvisor()` method, or |
|
(the recommended way) in XML configuration, like any other advisor. All proxy creation |
|
choices discussed below, including "auto proxy creators," correctly handle introductions |
|
and stateful mixins. |
|
|
|
|
|
|
|
|
|
[[aop-api-advisor]] |
|
=== Advisor API in Spring |
|
In Spring, an Advisor is an aspect that contains just a single advice object associated |
|
with a pointcut expression. |
|
|
|
Apart from the special case of introductions, any advisor can be used with any advice. |
|
`org.springframework.aop.support.DefaultPointcutAdvisor` is the most commonly used |
|
advisor class. For example, it can be used with a `MethodInterceptor`, `BeforeAdvice` or |
|
`ThrowsAdvice`. |
|
|
|
It is possible to mix advisor and advice types in Spring in the same AOP proxy. For |
|
example, you could use a interception around advice, throws advice and before advice in |
|
one proxy configuration: Spring will automatically create the necessary interceptor |
|
chain. |
|
|
|
|
|
|
|
|
|
[[aop-pfb]] |
|
=== Using the ProxyFactoryBean to create AOP proxies |
|
If you're using the Spring IoC container (an ApplicationContext or BeanFactory) for your |
|
business objects - and you should be! - you will want to use one of Spring's AOP |
|
FactoryBeans. (Remember that a factory bean introduces a layer of indirection, enabling |
|
it to create objects of a different type.) |
|
|
|
[NOTE] |
|
==== |
|
The Spring AOP support also uses factory beans under the covers. |
|
==== |
|
|
|
The basic way to create an AOP proxy in Spring is to use the |
|
__org.springframework.aop.framework.ProxyFactoryBean__. This gives complete control over |
|
the pointcuts and advice that will apply, and their ordering. However, there are simpler |
|
options that are preferable if you don't need such control. |
|
|
|
|
|
|
|
[[aop-pfb-1]] |
|
==== Basics |
|
The `ProxyFactoryBean`, like other Spring `FactoryBean` implementations, introduces a |
|
level of indirection. If you define a `ProxyFactoryBean` with name `foo`, what objects |
|
referencing `foo` see is not the `ProxyFactoryBean` instance itself, but an object |
|
created by the `ProxyFactoryBean`'s implementation of the `getObject()` method. This |
|
method will create an AOP proxy wrapping a target object. |
|
|
|
One of the most important benefits of using a `ProxyFactoryBean` or another IoC-aware |
|
class to create AOP proxies, is that it means that advices and pointcuts can also be |
|
managed by IoC. This is a powerful feature, enabling certain approaches that are hard to |
|
achieve with other AOP frameworks. For example, an advice may itself reference |
|
application objects (besides the target, which should be available in any AOP |
|
framework), benefiting from all the pluggability provided by Dependency Injection. |
|
|
|
|
|
|
|
[[aop-pfb-2]] |
|
==== JavaBean properties |
|
In common with most `FactoryBean` implementations provided with Spring, the |
|
`ProxyFactoryBean` class is itself a JavaBean. Its properties are used to: |
|
|
|
* Specify the target you want to proxy. |
|
* Specify whether to use CGLIB (see below and also <<aop-pfb-proxy-types>>). |
|
|
|
Some key properties are inherited from `org.springframework.aop.framework.ProxyConfig` |
|
(the superclass for all AOP proxy factories in Spring). These key properties include: |
|
|
|
* `proxyTargetClass`: `true` if the target class is to be proxied, rather than the |
|
target class' interfaces. If this property value is set to `true`, then CGLIB proxies |
|
will be created (but see also <<aop-pfb-proxy-types>>). |
|
* `optimize`: controls whether or not aggressive optimizations are applied to proxies |
|
__created via CGLIB__. One should not blithely use this setting unless one fully |
|
understands how the relevant AOP proxy handles optimization. This is currently used |
|
only for CGLIB proxies; it has no effect with JDK dynamic proxies. |
|
* `frozen`: if a proxy configuration is `frozen`, then changes to the configuration are |
|
no longer allowed. This is useful both as a slight optimization and for those cases |
|
when you don't want callers to be able to manipulate the proxy (via the `Advised` |
|
interface) after the proxy has been created. The default value of this property is |
|
`false`, so changes such as adding additional advice are allowed. |
|
* `exposeProxy`: determines whether or not the current proxy should be exposed in a |
|
`ThreadLocal` so that it can be accessed by the target. If a target needs to obtain |
|
the proxy and the `exposeProxy` property is set to `true`, the target can use the |
|
`AopContext.currentProxy()` method. |
|
|
|
Other properties specific to `ProxyFactoryBean` include: |
|
|
|
* `proxyInterfaces`: array of String interface names. If this isn't supplied, a CGLIB |
|
proxy for the target class will be used (but see also <<aop-pfb-proxy-types>>). |
|
* `interceptorNames`: String array of `Advisor`, interceptor or other advice names to |
|
apply. Ordering is significant, on a first come-first served basis. That is to say |
|
that the first interceptor in the list will be the first to be able to intercept the |
|
invocation. |
|
|
|
The names are bean names in the current factory, including bean names from ancestor |
|
factories. You can't mention bean references here since doing so would result in the |
|
`ProxyFactoryBean` ignoring the singleton setting of the advice. |
|
|
|
You can append an interceptor name with an asterisk ( `*`). This will result in the |
|
application of all advisor beans with names starting with the part before the asterisk |
|
to be applied. An example of using this feature can be found in <<aop-global-advisors>>. |
|
|
|
* singleton: whether or not the factory should return a single object, no matter how |
|
often the `getObject()` method is called. Several `FactoryBean` implementations offer |
|
such a method. The default value is `true`. If you want to use stateful advice - for |
|
example, for stateful mixins - use prototype advices along with a singleton value of |
|
`false`. |
|
|
|
|
|
|
|
[[aop-pfb-proxy-types]] |
|
==== JDK- and CGLIB-based proxies |
|
This section serves as the definitive documentation on how the `ProxyFactoryBean` |
|
chooses to create one of either a JDK- and CGLIB-based proxy for a particular target |
|
object (that is to be proxied). |
|
|
|
[NOTE] |
|
==== |
|
The behavior of the `ProxyFactoryBean` with regard to creating JDK- or CGLIB-based |
|
proxies changed between versions 1.2.x and 2.0 of Spring. The `ProxyFactoryBean` now |
|
exhibits similar semantics with regard to auto-detecting interfaces as those of the |
|
`TransactionProxyFactoryBean` class. |
|
==== |
|
|
|
If the class of a target object that is to be proxied (hereafter simply referred to as |
|
the target class) doesn't implement any interfaces, then a CGLIB-based proxy will be |
|
created. This is the easiest scenario, because JDK proxies are interface based, and no |
|
interfaces means JDK proxying isn't even possible. One simply plugs in the target bean, |
|
and specifies the list of interceptors via the `interceptorNames` property. Note that a |
|
CGLIB-based proxy will be created even if the `proxyTargetClass` property of the |
|
`ProxyFactoryBean` has been set to `false`. (Obviously this makes no sense, and is best |
|
removed from the bean definition because it is at best redundant, and at worst |
|
confusing.) |
|
|
|
If the target class implements one (or more) interfaces, then the type of proxy that is |
|
created depends on the configuration of the `ProxyFactoryBean`. |
|
|
|
If the `proxyTargetClass` property of the `ProxyFactoryBean` has been set to `true`, |
|
then a CGLIB-based proxy will be created. This makes sense, and is in keeping with the |
|
principle of least surprise. Even if the `proxyInterfaces` property of the |
|
`ProxyFactoryBean` has been set to one or more fully qualified interface names, the fact |
|
that the `proxyTargetClass` property is set to `true` __will__ cause CGLIB-based |
|
proxying to be in effect. |
|
|
|
If the `proxyInterfaces` property of the `ProxyFactoryBean` has been set to one or more |
|
fully qualified interface names, then a JDK-based proxy will be created. The created |
|
proxy will implement all of the interfaces that were specified in the `proxyInterfaces` |
|
property; if the target class happens to implement a whole lot more interfaces than |
|
those specified in the `proxyInterfaces` property, that is all well and good but those |
|
additional interfaces will not be implemented by the returned proxy. |
|
|
|
If the `proxyInterfaces` property of the `ProxyFactoryBean` has __not__ been set, but |
|
the target class __does implement one (or more)__ interfaces, then the |
|
`ProxyFactoryBean` will auto-detect the fact that the target class does actually |
|
implement at least one interface, and a JDK-based proxy will be created. The interfaces |
|
that are actually proxied will be __all__ of the interfaces that the target class |
|
implements; in effect, this is the same as simply supplying a list of each and every |
|
interface that the target class implements to the `proxyInterfaces` property. However, |
|
it is significantly less work, and less prone to typos. |
|
|
|
|
|
|
|
[[aop-api-proxying-intf]] |
|
==== Proxying interfaces |
|
Let's look at a simple example of `ProxyFactoryBean` in action. This example involves: |
|
|
|
* A __target bean__ that will be proxied. This is the "personTarget" bean definition in |
|
the example below. |
|
* An Advisor and an Interceptor used to provide advice. |
|
* An AOP proxy bean definition specifying the target object (the personTarget bean) and |
|
the interfaces to proxy, along with the advices to apply. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="personTarget" class="com.mycompany.PersonImpl"> |
|
<property name="name" value="Tony"/> |
|
<property name="age" value="51"/> |
|
</bean> |
|
|
|
<bean id="myAdvisor" class="com.mycompany.MyAdvisor"> |
|
<property name="someProperty" value="Custom string property value"/> |
|
</bean> |
|
|
|
<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor"> |
|
</bean> |
|
|
|
<bean id="person" |
|
class="org.springframework.aop.framework.ProxyFactoryBean"> |
|
<property name="proxyInterfaces" value="com.mycompany.Person"/> |
|
|
|
<property name="target" ref="personTarget"/> |
|
<property name="interceptorNames"> |
|
<list> |
|
<value>myAdvisor</value> |
|
<value>debugInterceptor</value> |
|
</list> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
Note that the `interceptorNames` property takes a list of String: the bean names of the |
|
interceptor or advisors in the current factory. Advisors, interceptors, before, after |
|
returning and throws advice objects can be used. The ordering of advisors is significant. |
|
|
|
[NOTE] |
|
==== |
|
You might be wondering why the list doesn't hold bean references. The reason for this is |
|
that if the ProxyFactoryBean's singleton property is set to false, it must be able to |
|
return independent proxy instances. If any of the advisors is itself a prototype, an |
|
independent instance would need to be returned, so it's necessary to be able to obtain |
|
an instance of the prototype from the factory; holding a reference isn't sufficient. |
|
==== |
|
|
|
The "person" bean definition above can be used in place of a Person implementation, as |
|
follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Person person = (Person) factory.getBean("person"); |
|
---- |
|
|
|
Other beans in the same IoC context can express a strongly typed dependency on it, as |
|
with an ordinary Java object: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="personUser" class="com.mycompany.PersonUser"> |
|
<property name="person"><ref bean="person"/></property> |
|
</bean> |
|
---- |
|
|
|
The `PersonUser` class in this example would expose a property of type Person. As far as |
|
it's concerned, the AOP proxy can be used transparently in place of a "real" person |
|
implementation. However, its class would be a dynamic proxy class. It would be possible |
|
to cast it to the `Advised` interface (discussed below). |
|
|
|
It's possible to conceal the distinction between target and proxy using an anonymous |
|
__inner bean__, as follows. Only the `ProxyFactoryBean` definition is different; the |
|
advice is included only for completeness: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="myAdvisor" class="com.mycompany.MyAdvisor"> |
|
<property name="someProperty" value="Custom string property value"/> |
|
</bean> |
|
|
|
<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor"/> |
|
|
|
<bean id="person" class="org.springframework.aop.framework.ProxyFactoryBean"> |
|
<property name="proxyInterfaces" value="com.mycompany.Person"/> |
|
<!-- Use inner bean, not local reference to target --> |
|
<property name="target"> |
|
<bean class="com.mycompany.PersonImpl"> |
|
<property name="name" value="Tony"/> |
|
<property name="age" value="51"/> |
|
</bean> |
|
</property> |
|
<property name="interceptorNames"> |
|
<list> |
|
<value>myAdvisor</value> |
|
<value>debugInterceptor</value> |
|
</list> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
This has the advantage that there's only one object of type `Person`: useful if we want |
|
to prevent users of the application context from obtaining a reference to the un-advised |
|
object, or need to avoid any ambiguity with Spring IoC __autowiring__. There's also |
|
arguably an advantage in that the ProxyFactoryBean definition is self-contained. |
|
However, there are times when being able to obtain the un-advised target from the |
|
factory might actually be an __advantage__: for example, in certain test scenarios. |
|
|
|
|
|
|
|
[[aop-api-proxying-class]] |
|
==== Proxying classes |
|
What if you need to proxy a class, rather than one or more interfaces? |
|
|
|
Imagine that in our example above, there was no `Person` interface: we needed to advise |
|
a class called `Person` that didn't implement any business interface. In this case, you |
|
can configure Spring to use CGLIB proxying, rather than dynamic proxies. Simply set the |
|
`proxyTargetClass` property on the ProxyFactoryBean above to true. While it's best to |
|
program to interfaces, rather than classes, the ability to advise classes that don't |
|
implement interfaces can be useful when working with legacy code. (In general, Spring |
|
isn't prescriptive. While it makes it easy to apply good practices, it avoids forcing a |
|
particular approach.) |
|
|
|
If you want to, you can force the use of CGLIB in any case, even if you do have |
|
interfaces. |
|
|
|
CGLIB proxying works by generating a subclass of the target class at runtime. Spring |
|
configures this generated subclass to delegate method calls to the original target: the |
|
subclass is used to implement the __Decorator__ pattern, weaving in the advice. |
|
|
|
CGLIB proxying should generally be transparent to users. However, there are some issues |
|
to consider: |
|
|
|
* `Final` methods can't be advised, as they can't be overridden. |
|
* There is no need to add CGLIB to your classpath. As of Spring 3.2, CGLIB is repackaged |
|
and included in the spring-core JAR. In other words, CGLIB-based AOP will work "out of |
|
the box" just as do JDK dynamic proxies. |
|
|
|
There's little performance difference between CGLIB proxying and dynamic proxies. As of |
|
Spring 1.0, dynamic proxies are slightly faster. However, this may change in the future. |
|
Performance should not be a decisive consideration in this case. |
|
|
|
|
|
|
|
[[aop-global-advisors]] |
|
==== Using 'global' advisors |
|
By appending an asterisk to an interceptor name, all advisors with bean names matching |
|
the part before the asterisk, will be added to the advisor chain. This can come in handy |
|
if you need to add a standard set of 'global' advisors: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean"> |
|
<property name="target" ref="service"/> |
|
<property name="interceptorNames"> |
|
<list> |
|
<value>global*</value> |
|
</list> |
|
</property> |
|
</bean> |
|
|
|
<bean id="global_debug" class="org.springframework.aop.interceptor.DebugInterceptor"/> |
|
<bean id="global_performance" class="org.springframework.aop.interceptor.PerformanceMonitorInterceptor"/> |
|
---- |
|
|
|
|
|
|
|
|
|
[[aop-concise-proxy]] |
|
=== Concise proxy definitions |
|
Especially when defining transactional proxies, you may end up with many similar proxy |
|
definitions. The use of parent and child bean definitions, along with inner bean |
|
definitions, can result in much cleaner and more concise proxy definitions. |
|
|
|
First a parent, __template__, bean definition is created for the proxy: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="txProxyTemplate" abstract="true" |
|
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> |
|
<property name="transactionManager" ref="transactionManager"/> |
|
<property name="transactionAttributes"> |
|
<props> |
|
<prop key="*">PROPAGATION_REQUIRED</prop> |
|
</props> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
This will never be instantiated itself, so may actually be incomplete. Then each proxy |
|
which needs to be created is just a child bean definition, which wraps the target of the |
|
proxy as an inner bean definition, since the target will never be used on its own anyway. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="myService" parent="txProxyTemplate"> |
|
<property name="target"> |
|
<bean class="org.springframework.samples.MyServiceImpl"> |
|
</bean> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
It is of course possible to override properties from the parent template, such as in |
|
this case, the transaction propagation settings: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="mySpecialService" parent="txProxyTemplate"> |
|
<property name="target"> |
|
<bean class="org.springframework.samples.MySpecialServiceImpl"> |
|
</bean> |
|
</property> |
|
<property name="transactionAttributes"> |
|
<props> |
|
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop> |
|
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop> |
|
<prop key="load*">PROPAGATION_REQUIRED,readOnly</prop> |
|
<prop key="store*">PROPAGATION_REQUIRED</prop> |
|
</props> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
Note that in the example above, we have explicitly marked the parent bean definition as |
|
__abstract__ by using the __abstract__ attribute, as described |
|
<<beans-child-bean-definitions,previously>>, so that it may not actually ever be |
|
instantiated. Application contexts (but not simple bean factories) will by default |
|
pre-instantiate all singletons. It is therefore important (at least for singleton beans) |
|
that if you have a (parent) bean definition which you intend to use only as a template, |
|
and this definition specifies a class, you must make sure to set the __abstract__ |
|
attribute to __true__, otherwise the application context will actually try to |
|
pre-instantiate it. |
|
|
|
|
|
|
|
|
|
[[aop-prog]] |
|
=== Creating AOP proxies programmatically with the ProxyFactory |
|
It's easy to create AOP proxies programmatically using Spring. This enables you to use |
|
Spring AOP without dependency on Spring IoC. |
|
|
|
The following listing shows creation of a proxy for a target object, with one |
|
interceptor and one advisor. The interfaces implemented by the target object will |
|
automatically be proxied: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl); |
|
factory.addAdvice(myMethodInterceptor); |
|
factory.addAdvisor(myAdvisor); |
|
MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy(); |
|
---- |
|
|
|
The first step is to construct an object of type |
|
`org.springframework.aop.framework.ProxyFactory`. You can create this with a target |
|
object, as in the above example, or specify the interfaces to be proxied in an alternate |
|
constructor. |
|
|
|
You can add advices (with interceptors as a specialized kind of advice) and/or advisors, |
|
and manipulate them for the life of the ProxyFactory. If you add an |
|
IntroductionInterceptionAroundAdvisor, you can cause the proxy to implement additional |
|
interfaces. |
|
|
|
There are also convenience methods on ProxyFactory (inherited from `AdvisedSupport`) |
|
which allow you to add other advice types such as before and throws advice. |
|
AdvisedSupport is the superclass of both ProxyFactory and ProxyFactoryBean. |
|
|
|
[TIP] |
|
==== |
|
|
|
Integrating AOP proxy creation with the IoC framework is best practice in most |
|
applications. We recommend that you externalize configuration from Java code with AOP, |
|
as in general. |
|
==== |
|
|
|
|
|
|
|
|
|
[[aop-api-advised]] |
|
=== Manipulating advised objects |
|
However you create AOP proxies, you can manipulate them using the |
|
`org.springframework.aop.framework.Advised` interface. Any AOP proxy can be cast to this |
|
interface, whichever other interfaces it implements. This interface includes the |
|
following methods: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Advisor[] getAdvisors(); |
|
|
|
void addAdvice(Advice advice) throws AopConfigException; |
|
|
|
void addAdvice(int pos, Advice advice) throws AopConfigException; |
|
|
|
void addAdvisor(Advisor advisor) throws AopConfigException; |
|
|
|
void addAdvisor(int pos, Advisor advisor) throws AopConfigException; |
|
|
|
int indexOf(Advisor advisor); |
|
|
|
boolean removeAdvisor(Advisor advisor) throws AopConfigException; |
|
|
|
void removeAdvisor(int index) throws AopConfigException; |
|
|
|
boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException; |
|
|
|
boolean isFrozen(); |
|
---- |
|
|
|
The `getAdvisors()` method will return an Advisor for every advisor, interceptor or |
|
other advice type that has been added to the factory. If you added an Advisor, the |
|
returned advisor at this index will be the object that you added. If you added an |
|
interceptor or other advice type, Spring will have wrapped this in an advisor with a |
|
pointcut that always returns true. Thus if you added a `MethodInterceptor`, the advisor |
|
returned for this index will be an `DefaultPointcutAdvisor` returning your |
|
`MethodInterceptor` and a pointcut that matches all classes and methods. |
|
|
|
The `addAdvisor()` methods can be used to add any Advisor. Usually the advisor holding |
|
pointcut and advice will be the generic `DefaultPointcutAdvisor`, which can be used with |
|
any advice or pointcut (but not for introductions). |
|
|
|
By default, it's possible to add or remove advisors or interceptors even once a proxy |
|
has been created. The only restriction is that it's impossible to add or remove an |
|
introduction advisor, as existing proxies from the factory will not show the interface |
|
change. (You can obtain a new proxy from the factory to avoid this problem.) |
|
|
|
A simple example of casting an AOP proxy to the `Advised` interface and examining and |
|
manipulating its advice: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Advised advised = (Advised) myObject; |
|
Advisor[] advisors = advised.getAdvisors(); |
|
int oldAdvisorCount = advisors.length; |
|
System.out.println(oldAdvisorCount + " advisors"); |
|
|
|
// Add an advice like an interceptor without a pointcut |
|
// Will match all proxied methods |
|
// Can use for interceptors, before, after returning or throws advice |
|
advised.addAdvice(new DebugInterceptor()); |
|
|
|
// Add selective advice using a pointcut |
|
advised.addAdvisor(new DefaultPointcutAdvisor(mySpecialPointcut, myAdvice)); |
|
|
|
assertEquals("Added two advisors", oldAdvisorCount + 2, advised.getAdvisors().length); |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
It's questionable whether it's advisable (no pun intended) to modify advice on a |
|
business object in production, although there are no doubt legitimate usage cases. |
|
However, it can be very useful in development: for example, in tests. I have sometimes |
|
found it very useful to be able to add test code in the form of an interceptor or other |
|
advice, getting inside a method invocation I want to test. (For example, the advice can |
|
get inside a transaction created for that method: for example, to run SQL to check that |
|
a database was correctly updated, before marking the transaction for roll back.) |
|
==== |
|
|
|
Depending on how you created the proxy, you can usually set a `frozen` flag, in which |
|
case the `Advised` `isFrozen()` method will return true, and any attempts to modify |
|
advice through addition or removal will result in an `AopConfigException`. The ability |
|
to freeze the state of an advised object is useful in some cases, for example, to |
|
prevent calling code removing a security interceptor. It may also be used in Spring 1.1 |
|
to allow aggressive optimization if runtime advice modification is known not to be |
|
required. |
|
|
|
|
|
|
|
|
|
[[aop-autoproxy]] |
|
=== Using the "auto-proxy" facility |
|
So far we've considered explicit creation of AOP proxies using a `ProxyFactoryBean` or |
|
similar factory bean. |
|
|
|
Spring also allows us to use "auto-proxy" bean definitions, which can automatically |
|
proxy selected bean definitions. This is built on Spring "bean post processor" |
|
infrastructure, which enables modification of any bean definition as the container loads. |
|
|
|
In this model, you set up some special bean definitions in your XML bean definition file |
|
to configure the auto proxy infrastructure. This allows you just to declare the targets |
|
eligible for auto-proxying: you don't need to use `ProxyFactoryBean`. |
|
|
|
There are two ways to do this: |
|
|
|
* Using an auto-proxy creator that refers to specific beans in the current context. |
|
* A special case of auto-proxy creation that deserves to be considered separately; |
|
auto-proxy creation driven by source-level metadata attributes. |
|
|
|
|
|
|
|
[[aop-autoproxy-choices]] |
|
==== Autoproxy bean definitions |
|
The `org.springframework.aop.framework.autoproxy` package provides the following |
|
standard auto-proxy creators. |
|
|
|
|
|
[[aop-api-autoproxy]] |
|
===== BeanNameAutoProxyCreator |
|
The `BeanNameAutoProxyCreator` class is a `BeanPostProcessor` that automatically creates |
|
AOP proxies for beans with names matching literal values or wildcards. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> |
|
<property name="beanNames" value="jdk*,onlyJdk"/> |
|
<property name="interceptorNames"> |
|
<list> |
|
<value>myInterceptor</value> |
|
</list> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
As with `ProxyFactoryBean`, there is an `interceptorNames` property rather than a list |
|
of interceptors, to allow correct behavior for prototype advisors. Named "interceptors" |
|
can be advisors or any advice type. |
|
|
|
As with auto proxying in general, the main point of using `BeanNameAutoProxyCreator` is |
|
to apply the same configuration consistently to multiple objects, with minimal volume of |
|
configuration. It is a popular choice for applying declarative transactions to multiple |
|
objects. |
|
|
|
Bean definitions whose names match, such as "jdkMyBean" and "onlyJdk" in the above |
|
example, are plain old bean definitions with the target class. An AOP proxy will be |
|
created automatically by the `BeanNameAutoProxyCreator`. The same advice will be applied |
|
to all matching beans. Note that if advisors are used (rather than the interceptor in |
|
the above example), the pointcuts may apply differently to different beans. |
|
|
|
|
|
[[aop-api-autoproxy-default]] |
|
===== DefaultAdvisorAutoProxyCreator |
|
A more general and extremely powerful auto proxy creator is |
|
`DefaultAdvisorAutoProxyCreator`. This will automagically apply eligible advisors in the |
|
current context, without the need to include specific bean names in the auto-proxy |
|
advisor's bean definition. It offers the same merit of consistent configuration and |
|
avoidance of duplication as `BeanNameAutoProxyCreator`. |
|
|
|
Using this mechanism involves: |
|
|
|
* Specifying a `DefaultAdvisorAutoProxyCreator` bean definition. |
|
* Specifying any number of Advisors in the same or related contexts. Note that these |
|
__must__ be Advisors, not just interceptors or other advices. This is necessary |
|
because there must be a pointcut to evaluate, to check the eligibility of each advice |
|
to candidate bean definitions. |
|
|
|
The `DefaultAdvisorAutoProxyCreator` will automatically evaluate the pointcut contained |
|
in each advisor, to see what (if any) advice it should apply to each business object |
|
(such as "businessObject1" and "businessObject2" in the example). |
|
|
|
This means that any number of advisors can be applied automatically to each business |
|
object. If no pointcut in any of the advisors matches any method in a business object, |
|
the object will not be proxied. As bean definitions are added for new business objects, |
|
they will automatically be proxied if necessary. |
|
|
|
Autoproxying in general has the advantage of making it impossible for callers or |
|
dependencies to obtain an un-advised object. Calling getBean("businessObject1") on this |
|
ApplicationContext will return an AOP proxy, not the target business object. (The "inner |
|
bean" idiom shown earlier also offers this benefit.) |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/> |
|
|
|
<bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor"> |
|
<property name="transactionInterceptor" ref="transactionInterceptor"/> |
|
</bean> |
|
|
|
<bean id="customAdvisor" class="com.mycompany.MyAdvisor"/> |
|
|
|
<bean id="businessObject1" class="com.mycompany.BusinessObject1"> |
|
<!-- Properties omitted --> |
|
</bean> |
|
|
|
<bean id="businessObject2" class="com.mycompany.BusinessObject2"/> |
|
---- |
|
|
|
The `DefaultAdvisorAutoProxyCreator` is very useful if you want to apply the same advice |
|
consistently to many business objects. Once the infrastructure definitions are in place, |
|
you can simply add new business objects without including specific proxy configuration. |
|
You can also drop in additional aspects very easily - for example, tracing or |
|
performance monitoring aspects - with minimal change to configuration. |
|
|
|
The DefaultAdvisorAutoProxyCreator offers support for filtering (using a naming |
|
convention so that only certain advisors are evaluated, allowing use of multiple, |
|
differently configured, AdvisorAutoProxyCreators in the same factory) and ordering. |
|
Advisors can implement the `org.springframework.core.Ordered` interface to ensure |
|
correct ordering if this is an issue. The TransactionAttributeSourceAdvisor used in the |
|
above example has a configurable order value; the default setting is unordered. |
|
|
|
|
|
[[aop-api-autoproxy-abstract]] |
|
===== AbstractAdvisorAutoProxyCreator |
|
This is the superclass of DefaultAdvisorAutoProxyCreator. You can create your own |
|
auto-proxy creators by subclassing this class, in the unlikely event that advisor |
|
definitions offer insufficient customization to the behavior of the framework |
|
`DefaultAdvisorAutoProxyCreator`. |
|
|
|
|
|
|
|
[[aop-autoproxy-metadata]] |
|
==== Using metadata-driven auto-proxying |
|
A particularly important type of auto-proxying is driven by metadata. This produces a |
|
similar programming model to .NET `ServicedComponents`. Instead of defining metadata in |
|
XML descriptors, configuration for transaction management and other enterprise services |
|
is held in source-level attributes. |
|
|
|
In this case, you use the `DefaultAdvisorAutoProxyCreator`, in combination with Advisors |
|
that understand metadata attributes. The metadata specifics are held in the pointcut |
|
part of the candidate advisors, rather than in the auto-proxy creation class itself. |
|
|
|
This is really a special case of the `DefaultAdvisorAutoProxyCreator`, but deserves |
|
consideration on its own. (The metadata-aware code is in the pointcuts contained in the |
|
advisors, not the AOP framework itself.) |
|
|
|
The `/attributes` directory of the JPetStore sample application shows the use of |
|
attribute-driven auto-proxying. In this case, there's no need to use the |
|
`TransactionProxyFactoryBean`. Simply defining transactional attributes on business |
|
objects is sufficient, because of the use of metadata-aware pointcuts. The bean |
|
definitions include the following code, in `/WEB-INF/declarativeServices.xml`. Note that |
|
this is generic, and can be used outside the JPetStore: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/> |
|
|
|
<bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor"> |
|
<property name="transactionInterceptor" ref="transactionInterceptor"/> |
|
</bean> |
|
|
|
<bean id="transactionInterceptor" |
|
class="org.springframework.transaction.interceptor.TransactionInterceptor"> |
|
<property name="transactionManager" ref="transactionManager"/> |
|
<property name="transactionAttributeSource"> |
|
<bean class="org.springframework.transaction.interceptor.AttributesTransactionAttributeSource"> |
|
<property name="attributes" ref="attributes"/> |
|
</bean> |
|
</property> |
|
</bean> |
|
|
|
<bean id="attributes" class="org.springframework.metadata.commons.CommonsAttributes"/> |
|
---- |
|
|
|
The `DefaultAdvisorAutoProxyCreator` bean definition (the name is not significant, hence |
|
it can even be omitted) will pick up all eligible pointcuts in the current application |
|
context. In this case, the "transactionAdvisor" bean definition, of type |
|
`TransactionAttributeSourceAdvisor`, will apply to classes or methods carrying a |
|
transaction attribute. The TransactionAttributeSourceAdvisor depends on a |
|
TransactionInterceptor, via constructor dependency. The example resolves this via |
|
autowiring. The `AttributesTransactionAttributeSource` depends on an implementation of |
|
the `org.springframework.metadata.Attributes` interface. In this fragment, the |
|
"attributes" bean satisfies this, using the Jakarta Commons Attributes API to obtain |
|
attribute information. (The application code must have been compiled using the Commons |
|
Attributes compilation task.) |
|
|
|
The `/annotation` directory of the JPetStore sample application contains an analogous |
|
example for auto-proxying driven by JDK 1.5+ annotations. The following configuration |
|
enables automatic detection of Spring's `Transactional` annotation, leading to implicit |
|
proxies for beans containing that annotation: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/> |
|
|
|
<bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor"> |
|
<property name="transactionInterceptor" ref="transactionInterceptor"/> |
|
</bean> |
|
|
|
<bean id="transactionInterceptor" |
|
class="org.springframework.transaction.interceptor.TransactionInterceptor"> |
|
<property name="transactionManager" ref="transactionManager"/> |
|
<property name="transactionAttributeSource"> |
|
<bean class="org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"/> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
The `TransactionInterceptor` defined here depends on a `PlatformTransactionManager` |
|
definition, which is not included in this generic file (although it could be) because it |
|
will be specific to the application's transaction requirements (typically JTA, as in |
|
this example, or Hibernate, JDO or JDBC): |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="transactionManager" |
|
class="org.springframework.transaction.jta.JtaTransactionManager"/> |
|
---- |
|
|
|
[TIP] |
|
==== |
|
|
|
If you require only declarative transaction management, using these generic XML |
|
definitions will result in Spring automatically proxying all classes or methods with |
|
transaction attributes. You won't need to work directly with AOP, and the programming |
|
model is similar to that of .NET ServicedComponents. |
|
==== |
|
|
|
This mechanism is extensible. It's possible to do auto-proxying based on custom |
|
attributes. You need to: |
|
|
|
* Define your custom attribute. |
|
* Specify an Advisor with the necessary advice, including a pointcut that is triggered |
|
by the presence of the custom attribute on a class or method. You may be able to use |
|
an existing advice, merely implementing a static pointcut that picks up the custom |
|
attribute. |
|
|
|
It's possible for such advisors to be unique to each advised class (for example, mixins): |
|
they simply need to be defined as prototype, rather than singleton, bean definitions. |
|
For example, the `LockMixin` introduction interceptor from the Spring test suite, |
|
shown above, could be used in conjunction with a generic `DefaultIntroductionAdvisor`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="lockMixin" class="test.mixin.LockMixin" scope="prototype"/> |
|
|
|
<bean id="lockableAdvisor" class="org.springframework.aop.support.DefaultIntroductionAdvisor" |
|
scope="prototype"> |
|
<constructor-arg ref="lockMixin"/> |
|
</bean> |
|
---- |
|
|
|
Note that both `lockMixin` and `lockableAdvisor` are defined as prototypes. |
|
|
|
|
|
|
|
|
|
[[aop-targetsource]] |
|
=== Using TargetSources |
|
Spring offers the concept of a __TargetSource__, expressed in the |
|
`org.springframework.aop.TargetSource` interface. This interface is responsible for |
|
returning the "target object" implementing the join point. The `TargetSource` |
|
implementation is asked for a target instance each time the AOP proxy handles a method |
|
invocation. |
|
|
|
Developers using Spring AOP don't normally need to work directly with TargetSources, but |
|
this provides a powerful means of supporting pooling, hot swappable and other |
|
sophisticated targets. For example, a pooling TargetSource can return a different target |
|
instance for each invocation, using a pool to manage instances. |
|
|
|
If you do not specify a TargetSource, a default implementation is used that wraps a |
|
local object. The same target is returned for each invocation (as you would expect). |
|
|
|
Let's look at the standard target sources provided with Spring, and how you can use them. |
|
|
|
[TIP] |
|
==== |
|
|
|
When using a custom target source, your target will usually need to be a prototype |
|
rather than a singleton bean definition. This allows Spring to create a new target |
|
instance when required. |
|
==== |
|
|
|
|
|
|
|
[[aop-ts-swap]] |
|
==== Hot swappable target sources |
|
The `org.springframework.aop.target.HotSwappableTargetSource` exists to allow the target |
|
of an AOP proxy to be switched while allowing callers to keep their references to it. |
|
|
|
Changing the target source's target takes effect immediately. The |
|
`HotSwappableTargetSource` is threadsafe. |
|
|
|
You can change the target via the `swap()` method on HotSwappableTargetSource as follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
HotSwappableTargetSource swapper = (HotSwappableTargetSource) beanFactory.getBean("swapper"); |
|
Object oldTarget = swapper.swap(newTarget); |
|
---- |
|
|
|
The XML definitions required look as follows: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="initialTarget" class="mycompany.OldTarget"/> |
|
|
|
<bean id="swapper" class="org.springframework.aop.target.HotSwappableTargetSource"> |
|
<constructor-arg ref="initialTarget"/> |
|
</bean> |
|
|
|
<bean id="swappable" class="org.springframework.aop.framework.ProxyFactoryBean"> |
|
<property name="targetSource" ref="swapper"/> |
|
</bean> |
|
---- |
|
|
|
The above `swap()` call changes the target of the swappable bean. Clients who hold a |
|
reference to that bean will be unaware of the change, but will immediately start hitting |
|
the new target. |
|
|
|
Although this example doesn't add any advice - and it's not necessary to add advice to |
|
use a `TargetSource` - of course any `TargetSource` can be used in conjunction with |
|
arbitrary advice. |
|
|
|
|
|
|
|
[[aop-ts-pool]] |
|
==== Pooling target sources |
|
Using a pooling target source provides a similar programming model to stateless session |
|
EJBs, in which a pool of identical instances is maintained, with method invocations |
|
going to free objects in the pool. |
|
|
|
A crucial difference between Spring pooling and SLSB pooling is that Spring pooling can |
|
be applied to any POJO. As with Spring in general, this service can be applied in a |
|
non-invasive way. |
|
|
|
Spring provides out-of-the-box support for Jakarta Commons Pool 1.3, which provides a |
|
fairly efficient pooling implementation. You'll need the commons-pool Jar on your |
|
application's classpath to use this feature. It's also possible to subclass |
|
`org.springframework.aop.target.AbstractPoolingTargetSource` to support any other |
|
pooling API. |
|
|
|
Sample configuration is shown below: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="businessObjectTarget" class="com.mycompany.MyBusinessObject" |
|
scope="prototype"> |
|
... properties omitted |
|
</bean> |
|
|
|
<bean id="poolTargetSource" class="org.springframework.aop.target.CommonsPoolTargetSource"> |
|
<property name="targetBeanName" value="businessObjectTarget"/> |
|
<property name="maxSize" value="25"/> |
|
</bean> |
|
|
|
<bean id="businessObject" class="org.springframework.aop.framework.ProxyFactoryBean"> |
|
<property name="targetSource" ref="poolTargetSource"/> |
|
<property name="interceptorNames" value="myInterceptor"/> |
|
</bean> |
|
---- |
|
|
|
Note that the target object - "businessObjectTarget" in the example - __must__ be a |
|
prototype. This allows the `PoolingTargetSource` implementation to create new instances |
|
of the target to grow the pool as necessary. See the javadocs of |
|
`AbstractPoolingTargetSource` and the concrete subclass you wish to use for information |
|
about its properties: "maxSize" is the most basic, and always guaranteed to be present. |
|
|
|
In this case, "myInterceptor" is the name of an interceptor that would need to be |
|
defined in the same IoC context. However, it isn't necessary to specify interceptors to |
|
use pooling. If you want only pooling, and no other advice, don't set the |
|
interceptorNames property at all. |
|
|
|
It's possible to configure Spring so as to be able to cast any pooled object to the |
|
`org.springframework.aop.target.PoolingConfig` interface, which exposes information |
|
about the configuration and current size of the pool through an introduction. You'll |
|
need to define an advisor like this: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="poolConfigAdvisor" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> |
|
<property name="targetObject" ref="poolTargetSource"/> |
|
<property name="targetMethod" value="getPoolingConfigMixin"/> |
|
</bean> |
|
---- |
|
|
|
This advisor is obtained by calling a convenience method on the |
|
`AbstractPoolingTargetSource` class, hence the use of MethodInvokingFactoryBean. This |
|
advisor's name ("poolConfigAdvisor" here) must be in the list of interceptors names in |
|
the ProxyFactoryBean exposing the pooled object. |
|
|
|
The cast will look as follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject"); |
|
System.out.println("Max pool size is " + conf.getMaxSize()); |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
Pooling stateless service objects is not usually necessary. We don't believe it should |
|
be the default choice, as most stateless objects are naturally thread safe, and instance |
|
pooling is problematic if resources are cached. |
|
==== |
|
|
|
Simpler pooling is available using auto-proxying. It's possible to set the TargetSources |
|
used by any auto-proxy creator. |
|
|
|
|
|
|
|
[[aop-ts-prototype]] |
|
==== Prototype target sources |
|
Setting up a "prototype" target source is similar to a pooling TargetSource. In this |
|
case, a new instance of the target will be created on every method invocation. Although |
|
the cost of creating a new object isn't high in a modern JVM, the cost of wiring up the |
|
new object (satisfying its IoC dependencies) may be more expensive. Thus you shouldn't |
|
use this approach without very good reason. |
|
|
|
To do this, you could modify the `poolTargetSource` definition shown above as follows. |
|
(I've also changed the name, for clarity.) |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="prototypeTargetSource" class="org.springframework.aop.target.PrototypeTargetSource"> |
|
<property name="targetBeanName" ref="businessObjectTarget"/> |
|
</bean> |
|
---- |
|
|
|
There's only one property: the name of the target bean. Inheritance is used in the |
|
TargetSource implementations to ensure consistent naming. As with the pooling target |
|
source, the target bean must be a prototype bean definition. |
|
|
|
|
|
|
|
[[aop-ts-threadlocal]] |
|
==== ThreadLocal target sources |
|
|
|
`ThreadLocal` target sources are useful if you need an object to be created for each |
|
incoming request (per thread that is). The concept of a `ThreadLocal` provide a JDK-wide |
|
facility to transparently store resource alongside a thread. Setting up a |
|
`ThreadLocalTargetSource` is pretty much the same as was explained for the other types |
|
of target source: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="threadlocalTargetSource" class="org.springframework.aop.target.ThreadLocalTargetSource"> |
|
<property name="targetBeanName" value="businessObjectTarget"/> |
|
</bean> |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
ThreadLocals come with serious issues (potentially resulting in memory leaks) when |
|
incorrectly using them in a multi-threaded and multi-classloader environments. One |
|
should always consider wrapping a threadlocal in some other class and never directly use |
|
the `ThreadLocal` itself (except of course in the wrapper class). Also, one should |
|
always remember to correctly set and unset (where the latter simply involved a call to |
|
`ThreadLocal.set(null)`) the resource local to the thread. Unsetting should be done in |
|
any case since not unsetting it might result in problematic behavior. Spring's |
|
ThreadLocal support does this for you and should always be considered in favor of using |
|
ThreadLocals without other proper handling code. |
|
==== |
|
|
|
|
|
|
|
|
|
[[aop-extensibility]] |
|
=== Defining new Advice types |
|
|
|
Spring AOP is designed to be extensible. While the interception implementation strategy |
|
is presently used internally, it is possible to support arbitrary advice types in |
|
addition to the out-of-the-box interception around advice, before, throws advice and |
|
after returning advice. |
|
|
|
The `org.springframework.aop.framework.adapter` package is an SPI package allowing |
|
support for new custom advice types to be added without changing the core framework. |
|
The only constraint on a custom `Advice` type is that it must implement the |
|
`org.aopalliance.aop.Advice` marker interface. |
|
|
|
Please refer to the `org.springframework.aop.framework.adapter` javadocs for further |
|
information. |
|
|
|
|
|
|
|
|
|
[[aop-api-resources]] |
|
=== Further resources |
|
Please refer to the Spring sample applications for further examples of Spring AOP: |
|
|
|
* The JPetStore's default configuration illustrates the use of the |
|
`TransactionProxyFactoryBean` for declarative transaction management. |
|
* The `/attributes` directory of the JPetStore illustrates the use of attribute-driven |
|
declarative transaction management. |
|
|
|
|
|
|
|
|
|
|
|
[[testing]] |
|
== Testing |
|
|
|
|
|
|
|
|
|
[[testing-introduction]] |
|
=== Introduction to Spring Testing |
|
Testing is an integral part of enterprise software development. This chapter focuses on |
|
the value-add of the IoC principle to <<unit-testing,unit testing>> and on the benefits |
|
of the Spring Framework's support for <<integration-testing,integration testing>>. __(A |
|
thorough treatment of testing in the enterprise is beyond the scope of this reference |
|
manual.)__ |
|
|
|
|
|
|
|
|
|
[[unit-testing]] |
|
=== Unit Testing |
|
Dependency Injection should make your code less dependent on the container than it would |
|
be with traditional Java EE development. The POJOs that make up your application should |
|
be testable in JUnit or TestNG tests, with objects simply instantiated using the `new` |
|
operator, __without Spring or any other container__. You can use <<mock-objects,mock |
|
objects>> (in conjunction with other valuable testing techniques) to test your code in |
|
isolation. If you follow the architecture recommendations for Spring, the resulting |
|
clean layering and componentization of your codebase will facilitate easier unit |
|
testing. For example, you can test service layer objects by stubbing or mocking DAO or |
|
Repository interfaces, without needing to access persistent data while running unit |
|
tests. |
|
|
|
True unit tests typically run extremely quickly, as there is no runtime infrastructure |
|
to set up. Emphasizing true unit tests as part of your development methodology will |
|
boost your productivity. You may not need this section of the testing chapter to help |
|
you write effective unit tests for your IoC-based applications. For certain unit testing |
|
scenarios, however, the Spring Framework provides the following mock objects and testing |
|
support classes. |
|
|
|
|
|
|
|
[[mock-objects]] |
|
==== Mock Objects |
|
|
|
|
|
[[mock-objects-env]] |
|
===== Environment |
|
The `org.springframework.mock.env` package contains mock implementations of the |
|
`Environment` and `PropertySource` abstractions (see <<beans-definition-profiles>> |
|
and <<beans-property-source-abstraction>>). `MockEnvironment` and |
|
`MockPropertySource` are useful for developing __out-of-container__ tests for code that |
|
depends on environment-specific properties. |
|
|
|
|
|
[[mock-objects-jndi]] |
|
===== JNDI |
|
The `org.springframework.mock.jndi` package contains an implementation of the JNDI SPI, |
|
which you can use to set up a simple JNDI environment for test suites or stand-alone |
|
applications. If, for example, JDBC ++DataSource++s get bound to the same JNDI names in |
|
test code as within a Java EE container, you can reuse both application code and |
|
configuration in testing scenarios without modification. |
|
|
|
|
|
[[mock-objects-servlet]] |
|
===== Servlet API |
|
The `org.springframework.mock.web` package contains a comprehensive set of Servlet API |
|
mock objects, targeted at usage with Spring's Web MVC framework, which are useful for |
|
testing web contexts and controllers. These mock objects are generally more convenient |
|
to use than dynamic mock objects such as http://www.easymock.org[EasyMock] or existing |
|
Servlet API mock objects such as http://www.mockobjects.com[MockObjects]. |
|
|
|
|
|
[[mock-objects-portlet]] |
|
===== Portlet API |
|
The `org.springframework.mock.web.portlet` package contains a set of Portlet API mock |
|
objects, targeted at usage with Spring's Portlet MVC framework. |
|
|
|
|
|
|
|
[[unit-testing-support-classes]] |
|
==== Unit Testing support Classes |
|
|
|
|
|
[[unit-testing-utilities]] |
|
===== General utilities |
|
The `org.springframework.test.util` package contains `ReflectionTestUtils`, which is a |
|
collection of reflection-based utility methods. Developers use these methods in unit and |
|
integration testing scenarios in which they need to set a non- `public` field or invoke |
|
a non- `public` setter method when testing application code involving, for example: |
|
|
|
* ORM frameworks such as JPA and Hibernate that condone `private` or `protected` field |
|
access as opposed to `public` setter methods for properties in a domain entity. |
|
* Spring's support for annotations such as `@Autowired`, `@Inject`, and `@Resource,` |
|
which provides dependency injection for `private` or `protected` fields, setter |
|
methods, and configuration methods. |
|
|
|
|
|
[[unit-testing-spring-mvc]] |
|
===== Spring MVC |
|
The `org.springframework.test.web` package contains `ModelAndViewAssert`, which you can |
|
use in combination with JUnit, TestNG, or any other testing framework for unit tests |
|
dealing with Spring MVC `ModelAndView` objects. |
|
|
|
.Unit testing Spring MVC Controllers |
|
[TIP] |
|
==== |
|
To test your Spring MVC ++Controller++s, use `ModelAndViewAssert` combined with |
|
`MockHttpServletRequest`, `MockHttpSession`, and so on from the <<mock-objects-servlet, |
|
`org.springframework.mock.web`>> package. |
|
|
|
Note: As of Spring 4.0, the set of mocks in the `org.springframework.mock.web` package |
|
is now based on the Servlet 3.0 API. |
|
==== |
|
|
|
|
|
|
|
|
|
[[integration-testing]] |
|
=== Integration Testing |
|
|
|
|
|
|
|
[[integration-testing-overview]] |
|
==== Overview |
|
It is important to be able to perform some integration testing without requiring |
|
deployment to your application server or connecting to other enterprise infrastructure. |
|
This will enable you to test things such as: |
|
|
|
* The correct wiring of your Spring IoC container contexts. |
|
* Data access using JDBC or an ORM tool. This would include such things as the |
|
correctness of SQL statements, Hibernate queries, JPA entity mappings, etc. |
|
|
|
The Spring Framework provides first-class support for integration testing in the |
|
`spring-test` module. The name of the actual JAR file might include the release version |
|
and might also be in the long `org.springframework.test` form, depending on where you |
|
get it from (see the <<dependency-management,section on Dependency Management>> for an |
|
explanation). This library includes the `org.springframework.test` package, which |
|
contains valuable classes for integration testing with a Spring container. This testing |
|
does not rely on an application server or other deployment environment. Such tests are |
|
slower to run than unit tests but much faster than the equivalent Selenium tests or remote |
|
tests that rely on deployment to an application server. |
|
|
|
In Spring 2.5 and later, unit and integration testing support is provided in the form of |
|
the annotation-driven <<testcontext-framework,Spring TestContext Framework>>. The |
|
TestContext framework is agnostic of the actual testing framework in use, thus allowing |
|
instrumentation of tests in various environments including JUnit, TestNG, and so on. |
|
|
|
|
|
|
|
[[integration-testing-goals]] |
|
==== Goals of Integration Testing |
|
Spring's integration testing support has the following primary goals: |
|
|
|
* To manage <<testing-ctx-management,Spring IoC container caching>> between test |
|
execution. |
|
* To provide <<testing-fixture-di,Dependency Injection of test fixture instances>>. |
|
* To provide <<testing-tx,transaction management>> appropriate to integration testing. |
|
* To supply <<testing-support-classes,Spring-specific base classes>> that assist |
|
developers in writing integration tests. |
|
|
|
The next few sections describe each goal and provide links to implementation and |
|
configuration details. |
|
|
|
|
|
[[testing-ctx-management]] |
|
===== Context management and caching |
|
The Spring TestContext Framework provides consistent loading of Spring |
|
++ApplicationContext++s and ++WebApplicationContext++s as well as caching of those |
|
contexts. Support for the caching of loaded contexts is important, because startup time |
|
can become an issue -- not because of the overhead of Spring itself, but because the |
|
objects instantiated by the Spring container take time to instantiate. For example, a |
|
project with 50 to 100 Hibernate mapping files might take 10 to 20 seconds to load the |
|
mapping files, and incurring that cost before running every test in every test fixture |
|
leads to slower overall test runs that reduce developer productivity. |
|
|
|
Test classes typically declare either an array of __resource locations__ for XML |
|
configuration metadata -- often in the classpath -- or an array of __annotated classes__ |
|
that is used to configure the application. These locations or classes are the same as or |
|
similar to those specified in `web.xml` or other deployment configuration files. |
|
|
|
By default, once loaded, the configured `ApplicationContext` is reused for each test. |
|
Thus the setup cost is incurred only once per test suite, and subsequent test execution |
|
is much faster. In this context, the term __test suite__ means all tests run in the same |
|
JVM -- for example, all tests run from an Ant, Maven, or Gradle build for a given |
|
project or module. In the unlikely case that a test corrupts the application context and |
|
requires reloading -- for example, by modifying a bean definition or the state of an |
|
application object -- the TestContext framework can be configured to reload the |
|
configuration and rebuild the application context before executing the next test. |
|
|
|
See <<testcontext-ctx-management>> and <<testcontext-ctx-management-caching>> with the |
|
TestContext framework. |
|
|
|
|
|
[[testing-fixture-di]] |
|
===== Dependency Injection of test fixtures |
|
When the TestContext framework loads your application context, it can optionally |
|
configure instances of your test classes via Dependency Injection. This provides a |
|
convenient mechanism for setting up test fixtures using preconfigured beans from your |
|
application context. A strong benefit here is that you can reuse application contexts |
|
across various testing scenarios (e.g., for configuring Spring-managed object graphs, |
|
transactional proxies, ++DataSource++s, etc.), thus avoiding the need to duplicate |
|
complex test fixture setup for individual test cases. |
|
|
|
As an example, consider the scenario where we have a class, `HibernateTitleRepository`, |
|
that implements data access logic for a `Title` domain entity. We want to write |
|
integration tests that test the following areas: |
|
|
|
* The Spring configuration: basically, is everything related to the configuration of the |
|
`HibernateTitleRepository` bean correct and present? |
|
* The Hibernate mapping file configuration: is everything mapped correctly, and are the |
|
correct lazy-loading settings in place? |
|
* The logic of the `HibernateTitleRepository`: does the configured instance of this |
|
class perform as anticipated? |
|
|
|
See dependency injection of test fixtures with the <<testcontext-fixture-di,TestContext |
|
framework>>. |
|
|
|
|
|
[[testing-tx]] |
|
===== Transaction management |
|
One common issue in tests that access a real database is their effect on the state of |
|
the persistence store. Even when you're using a development database, changes to the |
|
state may affect future tests. Also, many operations -- such as inserting or modifying |
|
persistent data -- cannot be performed (or verified) outside a transaction. |
|
|
|
The TestContext framework addresses this issue. By default, the framework will create |
|
and roll back a transaction for each test. You simply write code that can assume the |
|
existence of a transaction. If you call transactionally proxied objects in your tests, |
|
they will behave correctly, according to their configured transactional semantics. In |
|
addition, if a test method deletes the contents of selected tables while running within |
|
the transaction managed for the test, the transaction will roll back by default, and the |
|
database will return to its state prior to execution of the test. Transactional support |
|
is provided to a test via a `PlatformTransactionManager` bean defined in the test's |
|
application context. |
|
|
|
If you want a transaction to commit -- unusual, but occasionally useful when you want a |
|
particular test to populate or modify the database -- the TestContext framework can be |
|
instructed to cause the transaction to commit instead of roll back via the |
|
<<integration-testing-annotations, `@TransactionConfiguration`>> and |
|
<<integration-testing-annotations, `@Rollback`>> annotations. |
|
|
|
See transaction management with the <<testcontext-tx,TestContext framework>>. |
|
|
|
|
|
[[testing-support-classes]] |
|
===== Support classes for integration testing |
|
The Spring TestContext Framework provides several `abstract` support classes that |
|
simplify the writing of integration tests. These base test classes provide well-defined |
|
hooks into the testing framework as well as convenient instance variables and methods, |
|
which enable you to access: |
|
|
|
* The `ApplicationContext`, for performing explicit bean lookups or testing the state of |
|
the context as a whole. |
|
* A `JdbcTemplate`, for executing SQL statements to query the database. Such queries can |
|
be used to confirm database state both __prior to__ and __after__ execution of |
|
database-related application code, and Spring ensures that such queries run in the |
|
scope of the same transaction as the application code. When used in conjunction with |
|
an ORM tool, be sure to avoid <<testcontext-tx-false-positives,false positives>>. |
|
|
|
In addition, you may want to create your own custom, application-wide superclass with |
|
instance variables and methods specific to your project. |
|
|
|
See support classes for the <<testcontext-support-classes,TestContext framework>>. |
|
|
|
|
|
|
|
[[integration-testing-support-jdbc]] |
|
==== JDBC Testing Support |
|
The `org.springframework.test.jdbc` package contains `JdbcTestUtils`, which is a |
|
collection of JDBC related utility functions intended to simplify standard database |
|
testing scenarios. Specifically, `JdbcTestUtils` provides the following static utility |
|
methods. |
|
|
|
* `countRowsInTable(..)`: counts the number of rows in the given table |
|
* `countRowsInTableWhere(..)`: counts the number of rows in the given table, using |
|
the provided `WHERE` clause |
|
* `deleteFromTables(..)`: deletes all rows from the specified tables |
|
* `deleteFromTableWhere(..)`: deletes rows from the given table, using the provided |
|
`WHERE` clause |
|
* `dropTables(..)`: drops the specified tables |
|
|
|
__Note that <<testcontext-support-classes-junit4, |
|
`AbstractTransactionalJUnit4SpringContextTests`>> and |
|
<<testcontext-support-classes-testng, `AbstractTransactionalTestNGSpringContextTests`>> |
|
provide convenience methods which delegate to the aforementioned methods in |
|
`JdbcTestUtils`.__ |
|
|
|
The `spring-jdbc` module provides support for configuring and launching an embedded |
|
database which can be used in integration tests that interact with a database. For |
|
details, see <<jdbc-embedded-database-support>> and |
|
<<jdbc-embedded-database-dao-testing>>. |
|
|
|
|
|
|
|
[[integration-testing-annotations]] |
|
==== Annotations |
|
|
|
|
|
[[integration-testing-annotations-spring]] |
|
===== Spring Testing Annotations |
|
The Spring Framework provides the following set of __Spring-specific__ annotations that |
|
you can use in your unit and integration tests in conjunction with the TestContext |
|
framework. Refer to the corresponding javadocs for further information, including |
|
default attribute values, attribute aliases, and so on. |
|
|
|
* `@ContextConfiguration` |
|
|
|
+ |
|
|
|
Defines class-level metadata that is used to determine how to load and configure an |
|
`ApplicationContext` for integration tests. Specifically, `@ContextConfiguration` |
|
declares the application context resource `locations` or the annotated `classes` |
|
that will be used to load the context. |
|
|
|
+ |
|
|
|
Resource locations are typically XML configuration files located in the classpath; |
|
whereas, annotated classes are typically `@Configuration` classes. However, resource |
|
locations can also refer to files in the file system, and annotated classes can be |
|
component classes, etc. |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
**@ContextConfiguration**("/test-config.xml") |
|
public class XmlApplicationContextTests { |
|
// class body... |
|
} |
|
---- |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
**@ContextConfiguration**(**classes** = TestConfig.class) |
|
public class ConfigClassApplicationContextTests { |
|
// class body... |
|
} |
|
---- |
|
|
|
+ |
|
|
|
As an alternative or in addition to declaring resource locations or annotated classes, |
|
`@ContextConfiguration` may be used to declare `ApplicationContextInitializer` classes. |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
**@ContextConfiguration**(**initializers** = CustomContextIntializer.class) |
|
public class ContextInitializerTests { |
|
// class body... |
|
} |
|
---- |
|
|
|
+ |
|
|
|
`@ContextConfiguration` may optionally be used to declare the `ContextLoader` strategy |
|
as well. Note, however, that you typically do not need to explicitly configure the |
|
loader since the default loader supports either resource `locations` or annotated |
|
`classes` as well as `initializers`. |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
**@ContextConfiguration**(**locations** = "/test-context.xml", **loader** = CustomContextLoader.class) |
|
public class CustomLoaderXmlApplicationContextTests { |
|
// class body... |
|
} |
|
---- |
|
|
|
+ |
|
|
|
[NOTE] |
|
==== |
|
`@ContextConfiguration` provides support for __inheriting__ resource locations or |
|
configuration classes as well as context initializers declared by superclasses by |
|
default. |
|
==== |
|
|
|
+ |
|
|
|
See <<testcontext-ctx-management>> and the `@ContextConfiguration` javadocs for |
|
further details. |
|
|
|
* `@WebAppConfiguration` |
|
|
|
+ |
|
|
|
A class-level annotation that is used to declare that the `ApplicationContext` loaded |
|
for an integration test should be a `WebApplicationContext`. The mere presence of |
|
`@WebAppConfiguration` on a test class ensures that a `WebApplicationContext` will be |
|
loaded for the test, using the default value of `"file:src/main/webapp"` for the path to |
|
the root of the web application (i.e., the __resource base path__). The resource base |
|
path is used behind the scenes to create a `MockServletContext` which serves as the |
|
`ServletContext` for the test's `WebApplicationContext`. |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@ContextConfiguration |
|
**@WebAppConfiguration** |
|
public class WebAppTests { |
|
// class body... |
|
} |
|
---- |
|
|
|
+ |
|
|
|
To override the default, specify a different base resource path via the __implicit__ |
|
`value` attribute. Both `classpath:` and `file:` resource prefixes are supported. If no |
|
resource prefix is supplied the path is assumed to be a file system resource. |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@ContextConfiguration |
|
**@WebAppConfiguration("classpath:test-web-resources")** |
|
public class WebAppTests { |
|
// class body... |
|
} |
|
---- |
|
|
|
+ |
|
|
|
Note that `@WebAppConfiguration` must be used in conjunction with |
|
`@ContextConfiguration`, either within a single test class or within a test class |
|
hierarchy. See the `@WebAppConfiguration` javadocs for further details. |
|
|
|
+ |
|
|
|
* `@ContextHierarchy` |
|
|
|
+ |
|
|
|
A class-level annotation that is used to define a hierarchy of ++ApplicationContext++s |
|
for integration tests. `@ContextHierarchy` should be declared with a list of one or more |
|
`@ContextConfiguration` instances, each of which defines a level in the context |
|
hierarchy. The following examples demonstrate the use of `@ContextHierarchy` within a |
|
single test class; however, `@ContextHierarchy` can also be used within a test class |
|
hierarchy. |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@ContextHierarchy({ |
|
@ContextConfiguration("/parent-config.xml"), |
|
@ContextConfiguration("/child-config.xml") |
|
}) |
|
public class ContextHierarchyTests { |
|
// class body... |
|
} |
|
---- |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@WebAppConfiguration |
|
@ContextHierarchy({ |
|
@ContextConfiguration(classes = AppConfig.class), |
|
@ContextConfiguration(classes = WebConfig.class) |
|
}) |
|
public class WebIntegrationTests { |
|
// class body... |
|
} |
|
---- |
|
|
|
+ |
|
|
|
If you need to merge or override the configuration for a given level of the context |
|
hierarchy within a test class hierarchy, you must explicitly name that level by |
|
supplying the same value to the `name` attribute in `@ContextConfiguration` at each |
|
corresponding level in the class hierarchy. See |
|
<<testcontext-ctx-management-ctx-hierarchies>> and the `@ContextHierarchy` javadocs |
|
for further examples. |
|
|
|
* `@ActiveProfiles` |
|
|
|
+ |
|
|
|
A class-level annotation that is used to declare which __bean definition profiles__ |
|
should be active when loading an `ApplicationContext` for test classes. |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@ContextConfiguration |
|
**@ActiveProfiles**("dev") |
|
public class DeveloperTests { |
|
// class body... |
|
} |
|
---- |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@ContextConfiguration |
|
**@ActiveProfiles**({"dev", "integration"}) |
|
public class DeveloperIntegrationTests { |
|
// class body... |
|
} |
|
---- |
|
|
|
+ |
|
|
|
[NOTE] |
|
==== |
|
`@ActiveProfiles` provides support for __inheriting__ active bean definition profiles |
|
declared by superclasses by default. It is also possible to resolve active bean |
|
definition profiles programmatically by implementing a custom |
|
<<testcontext-ctx-management-env-profiles-ActiveProfilesResolver,`ActiveProfilesResolver`>> |
|
and registering it via the `resolver` attribute of `@ActiveProfiles`. |
|
==== |
|
|
|
+ |
|
|
|
See <<testcontext-ctx-management-env-profiles>> and the `@ActiveProfiles` javadocs |
|
for examples and further details. |
|
|
|
* `@TestPropertySource` |
|
|
|
+ |
|
|
|
A class-level annotation that is used to configure the locations of properties files and |
|
inlined properties to be added to the `Environment`'s set of `PropertySources` for an |
|
`ApplicationContext` loaded for an integration test. |
|
|
|
+ |
|
|
|
Test property sources have higher precedence than those loaded from the operating |
|
system's environment or Java system properties as well as property sources added by the |
|
application declaratively via `@PropertySource` or programmatically. Thus, test property |
|
sources can be used to selectively override properties defined in system and application |
|
property sources. Furthermore, inlined properties have higher precedence than properties |
|
loaded from resource locations. |
|
|
|
+ |
|
|
|
The following example demonstrates how to declare a properties file from the classpath. |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@ContextConfiguration |
|
**@TestPropertySource**("/test.properties") |
|
public class MyIntegrationTests { |
|
// class body... |
|
} |
|
---- |
|
|
|
+ |
|
|
|
The following example demonstrates how to declare _inlined_ properties. |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@ContextConfiguration |
|
**@TestPropertySource**(properties = { "timezone = GMT", "port: 4242" }) |
|
public class MyIntegrationTests { |
|
// class body... |
|
} |
|
---- |
|
|
|
* `@DirtiesContext` |
|
|
|
+ |
|
|
|
Indicates that the underlying Spring `ApplicationContext` has been __dirtied__ during |
|
the execution of a test (i.e., modified or corrupted in some manner -- for example, by |
|
changing the state of a singleton bean) and should be closed, regardless of whether the |
|
test passed. When an application context is marked __dirty__, it is removed from the |
|
testing framework's cache and closed. As a consequence, the underlying Spring container |
|
will be rebuilt for any subsequent test that requires a context with the same |
|
configuration metadata. |
|
|
|
+ |
|
|
|
`@DirtiesContext` can be used as both a class-level and method-level annotation within |
|
the same test class. In such scenarios, the `ApplicationContext` is marked as __dirty__ |
|
after any such annotated method as well as after the entire class. If the `ClassMode` is |
|
set to `AFTER_EACH_TEST_METHOD`, the context is marked dirty after each test method in |
|
the class. |
|
|
|
+ |
|
|
|
The following examples explain when the context would be dirtied for various |
|
configuration scenarios: |
|
|
|
+ |
|
|
|
** After the current test class, when declared on a class with class mode set to |
|
`AFTER_CLASS` (i.e., the default class mode). |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
**@DirtiesContext** |
|
public class ContextDirtyingTests { |
|
// some tests that result in the Spring container being dirtied |
|
} |
|
---- |
|
|
|
+ |
|
|
|
** After each test method in the current test class, when declared on a class with class |
|
mode set to `AFTER_EACH_TEST_METHOD.` |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
**@DirtiesContext**(**classMode** = ClassMode.AFTER_EACH_TEST_METHOD) |
|
public class ContextDirtyingTests { |
|
// some tests that result in the Spring container being dirtied |
|
} |
|
---- |
|
|
|
+ |
|
|
|
** After the current test, when declared on a method. |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
**@DirtiesContext** |
|
@Test |
|
public void testProcessWhichDirtiesAppCtx() { |
|
// some logic that results in the Spring container being dirtied |
|
} |
|
---- |
|
|
|
+ |
|
|
|
If `@DirtiesContext` is used in a test whose context is configured as part of a context |
|
hierarchy via `@ContextHierarchy`, the `hierarchyMode` flag can be used to control how |
|
the context cache is cleared. By default an __exhaustive__ algorithm will be used that |
|
clears the context cache including not only the current level but also all other context |
|
hierarchies that share an ancestor context common to the current test; all |
|
++ApplicationContext++s that reside in a sub-hierarchy of the common ancestor context |
|
will be removed from the context cache and closed. If the __exhaustive__ algorithm is |
|
overkill for a particular use case, the simpler __current level__ algorithm can be |
|
specified instead, as seen below. |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@ContextHierarchy({ |
|
@ContextConfiguration("/parent-config.xml"), |
|
@ContextConfiguration("/child-config.xml") |
|
}) |
|
public class BaseTests { |
|
// class body... |
|
} |
|
|
|
public class ExtendedTests extends BaseTests { |
|
|
|
@Test |
|
@DirtiesContext(**hierarchyMode = HierarchyMode.CURRENT_LEVEL**) |
|
public void test() { |
|
// some logic that results in the child context being dirtied |
|
} |
|
} |
|
---- |
|
|
|
+ |
|
|
|
For further details regarding the `EXHAUSTIVE` and `CURRENT_LEVEL` algorithms see the |
|
`DirtiesContext.HierarchyMode` javadocs. |
|
|
|
* `@TestExecutionListeners` |
|
|
|
+ |
|
|
|
Defines class-level metadata for configuring which ++TestExecutionListener++s should be |
|
registered with the `TestContextManager`. Typically, `@TestExecutionListeners` is used |
|
in conjunction with `@ContextConfiguration`. |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@ContextConfiguration |
|
**@TestExecutionListeners**({CustomTestExecutionListener.class, AnotherTestExecutionListener.class}) |
|
public class CustomTestExecutionListenerTests { |
|
// class body... |
|
} |
|
---- |
|
|
|
+ |
|
|
|
`@TestExecutionListeners` supports __inherited__ listeners by default. See the javadocs |
|
for an example and further details. |
|
|
|
* `@TransactionConfiguration` |
|
|
|
+ |
|
|
|
Defines class-level metadata for configuring transactional tests. Specifically, the bean |
|
name of the `PlatformTransactionManager` that should be used to drive transactions can |
|
be explicitly specified if there are multiple beans of type `PlatformTransactionManager` |
|
in the test's `ApplicationContext` and if the bean name of the desired |
|
`PlatformTransactionManager` is not "transactionManager". In addition, you can change |
|
the `defaultRollback` flag to `false`. Typically, `@TransactionConfiguration` is used in |
|
conjunction with `@ContextConfiguration`. |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@ContextConfiguration |
|
**@TransactionConfiguration**(**transactionManager** = "txMgr", **defaultRollback** = false) |
|
public class CustomConfiguredTransactionalTests { |
|
// class body... |
|
} |
|
---- |
|
|
|
+ |
|
|
|
[NOTE] |
|
==== |
|
If the default conventions are sufficient for your test configuration, you can avoid |
|
using `@TransactionConfiguration` altogether. In other words, if you have only one |
|
transaction manager -- or if you have multiple transaction managers but the transaction |
|
manager for tests is named "transactionManager" or specified via a |
|
`TransactionManagementConfigurer` -- and if you want transactions to roll back |
|
automatically, then there is no need to annotate your test class with |
|
`@TransactionConfiguration`. |
|
==== |
|
|
|
+ |
|
|
|
* `@Rollback` |
|
|
|
+ |
|
|
|
Indicates whether the transaction for the annotated test method should be __rolled |
|
back__ after the test method has completed. If `true`, the transaction is rolled back; |
|
otherwise, the transaction is committed. Use `@Rollback` to override the default |
|
rollback flag configured at the class level. |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
**@Rollback**(false) |
|
@Test |
|
public void testProcessWithoutRollback() { |
|
// ... |
|
} |
|
---- |
|
|
|
* `@BeforeTransaction` |
|
|
|
+ |
|
|
|
Indicates that the annotated `public void` method should be executed __before__ a |
|
transaction is started for test methods configured to run within a transaction via the |
|
`@Transactional` annotation. |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
**@BeforeTransaction** |
|
public void beforeTransaction() { |
|
// logic to be executed before a transaction is started |
|
} |
|
---- |
|
|
|
* `@AfterTransaction` |
|
|
|
+ |
|
|
|
Indicates that the annotated `public void` method should be executed __after__ a |
|
transaction has ended for test methods configured to run within a transaction via the |
|
`@Transactional` annotation. |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
**@AfterTransaction** |
|
public void afterTransaction() { |
|
// logic to be executed after a transaction has ended |
|
} |
|
---- |
|
|
|
* `@Sql` |
|
|
|
+ |
|
|
|
Used to annotate a test class or test method to configure SQL scripts to be executed |
|
against a given database during integration tests. |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Test |
|
**@Sql**({"/test-schema.sql", "/test-user-data.sql"}) |
|
public void userTest { |
|
// execute code that relies on the test schema and test data |
|
} |
|
---- |
|
|
|
+ |
|
|
|
See <<testcontext-executing-sql-declaratively>> for further details. |
|
|
|
* `@SqlConfig` |
|
|
|
+ |
|
|
|
Defines metadata that is used to determine how to parse and execute SQL scripts |
|
configured via the `@Sql` annotation. |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Test |
|
@Sql( |
|
scripts = "/test-user-data.sql", |
|
config = **@SqlConfig**(commentPrefix = "`", separator = "@@") |
|
) |
|
public void userTest { |
|
// execute code that relies on the test data |
|
} |
|
---- |
|
|
|
* `@SqlGroup` |
|
|
|
+ |
|
|
|
A container annotation that aggregates several `@Sql` annotations. Can be used natively, |
|
declaring several nested `@Sql` annotations. Can also be used in conjunction with Java |
|
8's support for repeatable annotations, where `@Sql` can simply be declared several times |
|
on the same class or method, implicitly generating this container annotation. |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Test |
|
**@SqlGroup**({ |
|
@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`")), |
|
@Sql("/test-user-data.sql") |
|
)} |
|
public void userTest { |
|
// execute code that uses the test schema and test data |
|
} |
|
---- |
|
|
|
|
|
[[integration-testing-annotations-standard]] |
|
===== Standard Annotation Support |
|
The following annotations are supported with standard semantics for all configurations |
|
of the Spring TestContext Framework. Note that these annotations are not specific to |
|
tests and can be used anywhere in the Spring Framework. |
|
|
|
* `@Autowired` |
|
* `@Qualifier` |
|
* `@Resource` (javax.annotation) _if JSR-250 is present_ |
|
* `@Inject` (javax.inject) _if JSR-330 is present_ |
|
* `@Named` (javax.inject) _if JSR-330 is present_ |
|
* `@PersistenceContext` (javax.persistence) _if JPA is present_ |
|
* `@PersistenceUnit` (javax.persistence) _if JPA is present_ |
|
* `@Required` |
|
* `@Transactional` |
|
|
|
.JSR-250 Lifecycle Annotations |
|
[NOTE] |
|
==== |
|
In the Spring TestContext Framework `@PostConstruct` and `@PreDestroy` may be used with |
|
standard semantics on any application components configured in the `ApplicationContext`; |
|
however, these lifecycle annotations have limited usage within an actual test class. |
|
|
|
If a method within a test class is annotated with `@PostConstruct`, that method will be |
|
executed before any __before__ methods of the underlying test framework (e.g., methods |
|
annotated with JUnit's `@Before`), and that will apply for every test method in the test |
|
class. On the other hand, if a method within a test class is annotated with |
|
`@PreDestroy`, that method will __never__ be executed. Within a test class it is |
|
therefore recommended to use test lifecycle callbacks from the underlying test framework |
|
instead of `@PostConstruct` and `@PreDestroy`. |
|
==== |
|
|
|
|
|
[[integration-testing-annotations-junit]] |
|
===== Spring JUnit Testing Annotations |
|
The following annotations are __only__ supported when used in conjunction with the |
|
<<testcontext-junit4-runner,SpringJUnit4ClassRunner>> or the |
|
<<testcontext-support-classes-junit4,JUnit>> support classes. |
|
|
|
* `@IfProfileValue` |
|
|
|
+ |
|
|
|
Indicates that the annotated test is enabled for a specific testing environment. If the |
|
configured `ProfileValueSource` returns a matching `value` for the provided `name`, the |
|
test is enabled. This annotation can be applied to an entire class or to individual |
|
methods. Class-level usage overrides method-level usage. |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
**@IfProfileValue**(**name**="java.vendor", **value**="Oracle Corporation") |
|
@Test |
|
public void testProcessWhichRunsOnlyOnOracleJvm() { |
|
// some logic that should run only on Java VMs from Oracle Corporation |
|
} |
|
---- |
|
|
|
+ |
|
|
|
Alternatively, you can configure `@IfProfileValue` with a list of `values` (with __OR__ |
|
semantics) to achieve TestNG-like support for __test groups__ in a JUnit environment. |
|
Consider the following example: |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
**@IfProfileValue**(**name**="test-groups", **values**={"unit-tests", "integration-tests"}) |
|
@Test |
|
public void testProcessWhichRunsForUnitOrIntegrationTestGroups() { |
|
// some logic that should run only for unit and integration test groups |
|
} |
|
---- |
|
|
|
+ |
|
|
|
* `@ProfileValueSourceConfiguration` |
|
|
|
+ |
|
|
|
Class-level annotation that specifies what type of `ProfileValueSource` to use when |
|
retrieving __profile values__ configured through the `@IfProfileValue` annotation. If |
|
`@ProfileValueSourceConfiguration` is not declared for a test, |
|
`SystemProfileValueSource` is used by default. |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
**@ProfileValueSourceConfiguration**(CustomProfileValueSource.class) |
|
public class CustomProfileValueSourceTests { |
|
// class body... |
|
} |
|
---- |
|
|
|
* `@Timed` |
|
|
|
+ |
|
|
|
Indicates that the annotated test method must finish execution in a specified time |
|
period (in milliseconds). If the text execution time exceeds the specified time period, |
|
the test fails. |
|
|
|
+ |
|
|
|
The time period includes execution of the test method itself, any repetitions of the |
|
test (see `@Repeat`), as well as any __set up__ or __tear down__ of the test fixture. |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
**@Timed**(millis=1000) |
|
public void testProcessWithOneSecondTimeout() { |
|
// some logic that should not take longer than 1 second to execute |
|
} |
|
---- |
|
|
|
+ |
|
|
|
Spring's `@Timed` annotation has different semantics than JUnit's `@Test(timeout=...)` |
|
support. Specifically, due to the manner in which JUnit handles test execution timeouts |
|
(that is, by executing the test method in a separate `Thread`), `@Test(timeout=...)` |
|
preemptively fails the test if the test takes too long. Spring's `@Timed`, on the other |
|
hand, does not preemptively fail the test but rather waits for the test to complete |
|
before failing. |
|
|
|
* `@Repeat` |
|
|
|
+ |
|
|
|
Indicates that the annotated test method must be executed repeatedly. The number of |
|
times that the test method is to be executed is specified in the annotation. |
|
|
|
+ |
|
|
|
The scope of execution to be repeated includes execution of the test method itself as |
|
well as any __set up__ or __tear down__ of the test fixture. |
|
|
|
+ |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
**@Repeat**(10) |
|
@Test |
|
public void testProcessRepeatedly() { |
|
// ... |
|
} |
|
---- |
|
|
|
|
|
[[integration-testing-annotations-meta]] |
|
===== Meta-Annotation Support for Testing |
|
As of Spring Framework 4.0, it is possible to use test-related annotations as |
|
<<beans-meta-annotations,meta-annotations>> in order to create custom _composed annotations_ |
|
and reduce configuration duplication across a test suite. |
|
|
|
Each of the following may be used as meta-annotations in conjunction with the |
|
<<testcontext-framework,TestContext framework>>. |
|
|
|
* `@ContextConfiguration` |
|
* `@ContextHierarchy` |
|
* `@ActiveProfiles` |
|
* `@TestPropertySource` |
|
* `@DirtiesContext` |
|
* `@WebAppConfiguration` |
|
* `@TestExecutionListeners` |
|
* `@Transactional` |
|
* `@BeforeTransaction` |
|
* `@AfterTransaction` |
|
* `@TransactionConfiguration` |
|
* `@Rollback` |
|
* `@Sql` |
|
* `@SqlConfig` |
|
* `@SqlGroup` |
|
* `@Repeat` |
|
* `@Timed` |
|
* `@IfProfileValue` |
|
* `@ProfileValueSourceConfiguration` |
|
|
|
For example, if we discover that we are repeating the following configuration |
|
across our JUnit-based test suite... |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"}) |
|
@ActiveProfiles("dev") |
|
@Transactional |
|
public class OrderRepositoryTests { } |
|
|
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"}) |
|
@ActiveProfiles("dev") |
|
@Transactional |
|
public class UserRepositoryTests { } |
|
---- |
|
|
|
We can reduce the above duplication by introducing a custom _composed annotation_ |
|
that centralizes the common test configuration like this: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Target(ElementType.TYPE) |
|
@Retention(RetentionPolicy.RUNTIME) |
|
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"}) |
|
@ActiveProfiles("dev") |
|
@Transactional |
|
public @interface TransactionalDevTest { } |
|
---- |
|
|
|
Then we can use our custom `@TransactionalDevTest` annotation to simplify the |
|
configuration of individual test classes as follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
@TransactionalDevTest |
|
public class OrderRepositoryTests { } |
|
|
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
@TransactionalDevTest |
|
public class UserRepositoryTests { } |
|
---- |
|
|
|
|
|
|
|
[[testcontext-framework]] |
|
==== Spring TestContext Framework |
|
The __Spring TestContext Framework__ (located in the |
|
`org.springframework.test.context` package) provides generic, annotation-driven unit and |
|
integration testing support that is agnostic of the testing framework in use. The |
|
TestContext framework also places a great deal of importance on __convention over |
|
configuration__ with reasonable defaults that can be overridden through annotation-based |
|
configuration. |
|
|
|
In addition to generic testing infrastructure, the TestContext framework provides |
|
explicit support for JUnit and TestNG in the form of `abstract` support classes. For |
|
JUnit, Spring also provides a custom JUnit `Runner` that allows one to write so-called |
|
__POJO test classes__. POJO test classes are not required to extend a particular class |
|
hierarchy. |
|
|
|
The following section provides an overview of the internals of the TestContext |
|
framework. If you are only interested in using the framework and not necessarily |
|
interested in extending it with your own custom listeners or custom loaders, feel free |
|
to go directly to the configuration (<<testcontext-ctx-management,context management>>, |
|
<<testcontext-fixture-di,dependency injection>>, <<testcontext-tx,transaction |
|
management>>), <<testcontext-support-classes,support classes>>, and |
|
<<integration-testing-annotations,annotation support>> sections. |
|
|
|
|
|
[[testcontext-key-abstractions]] |
|
===== Key abstractions |
|
The core of the framework consists of the `TestContext` and `TestContextManager` classes |
|
and the `TestExecutionListener`, `ContextLoader`, and `SmartContextLoader` interfaces. A |
|
`TestContextManager` is created on a per-test basis (e.g., for the execution of a single |
|
test method in JUnit). The `TestContextManager` in turn manages a `TestContext` that |
|
holds the context of the current test. The `TestContextManager` also updates the state |
|
of the `TestContext` as the test progresses and delegates to ++TestExecutionListener++s, |
|
which instrument the actual test execution by providing dependency injection, managing |
|
transactions, and so on. A `ContextLoader` (or `SmartContextLoader`) is responsible for |
|
loading an `ApplicationContext` for a given test class. Consult the javadocs and the |
|
Spring test suite for further information and examples of various implementations. |
|
|
|
* `TestContext`: Encapsulates the context in which a test is executed, agnostic of the |
|
actual testing framework in use, and provides context management and caching support |
|
for the test instance for which it is responsible. The `TestContext` also delegates to |
|
a `ContextLoader` (or `SmartContextLoader`) to load an `ApplicationContext` if |
|
requested. |
|
* `TestContextManager`: The main entry point into the __Spring TestContext Framework__, |
|
which manages a single `TestContext` and signals events to all registered |
|
++TestExecutionListener++s at well-defined test execution points: |
|
** prior to any __before class methods__ of a particular testing framework |
|
** test instance preparation |
|
** prior to any __before methods__ of a particular testing framework |
|
** after any __after methods__ of a particular testing framework |
|
** after any __after class methods__ of a particular testing framework |
|
* `TestExecutionListener`: Defines a __listener__ API for reacting to test execution |
|
events published by the `TestContextManager` with which the listener is registered. See |
|
<<testcontext-tel-config>>. |
|
* `ContextLoader`: Strategy interface introduced in Spring 2.5 for loading an |
|
`ApplicationContext` for an integration test managed by the Spring TestContext |
|
Framework. |
|
|
|
+ |
|
|
|
Implement `SmartContextLoader` instead of this interface in order to provide support for |
|
annotated classes, active bean definition profiles, test property sources, context |
|
hierarchies, and ++WebApplicationContext++s. |
|
|
|
* `SmartContextLoader`: Extension of the `ContextLoader` interface introduced in Spring |
|
3.1. |
|
|
|
+ |
|
|
|
The `SmartContextLoader` SPI supersedes the `ContextLoader` SPI that was introduced in |
|
Spring 2.5. Specifically, a `SmartContextLoader` can choose to process resource |
|
`locations`, annotated `classes`, or context `initializers`. Furthermore, a |
|
`SmartContextLoader` can set active bean definition profiles and test property sources in |
|
the context that it loads. |
|
|
|
+ |
|
|
|
Spring provides the following implementations: |
|
|
|
+ |
|
|
|
** `DelegatingSmartContextLoader`: one of two default loaders which delegates internally |
|
to an `AnnotationConfigContextLoader`, a `GenericXmlContextLoader`, or a |
|
`GenericGroovyXmlContextLoader` depending either on the configuration declared for the |
|
test class or on the presence of default locations or default configuration classes. |
|
Groovy support is only enabled if Groovy is on the classpath. |
|
** `WebDelegatingSmartContextLoader`: one of two default loaders which delegates |
|
internally to an `AnnotationConfigWebContextLoader`, a `GenericXmlWebContextLoader`, or a |
|
`GenericGroovyXmlWebContextLoader` depending either on the configuration declared for the |
|
test class or on the presence of default locations or default configuration classes. A |
|
web `ContextLoader` will only be used if `@WebAppConfiguration` is present on the test |
|
class. Groovy support is only enabled if Groovy is on the classpath. |
|
** `AnnotationConfigContextLoader`: loads a standard `ApplicationContext` from |
|
__annotated classes__. |
|
** `AnnotationConfigWebContextLoader`: loads a `WebApplicationContext` from __annotated |
|
classes__. |
|
** `GenericGroovyXmlContextLoader`: loads a standard `ApplicationContext` from __resource |
|
locations__ that are either Groovy scripts or XML configuration files. |
|
** `GenericGroovyXmlWebContextLoader`: loads a `WebApplicationContext` from __resource |
|
locations__ that are either Groovy scripts or XML configuration files. |
|
** `GenericXmlContextLoader`: loads a standard `ApplicationContext` from XML __resource |
|
locations__. |
|
** `GenericXmlWebContextLoader`: loads a `WebApplicationContext` from XML __resource |
|
locations__. |
|
** `GenericPropertiesContextLoader`: loads a standard `ApplicationContext` from Java |
|
Properties files. |
|
|
|
The following sections explain how to configure the TestContext framework through |
|
annotations and provide working examples of how to write unit and integration tests with |
|
the framework. |
|
|
|
[[testcontext-tel-config]] |
|
===== TestExecutionListener configuration |
|
|
|
Spring provides the following `TestExecutionListener` implementations that are registered |
|
by default, exactly in this order. |
|
|
|
* `ServletTestExecutionListener`: configures Servlet API mocks for a |
|
`WebApplicationContext` |
|
* `DependencyInjectionTestExecutionListener`: provides dependency injection for the test |
|
instance |
|
* `DirtiesContextTestExecutionListener`: handles the `@DirtiesContext` annotation |
|
* `TransactionalTestExecutionListener`: provides transactional test execution with |
|
default rollback semantics |
|
* `SqlScriptsTestExecutionListener`: executes SQL scripts configured via the `@Sql` |
|
annotation |
|
|
|
[[testcontext-tel-config-registering-tels]] |
|
====== Registering custom TestExecutionListeners |
|
|
|
Custom ++TestExecutionListener++s can be registered for a test class and its subclasses |
|
via the `@TestExecutionListeners` annotation. See |
|
<<integration-testing-annotations,annotation support>> and the javadocs for |
|
`@TestExecutionListeners` for details and examples. |
|
|
|
[[testcontext-tel-config-automatic-discovery]] |
|
====== Automatic discovery of default TestExecutionListeners |
|
|
|
Registering custom ++TestExecutionListener++s via `@TestExecutionListeners` is suitable |
|
for custom listeners that are used in limited testing scenarios; however, it can become |
|
cumbersome if a custom listener needs to be used across a test suite. To address this |
|
issue, Spring Framework 4.1 supports automatic discovery of _default_ |
|
`TestExecutionListener` implementations via the `SpringFactoriesLoader` mechanism. |
|
|
|
Specifically, the `spring-test` module declares all core default |
|
++TestExecutionListener++s under the |
|
`org.springframework.test.context.TestExecutionListener` key in its |
|
`META-INF/spring.factories` properties file. Third-party frameworks and developers can |
|
contribute their own ++TestExecutionListener++s to the list of default listeners in the |
|
same manner via their own `META-INF/spring.factories` properties file. |
|
|
|
[[testcontext-tel-config-ordering]] |
|
====== Ordering TestExecutionListeners |
|
|
|
When the TestContext framework discovers default ++TestExecutionListeners++ via the |
|
aforementioned `SpringFactoriesLoader` mechanism, the instantiated listeners are sorted |
|
using Spring's `AnnotationAwareOrderComparator` which honors Spring's `Ordered` interface |
|
and `@Order` annotation for ordering. `AbstractTestExecutionListener` and all default |
|
++TestExecutionListener++s provided by Spring implement `Ordered` with appropriate |
|
values. Third-party frameworks and developers should therefore make sure that their |
|
_default_ ++TestExecutionListener++s are registered in the proper order by implementing |
|
`Ordered` or declaring `@Order`. Consult the javadocs for the `getOrder()` methods of the |
|
core default ++TestExecutionListener++s for details on what values are assigned to each |
|
core listener. |
|
|
|
[[testcontext-tel-config-merging]] |
|
====== Merging TestExecutionListeners |
|
|
|
If a custom `TestExecutionListener` is registered via `@TestExecutionListeners`, the |
|
_default_ listeners will not be registered. In most common testing scenarios, this |
|
effectively forces the developer to manually declare all default listeners in addition to |
|
any custom listeners. The following listing demonstrates this style of configuration. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@ContextConfiguration |
|
@TestExecutionListeners({ |
|
MyCustomTestExecutionListener.class, |
|
ServletTestExecutionListener.class, |
|
DependencyInjectionTestExecutionListener.class, |
|
DirtiesContextTestExecutionListener.class, |
|
TransactionalTestExecutionListener.class, |
|
SqlScriptsTestExecutionListener.class |
|
}) |
|
public class MyTest { |
|
// class body... |
|
} |
|
---- |
|
|
|
The challenge with this approach is that it requires that the developer know exactly |
|
which listeners are registered by default. Moreover, the set of default listeners can |
|
change from release to release -- for example, `SqlScriptsTestExecutionListener` was |
|
introduced in Spring Framework 4.1. Furthermore, third-party frameworks like Spring |
|
Security register their own default ++TestExecutionListener++s via the aforementioned |
|
<<testcontext-tel-config-automatic-discovery, automatic discovery mechanism>>. |
|
|
|
To avoid having to be aware of and re-declare **all** _default_ listeners, the |
|
`mergeMode` attribute of `@TestExecutionListeners` can be set to |
|
`MergeMode.MERGE_WITH_DEFAULTS`. `MERGE_WITH_DEFAULTS` indicates that locally declared |
|
listeners should be merged with the default listeners. The merging algorithm ensures that |
|
duplicates are removed from the list and that the resulting set of merged listeners is |
|
sorted according to the semantics of `AnnotationAwareOrderComparator` as described in |
|
<<testcontext-tel-config-ordering>>. If a listener implements `Ordered` or is annotated |
|
with `@Order` it can influence the position in which it is merged with the defaults; |
|
otherwise, locally declared listeners will simply be appended to the list of default |
|
listeners when merged. |
|
|
|
For example, if the `MyCustomTestExecutionListener` class in the previous example |
|
configures its `order` value (for example, `500`) to be less than the order of the |
|
`ServletTestExecutionListener` (which happens to be `1000`), the |
|
`MyCustomTestExecutionListener` can then be automatically merged with the list of |
|
defaults _in front of_ the `ServletTestExecutionListener`, and the previous example could |
|
be replaced with the following. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@ContextConfiguration |
|
@TestExecutionListeners( |
|
listeners = MyCustomTestExecutionListener.class, |
|
mergeMode = MERGE_WITH_DEFAULTS, |
|
) |
|
public class MyTest { |
|
// class body... |
|
} |
|
---- |
|
|
|
|
|
[[testcontext-ctx-management]] |
|
===== Context management |
|
|
|
Each `TestContext` provides context management and caching support for the test instance |
|
it is responsible for. Test instances do not automatically receive access to the |
|
configured `ApplicationContext`. However, if a test class implements the |
|
`ApplicationContextAware` interface, a reference to the `ApplicationContext` is supplied |
|
to the test instance. Note that `AbstractJUnit4SpringContextTests` and |
|
`AbstractTestNGSpringContextTests` implement `ApplicationContextAware` and therefore |
|
provide access to the `ApplicationContext` automatically. |
|
|
|
.@Autowired ApplicationContext |
|
[TIP] |
|
==== |
|
As an alternative to implementing the `ApplicationContextAware` interface, you can |
|
inject the application context for your test class through the `@Autowired` annotation |
|
on either a field or setter method. For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
@ContextConfiguration |
|
public class MyTest { |
|
|
|
**@Autowired** |
|
private ApplicationContext applicationContext; |
|
|
|
// class body... |
|
} |
|
---- |
|
|
|
Similarly, if your test is configured to load a `WebApplicationContext`, you can inject |
|
the web application context into your test as follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
**@WebAppConfiguration** |
|
@ContextConfiguration |
|
public class MyWebAppTest { |
|
**@Autowired** |
|
private WebApplicationContext wac; |
|
|
|
// class body... |
|
} |
|
---- |
|
|
|
Dependency injection via `@Autowired` is provided by the |
|
`DependencyInjectionTestExecutionListener` which is configured by default (see |
|
<<testcontext-fixture-di>>). |
|
==== |
|
|
|
Test classes that use the TestContext framework do not need to extend any particular |
|
class or implement a specific interface to configure their application context. Instead, |
|
configuration is achieved simply by declaring the `@ContextConfiguration` annotation at |
|
the class level. If your test class does not explicitly declare application context |
|
resource `locations` or annotated `classes`, the configured `ContextLoader` determines |
|
how to load a context from a default location or default configuration classes. In |
|
addition to context resource `locations` and annotated `classes`, an application context |
|
can also be configured via application context `initializers`. |
|
|
|
The following sections explain how to configure an `ApplicationContext` via XML |
|
configuration files, annotated classes (typically `@Configuration` classes), or context |
|
initializers using Spring's `@ContextConfiguration` annotation. Alternatively, you can |
|
implement and configure your own custom `SmartContextLoader` for advanced use cases. |
|
|
|
[[testcontext-ctx-management-xml]] |
|
====== Context configuration with XML resources |
|
|
|
To load an `ApplicationContext` for your tests using XML configuration files, annotate |
|
your test class with `@ContextConfiguration` and configure the `locations` attribute with |
|
an array that contains the resource locations of XML configuration metadata. A plain or |
|
relative path -- for example `"context.xml"` -- will be treated as a classpath resource |
|
that is relative to the package in which the test class is defined. A path starting with |
|
a slash is treated as an absolute classpath location, for example |
|
`"/org/example/config.xml"`. A path which represents a resource URL (i.e., a path |
|
prefixed with `classpath:`, `file:`, `http:`, etc.) will be used __as is__. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
// ApplicationContext will be loaded from "/app-config.xml" and |
|
// "/test-config.xml" in the root of the classpath |
|
**@ContextConfiguration(locations={"/app-config.xml", "/test-config.xml"})** |
|
public class MyTest { |
|
// class body... |
|
} |
|
---- |
|
|
|
`@ContextConfiguration` supports an alias for the `locations` attribute through the |
|
standard Java `value` attribute. Thus, if you do not need to declare additional |
|
attributes in `@ContextConfiguration`, you can omit the declaration of the `locations` |
|
attribute name and declare the resource locations by using the shorthand format |
|
demonstrated in the following example. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
**@ContextConfiguration({"/app-config.xml", "/test-config.xml"})** |
|
public class MyTest { |
|
// class body... |
|
} |
|
---- |
|
|
|
If you omit both the `locations` and `value` attributes from the `@ContextConfiguration` |
|
annotation, the TestContext framework will attempt to detect a default XML resource |
|
location. Specifically, `GenericXmlContextLoader` and `GenericXmlWebContextLoader` detect |
|
a default location based on the name of the test class. If your class is named |
|
`com.example.MyTest`, `GenericXmlContextLoader` loads your application context from |
|
`"classpath:com/example/MyTest-context.xml"`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package com.example; |
|
|
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
// ApplicationContext will be loaded from |
|
// "classpath:com/example/MyTest-context.xml" |
|
**@ContextConfiguration** |
|
public class MyTest { |
|
// class body... |
|
} |
|
---- |
|
|
|
[[testcontext-ctx-management-groovy]] |
|
====== Context configuration with Groovy scripts |
|
|
|
To load an `ApplicationContext` for your tests using Groovy scripts that utilize the |
|
<<groovy-bean-definition-dsl,Groovy Bean Definition DSL>>, annotate your test class with |
|
`@ContextConfiguration` and configure the `locations` or `value` attribute with an array |
|
that contains the resource locations of Groovy scripts. Resource lookup semantics for |
|
Groovy scripts are the same as those described for <<testcontext-ctx-management-xml,XML |
|
configuration files>>. |
|
|
|
|
|
.Enabling Groovy script support |
|
[TIP] |
|
==== |
|
Support for using Groovy scripts to load an `ApplicationContext` in the Spring |
|
TestContext Framework is enabled automatically if Groovy is on the classpath. |
|
==== |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
// ApplicationContext will be loaded from "/AppConfig.groovy" and |
|
// "/TestConfig.groovy" in the root of the classpath |
|
**@ContextConfiguration({"/AppConfig.groovy", "/TestConfig.Groovy"})** |
|
public class MyTest { |
|
// class body... |
|
} |
|
---- |
|
|
|
If you omit both the `locations` and `value` attributes from the `@ContextConfiguration` |
|
annotation, the TestContext framework will attempt to detect a default Groovy script. |
|
Specifically, `GenericGroovyXmlContextLoader` and `GenericGroovyXmlWebContextLoader` |
|
detect a default location based on the name of the test class. If your class is named |
|
`com.example.MyTest`, the Groovy context loader will load your application context from |
|
`"classpath:com/example/MyTestContext.groovy"`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package com.example; |
|
|
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
// ApplicationContext will be loaded from |
|
// "classpath:com/example/MyTestContext.groovy" |
|
**@ContextConfiguration** |
|
public class MyTest { |
|
// class body... |
|
} |
|
---- |
|
|
|
.Declaring XML config and Groovy scripts simultaneously |
|
[TIP] |
|
==== |
|
Both XML configuration files and Groovy scripts can be declared simultaneously via the |
|
`locations` or `value` attribute of `@ContextConfiguration`. If the path to a configured |
|
resource location ends with `.xml` it will be loaded using an `XmlBeanDefinitionReader`; |
|
otherwise it will be loaded using a `GroovyBeanDefinitionReader`. |
|
|
|
The following listing demonstrates how to combine both in an integration test. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
// ApplicationContext will be loaded from |
|
// "/app-config.xml" and "/TestConfig.groovy" |
|
@ContextConfiguration({ "/app-config.xml", "/TestConfig.groovy" }) |
|
public class MyTest { |
|
// class body... |
|
} |
|
---- |
|
==== |
|
|
|
[[testcontext-ctx-management-javaconfig]] |
|
====== Context configuration with annotated classes |
|
|
|
To load an `ApplicationContext` for your tests using __annotated classes__ (see |
|
<<beans-java>>), annotate your test class with `@ContextConfiguration` and configure the |
|
`classes` attribute with an array that contains references to annotated classes. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
// ApplicationContext will be loaded from AppConfig and TestConfig |
|
**@ContextConfiguration(classes = {AppConfig.class, TestConfig.class})** |
|
public class MyTest { |
|
// class body... |
|
} |
|
---- |
|
|
|
.Annotated Classes |
|
[TIP] |
|
==== |
|
The term __annotated class__ can refer to any of the following. |
|
|
|
* A class annotated with `@Configuration` |
|
* A component (i.e., a class annotated with `@Component`, `@Service`, `@Repository`, etc.) |
|
* A JSR-330 compliant class that is annotated with `javax.inject` annotations |
|
* Any other class that contains `@Bean`-methods |
|
|
|
Consult the javadocs of `@Configuration` and `@Bean` for further information regarding |
|
the configuration and semantics of __annotated classes__, paying special attention to |
|
the discussion of __`@Bean` Lite Mode__. |
|
==== |
|
|
|
If you omit the `classes` attribute from the `@ContextConfiguration` annotation, the |
|
TestContext framework will attempt to detect the presence of default configuration |
|
classes. Specifically, `AnnotationConfigContextLoader` and |
|
`AnnotationConfigWebContextLoader` will detect all static inner classes of the test class |
|
that meet the requirements for configuration class implementations as specified in the |
|
`@Configuration` javadocs. In the following example, the `OrderServiceTest` class |
|
declares a static inner configuration class named `Config` that will be automatically |
|
used to load the `ApplicationContext` for the test class. Note that the name of the |
|
configuration class is arbitrary. In addition, a test class can contain more than one |
|
static inner configuration class if desired. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
// ApplicationContext will be loaded from the |
|
// static inner Config class |
|
**@ContextConfiguration** |
|
public class OrderServiceTest { |
|
|
|
@Configuration |
|
static class Config { |
|
|
|
// this bean will be injected into the OrderServiceTest class |
|
@Bean |
|
public OrderService orderService() { |
|
OrderService orderService = new OrderServiceImpl(); |
|
// set properties, etc. |
|
return orderService; |
|
} |
|
} |
|
|
|
@Autowired |
|
private OrderService orderService; |
|
|
|
@Test |
|
public void testOrderService() { |
|
// test the orderService |
|
} |
|
|
|
} |
|
---- |
|
|
|
[[testcontext-ctx-management-mixed-config]] |
|
====== Mixing XML, Groovy scripts, and annotated classes |
|
|
|
It may sometimes be desirable to mix XML configuration files, Groovy scripts, and |
|
annotated classes (i.e., typically `@Configuration` classes) to configure an |
|
`ApplicationContext` for your tests. For example, if you use XML configuration in |
|
production, you may decide that you want to use `@Configuration` classes to configure |
|
specific Spring-managed components for your tests, or vice versa. |
|
|
|
Furthermore, some third-party frameworks (like Spring Boot) provide first-class support |
|
for loading an `ApplicationContext` from different types of resources simultaneously |
|
(e.g., XML configuration files, Groovy scripts, and `@Configuration` classes). The Spring |
|
Framework historically has not supported this for standard deployments. Consequently, |
|
most of the `SmartContextLoader` implementations that the Spring Framework delivers in |
|
the `spring-test` module support only one resource type per test context; however, this |
|
does not mean that you cannot use both. One exception to the general rule is that the |
|
`GenericGroovyXmlContextLoader` and `GenericGroovyXmlWebContextLoader` support both XML |
|
configuration files and Groovy scripts simultaneously. Furthermore, third-party |
|
frameworks may choose to support the declaration of both `locations` and `classes` via |
|
`@ContextConfiguration`, and with the standard testing support in the TestContext |
|
framework, you have the following options. |
|
|
|
If you want to use resource locations (e.g., XML or Groovy) __and__ `@Configuration` |
|
classes to configure your tests, you will have to pick one as the __entry point__, and |
|
that one will have to include or import the other. For example, in XML or Groovy scripts |
|
you can include `@Configuration` classes via component scanning or define them as normal |
|
Spring beans; whereas, in a `@Configuration` class you can use `@ImportResource` to |
|
import XML configuration files. Note that this behavior is semantically equivalent to how |
|
you configure your application in production: in production configuration you will define |
|
either a set of XML or Groovy resource locations or a set of `@Configuration` classes |
|
that your production `ApplicationContext` will be loaded from, but you still have the |
|
freedom to include or import the other type of configuration. |
|
|
|
[[testcontext-ctx-management-initializers]] |
|
====== Context configuration with context initializers |
|
To configure an `ApplicationContext` for your tests using context initializers, annotate |
|
your test class with `@ContextConfiguration` and configure the `initializers` attribute |
|
with an array that contains references to classes that implement |
|
`ApplicationContextInitializer`. The declared context initializers will then be used to |
|
initialize the `ConfigurableApplicationContext` that is loaded for your tests. Note that |
|
the concrete `ConfigurableApplicationContext` type supported by each declared |
|
initializer must be compatible with the type of `ApplicationContext` created by the |
|
`SmartContextLoader` in use (i.e., typically a `GenericApplicationContext`). |
|
Furthermore, the order in which the initializers are invoked depends on whether they |
|
implement Spring's `Ordered` interface, are annotated with Spring's `@Order` or the |
|
standard `@Priority` annotation. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
// ApplicationContext will be loaded from TestConfig |
|
// and initialized by TestAppCtxInitializer |
|
**@ContextConfiguration( |
|
classes = TestConfig.class, |
|
initializers = TestAppCtxInitializer.class)** |
|
public class MyTest { |
|
// class body... |
|
} |
|
---- |
|
|
|
It is also possible to omit the declaration of XML configuration files or annotated |
|
classes in `@ContextConfiguration` entirely and instead declare only |
|
`ApplicationContextInitializer` classes which are then responsible for registering beans |
|
in the context -- for example, by programmatically loading bean definitions from XML |
|
files or configuration classes. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
// ApplicationContext will be initialized by EntireAppInitializer |
|
// which presumably registers beans in the context |
|
**@ContextConfiguration(initializers = EntireAppInitializer.class)** |
|
public class MyTest { |
|
// class body... |
|
} |
|
---- |
|
|
|
[[testcontext-ctx-management-inheritance]] |
|
====== Context configuration inheritance |
|
`@ContextConfiguration` supports boolean `inheritLocations` and `inheritInitializers` |
|
attributes that denote whether resource locations or annotated classes and context |
|
initializers declared by superclasses should be __inherited__. The default value for |
|
both flags is `true`. This means that a test class inherits the resource locations or |
|
annotated classes as well as the context initializers declared by any superclasses. |
|
Specifically, the resource locations or annotated classes for a test class are appended |
|
to the list of resource locations or annotated classes declared by superclasses. |
|
Similarly, the initializers for a given test class will be added to the set of |
|
initializers defined by test superclasses. Thus, subclasses have the option |
|
of __extending__ the resource locations, annotated classes, or context initializers. |
|
|
|
If `@ContextConfiguration`'s `inheritLocations` or `inheritInitializers` attribute is |
|
set to `false`, the resource locations or annotated classes and the context |
|
initializers, respectively, for the test class __shadow__ and effectively replace the |
|
configuration defined by superclasses. |
|
|
|
In the following example that uses XML resource locations, the `ApplicationContext` for |
|
`ExtendedTest` will be loaded from __"base-config.xml"__ __and__ |
|
__"extended-config.xml"__, in that order. Beans defined in __"extended-config.xml"__ may |
|
therefore __override__ (i.e., replace) those defined in __"base-config.xml"__. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
// ApplicationContext will be loaded from "/base-config.xml" |
|
// in the root of the classpath |
|
**@ContextConfiguration("/base-config.xml")** |
|
public class BaseTest { |
|
// class body... |
|
} |
|
|
|
// ApplicationContext will be loaded from "/base-config.xml" and |
|
// "/extended-config.xml" in the root of the classpath |
|
**@ContextConfiguration("/extended-config.xml")** |
|
public class ExtendedTest extends BaseTest { |
|
// class body... |
|
} |
|
---- |
|
|
|
Similarly, in the following example that uses annotated classes, the |
|
`ApplicationContext` for `ExtendedTest` will be loaded from the `BaseConfig` __and__ |
|
`ExtendedConfig` classes, in that order. Beans defined in `ExtendedConfig` may therefore |
|
override (i.e., replace) those defined in `BaseConfig`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
// ApplicationContext will be loaded from BaseConfig |
|
**@ContextConfiguration(classes = BaseConfig.class)** |
|
public class BaseTest { |
|
// class body... |
|
} |
|
|
|
// ApplicationContext will be loaded from BaseConfig and ExtendedConfig |
|
**@ContextConfiguration(classes = ExtendedConfig.class)** |
|
public class ExtendedTest extends BaseTest { |
|
// class body... |
|
} |
|
---- |
|
|
|
In the following example that uses context initializers, the `ApplicationContext` for |
|
`ExtendedTest` will be initialized using `BaseInitializer` __and__ |
|
`ExtendedInitializer`. Note, however, that the order in which the initializers are |
|
invoked depends on whether they implement Spring's `Ordered` interface, are annotated |
|
with Spring's `@Order` or the standard `@Priority` annotation. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
// ApplicationContext will be initialized by BaseInitializer |
|
**@ContextConfiguration(initializers = BaseInitializer.class)** |
|
public class BaseTest { |
|
// class body... |
|
} |
|
|
|
// ApplicationContext will be initialized by BaseInitializer |
|
// and ExtendedInitializer |
|
**@ContextConfiguration(initializers = ExtendedInitializer.class)** |
|
public class ExtendedTest extends BaseTest { |
|
// class body... |
|
} |
|
---- |
|
|
|
[[testcontext-ctx-management-env-profiles]] |
|
====== Context configuration with environment profiles |
|
Spring 3.1 introduced first-class support in the framework for the notion of |
|
environments and profiles (a.k.a., __bean definition profiles__), and integration tests |
|
can be configured to activate particular bean definition profiles for various testing |
|
scenarios. This is achieved by annotating a test class with the `@ActiveProfiles` |
|
annotation and supplying a list of profiles that should be activated when loading the |
|
`ApplicationContext` for the test. |
|
|
|
[NOTE] |
|
==== |
|
`@ActiveProfiles` may be used with any implementation of the new `SmartContextLoader` |
|
SPI, but `@ActiveProfiles` is not supported with implementations of the older |
|
`ContextLoader` SPI. |
|
==== |
|
|
|
Let's take a look at some examples with XML configuration and `@Configuration` classes. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<!-- app-config.xml --> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:jdbc="http://www.springframework.org/schema/jdbc" |
|
xmlns:jee="http://www.springframework.org/schema/jee" |
|
xsi:schemaLocation="..."> |
|
|
|
<bean id="transferService" |
|
class="com.bank.service.internal.DefaultTransferService"> |
|
<constructor-arg ref="accountRepository"/> |
|
<constructor-arg ref="feePolicy"/> |
|
</bean> |
|
|
|
<bean id="accountRepository" |
|
class="com.bank.repository.internal.JdbcAccountRepository"> |
|
<constructor-arg ref="dataSource"/> |
|
</bean> |
|
|
|
<bean id="feePolicy" |
|
class="com.bank.service.internal.ZeroFeePolicy"/> |
|
|
|
<beans profile="dev"> |
|
<jdbc:embedded-database id="dataSource"> |
|
<jdbc:script |
|
location="classpath:com/bank/config/sql/schema.sql"/> |
|
<jdbc:script |
|
location="classpath:com/bank/config/sql/test-data.sql"/> |
|
</jdbc:embedded-database> |
|
</beans> |
|
|
|
<beans profile="production"> |
|
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/> |
|
</beans> |
|
|
|
<beans profile="default"> |
|
<jdbc:embedded-database id="dataSource"> |
|
<jdbc:script |
|
location="classpath:com/bank/config/sql/schema.sql"/> |
|
</jdbc:embedded-database> |
|
</beans> |
|
|
|
</beans> |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package com.bank.service; |
|
|
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
// ApplicationContext will be loaded from "classpath:/app-config.xml" |
|
@ContextConfiguration("/app-config.xml") |
|
@ActiveProfiles("dev") |
|
public class TransferServiceTest { |
|
|
|
@Autowired |
|
private TransferService transferService; |
|
|
|
@Test |
|
public void testTransferService() { |
|
// test the transferService |
|
} |
|
} |
|
---- |
|
|
|
When `TransferServiceTest` is run, its `ApplicationContext` will be loaded from the |
|
`app-config.xml` configuration file in the root of the classpath. If you inspect |
|
`app-config.xml` you'll notice that the `accountRepository` bean has a dependency on a |
|
`dataSource` bean; however, `dataSource` is not defined as a top-level bean. Instead, |
|
`dataSource` is defined three times: in the __production__ profile, the |
|
__dev__ profile, and the __default__ profile. |
|
|
|
By annotating `TransferServiceTest` with `@ActiveProfiles("dev")` we instruct the Spring |
|
TestContext Framework to load the `ApplicationContext` with the active profiles set to |
|
`{"dev"}`. As a result, an embedded database will be created and populated with test data, |
|
and the `accountRepository` bean will be wired with a reference to the development |
|
`DataSource`. And that's likely what we want in an integration test. |
|
|
|
It is sometimes useful to assign beans to a `default` profile. Beans within the default profile |
|
are only included when no other profile is specifically activated. This can be used to define |
|
_fallback_ beans to be used in the application's default state. For example, you may |
|
explicitly provide a data source for `dev` and `production` profiles, but define an in-memory |
|
data source as a default when neither of these is active. |
|
|
|
The following code listings demonstrate how to implement the same configuration and |
|
integration test but using `@Configuration` classes instead of XML. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@Profile("dev") |
|
public class StandaloneDataConfig { |
|
|
|
@Bean |
|
public DataSource dataSource() { |
|
return new EmbeddedDatabaseBuilder() |
|
.setType(EmbeddedDatabaseType.HSQL) |
|
.addScript("classpath:com/bank/config/sql/schema.sql") |
|
.addScript("classpath:com/bank/config/sql/test-data.sql") |
|
.build(); |
|
} |
|
} |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@Profile("production") |
|
public class JndiDataConfig { |
|
|
|
@Bean |
|
public DataSource dataSource() throws Exception { |
|
Context ctx = new InitialContext(); |
|
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource"); |
|
} |
|
} |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@Profile("default") |
|
public class DefaultDataConfig { |
|
|
|
@Bean |
|
public DataSource dataSource() { |
|
return new EmbeddedDatabaseBuilder() |
|
.setType(EmbeddedDatabaseType.HSQL) |
|
.addScript("classpath:com/bank/config/sql/schema.sql") |
|
.build(); |
|
} |
|
} |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
public class TransferServiceConfig { |
|
|
|
@Autowired DataSource dataSource; |
|
|
|
@Bean |
|
public TransferService transferService() { |
|
return new DefaultTransferService(accountRepository(), feePolicy()); |
|
} |
|
|
|
@Bean |
|
public AccountRepository accountRepository() { |
|
return new JdbcAccountRepository(dataSource); |
|
} |
|
|
|
@Bean |
|
public FeePolicy feePolicy() { |
|
return new ZeroFeePolicy(); |
|
} |
|
|
|
} |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package com.bank.service; |
|
|
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
@ContextConfiguration(classes = { |
|
TransferServiceConfig.class, |
|
StandaloneDataConfig.class, |
|
JndiDataConfig.class, |
|
DefaultDataConfig.class}) |
|
@ActiveProfiles("dev") |
|
public class TransferServiceTest { |
|
|
|
@Autowired |
|
private TransferService transferService; |
|
|
|
@Test |
|
public void testTransferService() { |
|
// test the transferService |
|
} |
|
} |
|
---- |
|
|
|
In this variation, we have split the XML configuration into four independent |
|
`@Configuration` classes: |
|
|
|
* `TransferServiceConfig`: acquires a `dataSource` via dependency injection using |
|
`@Autowired` |
|
* `StandaloneDataConfig`: defines a `dataSource` for an embedded database suitable for |
|
developer tests |
|
* `JndiDataConfig`: defines a `dataSource` that is retrieved from JNDI in a production |
|
environment |
|
* `DefaultDataConfig`: defines a `dataSource` for a default embedded database in case |
|
no profile is active |
|
|
|
As with the XML-based configuration example, we still annotate `TransferServiceTest` |
|
with `@ActiveProfiles("dev")`, but this time we specify all four configuration classes |
|
via the `@ContextConfiguration` annotation. The body of the test class itself remains |
|
completely unchanged. |
|
|
|
It is often the case that a single set of profiles is used across multiple test classes |
|
within a given project. Thus, to avoid duplicate declarations of the `@ActiveProfiles` |
|
annotation it is possible to declare `@ActiveProfiles` once on a base class, and |
|
subclasses will automatically inherit the `@ActiveProfiles` configuration from the base |
|
class. In the following example, the declaration of `@ActiveProfiles` (as well as other |
|
annotations) has been moved to an abstract superclass, `AbstractIntegrationTest`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package com.bank.service; |
|
|
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
@ContextConfiguration(classes = { |
|
TransferServiceConfig.class, |
|
StandaloneDataConfig.class, |
|
JndiDataConfig.class, |
|
DefaultDataConfig.class}) |
|
@ActiveProfiles("dev") |
|
public abstract class AbstractIntegrationTest { |
|
} |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package com.bank.service; |
|
|
|
// "dev" profile inherited from superclass |
|
public class TransferServiceTest extends AbstractIntegrationTest { |
|
|
|
@Autowired |
|
private TransferService transferService; |
|
|
|
@Test |
|
public void testTransferService() { |
|
// test the transferService |
|
} |
|
} |
|
---- |
|
|
|
`@ActiveProfiles` also supports an `inheritProfiles` attribute that can be used to |
|
disable the inheritance of active profiles. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package com.bank.service; |
|
|
|
// "dev" profile overridden with "production" |
|
@ActiveProfiles(profiles = "production", inheritProfiles = false) |
|
public class ProductionTransferServiceTest extends AbstractIntegrationTest { |
|
// test body |
|
} |
|
---- |
|
|
|
[[testcontext-ctx-management-env-profiles-ActiveProfilesResolver]] |
|
Furthermore, it is sometimes necessary to resolve active profiles for tests |
|
__programmatically__ instead of declaratively -- for example, based on: |
|
|
|
* the current operating system |
|
* whether tests are being executed on a continuous integration build server |
|
* the presence of certain environment variables |
|
* the presence of custom class-level annotations |
|
* etc. |
|
|
|
To resolve active bean definition profiles programmatically, simply implement a custom |
|
`ActiveProfilesResolver` and register it via the `resolver` attribute of |
|
`@ActiveProfiles`. The following example demonstrates how to implement and register a |
|
custom `OperatingSystemActiveProfilesResolver`. For further information, refer to the |
|
corresponding javadocs. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package com.bank.service; |
|
|
|
// "dev" profile overridden programmatically via a custom resolver |
|
@ActiveProfiles( |
|
resolver = OperatingSystemActiveProfilesResolver.class, |
|
inheritProfiles = false) |
|
public class TransferServiceTest extends AbstractIntegrationTest { |
|
// test body |
|
} |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package com.bank.service.test; |
|
|
|
public class OperatingSystemActiveProfilesResolver implements ActiveProfilesResolver { |
|
|
|
@Override |
|
String[] resolve(Class<?> testClass) { |
|
String profile = ...; |
|
// determine the value of profile based on the operating system |
|
return new String[] {profile}; |
|
} |
|
} |
|
---- |
|
|
|
[[testcontext-ctx-management-property-sources]] |
|
====== Context configuration with test property sources |
|
|
|
Spring 3.1 introduced first-class support in the framework for the notion of an |
|
environment with a hierarchy of _property sources_, and since Spring 4.1 integration |
|
tests can be configured with test-specific property sources. In contrast to the |
|
`@PropertySource` annotation used on `@Configuration` classes, the `@TestPropertySource` |
|
annotation can be declared on a test class to declare resource locations for test |
|
properties files or _inlined_ properties. These test property sources will be added to |
|
the `Environment`'s set of `PropertySources` for the `ApplicationContext` loaded for the |
|
annotated integration test. |
|
|
|
[NOTE] |
|
==== |
|
`@TestPropertySource` may be used with any implementation of the `SmartContextLoader` |
|
SPI, but `@TestPropertySource` is not supported with implementations of the older |
|
`ContextLoader` SPI. |
|
|
|
Implementations of `SmartContextLoader` gain access to merged test property source values |
|
via the `getPropertySourceLocations()` and `getPropertySourceProperties()` methods in |
|
`MergedContextConfiguration`. |
|
==== |
|
|
|
*Declaring test property sources* |
|
|
|
Test properties files can be configured via the `locations` or `value` attribute of |
|
`@TestPropertySource` as shown in the following example. |
|
|
|
Both traditional and XML-based properties file formats are supported -- for example, |
|
`"classpath:/com/example/test.properties"` or `"file:///path/to/file.xml"`. |
|
|
|
Each path will be interpreted as a Spring `Resource`. A plain path -- for example, |
|
`"test.properties"` -- will be treated as a classpath resource that is _relative_ to the |
|
package in which the test class is defined. A path starting with a slash will be treated |
|
as an _absolute_ classpath resource, for example: `"/org/example/test.xml"`. A path which |
|
references a URL (e.g., a path prefixed with `classpath:`, `file:`, `http:`, etc.) will |
|
be loaded using the specified resource protocol. Resource location wildcards (e.g. |
|
`**/*.properties`) are not permitted: each location must evaluate to exactly one |
|
`.properties` or `.xml` resource. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@ContextConfiguration |
|
@TestPropertySource("/test.properties") |
|
public class MyIntegrationTests { |
|
// class body... |
|
} |
|
---- |
|
|
|
_Inlined_ properties in the form of key-value pairs can be configured via the |
|
`properties` attribute of `@TestPropertySource` as shown in the following example. All |
|
key-value pairs will be added to the enclosing `Environment` as a single test |
|
`PropertySource` with the highest precedence. |
|
|
|
The supported syntax for key-value pairs is the same as the syntax defined for entries in |
|
a Java properties file: |
|
|
|
* `"key=value"` |
|
* `"key:value"` |
|
* `"key value"` |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@ContextConfiguration |
|
@TestPropertySource(properties = {"timezone = GMT", "port: 4242"}) |
|
public class MyIntegrationTests { |
|
// class body... |
|
} |
|
---- |
|
|
|
*Default properties file detection* |
|
|
|
If `@TestPropertySource` is declared as an empty annotation (i.e., without explicit |
|
values for the `locations` or `properties` attributes), an attempt will be made to detect |
|
a _default_ properties file relative to the class that declared the annotation. For |
|
example, if the annotated test class is `com.example.MyTest`, the corresponding default |
|
properties file is `"classpath:com/example/MyTest.properties"`. If the default cannot be |
|
detected, an `IllegalStateException` will be thrown. |
|
|
|
*Precedence* |
|
|
|
Test property sources have higher precedence than those loaded from the operating |
|
system's environment or Java system properties as well as property sources added by the |
|
application declaratively via `@PropertySource` or programmatically. Thus, test property |
|
sources can be used to selectively override properties defined in system and application |
|
property sources. Furthermore, inlined properties have higher precedence than properties |
|
loaded from resource locations. |
|
|
|
In the following example, the `timezone` and `port` properties as well as any properties |
|
defined in `"/test.properties"` will override any properties of the same name that are |
|
defined in system and application property sources. Furthermore, if the |
|
`"/test.properties"` file defines entries for the `timezone` and `port` properties those |
|
will be overridden by the _inlined_ properties declared via the `properties` attribute. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@ContextConfiguration |
|
@TestPropertySource( |
|
locations = "/test.properties", |
|
properties = {"timezone = GMT", "port: 4242"} |
|
) |
|
public class MyIntegrationTests { |
|
// class body... |
|
} |
|
---- |
|
|
|
*Inheriting and overriding test property sources* |
|
|
|
`@TestPropertySource` supports boolean `inheritLocations` and `inheritProperties` |
|
attributes that denote whether resource locations for properties files and inlined |
|
properties declared by superclasses should be __inherited__. The default value for both |
|
flags is `true`. This means that a test class inherits the locations and inlined |
|
properties declared by any superclasses. Specifically, the locations and inlined |
|
properties for a test class are appended to the locations and inlined properties declared |
|
by superclasses. Thus, subclasses have the option of __extending__ the locations and |
|
inlined properties. Note that properties that appear later will __shadow__ (i.e.., |
|
override) properties of the same name that appear earlier. In addition, the |
|
aforementioned precedence rules apply for inherited test property sources as well. |
|
|
|
If `@TestPropertySource`'s `inheritLocations` or `inheritProperties` attribute is set to |
|
`false`, the locations or inlined properties, respectively, for the test class __shadow__ |
|
and effectively replace the configuration defined by superclasses. |
|
|
|
In the following example, the `ApplicationContext` for `BaseTest` will be loaded using |
|
only the `"base.properties"` file as a test property source. In contrast, the |
|
`ApplicationContext` for `ExtendedTest` will be loaded using the `"base.properties"` |
|
**and** `"extended.properties"` files as test property source locations. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@TestPropertySource("base.properties") |
|
@ContextConfiguration |
|
public class BaseTest { |
|
// ... |
|
} |
|
|
|
@TestPropertySource("extended.properties") |
|
@ContextConfiguration |
|
public class ExtendedTest extends BaseTest { |
|
// ... |
|
} |
|
---- |
|
|
|
In the following example, the `ApplicationContext` for `BaseTest` will be loaded using only |
|
the _inlined_ `key1` property. In contrast, the `ApplicationContext` for `ExtendedTest` will be |
|
loaded using the _inlined_ `key1` and `key2` properties. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@TestPropertySource(properties = "key1 = value1") |
|
@ContextConfiguration |
|
public class BaseTest { |
|
// ... |
|
} |
|
|
|
@TestPropertySource(properties = "key2 = value2") |
|
@ContextConfiguration |
|
public class ExtendedTest extends BaseTest { |
|
// ... |
|
} |
|
---- |
|
|
|
[[testcontext-ctx-management-web]] |
|
====== Loading a WebApplicationContext |
|
Spring 3.2 introduced support for loading a `WebApplicationContext` in integration |
|
tests. To instruct the TestContext framework to load a `WebApplicationContext` instead |
|
of a standard `ApplicationContext`, simply annotate the respective test class with |
|
`@WebAppConfiguration`. |
|
|
|
The presence of `@WebAppConfiguration` on your test class instructs the TestContext |
|
framework (TCF) that a `WebApplicationContext` (WAC) should be loaded for your |
|
integration tests. In the background the TCF makes sure that a `MockServletContext` is |
|
created and supplied to your test's WAC. By default the base resource path for your |
|
`MockServletContext` will be set to __"src/main/webapp"__. This is interpreted as a path |
|
relative to the root of your JVM (i.e., normally the path to your project). If you're |
|
familiar with the directory structure of a web application in a Maven project, you'll |
|
know that __"src/main/webapp"__ is the default location for the root of your WAR. If you |
|
need to override this default, simply provide an alternate path to the |
|
`@WebAppConfiguration` annotation (e.g., `@WebAppConfiguration("src/test/webapp")`). If |
|
you wish to reference a base resource path from the classpath instead of the file |
|
system, just use Spring's __classpath:__ prefix. |
|
|
|
Please note that Spring's testing support for `WebApplicationContexts` is on par with |
|
its support for standard `ApplicationContexts`. When testing with a |
|
`WebApplicationContext` you are free to declare either XML configuration files or |
|
`@Configuration` classes via `@ContextConfiguration`. You are of course also free to use |
|
any other test annotations such as `@TestExecutionListeners`, |
|
`@TransactionConfiguration`, `@ActiveProfiles`, etc. |
|
|
|
The following examples demonstrate some of the various configuration options for loading |
|
a `WebApplicationContext`. |
|
|
|
.Conventions |
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
|
|
// defaults to "file:src/main/webapp" |
|
@WebAppConfiguration |
|
|
|
// detects "WacTests-context.xml" in same package |
|
// or static nested @Configuration class |
|
@ContextConfiguration |
|
|
|
public class WacTests { |
|
//... |
|
} |
|
---- |
|
|
|
The above example demonstrates the TestContext framework's support for __convention over |
|
configuration__. If you annotate a test class with `@WebAppConfiguration` without |
|
specifying a resource base path, the resource path will effectively default |
|
to __"file:src/main/webapp"__. Similarly, if you declare `@ContextConfiguration` without |
|
specifying resource `locations`, annotated `classes`, or context `initializers`, Spring |
|
will attempt to detect the presence of your configuration using conventions |
|
(i.e., __"WacTests-context.xml"__ in the same package as the `WacTests` class or static |
|
nested `@Configuration` classes). |
|
|
|
.Default resource semantics |
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
|
|
// file system resource |
|
@WebAppConfiguration("webapp") |
|
|
|
// classpath resource |
|
@ContextConfiguration("/spring/test-servlet-config.xml") |
|
|
|
public class WacTests { |
|
//... |
|
} |
|
---- |
|
|
|
This example demonstrates how to explicitly declare a resource base path with |
|
`@WebAppConfiguration` and an XML resource location with `@ContextConfiguration`. The |
|
important thing to note here is the different semantics for paths with these two |
|
annotations. By default, `@WebAppConfiguration` resource paths are file system based; |
|
whereas, `@ContextConfiguration` resource locations are classpath based. |
|
|
|
.Explicit resource semantics |
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
|
|
// classpath resource |
|
@WebAppConfiguration("classpath:test-web-resources") |
|
|
|
// file system resource |
|
@ContextConfiguration("file:src/main/webapp/WEB-INF/servlet-config.xml") |
|
|
|
public class WacTests { |
|
//... |
|
} |
|
---- |
|
|
|
In this third example, we see that we can override the default resource semantics for |
|
both annotations by specifying a Spring resource prefix. Contrast the comments in this |
|
example with the previous example. |
|
|
|
.[[testcontext-ctx-management-web-mocks]]Working with Web Mocks |
|
-- |
|
To provide comprehensive web testing support, Spring 3.2 introduced a |
|
`ServletTestExecutionListener` that is enabled by default. When testing against a |
|
`WebApplicationContext` this <<testcontext-key-abstractions,TestExecutionListener>> sets |
|
up default thread-local state via Spring Web's `RequestContextHolder` before each test |
|
method and creates a `MockHttpServletRequest`, `MockHttpServletResponse`, and |
|
`ServletWebRequest` based on the base resource path configured via |
|
`@WebAppConfiguration`. `ServletTestExecutionListener` also ensures that the |
|
`MockHttpServletResponse` and `ServletWebRequest` can be injected into the test |
|
instance, and once the test is complete it cleans up thread-local state. |
|
|
|
Once you have a `WebApplicationContext` loaded for your test you might find that you |
|
need to interact with the web mocks -- for example, to set up your test fixture or to |
|
perform assertions after invoking your web component. The following example demonstrates |
|
which mocks can be autowired into your test instance. Note that the |
|
`WebApplicationContext` and `MockServletContext` are both cached across the test suite; |
|
whereas, the other mocks are managed per test method by the |
|
`ServletTestExecutionListener`. |
|
|
|
.Injecting mocks |
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@WebAppConfiguration |
|
@ContextConfiguration |
|
public class WacTests { |
|
|
|
@Autowired |
|
WebApplicationContext wac; // cached |
|
|
|
@Autowired |
|
MockServletContext servletContext; // cached |
|
|
|
@Autowired |
|
MockHttpSession session; |
|
|
|
@Autowired |
|
MockHttpServletRequest request; |
|
|
|
@Autowired |
|
MockHttpServletResponse response; |
|
|
|
@Autowired |
|
ServletWebRequest webRequest; |
|
|
|
//... |
|
} |
|
---- |
|
-- |
|
|
|
[[testcontext-ctx-management-caching]] |
|
====== Context caching |
|
|
|
Once the TestContext framework loads an `ApplicationContext` (or `WebApplicationContext`) |
|
for a test, that context will be cached and reused for __all__ subsequent tests that |
|
declare the same unique context configuration within the same test suite. To understand |
|
how caching works, it is important to understand what is meant by __unique__ and __test |
|
suite__. |
|
|
|
An `ApplicationContext` can be __uniquely__ identified by the combination of |
|
configuration parameters that are used to load it. Consequently, the unique combination |
|
of configuration parameters are used to generate a __key__ under which the context is |
|
cached. The TestContext framework uses the following configuration parameters to build |
|
the context cache key: |
|
|
|
* `locations` __(from @ContextConfiguration)__ |
|
* `classes` __(from @ContextConfiguration)__ |
|
* `contextInitializerClasses` __(from @ContextConfiguration)__ |
|
* `contextLoader` __(from @ContextConfiguration)__ |
|
* `parent` __(from @ContextHierarchy)__ |
|
* `activeProfiles` __(from @ActiveProfiles)__ |
|
* `propertySourceLocations` __(from @TestPropertySource)__ |
|
* `propertySourceProperties` __(from @TestPropertySource)__ |
|
* `resourceBasePath` __(from @WebAppConfiguration)__ |
|
|
|
For example, if `TestClassA` specifies `{"app-config.xml", "test-config.xml"}` for the |
|
`locations` (or `value`) attribute of `@ContextConfiguration`, the TestContext framework |
|
will load the corresponding `ApplicationContext` and store it in a `static` context cache |
|
under a key that is based solely on those locations. So if `TestClassB` also defines |
|
`{"app-config.xml", "test-config.xml"}` for its locations (either explicitly or |
|
implicitly through inheritance) but does not define `@WebAppConfiguration`, a different |
|
`ContextLoader`, different active profiles, different context initializers, different |
|
test property sources, or a different parent context, then the same `ApplicationContext` |
|
will be shared by both test classes. This means that the setup cost for loading an |
|
application context is incurred only once (per test suite), and subsequent test execution |
|
is much faster. |
|
|
|
.Test suites and forked processes |
|
[NOTE] |
|
==== |
|
The Spring TestContext framework stores application contexts in a __static__ cache. This |
|
means that the context is literally stored in a `static` variable. In other words, if |
|
tests execute in separate processes the static cache will be cleared between each test |
|
execution, and this will effectively disable the caching mechanism. |
|
|
|
To benefit from the caching mechanism, all tests must run within the same process or |
|
test suite. This can be achieved by executing all tests as a group within an IDE. |
|
Similarly, when executing tests with a build framework such as Ant, Maven, or Gradle it |
|
is important to make sure that the build framework does not __fork__ between tests. For |
|
example, if the |
|
http://maven.apache.org/plugins/maven-surefire-plugin/test-mojo.html#forkMode[forkMode] |
|
for the Maven Surefire plug-in is set to `always` or `pertest`, the TestContext |
|
framework will not be able to cache application contexts between test classes and the |
|
build process will run significantly slower as a result. |
|
==== |
|
|
|
Since having a large number of application contexts loaded within a given test suite can |
|
cause the suite to take an unnecessarily long time to execute, it is often beneficial to |
|
know exactly how many contexts have been loaded and cached. To view the statistics for |
|
the underlying context cache, simply set the log level for the |
|
`org.springframework.test.context.cache` logging category to `DEBUG`. |
|
|
|
In the unlikely case that a test corrupts the application context and requires reloading |
|
-- for example, by modifying a bean definition or the state of an application object -- |
|
you can annotate your test class or test method with `@DirtiesContext` (see the |
|
discussion of `@DirtiesContext` in <<integration-testing-annotations-spring>>). This |
|
instructs Spring to remove the context from the cache and rebuild the application |
|
context before executing the next test. Note that support for the `@DirtiesContext` |
|
annotation is provided by the `DirtiesContextTestExecutionListener` which is enabled by |
|
default. |
|
|
|
|
|
[[testcontext-ctx-management-ctx-hierarchies]] |
|
====== Context hierarchies |
|
|
|
When writing integration tests that rely on a loaded Spring `ApplicationContext`, it is |
|
often sufficient to test against a single context; however, there are times when it is |
|
beneficial or even necessary to test against a hierarchy of ++ApplicationContext++s. For |
|
example, if you are developing a Spring MVC web application you will typically have a |
|
root `WebApplicationContext` loaded via Spring's `ContextLoaderListener` and a child |
|
`WebApplicationContext` loaded via Spring's `DispatcherServlet`. This results in a |
|
parent-child context hierarchy where shared components and infrastructure configuration |
|
are declared in the root context and consumed in the child context by web-specific |
|
components. Another use case can be found in Spring Batch applications where you often |
|
have a parent context that provides configuration for shared batch infrastructure and a |
|
child context for the configuration of a specific batch job. |
|
|
|
As of Spring Framework 3.2.2, it is possible to write integration tests that use context |
|
hierarchies by declaring context configuration via the `@ContextHierarchy` annotation, |
|
either on an individual test class or within a test class hierarchy. If a context |
|
hierarchy is declared on multiple classes within a test class hierarchy it is also |
|
possible to merge or override the context configuration for a specific, named level in |
|
the context hierarchy. When merging configuration for a given level in the hierarchy the |
|
configuration resource type (i.e., XML configuration files or annotated classes) must be |
|
consistent; otherwise, it is perfectly acceptable to have different levels in a context |
|
hierarchy configured using different resource types. |
|
|
|
The following JUnit-based examples demonstrate common configuration scenarios for |
|
integration tests that require the use of context hierarchies. |
|
|
|
.Single test class with context hierarchy |
|
-- |
|
`ControllerIntegrationTests` represents a typical integration testing scenario for a |
|
Spring MVC web application by declaring a context hierarchy consisting of two levels, |
|
one for the __root__ WebApplicationContext (loaded using the `TestAppConfig` |
|
`@Configuration` class) and one for the __dispatcher servlet__ `WebApplicationContext` |
|
(loaded using the `WebConfig` `@Configuration` class). The `WebApplicationContext` that |
|
is __autowired__ into the test instance is the one for the child context (i.e., the |
|
lowest context in the hierarchy). |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
@WebAppConfiguration |
|
@ContextHierarchy({ |
|
@ContextConfiguration(classes = TestAppConfig.class), |
|
@ContextConfiguration(classes = WebConfig.class) |
|
}) |
|
public class ControllerIntegrationTests { |
|
|
|
@Autowired |
|
private WebApplicationContext wac; |
|
|
|
// ... |
|
} |
|
---- |
|
|
|
-- |
|
|
|
|
|
.Class hierarchy with implicit parent context |
|
-- |
|
The following test classes define a context hierarchy within a test class hierarchy. |
|
`AbstractWebTests` declares the configuration for a root `WebApplicationContext` in a |
|
Spring-powered web application. Note, however, that `AbstractWebTests` does not declare |
|
`@ContextHierarchy`; consequently, subclasses of `AbstractWebTests` can optionally |
|
participate in a context hierarchy or simply follow the standard semantics for |
|
`@ContextConfiguration`. `SoapWebServiceTests` and `RestWebServiceTests` both extend |
|
`AbstractWebTests` and define a context hierarchy via `@ContextHierarchy`. The result is |
|
that three application contexts will be loaded (one for each declaration of |
|
`@ContextConfiguration`), and the application context loaded based on the configuration |
|
in `AbstractWebTests` will be set as the parent context for each of the contexts loaded |
|
for the concrete subclasses. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
@WebAppConfiguration |
|
@ContextConfiguration("file:src/main/webapp/WEB-INF/applicationContext.xml") |
|
public abstract class AbstractWebTests {} |
|
|
|
@ContextHierarchy(@ContextConfiguration("/spring/soap-ws-config.xml") |
|
public class SoapWebServiceTests extends AbstractWebTests {} |
|
|
|
@ContextHierarchy(@ContextConfiguration("/spring/rest-ws-config.xml") |
|
public class RestWebServiceTests extends AbstractWebTests {} |
|
---- |
|
-- |
|
|
|
|
|
.Class hierarchy with merged context hierarchy configuration |
|
-- |
|
The following classes demonstrate the use of __named__ hierarchy levels in order to |
|
__merge__ the configuration for specific levels in a context hierarchy. `BaseTests` |
|
defines two levels in the hierarchy, `parent` and `child`. `ExtendedTests` extends |
|
`BaseTests` and instructs the Spring TestContext Framework to merge the context |
|
configuration for the `child` hierarchy level, simply by ensuring that the names |
|
declared via `ContextConfiguration`'s `name` attribute are both `"child"`. The result is |
|
that three application contexts will be loaded: one for `"/app-config.xml"`, one for |
|
`"/user-config.xml"`, and one for `{"/user-config.xml", "/order-config.xml"}`. As with |
|
the previous example, the application context loaded from `"/app-config.xml"` will be |
|
set as the parent context for the contexts loaded from `"/user-config.xml"` and |
|
`{"/user-config.xml", "/order-config.xml"}`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
@ContextHierarchy({ |
|
@ContextConfiguration(name = "parent", locations = "/app-config.xml"), |
|
@ContextConfiguration(name = "child", locations = "/user-config.xml") |
|
}) |
|
public class BaseTests {} |
|
|
|
@ContextHierarchy( |
|
@ContextConfiguration(name = "child", locations = "/order-config.xml") |
|
) |
|
public class ExtendedTests extends BaseTests {} |
|
---- |
|
-- |
|
|
|
.Class hierarchy with overridden context hierarchy configuration |
|
-- |
|
In contrast to the previous example, this example demonstrates how to __override__ the |
|
configuration for a given named level in a context hierarchy by setting |
|
++ContextConfiguration++'s `inheritLocations` flag to `false`. Consequently, the |
|
application context for `ExtendedTests` will be loaded only from |
|
`"/test-user-config.xml"` and will have its parent set to the context loaded from |
|
`"/app-config.xml"`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
@ContextHierarchy({ |
|
@ContextConfiguration(name = "parent", locations = "/app-config.xml"), |
|
@ContextConfiguration(name = "child", locations = "/user-config.xml") |
|
}) |
|
public class BaseTests {} |
|
|
|
@ContextHierarchy( |
|
@ContextConfiguration( |
|
name = "child", |
|
locations = "/test-user-config.xml", |
|
inheritLocations = false |
|
)) |
|
public class ExtendedTests extends BaseTests {} |
|
---- |
|
|
|
.Dirtying a context within a context hierarchy |
|
[NOTE] |
|
==== |
|
If `@DirtiesContext` is used in a test whose context is configured as part of a context |
|
hierarchy, the `hierarchyMode` flag can be used to control how the context cache is |
|
cleared. For further details consult the discussion of `@DirtiesContext` in |
|
<<integration-testing-annotations-spring,Spring Testing Annotations>> and the |
|
`@DirtiesContext` javadocs. |
|
==== |
|
-- |
|
|
|
|
|
[[testcontext-fixture-di]] |
|
===== Dependency injection of test fixtures |
|
When you use the `DependencyInjectionTestExecutionListener` -- which is configured by |
|
default -- the dependencies of your test instances are __injected__ from beans in the |
|
application context that you configured with `@ContextConfiguration`. You may use setter |
|
injection, field injection, or both, depending on which annotations you choose and |
|
whether you place them on setter methods or fields. For consistency with the annotation |
|
support introduced in Spring 2.5 and 3.0, you can use Spring's `@Autowired` annotation |
|
or the `@Inject` annotation from JSR 300. |
|
|
|
[TIP] |
|
==== |
|
|
|
The TestContext framework does not instrument the manner in which a test instance is |
|
instantiated. Thus the use of `@Autowired` or `@Inject` for constructors has no effect |
|
for test classes. |
|
==== |
|
|
|
Because `@Autowired` is used to perform <<beans-factory-autowire, __autowiring by type__ |
|
>>, if you have multiple bean definitions of the same type, you cannot rely on this |
|
approach for those particular beans. In that case, you can use `@Autowired` in |
|
conjunction with `@Qualifier`. As of Spring 3.0 you may also choose to use `@Inject` in |
|
conjunction with `@Named`. Alternatively, if your test class has access to its |
|
`ApplicationContext`, you can perform an explicit lookup by using (for example) a call |
|
to `applicationContext.getBean("titleRepository")`. |
|
|
|
If you do not want dependency injection applied to your test instances, simply do not |
|
annotate fields or setter methods with `@Autowired` or `@Inject`. Alternatively, you can |
|
disable dependency injection altogether by explicitly configuring your class with |
|
`@TestExecutionListeners` and omitting `DependencyInjectionTestExecutionListener.class` |
|
from the list of listeners. |
|
|
|
Consider the scenario of testing a `HibernateTitleRepository` class, as outlined in the |
|
<<integration-testing-goals,Goals>> section. The next two code listings demonstrate the |
|
use of `@Autowired` on fields and setter methods. The application context configuration |
|
is presented after all sample code listings. |
|
|
|
[NOTE] |
|
==== |
|
The dependency injection behavior in the following code listings is not specific to |
|
JUnit. The same DI techniques can be used in conjunction with any testing framework. |
|
|
|
The following examples make calls to static assertion methods such as `assertNotNull()` |
|
but without prepending the call with `Assert`. In such cases, assume that the method was |
|
properly imported through an `import static` declaration that is not shown in the |
|
example. |
|
==== |
|
|
|
The first code listing shows a JUnit-based implementation of the test class that uses |
|
`@Autowired` for field injection. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
// specifies the Spring configuration to load for this test fixture |
|
**@ContextConfiguration("repository-config.xml")** |
|
public class HibernateTitleRepositoryTests { |
|
|
|
// this instance will be dependency injected by type |
|
**@Autowired** |
|
private HibernateTitleRepository titleRepository; |
|
|
|
@Test |
|
public void findById() { |
|
Title title = titleRepository.findById(new Long(10)); |
|
assertNotNull(title); |
|
} |
|
} |
|
---- |
|
|
|
Alternatively, you can configure the class to use `@Autowired` for setter injection as |
|
seen below. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
// specifies the Spring configuration to load for this test fixture |
|
**@ContextConfiguration("repository-config.xml")** |
|
public class HibernateTitleRepositoryTests { |
|
|
|
// this instance will be dependency injected by type |
|
private HibernateTitleRepository titleRepository; |
|
|
|
**@Autowired** |
|
public void setTitleRepository(HibernateTitleRepository titleRepository) { |
|
this.titleRepository = titleRepository; |
|
} |
|
|
|
@Test |
|
public void findById() { |
|
Title title = titleRepository.findById(new Long(10)); |
|
assertNotNull(title); |
|
} |
|
} |
|
---- |
|
|
|
The preceding code listings use the same XML context file referenced by the |
|
`@ContextConfiguration` annotation (that is, `repository-config.xml`), which looks like |
|
this: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xsi:schemaLocation="http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd"> |
|
|
|
<!-- this bean will be injected into the HibernateTitleRepositoryTests class --> |
|
<bean id="**titleRepository**" class="**com.foo.repository.hibernate.HibernateTitleRepository**"> |
|
<property name="sessionFactory" ref="sessionFactory"/> |
|
</bean> |
|
|
|
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> |
|
<!-- configuration elided for brevity --> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
If you are extending from a Spring-provided test base class that happens to use |
|
`@Autowired` on one of its setter methods, you might have multiple beans of the affected |
|
type defined in your application context: for example, multiple `DataSource` beans. In |
|
such a case, you can override the setter method and use the `@Qualifier` annotation to |
|
indicate a specific target bean as follows, but make sure to delegate to the overridden |
|
method in the superclass as well. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// ... |
|
|
|
@Autowired |
|
@Override |
|
public void setDataSource(**@Qualifier("myDataSource")** DataSource dataSource) { |
|
**super**.setDataSource(dataSource); |
|
} |
|
|
|
// ... |
|
---- |
|
|
|
The specified qualifier value indicates the specific `DataSource` bean to inject, |
|
narrowing the set of type matches to a specific bean. Its value is matched against |
|
`<qualifier>` declarations within the corresponding `<bean>` definitions. The bean name |
|
is used as a fallback qualifier value, so you may effectively also point to a specific |
|
bean by name there (as shown above, assuming that "myDataSource" is the bean id). |
|
==== |
|
|
|
|
|
[[testcontext-web-scoped-beans]] |
|
===== Testing request and session scoped beans |
|
|
|
<<beans-factory-scopes-other,Request and session scoped beans>> have been supported by |
|
Spring for several years now, but it's always been a bit non-trivial to test them. As of |
|
Spring 3.2 it's a breeze to test your request-scoped and session-scoped beans by |
|
following these steps. |
|
|
|
* Ensure that a `WebApplicationContext` is loaded for your test by annotating your test |
|
class with `@WebAppConfiguration`. |
|
* Inject the mock request or session into your test instance and prepare your test |
|
fixture as appropriate. |
|
* Invoke your web component that you retrieved from the configured |
|
`WebApplicationContext` (i.e., via dependency injection). |
|
* Perform assertions against the mocks. |
|
|
|
The following code snippet displays the XML configuration for a login use case. Note |
|
that the `userService` bean has a dependency on a request-scoped `loginAction` bean. |
|
Also, the `LoginAction` is instantiated using <<expressions,SpEL expressions>> that |
|
retrieve the username and password from the current HTTP request. In our test, we will |
|
want to configure these request parameters via the mock managed by the TestContext |
|
framework. |
|
|
|
.Request-scoped bean configuration |
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<bean id="userService" |
|
class="com.example.SimpleUserService" |
|
c:loginAction-ref="loginAction" /> |
|
|
|
<bean id="loginAction" class="com.example.LoginAction" |
|
c:username="#{request.getParameter(''user'')}" |
|
c:password="#{request.getParameter(''pswd'')}" |
|
scope="request"> |
|
<aop:scoped-proxy /> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
In `RequestScopedBeanTests` we inject both the `UserService` (i.e., the subject under |
|
test) and the `MockHttpServletRequest` into our test instance. Within our |
|
`requestScope()` test method we set up our test fixture by setting request parameters in |
|
the provided `MockHttpServletRequest`. When the `loginUser()` method is invoked on our |
|
`userService` we are assured that the user service has access to the request-scoped |
|
`loginAction` for the current `MockHttpServletRequest` (i.e., the one we just set |
|
parameters in). We can then perform assertions against the results based on the known |
|
inputs for the username and password. |
|
|
|
.Request-scoped bean test |
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
@ContextConfiguration |
|
@WebAppConfiguration |
|
public class RequestScopedBeanTests { |
|
|
|
@Autowired UserService userService; |
|
@Autowired MockHttpServletRequest request; |
|
|
|
@Test |
|
public void requestScope() { |
|
|
|
request.setParameter("user", "enigma"); |
|
request.setParameter("pswd", "$pr!ng"); |
|
|
|
LoginResults results = userService.loginUser(); |
|
|
|
// assert results |
|
} |
|
} |
|
---- |
|
|
|
The following code snippet is similar to the one we saw above for a request-scoped bean; |
|
however, this time the `userService` bean has a dependency on a session-scoped |
|
`userPreferences` bean. Note that the `UserPreferences` bean is instantiated using a |
|
SpEL expression that retrieves the __theme__ from the current HTTP session. In our test, |
|
we will need to configure a theme in the mock session managed by the TestContext |
|
framework. |
|
|
|
.Session-scoped bean configuration |
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<bean id="userService" |
|
class="com.example.SimpleUserService" |
|
c:userPreferences-ref="userPreferences" /> |
|
|
|
<bean id="userPreferences" |
|
class="com.example.UserPreferences" |
|
c:theme="#{session.getAttribute(''theme'')}" |
|
scope="session"> |
|
<aop:scoped-proxy /> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
In `SessionScopedBeanTests` we inject the `UserService` and the `MockHttpSession` into |
|
our test instance. Within our `sessionScope()` test method we set up our test fixture by |
|
setting the expected "theme" attribute in the provided `MockHttpSession`. When the |
|
`processUserPreferences()` method is invoked on our `userService` we are assured that |
|
the user service has access to the session-scoped `userPreferences` for the current |
|
`MockHttpSession`, and we can perform assertions against the results based on the |
|
configured theme. |
|
|
|
.Session-scoped bean test |
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
@ContextConfiguration |
|
@WebAppConfiguration |
|
public class SessionScopedBeanTests { |
|
|
|
@Autowired UserService userService; |
|
@Autowired MockHttpSession session; |
|
|
|
@Test |
|
public void sessionScope() throws Exception { |
|
|
|
session.setAttribute("theme", "blue"); |
|
|
|
Results results = userService.processUserPreferences(); |
|
|
|
// assert results |
|
} |
|
} |
|
---- |
|
|
|
[[testcontext-tx]] |
|
===== Transaction management |
|
|
|
In the TestContext framework, transactions are managed by the |
|
`TransactionalTestExecutionListener` which is configured by default, even if you do not |
|
explicitly declare `@TestExecutionListeners` on your test class. To enable support for |
|
transactions, however, you must configure a `PlatformTransactionManager` bean in the |
|
`ApplicationContext` that is loaded via `@ContextConfiguration` semantics (further |
|
details are provided below). In addition, you must declare Spring's `@Transactional` |
|
annotation either at the class or method level for your tests. |
|
|
|
[[testcontext-tx-test-managed-transactions]] |
|
====== Test-managed transactions |
|
|
|
_Test-managed transactions_ are transactions that are managed _declaratively_ via the |
|
`TransactionalTestExecutionListener` or _programmatically_ via `TestTransaction` (see |
|
below). Such transactions should not be confused with _Spring-managed transactions_ |
|
(i.e., those managed directly by Spring within the `ApplicationContext` loaded for tests) |
|
or _application-managed transactions_ (i.e., those managed programmatically within |
|
application code that is invoked via tests). Spring-managed and application-managed |
|
transactions will typically participate in test-managed transactions; however, caution |
|
should be taken if Spring-managed or application-managed transactions are configured with |
|
any _propagation_ type other than `REQUIRED` or `SUPPORTS` (see the discussion on |
|
<<tx-propagation,transaction propagation>> for details). |
|
|
|
[[testcontext-tx-enabling-transactions]] |
|
====== Enabling and disabling transactions |
|
|
|
Annotating a test method with `@Transactional` causes the test to be run within a |
|
transaction that will, by default, be automatically rolled back after completion of the |
|
test. If a test class is annotated with `@Transactional`, each test method within that |
|
class hierarchy will be run within a transaction. Test methods that are not annotated |
|
with `@Transactional` (at the class or method level) will not be run within a |
|
transaction. Furthermore, tests that are annotated with `@Transactional` but have the |
|
`propagation` type set to `NOT_SUPPORTED` will not be run within a transaction. |
|
|
|
__Note that <<testcontext-support-classes-junit4, |
|
`AbstractTransactionalJUnit4SpringContextTests`>> and |
|
<<testcontext-support-classes-testng, `AbstractTransactionalTestNGSpringContextTests`>> |
|
are preconfigured for transactional support at the class level.__ |
|
|
|
The following example demonstrates a common scenario for writing an integration test for |
|
a Hibernate-based `UserRepository`. As explained in |
|
<<testcontext-tx-rollback-and-commit-behavior>>, there is no need to clean up the |
|
database after the `createUser()` method is executed since any changes made to the |
|
database will be automatically rolled back by the `TransactionalTestExecutionListener`. |
|
See <<testing-examples-petclinic>> for an additional example. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
@ContextConfiguration(classes = TestConfig.class) |
|
@Transactional |
|
public class HibernateUserRepositoryTests { |
|
|
|
@Autowired |
|
HibernateUserRepository repository; |
|
|
|
@Autowired |
|
SessionFactory sessionFactory; |
|
|
|
JdbcTemplate jdbcTemplate; |
|
|
|
@Autowired |
|
public void setDataSource(DataSource dataSource) { |
|
this.jdbcTemplate = new JdbcTemplate(dataSource); |
|
} |
|
|
|
@Test |
|
public void createUser() { |
|
// track initial state in test database: |
|
final int count = countRowsInTable("user"); |
|
|
|
User user = new User(...); |
|
repository.save(user); |
|
|
|
// Manual flush is required to avoid false positive in test |
|
sessionFactory.getCurrentSession().flush(); |
|
assertNumUsers(count + 1); |
|
} |
|
|
|
protected int countRowsInTable(String tableName) { |
|
return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName); |
|
} |
|
|
|
protected void assertNumUsers(int expected) { |
|
assertEquals("Number of rows in the ''user'' table.", expected, countRowsInTable("user")); |
|
} |
|
} |
|
---- |
|
|
|
[[testcontext-tx-rollback-and-commit-behavior]] |
|
====== Transaction rollback and commit behavior |
|
|
|
By default, test transactions will be automatically rolled back after completion of the |
|
test; however, transactional commit and rollback behavior can be configured declaratively |
|
via the class-level `@TransactionConfiguration` and method-level `@Rollback` annotations. |
|
See the corresponding entries in the <<integration-testing-annotations,annotation |
|
support>> section for further details. |
|
|
|
[[testcontext-tx-programmatic-tx-mgt]] |
|
====== Programmatic transaction management |
|
As of Spring Framework 4.1, it is possible to interact with test-managed transactions |
|
_programmatically_ via the static methods in `TestTransaction`. For example, |
|
`TestTransaction` may be used within _test_ methods, _before_ methods, and _after_ |
|
methods to start or end the current test-managed transaction or to configure the current |
|
test-managed transaction for rollback or commit. Support for `TestTransaction` is |
|
automatically available whenever the `TransactionalTestExecutionListener` is enabled. |
|
|
|
The following example demonstrates some of the features of `TestTransaction`. Consult the |
|
javadocs for `TestTransaction` for further details. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@ContextConfiguration(classes = TestConfig.class) |
|
public class ProgrammaticTransactionManagementTests extends |
|
AbstractTransactionalJUnit4SpringContextTests { |
|
|
|
@Test |
|
public void transactionalTest() { |
|
// assert initial state in test database: |
|
assertNumUsers(2); |
|
|
|
deleteFromTables("user"); |
|
|
|
// changes to the database will be committed! |
|
TestTransaction.flagForCommit(); |
|
TestTransaction.end(); |
|
assertFalse(TestTransaction.isActive()); |
|
assertNumUsers(0); |
|
|
|
TestTransaction.start(); |
|
// perform other actions against the database that will |
|
// be automatically rolled back after the test completes... |
|
} |
|
|
|
protected void assertNumUsers(int expected) { |
|
assertEquals("Number of rows in the ''user'' table.", expected, countRowsInTable("user")); |
|
} |
|
} |
|
---- |
|
|
|
[[testcontext-tx-before-and-after-tx]] |
|
====== Executing code outside of a transaction |
|
|
|
Occasionally you need to execute certain code before or after a transactional test method |
|
but outside the transactional context -- for example, to verify the initial database state |
|
prior to execution of your test or to verify expected transactional commit behavior after |
|
test execution (if the test was configured not to roll back the transaction). |
|
`TransactionalTestExecutionListener` supports the `@BeforeTransaction` and |
|
`@AfterTransaction` annotations exactly for such scenarios. Simply annotate any `public |
|
void` method in your test class with one of these annotations, and the |
|
`TransactionalTestExecutionListener` ensures that your __before transaction method__ or |
|
__after transaction method__ is executed at the appropriate time. |
|
|
|
[TIP] |
|
==== |
|
Any __before methods__ (such as methods annotated with JUnit's `@Before`) and any __after |
|
methods__ (such as methods annotated with JUnit's `@After`) are executed __within__ a |
|
transaction. In addition, methods annotated with `@BeforeTransaction` or |
|
`@AfterTransaction` are naturally not executed for test methods that are not configured |
|
to run within a transaction. |
|
==== |
|
|
|
[[testcontext-tx-mgr-config]] |
|
====== Configuring a transaction manager |
|
|
|
`TransactionalTestExecutionListener` expects a `PlatformTransactionManager` bean to be |
|
defined in the Spring `ApplicationContext` for the test. In case there are multiple |
|
instances of `PlatformTransactionManager` within the test's `ApplicationContext`, |
|
`@TransactionConfiguration` supports configuring the bean name of the |
|
`PlatformTransactionManager` that should be used to drive transactions. Alternatively, a |
|
_qualifier_ may be declared via `@Transactional("myQualifier")`, or |
|
`TransactionManagementConfigurer` can be implemented by an `@Configuration` class. |
|
Consult the javadocs for `TestContextTransactionUtils.retrieveTransactionManager()` for |
|
details on the algorithm used to look up a transaction manager in the test's |
|
`ApplicationContext`. |
|
|
|
[[testcontext-tx-annotation-demo]] |
|
====== Demonstration of all transaction-related annotations |
|
|
|
The following JUnit-based example displays a fictitious integration testing scenario |
|
highlighting all transaction-related annotations. The example is **not** intended to |
|
demonstrate best practices but rather to demonstrate how these annotations can be used. |
|
Consult the <<integration-testing-annotations,annotation support>> section for further |
|
information and configuration examples. <<testcontext-executing-sql-declaratively-tx, |
|
Transaction management for `@Sql`>> contains an additional example using `@Sql` for |
|
declarative SQL script execution with default transaction rollback semantics. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
@ContextConfiguration |
|
**@TransactionConfiguration(transactionManager="txMgr", defaultRollback=false) |
|
@Transactional** |
|
public class FictitiousTransactionalTest { |
|
|
|
**@BeforeTransaction** |
|
public void verifyInitialDatabaseState() { |
|
// logic to verify the initial state before a transaction is started |
|
} |
|
|
|
@Before |
|
public void setUpTestDataWithinTransaction() { |
|
// set up test data within the transaction |
|
} |
|
|
|
@Test |
|
// overrides the class-level defaultRollback setting |
|
**@Rollback(true)** |
|
public void modifyDatabaseWithinTransaction() { |
|
// logic which uses the test data and modifies database state |
|
} |
|
|
|
@After |
|
public void tearDownWithinTransaction() { |
|
// execute "tear down" logic within the transaction |
|
} |
|
|
|
**@AfterTransaction** |
|
public void verifyFinalDatabaseState() { |
|
// logic to verify the final state after transaction has rolled back |
|
} |
|
|
|
} |
|
---- |
|
|
|
[[testcontext-tx-false-positives]] |
|
.Avoid false positives when testing ORM code |
|
[NOTE] |
|
==== |
|
When you test application code that manipulates the state of the Hibernate session, make |
|
sure to __flush__ the underlying session within test methods that execute that code. |
|
Failing to flush the underlying session can produce __false positives__: your test may |
|
pass, but the same code throws an exception in a live, production environment. In the |
|
following Hibernate-based example test case, one method demonstrates a false positive, |
|
and the other method correctly exposes the results of flushing the session. Note that |
|
this applies to JPA and any other ORM frameworks that maintain an in-memory __unit of |
|
work__. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// ... |
|
|
|
@Autowired |
|
private SessionFactory sessionFactory; |
|
|
|
@Test // no expected exception! |
|
public void falsePositive() { |
|
updateEntityInHibernateSession(); |
|
// False positive: an exception will be thrown once the session is |
|
// finally flushed (i.e., in production code) |
|
} |
|
|
|
@Test(expected = GenericJDBCException.class) |
|
public void updateWithSessionFlush() { |
|
updateEntityInHibernateSession(); |
|
// Manual flush is required to avoid false positive in test |
|
sessionFactory.getCurrentSession().flush(); |
|
} |
|
|
|
// ... |
|
---- |
|
==== |
|
|
|
|
|
[[testcontext-executing-sql]] |
|
===== Executing SQL scripts |
|
|
|
When writing integration tests against a relational database, it is often beneficial |
|
to execute SQL scripts to modify the database schema or insert test data into tables. |
|
The `spring-jdbc` module provides support for _initializing_ an embedded or existing |
|
database by executing SQL scripts when the Spring `ApplicationContext` is loaded. See |
|
<<jdbc-embedded-database-support>> and <<jdbc-embedded-database-dao-testing>> for |
|
details. |
|
|
|
Although it is very useful to initialize a database for testing _once_ when the |
|
`ApplicationContext` is loaded, sometimes it is essential to be able to modify the |
|
database _during_ integration tests. The following sections explain how to execute SQL |
|
scripts programmatically and declaratively during integration tests. |
|
|
|
[[testcontext-executing-sql-programmatically]] |
|
====== Executing SQL scripts programmatically |
|
|
|
Spring provides the following options for executing SQL scripts programmatically within |
|
integration test methods. |
|
|
|
* `org.springframework.jdbc.datasource.init.ScriptUtils` |
|
* `org.springframework.jdbc.datasource.init.ResourceDatabasePopulator` |
|
* `org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests` |
|
* `org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests` |
|
|
|
`ScriptUtils` provides a collection of static utility methods for working with SQL scripts |
|
and is mainly intended for internal use within the framework. However, if you require |
|
full control over how SQL scripts are parsed and executed, `ScriptUtils` may suit your |
|
needs better than some of the other alternatives described below. Consult the javadocs for |
|
individual methods in `ScriptUtils` for further details. |
|
|
|
`ResourceDatabasePopulator` provides a simple object-based API for programmatically |
|
populating, initializing, or cleaning up a database using SQL scripts defined in |
|
external resources. `ResourceDatabasePopulator` provides options for configuring the |
|
character encoding, statement separator, comment delimiters, and error handling flags |
|
used when parsing and executing the scripts, and each of the configuration options has |
|
a reasonable default value. Consult the javadocs for details on default values. To |
|
execute the scripts configured in a `ResourceDatabasePopulator`, you can invoke either |
|
the `populate(Connection)` method to execute the populator against a |
|
`java.sql.Connection` or the `execute(DataSource)` method to execute the populator |
|
against a `javax.sql.DataSource`. The following example specifies SQL scripts for a test |
|
schema and test data, sets the statement separator to `"@@"`, and then executes the |
|
scripts against a `DataSource`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Test |
|
public void databaseTest { |
|
ResourceDatabasePopulator populator = new ResourceDatabasePopulator(); |
|
populator.addScripts( |
|
new ClassPathResource("test-schema.sql"), |
|
new ClassPathResource("test-data.sql")); |
|
populator.setSeparator("@@"); |
|
populator.execute(this.dataSource); |
|
// execute code that uses the test schema and data |
|
} |
|
---- |
|
|
|
Note that `ResourceDatabasePopulator` internally delegates to `ScriptUtils` for parsing |
|
and executing SQL scripts. Similarly, the `executeSqlScript(..)` methods in |
|
<<testcontext-support-classes-junit4, `AbstractTransactionalJUnit4SpringContextTests`>> and |
|
<<testcontext-support-classes-testng, `AbstractTransactionalTestNGSpringContextTests`>> |
|
internally use a `ResourceDatabasePopulator` for executing SQL scripts. Consult the javadocs |
|
for the various `executeSqlScript(..)` methods for further details. |
|
|
|
|
|
[[testcontext-executing-sql-declaratively]] |
|
====== Executing SQL scripts declaratively with `@Sql` |
|
|
|
In addition to the aforementioned mechanisms for executing SQL scripts |
|
_programmatically_, SQL scripts can also be configured _declaratively_ in the Spring |
|
TestContext Framework. Specifically, the `@Sql` annotation can be declared on a test |
|
class or test method to configure the resource paths to SQL scripts that should be |
|
executed against a given database either before or after an integration test method. Note |
|
that method-level declarations override class-level declarations and that support for |
|
`@Sql` is provided by the `SqlScriptsTestExecutionListener` which is enabled by default. |
|
|
|
*Path resource semantics* |
|
|
|
Each path will be interpreted as a Spring `Resource`. A plain path -- for example, |
|
`"schema.sql"` -- will be treated as a classpath resource that is _relative_ to the |
|
package in which the test class is defined. A path starting with a slash will be treated |
|
as an _absolute_ classpath resource, for example: `"/org/example/schema.sql"`. A path |
|
which references a URL (e.g., a path prefixed with `classpath:`, `file:`, `http:`, etc.) |
|
will be loaded using the specified resource protocol. |
|
|
|
The following example demonstrates how to use `@Sql` at the class level and at the method |
|
level within a JUnit-based integration test class. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
@ContextConfiguration |
|
@Sql("/test-schema.sql") |
|
public class DatabaseTests { |
|
|
|
@Test |
|
public void emptySchemaTest { |
|
// execute code that uses the test schema without any test data |
|
} |
|
|
|
@Test |
|
@Sql({"/test-schema.sql", "/test-user-data.sql"}) |
|
public void userTest { |
|
// execute code that uses the test schema and test data |
|
} |
|
} |
|
---- |
|
|
|
*Default script detection* |
|
|
|
If no SQL scripts are specified, an attempt will be made to detect a `default` script |
|
depending on where `@Sql` is declared. If a default cannot be detected, an |
|
`IllegalStateException` will be thrown. |
|
|
|
* __class-level declaration__: if the annotated test class is `com.example.MyTest`, the |
|
corresponding default script is `"classpath:com/example/MyTest.sql"`. |
|
* __method-level declaration__: if the annotated test method is named `testMethod()` and is |
|
defined in the class `com.example.MyTest`, the corresponding default script is |
|
`"classpath:com/example/MyTest.testMethod.sql"`. |
|
|
|
*Declaring multiple `@Sql` sets* |
|
|
|
If multiple sets of SQL scripts need to be configured for a given test class or test |
|
method but with different syntax configuration, different error handling rules, or |
|
different execution phases per set, it is possible to declare multiple instances of |
|
`@Sql`. With Java 8, `@Sql` can be used as a _repeatable_ annotation. Otherwise, the |
|
`@SqlGroup` annotation can be used as an explicit container for declaring multiple |
|
instances of `@Sql`. |
|
|
|
The following example demonstrates the use of `@Sql` as a repeatable annotation using |
|
Java 8. In this scenario the `test-schema.sql` script uses a different syntax for |
|
single-line comments. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Test |
|
@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`")) |
|
@Sql("/test-user-data.sql") |
|
public void userTest { |
|
// execute code that uses the test schema and test data |
|
} |
|
---- |
|
|
|
The following example is identical to the above except that the `@Sql` declarations are |
|
grouped together within `@SqlGroup` for compatibility with Java 6 and Java 7. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Test |
|
@SqlGroup({ |
|
@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`")), |
|
@Sql("/test-user-data.sql") |
|
)} |
|
public void userTest { |
|
// execute code that uses the test schema and test data |
|
} |
|
---- |
|
|
|
*Script execution phases* |
|
|
|
By default, SQL scripts will be executed _before_ the corresponding test method. However, |
|
if a particular set of scripts needs to be executed _after_ the test method -- for |
|
example, to clean up database state -- the `executionPhase` attribute in `@Sql` can be |
|
used as seen in the following example. Note that `ISOLATED` and `AFTER_TEST_METHOD` are |
|
statically imported from `Sql.TransactionMode` and `Sql.ExecutionPhase` respectively. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Test |
|
@Sql( |
|
scripts = "create-test-data.sql", |
|
config = @SqlConfig(transactionMode = ISOLATED) |
|
) |
|
@Sql( |
|
scripts = "delete-test-data.sql", |
|
config = @SqlConfig(transactionMode = ISOLATED), |
|
executionPhase = AFTER_TEST_METHOD |
|
) |
|
public void userTest { |
|
// execute code that needs the test data to be committed |
|
// to the database outside of the test's transaction |
|
} |
|
---- |
|
|
|
*Script configuration with `@SqlConfig`* |
|
|
|
Configuration for script parsing and error handling can be configured via the |
|
`@SqlConfig` annotation. When declared as a class-level annotation on an integration test |
|
class, `@SqlConfig` serves as _global_ configuration for all SQL scripts within the test |
|
class hierarchy. When declared directly via the `config` attribute of the `@Sql` |
|
annotation, `@SqlConfig` serves as _local_ configuration for the SQL scripts declared |
|
within the enclosing `@Sql` annotation. Every attribute in `@SqlConfig` has an implicit |
|
default value which is documented in the javadocs of the corresponding attribute. Due to |
|
the rules defined for annotation attributes in the Java Language Specification, it is |
|
unfortunately not possible to assign a value of `null` to an annotation attribute. Thus, |
|
in order to support overrides of inherited global configuration, `@SqlConfig` attributes |
|
have an explicit default value of either `""` for Strings or `DEFAULT` for Enums. This |
|
approach allows local declarations of `@SqlConfig` to selectively override individual |
|
attributes from global declarations of `@SqlConfig` by providing a value other than `""` |
|
or `DEFAULT`. Global `@SqlConfig` attributes are inherited whenever local `@SqlConfig` |
|
attributes do not supply an explicit value other than `""` or `DEFAULT`. Explicit _local_ |
|
configuration therefore overrides _global_ configuration. |
|
|
|
The configuration options provided by `@Sql` and `@SqlConfig` are equivalent to those |
|
supported by `ScriptUtils` and `ResourceDatabasePopulator` but are a superset of those |
|
provided by the `<jdbc:initialize-database/>` XML namespace element. Consult the javadocs |
|
of individual attributes in `@Sql` and `@SqlConfig` for details. |
|
|
|
[[testcontext-executing-sql-declaratively-tx]] |
|
*Transaction management for `@Sql`* |
|
|
|
By default, the `SqlScriptsTestExecutionListener` will infer the desired transaction |
|
semantics for scripts configured via `@Sql`. Specifically, SQL scripts will be executed |
|
without a transaction, within an existing Spring-managed transaction -- for example, a |
|
transaction managed by the `TransactionalTestExecutionListener` for a test annotated with |
|
`@Transactional` -- or within an isolated transaction, depending on the configured value |
|
of the `transactionMode` attribute in `@SqlConfig` and the presence of a |
|
`PlatformTransactionManager` in the test's `ApplicationContext`. As a bare minimum |
|
however, a `javax.sql.DataSource` must be present in the test's `ApplicationContext`. |
|
|
|
If the algorithms used by `SqlScriptsTestExecutionListener` to detect a `DataSource` and |
|
`PlatformTransactionManager` and infer the transaction semantics do not suit your needs, |
|
you may specify explicit names via the `dataSource` and `transactionManager` attributes |
|
of `@SqlConfig`. Furthermore, the transaction propagation behavior can be controlled via |
|
the `transactionMode` attribute of `@SqlConfig` -- for example, if scripts should be |
|
executed in an isolated transaction. Although a thorough discussion of all supported |
|
options for transaction management with `@Sql` is beyond the scope of this reference |
|
manual, the javadocs for `@SqlConfig` and `SqlScriptsTestExecutionListener` provide |
|
detailed information, and the following example demonstrates a typical testing scenario |
|
using JUnit and transactional tests with `@Sql`. Note that there is no need to clean up |
|
the database after the `usersTest()` method is executed since any changes made to the |
|
database (either within the the test method or within the `/test-data.sql` script) will |
|
be automatically rolled back by the `TransactionalTestExecutionListener` (see |
|
<<testcontext-tx,transaction management>> for details). |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
@ContextConfiguration(classes = TestDatabaseConfig.class) |
|
@Transactional |
|
public class TransactionalSqlScriptsTests { |
|
|
|
protected JdbcTemplate jdbcTemplate; |
|
|
|
@Autowired |
|
public void setDataSource(DataSource dataSource) { |
|
this.jdbcTemplate = new JdbcTemplate(dataSource); |
|
} |
|
|
|
@Test |
|
@Sql("/test-data.sql") |
|
public void usersTest() { |
|
// verify state in test database: |
|
assertNumUsers(2); |
|
// execute code that uses the test data... |
|
} |
|
|
|
protected int countRowsInTable(String tableName) { |
|
return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName); |
|
} |
|
|
|
protected void assertNumUsers(int expected) { |
|
assertEquals("Number of rows in the ''user'' table.", expected, countRowsInTable("user")); |
|
} |
|
} |
|
---- |
|
|
|
|
|
[[testcontext-support-classes]] |
|
===== TestContext Framework support classes |
|
|
|
[[testcontext-support-classes-junit4]] |
|
====== JUnit support classes |
|
The `org.springframework.test.context.junit4` package provides the following support |
|
classes for JUnit-based test cases. |
|
|
|
* `AbstractJUnit4SpringContextTests` |
|
* `AbstractTransactionalJUnit4SpringContextTests` |
|
|
|
`AbstractJUnit4SpringContextTests` is an abstract base test class that integrates the |
|
__Spring TestContext Framework__ with explicit `ApplicationContext` testing support in |
|
a JUnit 4.9+ environment. When you extend `AbstractJUnit4SpringContextTests`, you can |
|
access a `protected` `applicationContext` instance variable that can be used to perform |
|
explicit bean lookups or to test the state of the context as a whole. |
|
|
|
`AbstractTransactionalJUnit4SpringContextTests` is an abstract __transactional__ extension |
|
of `AbstractJUnit4SpringContextTests` that adds some convenience functionality for JDBC |
|
access. This class expects a `javax.sql.DataSource` bean and a `PlatformTransactionManager` |
|
bean to be defined in the `ApplicationContext`. When you extend |
|
`AbstractTransactionalJUnit4SpringContextTests` you can access a `protected` `jdbcTemplate` |
|
instance variable that can be used to execute SQL statements to query the database. Such |
|
queries can be used to confirm database state both __prior to__ and __after__ execution of |
|
database-related application code, and Spring ensures that such queries run in the scope of |
|
the same transaction as the application code. When used in conjunction with an ORM tool, |
|
be sure to avoid <<testcontext-tx-false-positives,false positives>>. As mentioned in |
|
<<integration-testing-support-jdbc>>, `AbstractTransactionalJUnit4SpringContextTests` |
|
also provides convenience methods which delegate to methods in `JdbcTestUtils` using the |
|
aforementioned `jdbcTemplate`. Furthermore, `AbstractTransactionalJUnit4SpringContextTests` |
|
provides an `executeSqlScript(..)` method for executing SQL scripts against the configured |
|
`DataSource`. |
|
|
|
[TIP] |
|
==== |
|
These classes are a convenience for extension. If you do not want your test classes to be |
|
tied to a Spring-specific class hierarchy, you can configure your own custom test classes |
|
by using `@RunWith(SpringJUnit4ClassRunner.class)`, `@ContextConfiguration`, |
|
`@TestExecutionListeners`, and so on. |
|
==== |
|
|
|
[[testcontext-junit4-runner]] |
|
====== Spring JUnit Runner |
|
The __Spring TestContext Framework__ offers full integration with JUnit 4.9+ through a |
|
custom runner (tested on JUnit 4.9 -- 4.11). By annotating test classes with |
|
`@RunWith(SpringJUnit4ClassRunner.class)`, developers can implement standard JUnit-based |
|
unit and integration tests and simultaneously reap the benefits of the TestContext |
|
framework such as support for loading application contexts, dependency injection of test |
|
instances, transactional test method execution, and so on. The following code listing |
|
displays the minimal requirements for configuring a test class to run with the custom |
|
Spring Runner. `@TestExecutionListeners` is configured with an empty list in order to |
|
disable the default listeners, which otherwise would require an ApplicationContext to be |
|
configured through `@ContextConfiguration`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
@TestExecutionListeners({}) |
|
public class SimpleTest { |
|
|
|
@Test |
|
public void testMethod() { |
|
// execute test logic... |
|
} |
|
} |
|
---- |
|
|
|
[[testcontext-support-classes-testng]] |
|
====== TestNG support classes |
|
The `org.springframework.test.context.testng` package provides the following support |
|
classes for TestNG based test cases. |
|
|
|
* `AbstractTestNGSpringContextTests` |
|
* `AbstractTransactionalTestNGSpringContextTests` |
|
|
|
`AbstractTestNGSpringContextTests` is an abstract base test class that integrates the |
|
__Spring TestContext Framework__ with explicit `ApplicationContext` testing support in |
|
a TestNG environment. When you extend `AbstractTestNGSpringContextTests`, you can |
|
access a `protected` `applicationContext` instance variable that can be used to perform |
|
explicit bean lookups or to test the state of the context as a whole. |
|
|
|
`AbstractTransactionalTestNGSpringContextTests` is an abstract __transactional__ extension |
|
of `AbstractTestNGSpringContextTests` that adds some convenience functionality for JDBC |
|
access. This class expects a `javax.sql.DataSource` bean and a `PlatformTransactionManager` |
|
bean to be defined in the `ApplicationContext`. When you extend |
|
`AbstractTransactionalTestNGSpringContextTests` you can access a `protected` `jdbcTemplate` |
|
instance variable that can be used to execute SQL statements to query the database. Such |
|
queries can be used to confirm database state both __prior to__ and __after__ execution of |
|
database-related application code, and Spring ensures that such queries run in the scope of |
|
the same transaction as the application code. When used in conjunction with an ORM tool, |
|
be sure to avoid <<testcontext-tx-false-positives,false positives>>. As mentioned in |
|
<<integration-testing-support-jdbc>>, `AbstractTransactionalTestNGSpringContextTests` |
|
also provides convenience methods which delegate to methods in `JdbcTestUtils` using the |
|
aforementioned `jdbcTemplate`. Furthermore, `AbstractTransactionalTestNGSpringContextTests` |
|
provides an `executeSqlScript(..)` method for executing SQL scripts against the configured |
|
`DataSource`. |
|
|
|
|
|
[TIP] |
|
==== |
|
These classes are a convenience for extension. If you do not want your test classes to be |
|
tied to a Spring-specific class hierarchy, you can configure your own custom test classes |
|
by using `@ContextConfiguration`, `@TestExecutionListeners`, and so on, and by manually |
|
instrumenting your test class with a `TestContextManager`. See the source code of |
|
`AbstractTestNGSpringContextTests` for an example of how to instrument your test class. |
|
==== |
|
|
|
|
|
|
|
[[spring-mvc-test-framework]] |
|
==== Spring MVC Test Framework |
|
|
|
.Standalone project |
|
**** |
|
Before inclusion in Spring Framework 3.2, the Spring MVC Test framework had already |
|
existed as a separate project on GitHub where it grew and evolved through actual use, |
|
feedback, and the contribution of many. |
|
|
|
The standalone https://github.com/spring-projects/spring-test-mvc[spring-test-mvc project] |
|
is still available on GitHub and can be used in conjunction with Spring Framework 3.1.x. |
|
Applications upgrading to 3.2 or later should replace the `spring-test-mvc` dependency with a |
|
dependency on `spring-test`. |
|
|
|
The `spring-test` module uses a different package `org.springframework.test.web` but |
|
otherwise is nearly identical with two exceptions. One is support for features new in |
|
3.2 (e.g. asynchronous web requests). The other relates to the options for creating a |
|
`MockMvc` instance. In Spring Framework 3.2 and later, this can only be done through the |
|
TestContext framework, which provides caching benefits for the loaded configuration. |
|
**** |
|
|
|
The __Spring MVC Test framework__ provides first class JUnit support for testing client |
|
and server-side Spring MVC code through a fluent API. Typically it loads the actual |
|
Spring configuration through the __TestContext framework__ and always uses the |
|
`DispatcherServlet` to process requests thus approximating full integration tests |
|
without requiring a running Servlet container. |
|
|
|
Client-side tests are `RestTemplate`-based and allow tests for code that relies on the |
|
`RestTemplate` without requiring a running server to respond to the requests. |
|
|
|
|
|
[[spring-mvc-test-server]] |
|
===== Server-Side Tests |
|
Before Spring Framework 3.2, the most likely way to test a Spring MVC controller was to |
|
write a unit test that instantiates the controller, injects it with mock or stub |
|
dependencies, and then calls its methods directly, using a `MockHttpServletRequest` and |
|
`MockHttpServletResponse` where necessary. |
|
|
|
Although this is pretty easy to do, controllers have many annotations, and much remains |
|
untested. Request mappings, data binding, type conversion, and validation are just a few |
|
examples of what isn't tested. Furthermore, there are other types of annotated methods |
|
such as `@InitBinder`, `@ModelAttribute`, and `@ExceptionHandler` that get invoked as |
|
part of request processing. |
|
|
|
The idea behind Spring MVC Test is to be able to re-write those controller tests by |
|
performing actual requests and generating responses, as they would be at runtime, along |
|
the way invoking controllers through the Spring MVC `DispatcherServlet`. Controllers can |
|
still be injected with mock dependencies, so tests can remain focused on the web layer. |
|
|
|
Spring MVC Test builds on the familiar "mock" implementations of the Servlet API |
|
available in the `spring-test` module. This allows performing requests and generating |
|
responses without the need for running in a Servlet container. For the most part |
|
everything should work as it does at runtime with the exception of JSP rendering, which |
|
is not available outside a Servlet container. Furthermore, if you are familiar with how |
|
the `MockHttpServletResponse` works, you'll know that forwards and redirects are not |
|
actually executed. Instead "forwarded" and "redirected" URLs are saved and can be |
|
asserted in tests. This means if you are using JSPs, you can verify the JSP page to |
|
which the request was forwarded. |
|
|
|
All other means of rendering including `@ResponseBody` methods and `View` types (besides |
|
JSPs) such as Freemarker, Velocity, Thymeleaf, and others for rendering HTML, JSON, XML, |
|
and so on should work as expected, and the response will contain the generated content. |
|
|
|
Below is an example of a test requesting account information in JSON format: |
|
|
|
[source,java,indent=0] |
|
---- |
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; |
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; |
|
|
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
@WebAppConfiguration |
|
@ContextConfiguration("test-servlet-context.xml") |
|
public class ExampleTests { |
|
|
|
@Autowired |
|
private WebApplicationContext wac; |
|
|
|
private MockMvc mockMvc; |
|
|
|
@Before |
|
public void setup() { |
|
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); |
|
} |
|
|
|
@Test |
|
public void getAccount() throws Exception { |
|
this.mockMvc.perform(get("/accounts/1").accept(MediaType.parseMediaType("application/json;charset=UTF-8"))) |
|
.andExpect(status().isOk()) |
|
.andExpect(content().contentType("application/json")) |
|
.andExpect(jsonPath("$.name").value("Lee")); |
|
} |
|
|
|
} |
|
---- |
|
|
|
The test relies on the `WebApplicationContext` support of the __TestContext framework__. |
|
It loads Spring configuration from an XML configuration file located in the same package |
|
as the test class (also supports JavaConfig) and injects the created |
|
`WebApplicationContext` into the test so a `MockMvc` instance can be created with it. |
|
|
|
The `MockMvc` is then used to perform a request to `"/accounts/1"` and verify the |
|
resulting response status is 200, the response content type is `"application/json"`, and |
|
response content has a JSON property called "name" with the value "Lee". JSON content is |
|
inspected with the help of Jayway's https://github.com/jayway/JsonPath[JsonPath |
|
project]. There are lots of other options for verifying the result of the performed |
|
request and those will be discussed later. |
|
|
|
[[spring-mvc-test-server-static-imports]] |
|
====== Static Imports |
|
The fluent API in the example above requires a few static imports such as |
|
`MockMvcRequestBuilders.*`, `MockMvcResultMatchers.*`, and `MockMvcBuilders.*`. An easy |
|
way to find these classes is to search for types matching __"MockMvc*"__. If using |
|
Eclipse, be sure to add them as "favorite static members" in the Eclipse preferences |
|
under__Java -> Editor -> Content Assist -> Favorites__. That will allow use of content |
|
assist after typing the first character of the static method name. Other IDEs (e.g. |
|
IntelliJ) may not require any additional configuration. Just check the support for code |
|
completion on static members. |
|
|
|
[[spring-mvc-test-server-setup-options]] |
|
====== Setup Options |
|
The goal of server-side test setup is to create an instance of `MockMvc` that can be |
|
used to perform requests. There are two main options. |
|
|
|
The first option is to point to Spring MVC configuration through the __TestContext |
|
framework__, which loads the Spring configuration and injects a `WebApplicationContext` |
|
into the test to use to create a `MockMvc`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
@WebAppConfiguration |
|
@ContextConfiguration("my-servlet-context.xml") |
|
public class MyWebTests { |
|
|
|
@Autowired |
|
private WebApplicationContext wac; |
|
|
|
private MockMvc mockMvc; |
|
|
|
@Before |
|
public void setup() { |
|
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
The second option is to simply register a controller instance without loading any Spring |
|
configuration. Instead basic Spring MVC configuration suitable for testing annotated |
|
controllers is automatically created. The created configuration is comparable to that of |
|
the MVC JavaConfig (and the MVC namespace) and can be customized to a degree through |
|
builder-style methods: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MyWebTests { |
|
|
|
private MockMvc mockMvc; |
|
|
|
@Before |
|
public void setup() { |
|
this.mockMvc = MockMvcBuilders.standaloneSetup(new AccountController()).build(); |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
Which option should you use? |
|
|
|
The __"webAppContextSetup"__ loads the actual Spring MVC configuration resulting in a |
|
more complete integration test. Since the __TestContext framework__ caches the loaded |
|
Spring configuration, it helps to keep tests running fast even as more tests get added. |
|
Furthermore, you can inject mock services into controllers through Spring configuration, |
|
in order to remain focused on testing the web layer. Here is an example of declaring a |
|
mock service with Mockito: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="accountService" class="org.mockito.Mockito" factory-method="mock"> |
|
<constructor-arg value="org.example.AccountService"/> |
|
</bean> |
|
---- |
|
|
|
Then you can inject the mock service into the test in order set up and verify |
|
expectations: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringJUnit4ClassRunner.class) |
|
@WebAppConfiguration |
|
@ContextConfiguration("test-servlet-context.xml") |
|
public class AccountTests { |
|
|
|
@Autowired |
|
private WebApplicationContext wac; |
|
|
|
private MockMvc mockMvc; |
|
|
|
@Autowired |
|
private AccountService accountService; |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
The __"standaloneSetup"__ on the other hand is a little closer to a unit test. It tests |
|
one controller at a time, the controller can be injected with mock dependencies |
|
manually, and it doesn't involve loading Spring configuration. Such tests are more |
|
focused in style and make it easier to see which controller is being tested, whether any |
|
specific Spring MVC configuration is required to work, and so on. The "standaloneSetup" |
|
is also a very convenient way to write ad-hoc tests to verify some behavior or to debug |
|
an issue. |
|
|
|
Just like with integration vs unit testing, there is no right or wrong answer. Using the |
|
"standaloneSetup" does imply the need for some additional "webAppContextSetup" tests to |
|
verify the Spring MVC configuration. Alternatively, you can decide write all tests with |
|
"webAppContextSetup" and always test against actual Spring MVC configuration. |
|
|
|
[[spring-mvc-test-server-performing-requests]] |
|
====== Performing Requests |
|
To perform requests, use the appropriate HTTP method and additional builder-style |
|
methods corresponding to properties of `MockHttpServletRequest`. For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
mockMvc.perform(post("/hotels/{id}", 42).accept(MediaType.APPLICATION_JSON)); |
|
---- |
|
|
|
In addition to all the HTTP methods, you can also perform file upload requests, which |
|
internally creates an instance of `MockMultipartHttpServletRequest`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
mockMvc.perform(fileUpload("/doc").file("a1", "ABC".getBytes("UTF-8"))); |
|
---- |
|
|
|
Query string parameters can be specified in the URI template: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
mockMvc.perform(get("/hotels?foo={foo}", "bar")); |
|
---- |
|
|
|
Or by adding Servlet request parameters: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
mockMvc.perform(get("/hotels").param("foo", "bar")); |
|
---- |
|
|
|
If application code relies on Servlet request parameters, and doesn't check the query |
|
string, as is most often the case, then it doesn't matter how parameters are added. Keep |
|
in mind though that parameters provided in the URI template will be decoded while |
|
parameters provided through the `param(...)` method are expected to be decoded. |
|
|
|
In most cases it's preferable to leave out the context path and the Servlet path from |
|
the request URI. If you must test with the full request URI, be sure to set the |
|
`contextPath` and `servletPath` accordingly so that request mappings will work: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
mockMvc.perform(get("/app/main/hotels/{id}").contextPath("/app").servletPath("/main")) |
|
---- |
|
|
|
Looking at the above example, it would be cumbersome to set the contextPath and |
|
servletPath with every performed request. That's why you can define default request |
|
properties when building the `MockMvc`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MyWebTests { |
|
|
|
private MockMvc mockMvc; |
|
|
|
@Before |
|
public void setup() { |
|
mockMvc = standaloneSetup(new AccountController()) |
|
.defaultRequest(get("/") |
|
.contextPath("/app").servletPath("/main") |
|
.accept(MediaType.APPLICATION_JSON).build(); |
|
} |
|
---- |
|
|
|
The above properties will apply to every request performed through the `MockMvc`. If the |
|
same property is also specified on a given request, it will override the default value. |
|
That is why, the HTTP method and URI don't matter, when setting default request |
|
properties, since they must be specified on every request. |
|
|
|
[[spring-mvc-test-server-defining-expectations]] |
|
====== Defining Expectations |
|
Expectations can be defined by appending one or more `.andExpect(..)` after call to |
|
perform the request: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
mockMvc.perform(get("/accounts/1")).andExpect(status().isOk()); |
|
---- |
|
|
|
`MockMvcResultMatchers.*` defines a number of static members, some of which return types |
|
with additional methods, for asserting the result of the performed request. The |
|
assertions fall in two general categories. |
|
|
|
The first category of assertions verify properties of the response, i.e the response |
|
status, headers, and content. Those are the most important things to test for. |
|
|
|
The second category of assertions go beyond the response, and allow inspecting Spring |
|
MVC specific constructs such as which controller method processed the request, whether |
|
an exception was raised and handled, what the content of the model is, what view was |
|
selected, what flash attributes were added, and so on. It is also possible to verify |
|
Servlet specific constructs such as request and session attributes. The following test |
|
asserts that binding/validation failed: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
mockMvc.perform(post("/persons")) |
|
.andExpect(status().isOk()) |
|
.andExpect(model().attributeHasErrors("person")); |
|
---- |
|
|
|
Many times when writing tests, it's useful to dump the result of the performed request. |
|
This can be done as follows, where `print()` is a static import from |
|
`MockMvcResultHandlers`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
mockMvc.perform(post("/persons")) |
|
.andDo(print()) |
|
.andExpect(status().isOk()) |
|
.andExpect(model().attributeHasErrors("person")); |
|
---- |
|
|
|
As long as request processing causes an unhandled exception, the `print()` method will |
|
print all the available result data to `System.out`. |
|
|
|
In some cases, you may want to get direct access to the result and verify something that |
|
cannot be verified otherwise. This can be done by appending `.andReturn()` at the end |
|
after all expectations: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
MvcResult mvcResult = mockMvc.perform(post("/persons")).andExpect(status().isOk()).andReturn(); |
|
// ... |
|
---- |
|
|
|
When all tests repeat the same expectations, you can define the common expectations once |
|
when building the `MockMvc`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
standaloneSetup(new SimpleController()) |
|
.alwaysExpect(status().isOk()) |
|
.alwaysExpect(content().contentType("application/json;charset=UTF-8")) |
|
.build() |
|
---- |
|
|
|
Note that the expectation is __always__ applied and cannot be overridden without |
|
creating a separate `MockMvc` instance. |
|
|
|
When JSON response content contains hypermedia links created with |
|
https://github.com/spring-projects/spring-hateoas[Spring HATEOAS], the resulting links can |
|
be verified: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
mockMvc.perform(get("/people").accept(MediaType.APPLICATION_JSON)) |
|
.andExpect(jsonPath("$.links[?(@.rel == ''self'')].href").value("http://localhost:8080/people")); |
|
---- |
|
|
|
When XML response content contains hypermedia links created with |
|
https://github.com/spring-projects/spring-hateoas[Spring HATEOAS], the resulting links can |
|
be verified: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Map<String, String> ns = Collections.singletonMap("ns", "http://www.w3.org/2005/Atom"); |
|
mockMvc.perform(get("/handle").accept(MediaType.APPLICATION_XML)) |
|
.andExpect(xpath("/person/ns:link[@rel=''self'']/@href", ns).string("http://localhost:8080/people")); |
|
---- |
|
|
|
[[spring-mvc-test-server-filters]] |
|
====== Filter Registrations |
|
When setting up a `MockMvc`, you can register one or more `Filter` instances: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
mockMvc = standaloneSetup(new PersonController()).addFilters(new CharacterEncodingFilter()).build(); |
|
---- |
|
|
|
Registered filters will be invoked through `MockFilterChain` from `spring-test` and the |
|
last filter will delegates to the `DispatcherServlet`. |
|
|
|
[[spring-mvc-test-server-resources]] |
|
====== Further Server-Side Test Examples |
|
The framework's own tests include |
|
https://github.com/spring-projects/spring-framework/tree/master/spring-test/src/test/java/org/springframework/test/web/servlet/samples[many |
|
sample tests] intended to demonstrate how to use Spring MVC Test. Browse these examples |
|
for further ideas. Also the |
|
https://github.com/spring-projects/spring-mvc-showcase[spring-mvc-showcase] has full test |
|
coverage based on Spring MVC Test. |
|
|
|
|
|
[[spring-mvc-test-client]] |
|
===== Client-Side REST Tests |
|
Client-side tests are for code using the `RestTemplate`. The goal is to define expected |
|
requests and provide "stub" responses: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
RestTemplate restTemplate = new RestTemplate(); |
|
|
|
MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate); |
|
mockServer.expect(requestTo("/greeting")).andRespond(withSuccess("Hello world", MediaType.TEXT_PLAIN)); |
|
|
|
// use RestTemplate ... |
|
|
|
mockServer.verify(); |
|
---- |
|
|
|
In the above example, `MockRestServiceServer` -- the central class for client-side REST |
|
tests -- configures the `RestTemplate` with a custom `ClientHttpRequestFactory` that |
|
asserts actual requests against expectations and returns "stub" responses. In this case |
|
we expect a single request to "/greeting" and want to return a 200 response with |
|
"text/plain" content. We could define as many additional requests and stub responses as |
|
necessary. |
|
|
|
Once expected requests and stub responses have been defined, the `RestTemplate` can be |
|
used in client-side code as usual. At the end of the tests `mockServer.verify()` can be |
|
used to verify that all expected requests were performed. |
|
|
|
[[spring-mvc-test-client-static-imports]] |
|
====== Static Imports |
|
Just like with server-side tests, the fluent API for client-side tests requires a few |
|
static imports. Those are easy to find by searching __"MockRest*"__. Eclipse users |
|
should add `"MockRestRequestMatchers.*"` and `"MockRestResponseCreators.*"` as "favorite |
|
static members" in the Eclipse preferences under __Java -> Editor -> Content Assist -> |
|
Favorites__. That allows using content assist after typing the first character of the |
|
static method name. Other IDEs (e.g. IntelliJ) may not require any additional |
|
configuration. Just check the support for code completion on static members. |
|
|
|
[[spring-mvc-test-client-resources]] |
|
====== Further Examples of Client-side REST Tests |
|
Spring MVC Test's own tests include |
|
https://github.com/spring-projects/spring-framework/tree/master/spring-test/src/test/java/org/springframework/test/web/client/samples[example |
|
tests] of client-side REST tests. |
|
|
|
|
|
|
|
[[testing-examples-petclinic]] |
|
==== PetClinic Example |
|
|
|
The PetClinic application, available on |
|
https://github.com/spring-projects/spring-petclinic[GitHub], illustrates several features |
|
of the __Spring TestContext Framework__ in a JUnit environment. Most test functionality |
|
is included in the `AbstractClinicTests`, for which a partial listing is shown below: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import static org.junit.Assert.assertEquals; |
|
// import ... |
|
|
|
**@ContextConfiguration** |
|
public abstract class AbstractClinicTests **extends AbstractTransactionalJUnit4SpringContextTests** { |
|
|
|
**@Autowired** |
|
protected Clinic clinic; |
|
|
|
@Test |
|
public void getVets() { |
|
Collection<Vet> vets = this.clinic.getVets(); |
|
assertEquals("JDBC query must show the same number of vets", |
|
**super.countRowsInTable("VETS")**, vets.size()); |
|
Vet v1 = EntityUtils.getById(vets, Vet.class, 2); |
|
assertEquals("Leary", v1.getLastName()); |
|
assertEquals(1, v1.getNrOfSpecialties()); |
|
assertEquals("radiology", (v1.getSpecialties().get(0)).getName()); |
|
// ... |
|
} |
|
|
|
// ... |
|
} |
|
---- |
|
|
|
Notes: |
|
|
|
* This test case extends the `AbstractTransactionalJUnit4SpringContextTests` class, from |
|
which it inherits configuration for Dependency Injection (through the |
|
`DependencyInjectionTestExecutionListener`) and transactional behavior (through the |
|
`TransactionalTestExecutionListener`). |
|
* The `clinic` instance variable -- the application object being tested -- is set by |
|
Dependency Injection through `@Autowired` semantics. |
|
* The `getVets()` method illustrates how you can use the inherited `countRowsInTable()` |
|
method to easily verify the number of rows in a given table, thus verifying correct |
|
behavior of the application code being tested. This allows for stronger tests and |
|
lessens dependency on the exact test data. For example, you can add additional rows in |
|
the database without breaking tests. |
|
* Like many integration tests that use a database, most of the tests in |
|
`AbstractClinicTests` depend on a minimum amount of data already in the database before |
|
the test cases run. Alternatively, you might choose to populate the database within the |
|
test fixture set up of your test cases -- again, within the same transaction as the |
|
tests. |
|
|
|
The PetClinic application supports three data access technologies: JDBC, Hibernate, and |
|
JPA. By declaring `@ContextConfiguration` without any specific resource locations, the |
|
`AbstractClinicTests` class will have its application context loaded from the default |
|
location, `AbstractClinicTests-context.xml`, which declares a common `DataSource`. |
|
Subclasses specify additional context locations that must declare a |
|
`PlatformTransactionManager` and a concrete implementation of `Clinic`. |
|
|
|
For example, the Hibernate implementation of the PetClinic tests contains the following |
|
implementation. For this example, `HibernateClinicTests` does not contain a single line |
|
of code: we only need to declare `@ContextConfiguration`, and the tests are inherited |
|
from `AbstractClinicTests`. Because `@ContextConfiguration` is declared without any |
|
specific resource locations, the __Spring TestContext Framework__ loads an application |
|
context from all the beans defined in `AbstractClinicTests-context.xml` (i.e., the |
|
inherited locations) and `HibernateClinicTests-context.xml`, with |
|
`HibernateClinicTests-context.xml` possibly overriding beans defined in |
|
`AbstractClinicTests-context.xml`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
**@ContextConfiguration** |
|
public class HibernateClinicTests extends AbstractClinicTests { } |
|
---- |
|
|
|
In a large-scale application, the Spring configuration is often split across multiple |
|
files. Consequently, configuration locations are typically specified in a common base |
|
class for all application-specific integration tests. Such a base class may also add |
|
useful instance variables -- populated by Dependency Injection, naturally -- such as a |
|
`SessionFactory` in the case of an application using Hibernate. |
|
|
|
As far as possible, you should have exactly the same Spring configuration files in your |
|
integration tests as in the deployed environment. One likely point of difference |
|
concerns database connection pooling and transaction infrastructure. If you are |
|
deploying to a full-blown application server, you will probably use its connection pool |
|
(available through JNDI) and JTA implementation. Thus in production you will use a |
|
`JndiObjectFactoryBean` or `<jee:jndi-lookup>` for the `DataSource` and |
|
`JtaTransactionManager`. JNDI and JTA will not be available in out-of-container |
|
integration tests, so you should use a combination like the Commons DBCP |
|
`BasicDataSource` and `DataSourceTransactionManager` or `HibernateTransactionManager` |
|
for them. You can factor out this variant behavior into a single XML file, having the |
|
choice between application server and a 'local' configuration separated from all other |
|
configuration, which will not vary between the test and production environments. In |
|
addition, it is advisable to use properties files for connection settings. See the |
|
PetClinic application for an example. |
|
|
|
|
|
|
|
|
|
[[testing-resources]] |
|
=== Further Resources |
|
Consult the following resources for more information about testing: |
|
|
|
* http://www.junit.org/[JUnit]: "__A programmer-oriented testing framework for Java__". |
|
Used by the Spring Framework in its test suite. |
|
* http://testng.org/[TestNG]: A testing framework inspired by JUnit with added support |
|
for annotations, test groups, data-driven testing, distributed testing, etc. |
|
* http://www.mockobjects.com/[MockObjects.com]: Web site dedicated to mock objects, a |
|
technique for improving the design of code within test-driven development. |
|
* http://en.wikipedia.org/wiki/Mock_Object["Mock Objects"]: Article in Wikipedia. |
|
* http://www.easymock.org/[EasyMock]: Java library " __that provides Mock Objects for |
|
interfaces (and objects through the class extension) by generating them on the fly |
|
using Java's proxy mechanism.__ " Used by the Spring Framework in its test suite. |
|
* http://www.jmock.org/[JMock]: Library that supports test-driven development of Java |
|
code with mock objects. |
|
* http://mockito.org/[Mockito]: Java mock library based on the |
|
http://xunitpatterns.com/Test%20Spy.html[test spy] pattern. |
|
* http://dbunit.sourceforge.net/[DbUnit]: JUnit extension (also usable with Ant and |
|
Maven) targeted for database-driven projects that, among other things, puts your |
|
database into a known state between test runs. |
|
* http://grinder.sourceforge.net/[The Grinder]: Java load testing framework. |
|
|
|
|
|
|
|
|
|
|
|
|
|
[[spring-data-tier]] |
|
= Data Access |
|
[partintro] |
|
-- |
|
This part of the reference documentation is concerned with data access and the |
|
interaction between the data access layer and the business or service layer. |
|
|
|
Spring's comprehensive transaction management support is covered in some detail, |
|
followed by thorough coverage of the various data access frameworks and technologies |
|
that the Spring Framework integrates with. |
|
|
|
* <<transaction>> |
|
* <<dao>> |
|
* <<jdbc>> |
|
* <<orm>> |
|
* <<oxm>> |
|
-- |
|
|
|
|
|
|
|
|
|
[[transaction]] |
|
== Transaction Management |
|
|
|
|
|
|
|
|
|
[[transaction-intro]] |
|
=== Introduction to Spring Framework transaction management |
|
Comprehensive transaction support is among the most compelling reasons to use the Spring |
|
Framework. The Spring Framework provides a consistent abstraction for transaction |
|
management that delivers the following benefits: |
|
|
|
* Consistent programming model across different transaction APIs such as Java |
|
Transaction API (JTA), JDBC, Hibernate, Java Persistence API (JPA), and Java Data |
|
Objects (JDO). |
|
* Support for <<transaction-declarative,declarative transaction management>>. |
|
* Simpler API for <<transaction-programmatic,programmatic>> transaction management than |
|
complex transaction APIs such as JTA. |
|
* Excellent integration with Spring's data access abstractions. |
|
|
|
The following sections describe the Spring Framework's transaction value-adds and |
|
technologies. (The chapter also includes discussions of best practices, application |
|
server integration, and solutions to common problems.) |
|
|
|
* <<transaction-motivation,Advantages of the Spring Framework's transaction support |
|
model>> describes __why__ you would use the Spring Framework's transaction abstraction |
|
instead of EJB Container-Managed Transactions (CMT) or choosing to drive local |
|
transactions through a proprietary API such as Hibernate. |
|
* <<transaction-strategies,Understanding the Spring Framework transaction abstraction>> |
|
outlines the core classes and describes how to configure and obtain `DataSource` |
|
instances from a variety of sources. |
|
* <<tx-resource-synchronization,Synchronizing resources with transactions >>describes |
|
how the application code ensures that resources are created, reused, and cleaned up |
|
properly. |
|
* <<transaction-declarative,Declarative transaction management>> describes support for |
|
declarative transaction management. |
|
* <<transaction-programmatic,Programmatic transaction management>> covers support for |
|
programmatic (that is, explicitly coded) transaction management. |
|
|
|
|
|
|
|
|
|
[[transaction-motivation]] |
|
=== Advantages of the Spring Framework's transaction support model |
|
Traditionally, Java EE developers have had two choices for transaction management: |
|
__global__ or __local__ transactions, both of which have profound limitations. Global |
|
and local transaction management is reviewed in the next two sections, followed by a |
|
discussion of how the Spring Framework's transaction management support addresses the |
|
limitations of the global and local transaction models. |
|
|
|
|
|
|
|
[[transaction-global]] |
|
==== Global transactions |
|
Global transactions enable you to work with multiple transactional resources, typically |
|
relational databases and message queues. The application server manages global |
|
transactions through the JTA, which is a cumbersome API to use (partly due to its |
|
exception model). Furthermore, a JTA `UserTransaction` normally needs to be sourced from |
|
JNDI, meaning that you __also__ need to use JNDI in order to use JTA. Obviously the use |
|
of global transactions would limit any potential reuse of application code, as JTA is |
|
normally only available in an application server environment. |
|
|
|
Previously, the preferred way to use global transactions was via EJB __CMT__ |
|
(__Container Managed Transaction__): CMT is a form of __declarative transaction |
|
management__ (as distinguished from __programmatic transaction management__). EJB CMT |
|
removes the need for transaction-related JNDI lookups, although of course the use of EJB |
|
itself necessitates the use of JNDI. It removes most but not all of the need to write |
|
Java code to control transactions. The significant downside is that CMT is tied to JTA |
|
and an application server environment. Also, it is only available if one chooses to |
|
implement business logic in EJBs, or at least behind a transactional EJB facade. The |
|
negatives of EJB in general are so great that this is not an attractive proposition, |
|
especially in the face of compelling alternatives for declarative transaction management. |
|
|
|
|
|
|
|
[[transaction-local]] |
|
==== Local transactions |
|
Local transactions are resource-specific, such as a transaction associated with a JDBC |
|
connection. Local transactions may be easier to use, but have significant disadvantages: |
|
they cannot work across multiple transactional resources. For example, code that manages |
|
transactions using a JDBC connection cannot run within a global JTA transaction. Because |
|
the application server is not involved in transaction management, it cannot help ensure |
|
correctness across multiple resources. (It is worth noting that most applications use a |
|
single transaction resource.) Another downside is that local transactions are invasive |
|
to the programming model. |
|
|
|
|
|
|
|
[[transaction-programming-model]] |
|
==== Spring Framework's consistent programming model |
|
|
|
Spring resolves the disadvantages of global and local transactions. It enables |
|
application developers to use a __consistent__ programming model __in any environment__. |
|
You write your code once, and it can benefit from different transaction management |
|
strategies in different environments. The Spring Framework provides both declarative and |
|
programmatic transaction management. Most users prefer declarative transaction |
|
management, which is recommended in most cases. |
|
|
|
With programmatic transaction management, developers work with the Spring Framework |
|
transaction abstraction, which can run over any underlying transaction infrastructure. |
|
With the preferred declarative model, developers typically write little or no code |
|
related to transaction management, and hence do not depend on the Spring Framework |
|
transaction API, or any other transaction API. |
|
|
|
.Do you need an application server for transaction management? |
|
**** |
|
The Spring Framework's transaction management support changes traditional rules as to |
|
when an enterprise Java application requires an application server. |
|
|
|
In particular, you do not need an application server simply for declarative transactions |
|
through EJBs. In fact, even if your application server has powerful JTA capabilities, |
|
you may decide that the Spring Framework's declarative transactions offer more power and |
|
a more productive programming model than EJB CMT. |
|
|
|
Typically you need an application server's JTA capability only if your application needs |
|
to handle transactions across multiple resources, which is not a requirement for many |
|
applications. Many high-end applications use a single, highly scalable database (such as |
|
Oracle RAC) instead. Standalone transaction managers such as |
|
http://www.atomikos.com/[Atomikos Transactions] and http://jotm.objectweb.org/[JOTM] |
|
are other options. Of course, you may need other application server capabilities such as |
|
Java Message Service (JMS) and Java EE Connector Architecture (JCA). |
|
|
|
The Spring Framework __gives you the choice of when to scale your application to a fully |
|
loaded application server__. Gone are the days when the only alternative to using EJB |
|
CMT or JTA was to write code with local transactions such as those on JDBC connections, |
|
and face a hefty rework if you need that code to run within global, container-managed |
|
transactions. With the Spring Framework, only some of the bean definitions in your |
|
configuration file, rather than your code, need to change. |
|
**** |
|
|
|
|
|
|
|
|
|
[[transaction-strategies]] |
|
=== Understanding the Spring Framework transaction abstraction |
|
The key to the Spring transaction abstraction is the notion of a __transaction |
|
strategy__. A transaction strategy is defined by the |
|
`org.springframework.transaction.PlatformTransactionManager` interface: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface PlatformTransactionManager { |
|
|
|
TransactionStatus getTransaction( |
|
TransactionDefinition definition) throws TransactionException; |
|
|
|
void commit(TransactionStatus status) throws TransactionException; |
|
|
|
void rollback(TransactionStatus status) throws TransactionException; |
|
} |
|
---- |
|
|
|
This is primarily a service provider interface (SPI), although it can be used |
|
<<transaction-programmatic-ptm,programmatically>> from your application code. Because |
|
`PlatformTransactionManager` is an __interface__, it can be easily mocked or stubbed as |
|
necessary. It is not tied to a lookup strategy such as JNDI. |
|
`PlatformTransactionManager` implementations are defined like any other object (or bean) |
|
in the Spring Framework IoC container. This benefit alone makes Spring Framework |
|
transactions a worthwhile abstraction even when you work with JTA. Transactional code |
|
can be tested much more easily than if it used JTA directly. |
|
|
|
Again in keeping with Spring's philosophy, the `TransactionException` that can be thrown |
|
by any of the `PlatformTransactionManager` interface's methods is __unchecked__ (that |
|
is, it extends the `java.lang.RuntimeException` class). Transaction infrastructure |
|
failures are almost invariably fatal. In rare cases where application code can actually |
|
recover from a transaction failure, the application developer can still choose to catch |
|
and handle `TransactionException`. The salient point is that developers are not |
|
__forced__ to do so. |
|
|
|
The `getTransaction(..)` method returns a `TransactionStatus` object, depending on a |
|
`TransactionDefinition` parameter. The returned `TransactionStatus` might represent a |
|
new transaction, or can represent an existing transaction if a matching transaction |
|
exists in the current call stack. The implication in this latter case is that, as with |
|
Java EE transaction contexts, a `TransactionStatus` is associated with a __thread__ of |
|
execution. |
|
|
|
The `TransactionDefinition` interface specifies: |
|
|
|
* __Isolation__: The degree to which this transaction is isolated from the work of other |
|
transactions. For example, can this transaction see uncommitted writes from other |
|
transactions? |
|
* __Propagation__: Typically, all code executed within a transaction scope will run in |
|
that transaction. However, you have the option of specifying the behavior in the event |
|
that a transactional method is executed when a transaction context already exists. For |
|
example, code can continue running in the existing transaction (the common case); or |
|
the existing transaction can be suspended and a new transaction created. __Spring |
|
offers all of the transaction propagation options familiar from EJB CMT__. To read |
|
about the semantics of transaction propagation in Spring, see <<tx-propagation>>. |
|
* __Timeout__: How long this transaction runs before timing out and being rolled back |
|
automatically by the underlying transaction infrastructure. |
|
* __Read-only status__: A read-only transaction can be used when your code reads but |
|
does not modify data. Read-only transactions can be a useful optimization in some |
|
cases, such as when you are using Hibernate. |
|
|
|
These settings reflect standard transactional concepts. If necessary, refer to resources |
|
that discuss transaction isolation levels and other core transaction concepts. |
|
Understanding these concepts is essential to using the Spring Framework or any |
|
transaction management solution. |
|
|
|
The `TransactionStatus` interface provides a simple way for transactional code to |
|
control transaction execution and query transaction status. The concepts should be |
|
familiar, as they are common to all transaction APIs: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface TransactionStatus extends SavepointManager { |
|
|
|
boolean isNewTransaction(); |
|
|
|
boolean hasSavepoint(); |
|
|
|
void setRollbackOnly(); |
|
|
|
boolean isRollbackOnly(); |
|
|
|
void flush(); |
|
|
|
boolean isCompleted(); |
|
|
|
} |
|
---- |
|
|
|
Regardless of whether you opt for declarative or programmatic transaction management in |
|
Spring, defining the correct `PlatformTransactionManager` implementation is absolutely |
|
essential. You typically define this implementation through dependency injection. |
|
|
|
`PlatformTransactionManager` implementations normally require knowledge of the |
|
environment in which they work: JDBC, JTA, Hibernate, and so on. The following examples |
|
show how you can define a local `PlatformTransactionManager` implementation. (This |
|
example works with plain JDBC.) |
|
|
|
You define a JDBC `DataSource` |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> |
|
<property name="driverClassName" value="${jdbc.driverClassName}" /> |
|
<property name="url" value="${jdbc.url}" /> |
|
<property name="username" value="${jdbc.username}" /> |
|
<property name="password" value="${jdbc.password}" /> |
|
</bean> |
|
---- |
|
|
|
The related `PlatformTransactionManager` bean definition will then have a reference to |
|
the `DataSource` definition. It will look like this: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> |
|
<property name="dataSource" ref="dataSource"/> |
|
</bean> |
|
---- |
|
|
|
If you use JTA in a Java EE container then you use a container `DataSource`, obtained |
|
through JNDI, in conjunction with Spring's `JtaTransactionManager`. This is what the JTA |
|
and JNDI lookup version would look like: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:jee="http://www.springframework.org/schema/jee" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/jee |
|
http://www.springframework.org/schema/jee/spring-jee.xsd"> |
|
|
|
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/jpetstore"/> |
|
|
|
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" /> |
|
|
|
<!-- other <bean/> definitions here --> |
|
|
|
</beans> |
|
---- |
|
|
|
The `JtaTransactionManager` does not need to know about the `DataSource`, or any other |
|
specific resources, because it uses the container's global transaction management |
|
infrastructure. |
|
|
|
[NOTE] |
|
==== |
|
The above definition of the `dataSource` bean uses the `<jndi-lookup/>` tag from the |
|
`jee` namespace. For more information on schema-based configuration, see <<xsd-config>>, |
|
and for more information on the `<jee/>` tags see the section entitled |
|
<<xsd-config-body-schemas-jee>>. |
|
==== |
|
|
|
You can also use Hibernate local transactions easily, as shown in the following |
|
examples. In this case, you need to define a Hibernate `LocalSessionFactoryBean`, which |
|
your application code will use to obtain Hibernate `Session` instances. |
|
|
|
The `DataSource` bean definition will be similar to the local JDBC example shown |
|
previously and thus is not shown in the following example. |
|
|
|
[NOTE] |
|
==== |
|
If the `DataSource`, used by any non-JTA transaction manager, is looked up via JNDI and |
|
managed by a Java EE container, then it should be non-transactional because the Spring |
|
Framework, rather than the Java EE container, will manage the transactions. |
|
==== |
|
|
|
The `txManager` bean in this case is of the `HibernateTransactionManager` type. In the |
|
same way as the `DataSourceTransactionManager` needs a reference to the `DataSource`, |
|
the `HibernateTransactionManager` needs a reference to the `SessionFactory`. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> |
|
<property name="dataSource" ref="dataSource" /> |
|
<property name="mappingResources"> |
|
<list> |
|
<value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value> |
|
</list> |
|
</property> |
|
<property name="hibernateProperties"> |
|
<value> |
|
hibernate.dialect=${hibernate.dialect} |
|
</value> |
|
</property> |
|
</bean> |
|
|
|
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> |
|
<property name="sessionFactory" ref="sessionFactory" /> |
|
</bean> |
|
---- |
|
|
|
If you are using Hibernate and Java EE container-managed JTA transactions, then you |
|
should simply use the same `JtaTransactionManager` as in the previous JTA example for |
|
JDBC. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/> |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
If you use JTA , then your transaction manager definition will look the same regardless |
|
of what data access technology you use, be it JDBC, Hibernate JPA or any other supported |
|
technology. This is due to the fact that JTA transactions are global transactions, which |
|
can enlist any transactional resource. |
|
==== |
|
|
|
In all these cases, application code does not need to change. You can change how |
|
transactions are managed merely by changing configuration, even if that change means |
|
moving from local to global transactions or vice versa. |
|
|
|
|
|
|
|
|
|
[[tx-resource-synchronization]] |
|
=== Synchronizing resources with transactions |
|
It should now be clear how you create different transaction managers, and how they are |
|
linked to related resources that need to be synchronized to transactions (for example |
|
`DataSourceTransactionManager` to a JDBC `DataSource`, `HibernateTransactionManager` to |
|
a Hibernate `SessionFactory`, and so forth). This section describes how the application |
|
code, directly or indirectly using a persistence API such as JDBC, Hibernate, or JDO, |
|
ensures that these resources are created, reused, and cleaned up properly. The section |
|
also discusses how transaction synchronization is triggered (optionally) through the |
|
relevant `PlatformTransactionManager`. |
|
|
|
|
|
|
|
[[tx-resource-synchronization-high]] |
|
==== High-level synchronization approach |
|
The preferred approach is to use Spring's highest level template based persistence |
|
integration APIs or to use native ORM APIs with transaction- aware factory beans or |
|
proxies for managing the native resource factories. These transaction-aware solutions |
|
internally handle resource creation and reuse, cleanup, optional transaction |
|
synchronization of the resources, and exception mapping. Thus user data access code does |
|
not have to address these tasks, but can be focused purely on non-boilerplate |
|
persistence logic. Generally, you use the native ORM API or take a __template__ approach |
|
for JDBC access by using the `JdbcTemplate`. These solutions are detailed in subsequent |
|
chapters of this reference documentation. |
|
|
|
|
|
|
|
[[tx-resource-synchronization-low]] |
|
==== Low-level synchronization approach |
|
Classes such as `DataSourceUtils` (for JDBC), `EntityManagerFactoryUtils` (for JPA), |
|
`SessionFactoryUtils` (for Hibernate), `PersistenceManagerFactoryUtils` (for JDO), and |
|
so on exist at a lower level. When you want the application code to deal directly with |
|
the resource types of the native persistence APIs, you use these classes to ensure that |
|
proper Spring Framework-managed instances are obtained, transactions are (optionally) |
|
synchronized, and exceptions that occur in the process are properly mapped to a |
|
consistent API. |
|
|
|
For example, in the case of JDBC, instead of the traditional JDBC approach of calling |
|
the `getConnection()` method on the `DataSource`, you instead use Spring's |
|
`org.springframework.jdbc.datasource.DataSourceUtils` class as follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Connection conn = DataSourceUtils.getConnection(dataSource); |
|
---- |
|
|
|
If an existing transaction already has a connection synchronized (linked) to it, that |
|
instance is returned. Otherwise, the method call triggers the creation of a new |
|
connection, which is (optionally) synchronized to any existing transaction, and made |
|
available for subsequent reuse in that same transaction. As mentioned, any |
|
`SQLException` is wrapped in a Spring Framework `CannotGetJdbcConnectionException`, one |
|
of the Spring Framework's hierarchy of unchecked DataAccessExceptions. This approach |
|
gives you more information than can be obtained easily from the `SQLException`, and |
|
ensures portability across databases, even across different persistence technologies. |
|
|
|
This approach also works without Spring transaction management (transaction |
|
synchronization is optional), so you can use it whether or not you are using Spring for |
|
transaction management. |
|
|
|
Of course, once you have used Spring's JDBC support, JPA support or Hibernate support, |
|
you will generally prefer not to use `DataSourceUtils` or the other helper classes, |
|
because you will be much happier working through the Spring abstraction than directly |
|
with the relevant APIs. For example, if you use the Spring `JdbcTemplate` or |
|
`jdbc.object` package to simplify your use of JDBC, correct connection retrieval occurs |
|
behind the scenes and you won't need to write any special code. |
|
|
|
|
|
|
|
[[tx-resource-synchronization-tadsp]] |
|
==== TransactionAwareDataSourceProxy |
|
|
|
At the very lowest level exists the `TransactionAwareDataSourceProxy` class. This is a |
|
proxy for a target `DataSource`, which wraps the target `DataSource` to add awareness of |
|
Spring-managed transactions. In this respect, it is similar to a transactional JNDI |
|
`DataSource` as provided by a Java EE server. |
|
|
|
It should almost never be necessary or desirable to use this class, except when existing |
|
code must be called and passed a standard JDBC `DataSource` interface implementation. In |
|
that case, it is possible that this code is usable, but participating in Spring managed |
|
transactions. It is preferable to write your new code by using the higher level |
|
abstractions mentioned above. |
|
|
|
|
|
|
|
|
|
[[transaction-declarative]] |
|
=== Declarative transaction management |
|
[NOTE] |
|
==== |
|
Most Spring Framework users choose declarative transaction management. This option has |
|
the least impact on application code, and hence is most consistent with the ideals of a |
|
__non-invasive__ lightweight container. |
|
==== |
|
|
|
The Spring Framework's declarative transaction management is made possible with Spring |
|
aspect-oriented programming (AOP), although, as the transactional aspects code comes |
|
with the Spring Framework distribution and may be used in a boilerplate fashion, AOP |
|
concepts do not generally have to be understood to make effective use of this code. |
|
|
|
The Spring Framework's declarative transaction management is similar to EJB CMT in that |
|
you can specify transaction behavior (or lack of it) down to individual method level. It |
|
is possible to make a `setRollbackOnly()` call within a transaction context if |
|
necessary. The differences between the two types of transaction management are: |
|
|
|
* Unlike EJB CMT, which is tied to JTA, the Spring Framework's declarative transaction |
|
management works in any environment. It can work with JTA transactions or local |
|
transactions using JDBC, JPA, Hibernate or JDO by simply adjusting the configuration |
|
files. |
|
* You can apply the Spring Framework declarative transaction management to any class, |
|
not merely special classes such as EJBs. |
|
* The Spring Framework offers declarative |
|
<<transaction-declarative-rolling-back,__rollback rules__, >>a feature with no EJB |
|
equivalent. Both programmatic and declarative support for rollback rules is provided. |
|
* The Spring Framework enables you to customize transactional behavior, by using AOP. |
|
For example, you can insert custom behavior in the case of transaction rollback. You |
|
can also add arbitrary advice, along with the transactional advice. With EJB CMT, you |
|
cannot influence the container's transaction management except with |
|
`setRollbackOnly()`. |
|
* The Spring Framework does not support propagation of transaction contexts across |
|
remote calls, as do high-end application servers. If you need this feature, we |
|
recommend that you use EJB. However, consider carefully before using such a feature, |
|
because normally, one does not want transactions to span remote calls. |
|
|
|
.Where is TransactionProxyFactoryBean? |
|
**** |
|
Declarative transaction configuration in versions of Spring 2.0 and above differs |
|
considerably from previous versions of Spring. The main difference is that there is no |
|
longer any need to configure `TransactionProxyFactoryBean` beans. |
|
|
|
The pre-Spring 2.0 configuration style is still 100% valid configuration; think of the |
|
new `<tx:tags/>` as simply defining `TransactionProxyFactoryBean` beans on your behalf. |
|
**** |
|
|
|
The concept of rollback rules is important: they enable you to specify which exceptions |
|
(and throwables) should cause automatic rollback. You specify this declaratively, in |
|
configuration, not in Java code. So, although you can still call `setRollbackOnly()` on |
|
the `TransactionStatus` object to roll back the current transaction back, most often you |
|
can specify a rule that `MyApplicationException` must always result in rollback. The |
|
significant advantage to this option is that business objects do not depend on the |
|
transaction infrastructure. For example, they typically do not need to import Spring |
|
transaction APIs or other Spring APIs. |
|
|
|
Although EJB container default behavior automatically rolls back the transaction on a |
|
__system exception__ (usually a runtime exception), EJB CMT does not roll back the |
|
transaction automatically on an__application exception__ (that is, a checked exception |
|
other than `java.rmi.RemoteException`). While the Spring default behavior for |
|
declarative transaction management follows EJB convention (roll back is automatic only |
|
on unchecked exceptions), it is often useful to customize this behavior. |
|
|
|
|
|
|
|
[[tx-decl-explained]] |
|
==== Understanding the Spring Framework's declarative transaction implementation |
|
It is not sufficient to tell you simply to annotate your classes with the |
|
`@Transactional` annotation, add `@EnableTransactionManagement` to your configuration, |
|
and then expect you to understand how it all works. This section explains the inner |
|
workings of the Spring Framework's declarative transaction infrastructure in the event |
|
of transaction-related issues. |
|
|
|
The most important concepts to grasp with regard to the Spring Framework's declarative |
|
transaction support are that this support is enabled |
|
<<aop-understanding-aop-proxies,__via AOP proxies__>>, and that the transactional advice |
|
is driven by __metadata__ (currently XML- or annotation-based). The combination of AOP |
|
with transactional metadata yields an AOP proxy that uses a `TransactionInterceptor` in |
|
conjunction with an appropriate `PlatformTransactionManager` implementation to drive |
|
transactions __around method invocations__. |
|
|
|
[NOTE] |
|
==== |
|
Spring AOP is covered in <<aop>>. |
|
==== |
|
|
|
Conceptually, calling a method on a transactional proxy looks like this... |
|
|
|
image::images/tx.png[width=400] |
|
|
|
|
|
|
|
[[transaction-declarative-first-example]] |
|
==== Example of declarative transaction implementation |
|
Consider the following interface, and its attendant implementation. This example uses |
|
`Foo` and `Bar` classes as placeholders so that you can concentrate on the transaction |
|
usage without focusing on a particular domain model. For the purposes of this example, |
|
the fact that the `DefaultFooService` class throws `UnsupportedOperationException` |
|
instances in the body of each implemented method is good; it allows you to see |
|
transactions created and then rolled back in response to the |
|
`UnsupportedOperationException` instance. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// the service interface that we want to make transactional |
|
|
|
package x.y.service; |
|
|
|
public interface FooService { |
|
|
|
Foo getFoo(String fooName); |
|
|
|
Foo getFoo(String fooName, String barName); |
|
|
|
void insertFoo(Foo foo); |
|
|
|
void updateFoo(Foo foo); |
|
|
|
} |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// an implementation of the above interface |
|
|
|
package x.y.service; |
|
|
|
public class DefaultFooService implements FooService { |
|
|
|
public Foo getFoo(String fooName) { |
|
throw new UnsupportedOperationException(); |
|
} |
|
|
|
public Foo getFoo(String fooName, String barName) { |
|
throw new UnsupportedOperationException(); |
|
} |
|
|
|
public void insertFoo(Foo foo) { |
|
throw new UnsupportedOperationException(); |
|
} |
|
|
|
public void updateFoo(Foo foo) { |
|
throw new UnsupportedOperationException(); |
|
} |
|
|
|
} |
|
---- |
|
|
|
Assume that the first two methods of the `FooService` interface, `getFoo(String)` and |
|
`getFoo(String, String)`, must execute in the context of a transaction with read-only |
|
semantics, and that the other methods, `insertFoo(Foo)` and `updateFoo(Foo)`, must |
|
execute in the context of a transaction with read-write semantics. The following |
|
configuration is explained in detail in the next few paragraphs. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<!-- from the file 'context.xml' --> |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:aop="http://www.springframework.org/schema/aop" |
|
xmlns:tx="http://www.springframework.org/schema/tx" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/tx |
|
http://www.springframework.org/schema/tx/spring-tx.xsd |
|
http://www.springframework.org/schema/aop |
|
http://www.springframework.org/schema/aop/spring-aop.xsd"> |
|
|
|
<!-- this is the service object that we want to make transactional --> |
|
<bean id="fooService" class="x.y.service.DefaultFooService"/> |
|
|
|
<!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) --> |
|
<tx:advice id="txAdvice" transaction-manager="txManager"> |
|
<!-- the transactional semantics... --> |
|
<tx:attributes> |
|
<!-- all methods starting with 'get' are read-only --> |
|
<tx:method name="get*" read-only="true"/> |
|
<!-- other methods use the default transaction settings (see below) --> |
|
<tx:method name="*"/> |
|
</tx:attributes> |
|
</tx:advice> |
|
|
|
<!-- ensure that the above transactional advice runs for any execution |
|
of an operation defined by the FooService interface --> |
|
<aop:config> |
|
<aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/> |
|
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/> |
|
</aop:config> |
|
|
|
<!-- don't forget the DataSource --> |
|
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> |
|
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/> |
|
<property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/> |
|
<property name="username" value="scott"/> |
|
<property name="password" value="tiger"/> |
|
</bean> |
|
|
|
<!-- similarly, don't forget the PlatformTransactionManager --> |
|
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> |
|
<property name="dataSource" ref="dataSource"/> |
|
</bean> |
|
|
|
<!-- other <bean/> definitions here --> |
|
|
|
</beans> |
|
---- |
|
|
|
Examine the preceding configuration. You want to make a service object, the `fooService` |
|
bean, transactional. The transaction semantics to apply are encapsulated in the |
|
`<tx:advice/>` definition. The `<tx:advice/>` definition reads as "__... all methods on |
|
starting with `'get'` are to execute in the context of a read-only transaction, and all |
|
other methods are to execute with the default transaction semantics__". The |
|
`transaction-manager` attribute of the `<tx:advice/>` tag is set to the name of the |
|
`PlatformTransactionManager` bean that is going to __drive__ the transactions, in this |
|
case, the `txManager` bean. |
|
|
|
[TIP] |
|
==== |
|
|
|
You can omit the `transaction-manager` attribute in the transactional advice ( |
|
`<tx:advice/>`) if the bean name of the `PlatformTransactionManager` that you want to |
|
wire in has the name `transactionManager`. If the `PlatformTransactionManager` bean that |
|
you want to wire in has any other name, then you must use the `transaction-manager` |
|
attribute explicitly, as in the preceding example. |
|
==== |
|
|
|
The `<aop:config/>` definition ensures that the transactional advice defined by the |
|
`txAdvice` bean executes at the appropriate points in the program. First you define a |
|
pointcut that matches the execution of any operation defined in the `FooService` |
|
interface ( `fooServiceOperation`). Then you associate the pointcut with the `txAdvice` |
|
using an advisor. The result indicates that at the execution of a `fooServiceOperation`, |
|
the advice defined by `txAdvice` will be run. |
|
|
|
The expression defined within the `<aop:pointcut/>` element is an AspectJ pointcut |
|
expression; see <<aop>> for more details on pointcut expressions in Spring. |
|
|
|
A common requirement is to make an entire service layer transactional. The best way to |
|
do this is simply to change the pointcut expression to match any operation in your |
|
service layer. For example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<aop:config> |
|
<aop:pointcut id="fooServiceMethods" expression="execution(* x.y.service.*.*(..))"/> |
|
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceMethods"/> |
|
</aop:config> |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
__In this example it is assumed that all your service interfaces are defined in the |
|
`x.y.service` package; see <<aop>> for more details.__ |
|
==== |
|
|
|
Now that we've analyzed the configuration, you may be asking yourself, "__Okay... but |
|
what does all this configuration actually do?__". |
|
|
|
The above configuration will be used to create a transactional proxy around the object |
|
that is created from the `fooService` bean definition. The proxy will be configured with |
|
the transactional advice, so that when an appropriate method is invoked __on the |
|
proxy__, a transaction is started, suspended, marked as read-only, and so on, depending |
|
on the transaction configuration associated with that method. Consider the following |
|
program that test drives the above configuration: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public final class Boot { |
|
|
|
public static void main(final String[] args) throws Exception { |
|
ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml", Boot.class); |
|
FooService fooService = (FooService) ctx.getBean("fooService"); |
|
fooService.insertFoo (new Foo()); |
|
} |
|
} |
|
---- |
|
|
|
The output from running the preceding program will resemble the following. (The Log4J |
|
output and the stack trace from the UnsupportedOperationException thrown by the |
|
insertFoo(..) method of the DefaultFooService class have been truncated for clarity.) |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<!-- the Spring container is starting up... --> |
|
[AspectJInvocationContextExposingAdvisorAutoProxyCreator] - Creating implicit proxy for bean 'fooService' with 0 common interceptors and 1 specific interceptors |
|
|
|
<!-- the DefaultFooService is actually proxied --> |
|
[JdkDynamicAopProxy] - Creating JDK dynamic proxy for [x.y.service.DefaultFooService] |
|
|
|
<!-- ... the insertFoo(..) method is now being invoked on the proxy --> |
|
[TransactionInterceptor] - Getting transaction for x.y.service.FooService.insertFoo |
|
|
|
<!-- the transactional advice kicks in here... --> |
|
[DataSourceTransactionManager] - Creating new transaction with name [x.y.service.FooService.insertFoo] |
|
[DataSourceTransactionManager] - Acquired Connection [org.apache.commons.dbcp.PoolableConnection@a53de4] for JDBC transaction |
|
|
|
<!-- the insertFoo(..) method from DefaultFooService throws an exception... --> |
|
[RuleBasedTransactionAttribute] - Applying rules to determine whether transaction should rollback on java.lang.UnsupportedOperationException |
|
[TransactionInterceptor] - Invoking rollback for transaction on x.y.service.FooService.insertFoo due to throwable [java.lang.UnsupportedOperationException] |
|
|
|
<!-- and the transaction is rolled back (by default, RuntimeException instances cause rollback) --> |
|
[DataSourceTransactionManager] - Rolling back JDBC transaction on Connection [org.apache.commons.dbcp.PoolableConnection@a53de4] |
|
[DataSourceTransactionManager] - Releasing JDBC Connection after transaction |
|
[DataSourceUtils] - Returning JDBC Connection to DataSource |
|
|
|
Exception in thread "main" java.lang.UnsupportedOperationException at x.y.service.DefaultFooService.insertFoo(DefaultFooService.java:14) |
|
<!-- AOP infrastructure stack trace elements removed for clarity --> |
|
at $Proxy0.insertFoo(Unknown Source) |
|
at Boot.main(Boot.java:11) |
|
---- |
|
|
|
|
|
|
|
[[transaction-declarative-rolling-back]] |
|
==== Rolling back a declarative transaction |
|
The previous section outlined the basics of how to specify transactional settings for |
|
classes, typically service layer classes, declaratively in your application. This |
|
section describes how you can control the rollback of transactions in a simple |
|
declarative fashion. |
|
|
|
The recommended way to indicate to the Spring Framework's transaction infrastructure |
|
that a transaction's work is to be rolled back is to throw an `Exception` from code that |
|
is currently executing in the context of a transaction. The Spring Framework's |
|
transaction infrastructure code will catch any unhandled `Exception` as it bubbles up |
|
the call stack, and make a determination whether to mark the transaction for rollback. |
|
|
|
In its default configuration, the Spring Framework's transaction infrastructure code |
|
__only__ marks a transaction for rollback in the case of runtime, unchecked exceptions; |
|
that is, when the thrown exception is an instance or subclass of `RuntimeException`. ( |
|
++Error++s will also - by default - result in a rollback). Checked exceptions that are |
|
thrown from a transactional method do __not__ result in rollback in the default |
|
configuration. |
|
|
|
You can configure exactly which `Exception` types mark a transaction for rollback, |
|
including checked exceptions. The following XML snippet demonstrates how you configure |
|
rollback for a checked, application-specific `Exception` type. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<tx:advice id="txAdvice" transaction-manager="txManager"> |
|
<tx:attributes> |
|
<tx:method name="get*" read-only="true" rollback-for="NoProductInStockException"/> |
|
<tx:method name="*"/> |
|
</tx:attributes> |
|
</tx:advice> |
|
---- |
|
|
|
You can also specify 'no rollback rules', if you do __not__ want a transaction rolled |
|
back when an exception is thrown. The following example tells the Spring Framework's |
|
transaction infrastructure to commit the attendant transaction even in the face of an |
|
unhandled `InstrumentNotFoundException`. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<tx:advice id="txAdvice"> |
|
<tx:attributes> |
|
<tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/> |
|
<tx:method name="*"/> |
|
</tx:attributes> |
|
</tx:advice> |
|
---- |
|
|
|
When the Spring Framework's transaction infrastructure catches an exception and is |
|
consults configured rollback rules to determine whether to mark the transaction for |
|
rollback, the __strongest__ matching rule wins. So in the case of the following |
|
configuration, any exception other than an `InstrumentNotFoundException` results in a |
|
rollback of the attendant transaction. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<tx:advice id="txAdvice"> |
|
<tx:attributes> |
|
<tx:method name="*" rollback-for="Throwable" no-rollback-for="InstrumentNotFoundException"/> |
|
</tx:attributes> |
|
</tx:advice> |
|
---- |
|
|
|
You can also indicate a required rollback __programmatically__. Although very simple, |
|
this process is quite invasive, and tightly couples your code to the Spring Framework's |
|
transaction infrastructure: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public void resolvePosition() { |
|
try { |
|
// some business logic... |
|
} catch (NoProductInStockException ex) { |
|
// trigger rollback programmatically |
|
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); |
|
} |
|
} |
|
---- |
|
|
|
You are strongly encouraged to use the declarative approach to rollback if at all |
|
possible. Programmatic rollback is available should you absolutely need it, but its |
|
usage flies in the face of achieving a clean POJO-based architecture. |
|
|
|
|
|
|
|
[[transaction-declarative-diff-tx]] |
|
==== Configuring different transactional semantics for different beans |
|
Consider the scenario where you have a number of service layer objects, and you want to |
|
apply a __totally different__ transactional configuration to each of them. You do this |
|
by defining distinct `<aop:advisor/>` elements with differing `pointcut` and |
|
`advice-ref` attribute values. |
|
|
|
As a point of comparison, first assume that all of your service layer classes are |
|
defined in a root `x.y.service` package. To make all beans that are instances of classes |
|
defined in that package (or in subpackages) and that have names ending in `Service` have |
|
the default transactional configuration, you would write the following: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:aop="http://www.springframework.org/schema/aop" |
|
xmlns:tx="http://www.springframework.org/schema/tx" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/tx |
|
http://www.springframework.org/schema/tx/spring-tx.xsd |
|
http://www.springframework.org/schema/aop |
|
http://www.springframework.org/schema/aop/spring-aop.xsd"> |
|
|
|
<aop:config> |
|
|
|
<aop:pointcut id="serviceOperation" |
|
expression="execution(* x.y.service..*Service.*(..))"/> |
|
|
|
<aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice"/> |
|
|
|
</aop:config> |
|
|
|
<!-- these two beans will be transactional... --> |
|
<bean id="fooService" class="x.y.service.DefaultFooService"/> |
|
<bean id="barService" class="x.y.service.extras.SimpleBarService"/> |
|
|
|
<!-- ... and these two beans won't --> |
|
<bean id="anotherService" class="org.xyz.SomeService"/> <!-- (not in the right package) --> |
|
<bean id="barManager" class="x.y.service.SimpleBarManager"/> <!-- (doesn't end in 'Service') --> |
|
|
|
<tx:advice id="txAdvice"> |
|
<tx:attributes> |
|
<tx:method name="get*" read-only="true"/> |
|
<tx:method name="*"/> |
|
</tx:attributes> |
|
</tx:advice> |
|
|
|
<!-- other transaction infrastructure beans such as a PlatformTransactionManager omitted... --> |
|
|
|
</beans> |
|
---- |
|
|
|
The following example shows how to configure two distinct beans with totally different |
|
transactional settings. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:aop="http://www.springframework.org/schema/aop" |
|
xmlns:tx="http://www.springframework.org/schema/tx" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/tx |
|
http://www.springframework.org/schema/tx/spring-tx.xsd |
|
http://www.springframework.org/schema/aop |
|
http://www.springframework.org/schema/aop/spring-aop.xsd"> |
|
|
|
<aop:config> |
|
|
|
<aop:pointcut id="defaultServiceOperation" |
|
expression="execution(* x.y.service.*Service.*(..))"/> |
|
|
|
<aop:pointcut id="noTxServiceOperation" |
|
expression="execution(* x.y.service.ddl.DefaultDdlManager.*(..))"/> |
|
|
|
<aop:advisor pointcut-ref="defaultServiceOperation" advice-ref="defaultTxAdvice"/> |
|
|
|
<aop:advisor pointcut-ref="noTxServiceOperation" advice-ref="noTxAdvice"/> |
|
|
|
</aop:config> |
|
|
|
<!-- this bean will be transactional (see the 'defaultServiceOperation' pointcut) --> |
|
<bean id="fooService" class="x.y.service.DefaultFooService"/> |
|
|
|
<!-- this bean will also be transactional, but with totally different transactional settings --> |
|
<bean id="anotherFooService" class="x.y.service.ddl.DefaultDdlManager"/> |
|
|
|
<tx:advice id="defaultTxAdvice"> |
|
<tx:attributes> |
|
<tx:method name="get*" read-only="true"/> |
|
<tx:method name="*"/> |
|
</tx:attributes> |
|
</tx:advice> |
|
|
|
<tx:advice id="noTxAdvice"> |
|
<tx:attributes> |
|
<tx:method name="*" propagation="NEVER"/> |
|
</tx:attributes> |
|
</tx:advice> |
|
|
|
<!-- other transaction infrastructure beans such as a PlatformTransactionManager omitted... --> |
|
|
|
</beans> |
|
---- |
|
|
|
|
|
|
|
[[transaction-declarative-txadvice-settings]] |
|
==== <tx:advice/> settings |
|
|
|
This section summarizes the various transactional settings that can be specified using |
|
the `<tx:advice/>` tag. The default `<tx:advice/>` settings are: |
|
|
|
* <<tx-propagation,Propagation setting>> is `REQUIRED.` |
|
* Isolation level is `DEFAULT.` |
|
* Transaction is read/write. |
|
* Transaction timeout defaults to the default timeout of the underlying transaction |
|
system, or none if timeouts are not supported. |
|
* Any `RuntimeException` triggers rollback, and any checked `Exception` does not. |
|
|
|
You can change these default settings; the various attributes of the `<tx:method/>` tags |
|
that are nested within `<tx:advice/>` and `<tx:attributes/>` tags are summarized below: |
|
|
|
[[tx-method-settings]] |
|
.<tx:method/> settings |
|
|=== |
|
| Attribute| Required?| Default| Description |
|
|
|
| `name` |
|
| Yes |
|
| |
|
| Method name(s) with which the transaction attributes are to be associated. The |
|
wildcard (*) character can be used to associate the same transaction attribute |
|
settings with a number of methods; for example, `get*`, `handle*`, `on*Event`, and so |
|
forth. |
|
|
|
| `propagation` |
|
| No |
|
| REQUIRED |
|
| Transaction propagation behavior. |
|
|
|
| `isolation` |
|
| No |
|
| DEFAULT |
|
| Transaction isolation level. |
|
|
|
| `timeout` |
|
| No |
|
| -1 |
|
| Transaction timeout value (in seconds). |
|
|
|
| `read-only` |
|
| No |
|
| false |
|
| Is this transaction read-only? |
|
|
|
| `rollback-for` |
|
| No |
|
| |
|
| `Exception(s)` that trigger rollback; comma-delimited. For example, |
|
`com.foo.MyBusinessException,ServletException.` |
|
|
|
| `no-rollback-for` |
|
| No |
|
| |
|
| `Exception(s)` that do __not__ trigger rollback; comma-delimited. For example, |
|
`com.foo.MyBusinessException,ServletException.` |
|
|=== |
|
|
|
|
|
|
|
[[transaction-declarative-annotations]] |
|
==== Using @Transactional |
|
|
|
In addition to the XML-based declarative approach to transaction configuration, you can |
|
use an annotation-based approach. Declaring transaction semantics directly in the Java |
|
source code puts the declarations much closer to the affected code. There is not much |
|
danger of undue coupling, because code that is meant to be used transactionally is |
|
almost always deployed that way anyway. |
|
|
|
The ease-of-use afforded by the use of the `@Transactional` annotation is best |
|
illustrated with an example, which is explained in the text that follows. Consider the |
|
following class definition: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// the service class that we want to make transactional |
|
**@Transactional** |
|
public class DefaultFooService implements FooService { |
|
|
|
Foo getFoo(String fooName); |
|
|
|
Foo getFoo(String fooName, String barName); |
|
|
|
void insertFoo(Foo foo); |
|
|
|
void updateFoo(Foo foo); |
|
} |
|
---- |
|
|
|
When the above POJO is defined as a bean in a Spring IoC container, the bean instance |
|
can be made transactional by adding merely __one__ line of XML configuration: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<!-- from the file 'context.xml' --> |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:aop="http://www.springframework.org/schema/aop" |
|
xmlns:tx="http://www.springframework.org/schema/tx" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/tx |
|
http://www.springframework.org/schema/tx/spring-tx.xsd |
|
http://www.springframework.org/schema/aop |
|
http://www.springframework.org/schema/aop/spring-aop.xsd"> |
|
|
|
<!-- this is the service object that we want to make transactional --> |
|
<bean id="fooService" class="x.y.service.DefaultFooService"/> |
|
|
|
<!-- enable the configuration of transactional behavior based on annotations --> |
|
__<tx:annotation-driven transaction-manager="txManager"/>__<!-- a PlatformTransactionManager is still required --> |
|
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> |
|
<!-- (this dependency is defined somewhere else) --> |
|
<property name="dataSource" ref="dataSource"/> |
|
</bean> |
|
|
|
<!-- other <bean/> definitions here --> |
|
|
|
</beans> |
|
---- |
|
|
|
[TIP] |
|
==== |
|
You can omit the `transaction-manager` attribute in the `<tx:annotation-driven/>` tag if |
|
the bean name of the `PlatformTransactionManager` that you want to wire in has the name |
|
`transactionManager`. If the `PlatformTransactionManager` bean that you want to |
|
dependency-inject has any other name, then you have to use the `transaction-manager` |
|
attribute explicitly, as in the preceding example. |
|
==== |
|
|
|
[NOTE] |
|
==== |
|
The `@EnableTransactionManagement` annotation provides equivalent support if you are |
|
using Java based configuration. Simply add the annotation to a `@Configuration` class. |
|
See the javadocs for full details. |
|
==== |
|
|
|
.Method visibility and @Transactional |
|
**** |
|
When using proxies, you should apply the `@Transactional` annotation only to methods |
|
with __public__ visibility. If you do annotate protected, private or package-visible |
|
methods with the `@Transactional` annotation, no error is raised, but the annotated |
|
method does not exhibit the configured transactional settings. Consider the use of |
|
AspectJ (see below) if you need to annotate non-public methods. |
|
**** |
|
|
|
You can place the `@Transactional` annotation before an interface definition, a method |
|
on an interface, a class definition, or a __public__ method on a class. However, the |
|
mere presence of the `@Transactional` annotation is not enough to activate the |
|
transactional behavior. The `@Transactional` annotation is simply metadata that can be |
|
consumed by some runtime infrastructure that is `@Transactional`-aware and that can use |
|
the metadata to configure the appropriate beans with transactional behavior. In the |
|
preceding example, the `<tx:annotation-driven/>` element __switches on__ the |
|
transactional behavior. |
|
|
|
[TIP] |
|
==== |
|
|
|
Spring recommends that you only annotate concrete classes (and methods of concrete |
|
classes) with the `@Transactional` annotation, as opposed to annotating interfaces. You |
|
certainly can place the `@Transactional` annotation on an interface (or an interface |
|
method), but this works only as you would expect it to if you are using interface-based |
|
proxies. The fact that Java annotations are __not inherited from interfaces__ means that |
|
if you are using class-based proxies ( `proxy-target-class="true"`) or the weaving-based |
|
aspect ( `mode="aspectj"`), then the transaction settings are not recognized by the |
|
proxying and weaving infrastructure, and the object will not be wrapped in a |
|
transactional proxy, which would be decidedly __bad__. |
|
==== |
|
|
|
[NOTE] |
|
==== |
|
In proxy mode (which is the default), only external method calls coming in through the |
|
proxy are intercepted. This means that self-invocation, in effect, a method within the |
|
target object calling another method of the target object, will not lead to an actual |
|
transaction at runtime even if the invoked method is marked with `@Transactional`. Also, |
|
the proxy must be fully initialized to provide the expected behaviour so you should not |
|
rely on this feature in your initialization code, i.e. `@PostConstruct`. |
|
==== |
|
|
|
Consider the use of AspectJ mode (see mode attribute in table below) if you expect |
|
self-invocations to be wrapped with transactions as well. In this case, there will not |
|
be a proxy in the first place; instead, the target class will be weaved (that is, its |
|
byte code will be modified) in order to turn `@Transactional` into runtime behavior on |
|
any kind of method. |
|
|
|
[[tx-annotation-driven-settings]] |
|
.Annotation driven transaction settings |
|
|=== |
|
| XML Attribute| Annotation Attribute| Default| Description |
|
|
|
| `transaction-manager` |
|
| N/A (See `TransactionManagementConfigurer` javadocs) |
|
| transactionManager |
|
| Name of transaction manager to use. Only required if the name of the transaction |
|
manager is not `transactionManager`, as in the example above. |
|
|
|
| `mode` |
|
| `mode` |
|
| proxy |
|
| The default mode "proxy" processes annotated beans to be proxied using Spring's AOP |
|
framework (following proxy semantics, as discussed above, applying to method calls |
|
coming in through the proxy only). The alternative mode "aspectj" instead weaves the |
|
affected classes with Spring's AspectJ transaction aspect, modifying the target class |
|
byte code to apply to any kind of method call. AspectJ weaving requires |
|
spring-aspects.jar in the classpath as well as load-time weaving (or compile-time |
|
weaving) enabled. (See <<aop-aj-ltw-spring>> for details on how to set up load-time |
|
weaving.) |
|
|
|
| `proxy-target-class` |
|
| `proxyTargetClass` |
|
| false |
|
| Applies to proxy mode only. Controls what type of transactional proxies are created |
|
for classes annotated with the `@Transactional` annotation. If the |
|
`proxy-target-class` attribute is set to `true`, then class-based proxies are created. |
|
If `proxy-target-class` is `false` or if the attribute is omitted, then standard JDK |
|
interface-based proxies are created. (See <<aop-proxying>> for a detailed examination |
|
of the different proxy types.) |
|
|
|
| `order` |
|
| `order` |
|
| Ordered.LOWEST_PRECEDENCE |
|
| Defines the order of the transaction advice that is applied to beans annotated with |
|
`@Transactional`. (For more information about the rules related to ordering of AOP |
|
advice, see <<aop-ataspectj-advice-ordering>>.) No specified ordering means that the |
|
AOP subsystem determines the order of the advice. |
|
|=== |
|
|
|
[NOTE] |
|
==== |
|
The `proxy-target-class` attribute controls what type of transactional proxies are |
|
created for classes annotated with the `@Transactional` annotation. If |
|
`proxy-target-class` is set to `true`, class-based proxies are created. If |
|
`proxy-target-class` is `false` or if the attribute is omitted, standard JDK |
|
interface-based proxies are created. (See <<aop-proxying>> for a discussion of the |
|
different proxy types.) |
|
==== |
|
|
|
[NOTE] |
|
==== |
|
`@EnableTransactionManagement` and `<tx:annotation-driven/>` only looks for |
|
`@Transactional` on beans in the same application context they are defined in. This |
|
means that, if you put annotation driven configuration in a `WebApplicationContext` for |
|
a `DispatcherServlet`, it only checks for `@Transactional` beans in your controllers, |
|
and not your services. See <<mvc-servlet>> for more information. |
|
==== |
|
|
|
The most derived location takes precedence when evaluating the transactional settings |
|
for a method. In the case of the following example, the `DefaultFooService` class is |
|
annotated at the class level with the settings for a read-only transaction, but the |
|
`@Transactional` annotation on the `updateFoo(Foo)` method in the same class takes |
|
precedence over the transactional settings defined at the class level. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Transactional(readOnly = true) |
|
public class DefaultFooService implements FooService { |
|
|
|
public Foo getFoo(String fooName) { |
|
// do something |
|
} |
|
|
|
// these settings have precedence for this method |
|
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW) |
|
public void updateFoo(Foo foo) { |
|
// do something |
|
} |
|
} |
|
---- |
|
|
|
|
|
[[transaction-declarative-attransactional-settings]] |
|
===== @Transactional settings |
|
|
|
The `@Transactional` annotation is metadata that specifies that an interface, class, or |
|
method must have transactional semantics; for example, "__start a brand new read-only |
|
transaction when this method is invoked, suspending any existing transaction__". The |
|
default `@Transactional` settings are as follows: |
|
|
|
* Propagation setting is `PROPAGATION_REQUIRED.` |
|
* Isolation level is `ISOLATION_DEFAULT.` |
|
* Transaction is read/write. |
|
* Transaction timeout defaults to the default timeout of the underlying transaction |
|
system, or to none if timeouts are not supported. |
|
* Any `RuntimeException` triggers rollback, and any checked `Exception` does not. |
|
|
|
These default settings can be changed; the various properties of the `@Transactional` |
|
annotation are summarized in the following table: |
|
|
|
[[tx-attransactional-properties]] |
|
.@ |
|
|=== |
|
| Property| Type| Description |
|
|
|
| <<tx-multiple-tx-mgrs-with-attransactional,value>> |
|
| String |
|
| Optional qualifier specifying the transaction manager to be used. |
|
|
|
| <<tx-propagation,propagation>> |
|
| enum: `Propagation` |
|
| Optional propagation setting. |
|
|
|
| `isolation` |
|
| enum: `Isolation` |
|
| Optional isolation level. |
|
|
|
| `readOnly` |
|
| boolean |
|
| Read/write vs. read-only transaction |
|
|
|
| `timeout` |
|
| int (in seconds granularity) |
|
| Transaction timeout. |
|
|
|
| `rollbackFor` |
|
| Array of `Class` objects, which must be derived from `Throwable.` |
|
| Optional array of exception classes that __must__ cause rollback. |
|
|
|
| `rollbackForClassName` |
|
| Array of class names. Classes must be derived from `Throwable.` |
|
| Optional array of names of exception classes that __must__ cause rollback. |
|
|
|
| `noRollbackFor` |
|
| Array of `Class` objects, which must be derived from `Throwable.` |
|
| Optional array of exception classes that __must not__ cause rollback. |
|
|
|
| `noRollbackForClassName` |
|
| Array of `String` class names, which must be derived from `Throwable.` |
|
| Optional array of names of exception classes that __must not__ cause rollback. |
|
|=== |
|
|
|
Currently you cannot have explicit control over the name of a transaction, where 'name' |
|
means the transaction name that will be shown in a transaction monitor, if applicable |
|
(for example, WebLogic's transaction monitor), and in logging output. For declarative |
|
transactions, the transaction name is always the fully-qualified class name + "." |
|
+ method name of the transactionally-advised class. For example, if the |
|
`handlePayment(..)` method of the `BusinessService` class started a transaction, the |
|
name of the transaction would be: `com.foo.BusinessService.handlePayment`. |
|
|
|
|
|
[[tx-multiple-tx-mgrs-with-attransactional]] |
|
===== Multiple Transaction Managers with @Transactional |
|
Most Spring applications only need a single transaction manager, but there may be |
|
situations where you want multiple independent transaction managers in a single |
|
application. The value attribute of the `@Transactional` annotation can be used to |
|
optionally specify the identity of the `PlatformTransactionManager` to be used. This can |
|
either be the bean name or the qualifier value of the transaction manager bean. For |
|
example, using the qualifier notation, the following Java code |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class TransactionalService { |
|
|
|
@Transactional("order") |
|
public void setSomething(String name) { ... } |
|
|
|
@Transactional("account") |
|
public void doSomething() { ... } |
|
} |
|
---- |
|
|
|
could be combined with the following transaction manager bean declarations in the |
|
application context. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<tx:annotation-driven/> |
|
|
|
<bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> |
|
... |
|
<qualifier value="order"/> |
|
</bean> |
|
|
|
<bean id="transactionManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> |
|
... |
|
<qualifier value="account"/> |
|
</bean> |
|
---- |
|
|
|
In this case, the two methods on `TransactionalService` will run under separate |
|
transaction managers, differentiated by the "order" and "account" qualifiers. The |
|
default `<tx:annotation-driven>` target bean name `transactionManager` will still be |
|
used if no specifically qualified PlatformTransactionManager bean is found. |
|
|
|
|
|
[[tx-custom-attributes]] |
|
===== Custom shortcut annotations |
|
If you find you are repeatedly using the same attributes with `@Transactional` on many |
|
different methods, then <<beans-meta-annotations,Spring's meta-annotation support>> allows |
|
you to define custom shortcut annotations for your specific use cases. For example, |
|
defining the following annotations |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Target({ElementType.METHOD, ElementType.TYPE}) |
|
@Retention(RetentionPolicy.RUNTIME) |
|
@Transactional("order") |
|
public @interface OrderTx { |
|
} |
|
|
|
@Target({ElementType.METHOD, ElementType.TYPE}) |
|
@Retention(RetentionPolicy.RUNTIME) |
|
@Transactional("account") |
|
public @interface AccountTx { |
|
} |
|
---- |
|
|
|
allows us to write the example from the previous section as |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class TransactionalService { |
|
|
|
@OrderTx |
|
public void setSomething(String name) { ... } |
|
|
|
@AccountTx |
|
public void doSomething() { ... } |
|
} |
|
---- |
|
|
|
Here we have used the syntax to define the transaction manager qualifier, but could also |
|
have included propagation behavior, rollback rules, timeouts etc. |
|
|
|
|
|
|
|
[[tx-propagation]] |
|
==== Transaction propagation |
|
This section describes some semantics of transaction propagation in Spring. Please note |
|
that this section is not an introduction to transaction propagation proper; rather it |
|
details some of the semantics regarding transaction propagation in Spring. |
|
|
|
In Spring-managed transactions, be aware of the difference between __physical__ and |
|
__logical__ transactions, and how the propagation setting applies to this difference. |
|
|
|
|
|
[[tx-propagation-required]] |
|
===== Required |
|
image::images/tx_prop_required.png[width=400] |
|
|
|
PROPAGATION_REQUIRED |
|
|
|
When the propagation setting is `PROPAGATION_REQUIRED`, a __logical__ transaction scope |
|
is created for each method upon which the setting is applied. Each such logical |
|
transaction scope can determine rollback-only status individually, with an outer |
|
transaction scope being logically independent from the inner transaction scope. Of |
|
course, in case of standard `PROPAGATION_REQUIRED` behavior, all these scopes will be |
|
mapped to the same physical transaction. So a rollback-only marker set in the inner |
|
transaction scope does affect the outer transaction's chance to actually commit (as you |
|
would expect it to). |
|
|
|
However, in the case where an inner transaction scope sets the rollback-only marker, the |
|
outer transaction has not decided on the rollback itself, and so the rollback (silently |
|
triggered by the inner transaction scope) is unexpected. A corresponding |
|
`UnexpectedRollbackException` is thrown at that point. This is __expected behavior__ so |
|
that the caller of a transaction can never be misled to assume that a commit was |
|
performed when it really was not. So if an inner transaction (of which the outer caller |
|
is not aware) silently marks a transaction as rollback-only, the outer caller still |
|
calls commit. The outer caller needs to receive an `UnexpectedRollbackException` to |
|
indicate clearly that a rollback was performed instead. |
|
|
|
|
|
[[tx-propagation-requires_new]] |
|
===== RequiresNew |
|
image::images/tx_prop_requires_new.png[width=400] |
|
|
|
PROPAGATION_REQUIRES_NEW |
|
|
|
`PROPAGATION_REQUIRES_NEW`, in contrast to `PROPAGATION_REQUIRED`, uses a __completely__ |
|
independent transaction for each affected transaction scope. In that case, the |
|
underlying physical transactions are different and hence can commit or roll back |
|
independently, with an outer transaction not affected by an inner transaction's rollback |
|
status. |
|
|
|
|
|
[[tx-propagation-nested]] |
|
===== Nested |
|
`PROPAGATION_NESTED` uses a __single__ physical transaction with multiple savepoints |
|
that it can roll back to. Such partial rollbacks allow an inner transaction scope to |
|
trigger a rollback __for its scope__, with the outer transaction being able to continue |
|
the physical transaction despite some operations having been rolled back. This setting |
|
is typically mapped onto JDBC savepoints, so will only work with JDBC resource |
|
transactions. See Spring's `DataSourceTransactionManager`. |
|
|
|
|
|
|
|
[[transaction-declarative-applying-more-than-just-tx-advice]] |
|
==== Advising transactional operations |
|
Suppose you want to execute __both__ transactional __and__ some basic profiling advice. |
|
How do you effect this in the context of `<tx:annotation-driven/>`? |
|
|
|
When you invoke the `updateFoo(Foo)` method, you want to see the following actions: |
|
|
|
* Configured profiling aspect starts up. |
|
* Transactional advice executes. |
|
* Method on the advised object executes. |
|
* Transaction commits. |
|
* Profiling aspect reports exact duration of the whole transactional method invocation. |
|
|
|
[NOTE] |
|
==== |
|
This chapter is not concerned with explaining AOP in any great detail (except as it |
|
applies to transactions). See <<aop>> for detailed coverage of the following AOP |
|
configuration and AOP in general. |
|
==== |
|
|
|
Here is the code for a simple profiling aspect discussed above. The ordering of advice |
|
is controlled through the `Ordered` interface. For full details on advice ordering, see |
|
<<aop-ataspectj-advice-ordering>>. |
|
. |
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package x.y; |
|
|
|
import org.aspectj.lang.ProceedingJoinPoint; |
|
import org.springframework.util.StopWatch; |
|
import org.springframework.core.Ordered; |
|
|
|
public class SimpleProfiler implements Ordered { |
|
|
|
private int order; |
|
|
|
// allows us to control the ordering of advice |
|
public int getOrder() { |
|
return this.order; |
|
} |
|
|
|
public void setOrder(int order) { |
|
this.order = order; |
|
} |
|
|
|
// this method *is* the around advice |
|
public Object profile(ProceedingJoinPoint call) throws Throwable { |
|
Object returnValue; |
|
StopWatch clock = new StopWatch(getClass().getName()); |
|
try { |
|
clock.start(call.toShortString()); |
|
returnValue = call.proceed(); |
|
} finally { |
|
clock.stop(); |
|
System.out.println(clock.prettyPrint()); |
|
} |
|
return returnValue; |
|
} |
|
} |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:aop="http://www.springframework.org/schema/aop" |
|
xmlns:tx="http://www.springframework.org/schema/tx" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/tx |
|
http://www.springframework.org/schema/tx/spring-tx.xsd |
|
http://www.springframework.org/schema/aop |
|
http://www.springframework.org/schema/aop/spring-aop.xsd"> |
|
|
|
<bean id="fooService" class="x.y.service.DefaultFooService"/> |
|
|
|
<!-- this is the aspect --> |
|
<bean id="profiler" class="x.y.SimpleProfiler"> |
|
<!-- execute before the transactional advice (hence the lower order number) --> |
|
<property name="order" __value="1"__/> |
|
</bean> |
|
|
|
<tx:annotation-driven transaction-manager="txManager" __order="200"__/> |
|
|
|
<aop:config> |
|
<!-- this advice will execute around the transactional advice --> |
|
<aop:aspect id="profilingAspect" ref="profiler"> |
|
<aop:pointcut id="serviceMethodWithReturnValue" |
|
expression="execution(!void x.y..*Service.*(..))"/> |
|
<aop:around method="profile" pointcut-ref="serviceMethodWithReturnValue"/> |
|
</aop:aspect> |
|
</aop:config> |
|
|
|
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> |
|
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/> |
|
<property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/> |
|
<property name="username" value="scott"/> |
|
<property name="password" value="tiger"/> |
|
</bean> |
|
|
|
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> |
|
<property name="dataSource" ref="dataSource"/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
The result of the above configuration is a `fooService` bean that has profiling and |
|
transactional aspects applied to it __in the desired order__. You configure any number |
|
of additional aspects in similar fashion. |
|
|
|
The following example effects the same setup as above, but uses the purely XML |
|
declarative approach. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:aop="http://www.springframework.org/schema/aop" |
|
xmlns:tx="http://www.springframework.org/schema/tx" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/tx |
|
http://www.springframework.org/schema/tx/spring-tx.xsd |
|
http://www.springframework.org/schema/aop |
|
http://www.springframework.org/schema/aop/spring-aop.xsd"> |
|
|
|
<bean id="fooService" class="x.y.service.DefaultFooService"/> |
|
|
|
<!-- the profiling advice --> |
|
<bean id="profiler" class="x.y.SimpleProfiler"> |
|
<!-- execute before the transactional advice (hence the lower order number) --> |
|
__<property name="order" value="1__"/> |
|
</bean> |
|
|
|
<aop:config> |
|
<aop:pointcut id="entryPointMethod" expression="execution(* x.y..*Service.*(..))"/> |
|
<!-- will execute after the profiling advice (c.f. the order attribute) --> |
|
|
|
<aop:advisor advice-ref="txAdvice" pointcut-ref="entryPointMethod" __order="2__"/> |
|
<!-- order value is higher than the profiling aspect --> |
|
|
|
<aop:aspect id="profilingAspect" ref="profiler"> |
|
<aop:pointcut id="serviceMethodWithReturnValue" |
|
expression="execution(!void x.y..*Service.*(..))"/> |
|
<aop:around method="profile" pointcut-ref="serviceMethodWithReturnValue"/> |
|
</aop:aspect> |
|
|
|
</aop:config> |
|
|
|
<tx:advice id="txAdvice" transaction-manager="txManager"> |
|
<tx:attributes> |
|
<tx:method name="get*" read-only="true"/> |
|
<tx:method name="*"/> |
|
</tx:attributes> |
|
</tx:advice> |
|
|
|
<!-- other <bean/> definitions such as a DataSource and a PlatformTransactionManager here --> |
|
|
|
</beans> |
|
---- |
|
|
|
The result of the above configuration will be a `fooService` bean that has profiling and |
|
transactional aspects applied to it __in that order__. If you want the profiling advice |
|
to execute __after__ the transactional advice on the way in, and __before__ the |
|
transactional advice on the way out, then you simply swap the value of the profiling |
|
aspect bean's `order` property so that it is higher than the transactional advice's |
|
order value. |
|
|
|
You configure additional aspects in similar fashion. |
|
|
|
|
|
|
|
[[transaction-declarative-aspectj]] |
|
==== Using @Transactional with AspectJ |
|
|
|
It is also possible to use the Spring Framework's `@Transactional` support outside of a |
|
Spring container by means of an AspectJ aspect. To do so, you first annotate your |
|
classes (and optionally your classes' methods) with the `@Transactional` annotation, and |
|
then you link (weave) your application with the |
|
`org.springframework.transaction.aspectj.AnnotationTransactionAspect` defined in the |
|
`spring-aspects.jar` file. The aspect must also be configured with a transaction |
|
manager. You can of course use the Spring Framework's IoC container to take care of |
|
dependency-injecting the aspect. The simplest way to configure the transaction |
|
management aspect is to use the `<tx:annotation-driven/>` element and specify the `mode` |
|
attribute to `aspectj` as described in <<transaction-declarative-annotations>>. Because |
|
we're focusing here on applications running outside of a Spring container, we'll show |
|
you how to do it programmatically. |
|
|
|
[NOTE] |
|
==== |
|
Prior to continuing, you may want to read <<transaction-declarative-annotations>> and |
|
<<aop>> respectively. |
|
==== |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// construct an appropriate transaction manager |
|
DataSourceTransactionManager txManager = new DataSourceTransactionManager(getDataSource()); |
|
|
|
// configure the AnnotationTransactionAspect to use it; this must be done before executing any transactional methods |
|
AnnotationTransactionAspect.aspectOf().setTransactionManager(txManager); |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
When using this aspect, you must annotate the __implementation__ class (and/or methods |
|
within that class), __not__ the interface (if any) that the class implements. AspectJ |
|
follows Java's rule that annotations on interfaces are __not inherited__. |
|
==== |
|
|
|
The `@Transactional` annotation on a class specifies the default transaction semantics |
|
for the execution of any method in the class. |
|
|
|
The `@Transactional` annotation on a method within the class overrides the default |
|
transaction semantics given by the class annotation (if present). Any method may be |
|
annotated, regardless of visibility. |
|
|
|
To weave your applications with the `AnnotationTransactionAspect` you must either build |
|
your application with AspectJ (see the |
|
http://www.eclipse.org/aspectj/doc/released/devguide/index.html[AspectJ Development |
|
Guide]) or use load-time weaving. See <<aop-aj-ltw>> for a discussion of load-time |
|
weaving with AspectJ. |
|
|
|
|
|
|
|
|
|
[[transaction-programmatic]] |
|
=== Programmatic transaction management |
|
The Spring Framework provides two means of programmatic transaction management: |
|
|
|
* Using the `TransactionTemplate`. |
|
* Using a `PlatformTransactionManager` implementation directly. |
|
|
|
The Spring team generally recommends the `TransactionTemplate` for programmatic |
|
transaction management. The second approach is similar to using the JTA |
|
`UserTransaction` API, although exception handling is less cumbersome. |
|
|
|
|
|
|
|
[[tx-prog-template]] |
|
==== Using the TransactionTemplate |
|
|
|
The `TransactionTemplate` adopts the same approach as other Spring __templates__ such as |
|
the `JdbcTemplate`. It uses a callback approach, to free application code from having to |
|
do the boilerplate acquisition and release of transactional resources, and results in |
|
code that is intention driven, in that the code that is written focuses solely on what |
|
the developer wants to do. |
|
|
|
[NOTE] |
|
==== |
|
As you will see in the examples that follow, using the `TransactionTemplate` absolutely |
|
couples you to Spring's transaction infrastructure and APIs. Whether or not programmatic |
|
transaction management is suitable for your development needs is a decision that you |
|
will have to make yourself. |
|
==== |
|
|
|
Application code that must execute in a transactional context, and that will use the |
|
`TransactionTemplate` explicitly, looks like the following. You, as an application |
|
developer, write a `TransactionCallback` implementation (typically expressed as an |
|
anonymous inner class) that contains the code that you need to execute in the context of |
|
a transaction. You then pass an instance of your custom `TransactionCallback` to the |
|
`execute(..)` method exposed on the `TransactionTemplate`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class SimpleService implements Service { |
|
|
|
// single TransactionTemplate shared amongst all methods in this instance |
|
private final TransactionTemplate transactionTemplate; |
|
|
|
// use constructor-injection to supply the PlatformTransactionManager |
|
public SimpleService(PlatformTransactionManager transactionManager) { |
|
Assert.notNull(transactionManager, "The ''transactionManager'' argument must not be null."); |
|
this.transactionTemplate = new TransactionTemplate(transactionManager); |
|
} |
|
|
|
public Object someServiceMethod() { |
|
return transactionTemplate.execute(new TransactionCallback() { |
|
// the code in this method executes in a transactional context |
|
public Object doInTransaction(TransactionStatus status) { |
|
updateOperation1(); |
|
return resultOfUpdateOperation2(); |
|
} |
|
}); |
|
} |
|
} |
|
---- |
|
|
|
If there is no return value, use the convenient `TransactionCallbackWithoutResult` class |
|
with an anonymous class as follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
transactionTemplate.execute(new **TransactionCallbackWithoutResult**() { |
|
protected void doInTransactionWithoutResult(TransactionStatus status) { |
|
updateOperation1(); |
|
updateOperation2(); |
|
} |
|
}); |
|
---- |
|
|
|
Code within the callback can roll the transaction back by calling the |
|
`setRollbackOnly()` method on the supplied `TransactionStatus` object: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
transactionTemplate.execute(new TransactionCallbackWithoutResult() { |
|
|
|
protected void doInTransactionWithoutResult(TransactionStatus status) { |
|
try { |
|
updateOperation1(); |
|
updateOperation2(); |
|
} catch (SomeBusinessExeption ex) { |
|
**status.setRollbackOnly();** |
|
} |
|
} |
|
}); |
|
---- |
|
|
|
|
|
[[tx-prog-template-settings]] |
|
===== Specifying transaction settings |
|
You can specify transaction settings such as the propagation mode, the isolation level, |
|
the timeout, and so forth on the `TransactionTemplate` either programmatically or in |
|
configuration. `TransactionTemplate` instances by default have the |
|
<<transaction-declarative-txadvice-settings,default transactional settings>>. The |
|
following example shows the programmatic customization of the transactional settings for |
|
a specific `TransactionTemplate:` |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class SimpleService implements Service { |
|
|
|
private final TransactionTemplate transactionTemplate; |
|
|
|
public SimpleService(PlatformTransactionManager transactionManager) { |
|
Assert.notNull(transactionManager, "The ''transactionManager'' argument must not be null."); |
|
this.transactionTemplate = new TransactionTemplate(transactionManager); |
|
|
|
// the transaction settings can be set here explicitly if so desired |
|
this.transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED); |
|
this.transactionTemplate.setTimeout(30); // 30 seconds |
|
// and so forth... |
|
} |
|
} |
|
---- |
|
|
|
The following example defines a `TransactionTemplate` with some custom transactional |
|
settings, using Spring XML configuration. The `sharedTransactionTemplate` can then be |
|
injected into as many services as are required. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="sharedTransactionTemplate" |
|
class="org.springframework.transaction.support.TransactionTemplate"> |
|
<property name="isolationLevelName" value="ISOLATION_READ_UNCOMMITTED"/> |
|
<property name="timeout" value="30"/> |
|
</bean>" |
|
---- |
|
|
|
Finally, instances of the `TransactionTemplate` class are threadsafe, in that instances |
|
do not maintain any conversational state. `TransactionTemplate` instances __do__ however |
|
maintain configuration state, so while a number of classes may share a single instance |
|
of a `TransactionTemplate`, if a class needs to use a `TransactionTemplate` with |
|
different settings (for example, a different isolation level), then you need to create |
|
two distinct `TransactionTemplate` instances. |
|
|
|
|
|
|
|
[[transaction-programmatic-ptm]] |
|
==== Using the PlatformTransactionManager |
|
|
|
You can also use the `org.springframework.transaction.PlatformTransactionManager` |
|
directly to manage your transaction. Simply pass the implementation of the |
|
`PlatformTransactionManager` you are using to your bean through a bean reference. Then, |
|
using the `TransactionDefinition` and `TransactionStatus` objects you can initiate |
|
transactions, roll back, and commit. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
DefaultTransactionDefinition def = new DefaultTransactionDefinition(); |
|
// explicitly setting the transaction name is something that can only be done programmatically |
|
def.setName("SomeTxName"); |
|
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); |
|
|
|
TransactionStatus status = txManager.getTransaction(def); |
|
try { |
|
// execute your business logic here |
|
} |
|
catch (MyException ex) { |
|
txManager.rollback(status); |
|
throw ex; |
|
} |
|
txManager.commit(status); |
|
---- |
|
|
|
|
|
|
|
|
|
[[tx-decl-vs-prog]] |
|
=== Choosing between programmatic and declarative transaction management |
|
Programmatic transaction management is usually a good idea only if you have a small |
|
number of transactional operations. For example, if you have a web application that |
|
require transactions only for certain update operations, you may not want to set up |
|
transactional proxies using Spring or any other technology. In this case, using the |
|
`TransactionTemplate` __may__ be a good approach. Being able to set the transaction name |
|
explicitly is also something that can only be done using the programmatic approach to |
|
transaction management. |
|
|
|
On the other hand, if your application has numerous transactional operations, |
|
declarative transaction management is usually worthwhile. It keeps transaction |
|
management out of business logic, and is not difficult to configure. When using the |
|
Spring Framework, rather than EJB CMT, the configuration cost of declarative transaction |
|
management is greatly reduced. |
|
|
|
|
|
|
|
|
|
[[transaction-application-server-integration]] |
|
=== Application server-specific integration |
|
Spring's transaction abstraction generally is application server agnostic. Additionally, |
|
Spring's `JtaTransactionManager` class, which can optionally perform a JNDI lookup for |
|
the JTA `UserTransaction` and `TransactionManager` objects, autodetects the location for |
|
the latter object, which varies by application server. Having access to the JTA |
|
`TransactionManager` allows for enhanced transaction semantics, in particular supporting |
|
transaction suspension. See the `JtaTransactionManager` javadocs for details. |
|
|
|
Spring's `JtaTransactionManager` is the standard choice to run on Java EE application |
|
servers, and is known to work on all common servers. Advanced functionality such as |
|
transaction suspension works on many servers as well -- including GlassFish, JBoss and |
|
Geronimo -- without any special configuration required. However, for fully supported |
|
transaction suspension and further advanced integration, Spring ships special adapters |
|
for WebLogic Server and WebSphere. These adapters are discussed in the following |
|
sections. |
|
|
|
__For standard scenarios, including WebLogic Server and WebSphere, consider using the |
|
convenient `<tx:jta-transaction-manager/>` configuration element.__ When configured, |
|
this element automatically detects the underlying server and chooses the best |
|
transaction manager available for the platform. This means that you won't have to |
|
configure server-specific adapter classes (as discussed in the following sections) |
|
explicitly; rather, they are chosen automatically, with the standard |
|
`JtaTransactionManager` as default fallback. |
|
|
|
|
|
|
|
[[transaction-application-server-integration-websphere]] |
|
==== IBM WebSphere |
|
On WebSphere 6.1.0.9 and above, the recommended Spring JTA transaction manager to use is |
|
`WebSphereUowTransactionManager`. This special adapter leverages IBM's `UOWManager` API, |
|
which is available in WebSphere Application Server 6.0.2.19 and later and 6.1.0.9 and |
|
later. With this adapter, Spring-driven transaction suspension (suspend/resume as |
|
initiated by `PROPAGATION_REQUIRES_NEW`) is officially supported by IBM! |
|
|
|
|
|
|
|
[[transaction-application-server-integration-weblogic]] |
|
==== Oracle WebLogic Server |
|
On WebLogic Server 9.0 or above, you typically would use the |
|
`WebLogicJtaTransactionManager` instead of the stock `JtaTransactionManager` class. This |
|
special WebLogic-specific subclass of the normal `JtaTransactionManager` supports the |
|
full power of Spring's transaction definitions in a WebLogic-managed transaction |
|
environment, beyond standard JTA semantics: Features include transaction names, |
|
per-transaction isolation levels, and proper resuming of transactions in all cases. |
|
|
|
|
|
|
|
|
|
[[transaction-solutions-to-common-problems]] |
|
=== Solutions to common problems |
|
|
|
|
|
|
|
[[transaction-solutions-to-common-problems-wrong-ptm]] |
|
==== Use of the wrong transaction manager for a specific DataSource |
|
|
|
Use the __correct__ `PlatformTransactionManager` implementation based on your choice of |
|
transactional technologies and requirements. Used properly, the Spring Framework merely |
|
provides a straightforward and portable abstraction. If you are using global |
|
transactions, you __must__ use the |
|
`org.springframework.transaction.jta.JtaTransactionManager` class (or an |
|
<<transaction-application-server-integration,application server-specific subclass>> of |
|
it) for all your transactional operations. Otherwise the transaction infrastructure |
|
attempts to perform local transactions on resources such as container `DataSource` |
|
instances. Such local transactions do not make sense, and a good application server |
|
treats them as errors. |
|
|
|
|
|
|
|
|
|
[[transaction-resources]] |
|
=== Further Resources |
|
For more information about the Spring Framework's transaction support: |
|
|
|
* http://www.javaworld.com/javaworld/jw-01-2009/jw-01-spring-transactions.html[Distributed |
|
transactions in Spring, with and without XA] is a JavaWorld presentation in which |
|
Spring's David Syer guides you through seven patterns for distributed |
|
transactions in Spring applications, three of them with XA and four without. |
|
* http://www.infoq.com/minibooks/JTDS[Java Transaction Design Strategies] is a book |
|
available from http://www.infoq.com/[InfoQ] that provides a well-paced introduction |
|
to transactions in Java. It also includes side-by-side examples of how to configure |
|
and use transactions with both the Spring Framework and EJB3. |
|
|
|
|
|
|
|
|
|
|
|
[[dao]] |
|
== DAO support |
|
|
|
|
|
|
|
|
|
[[dao-introduction]] |
|
=== Introduction |
|
The Data Access Object (DAO) support in Spring is aimed at making it easy to work with |
|
data access technologies like JDBC, Hibernate, JPA or JDO in a consistent way. This |
|
allows one to switch between the aforementioned persistence technologies fairly easily |
|
and it also allows one to code without worrying about catching exceptions that are |
|
specific to each technology. |
|
|
|
|
|
|
|
|
|
[[dao-exceptions]] |
|
=== Consistent exception hierarchy |
|
Spring provides a convenient translation from technology-specific exceptions like |
|
`SQLException` to its own exception class hierarchy with the `DataAccessException` as |
|
the root exception. These exceptions wrap the original exception so there is never any |
|
risk that one might lose any information as to what might have gone wrong. |
|
|
|
In addition to JDBC exceptions, Spring can also wrap Hibernate-specific exceptions, |
|
converting them from proprietary, checked exceptions (in the case of versions of |
|
Hibernate prior to Hibernate 3.0), to a set of focused runtime exceptions (the same is |
|
true for JDO and JPA exceptions). This allows one to handle most persistence exceptions, |
|
which are non-recoverable, only in the appropriate layers, without having annoying |
|
boilerplate catch-and-throw blocks and exception declarations in one's DAOs. (One can |
|
still trap and handle exceptions anywhere one needs to though.) As mentioned above, JDBC |
|
exceptions (including database-specific dialects) are also converted to the same |
|
hierarchy, meaning that one can perform some operations with JDBC within a consistent |
|
programming model. |
|
|
|
The above holds true for the various template classes in Springs support for various ORM |
|
frameworks. If one uses the interceptor-based classes then the application must care |
|
about handling `HibernateExceptions` and `JDOExceptions` itself, preferably via |
|
delegating to `SessionFactoryUtils`' `convertHibernateAccessException(..)` or |
|
`convertJdoAccessException()` methods respectively. These methods convert the exceptions |
|
to ones that are compatible with the exceptions in the `org.springframework.dao` |
|
exception hierarchy. As `JDOExceptions` are unchecked, they can simply get thrown too, |
|
sacrificing generic DAO abstraction in terms of exceptions though. |
|
|
|
The exception hierarchy that Spring provides can be seen below. (Please note that the |
|
class hierarchy detailed in the image shows only a subset of the entire |
|
`DataAccessException` hierarchy.) |
|
|
|
image::images/DataAccessException.gif[width=400] |
|
|
|
|
|
|
|
|
|
[[dao-annotations]] |
|
=== Annotations used for configuring DAO or Repository classes |
|
The best way to guarantee that your Data Access Objects (DAOs) or repositories provide |
|
exception translation is to use the `@Repository` annotation. This annotation also |
|
allows the component scanning support to find and configure your DAOs and repositories |
|
without having to provide XML configuration entries for them. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
**@Repository** |
|
public class SomeMovieFinder implements MovieFinder { |
|
// ... |
|
} |
|
---- |
|
|
|
Any DAO or repository implementation will need to access to a persistence resource, |
|
depending on the persistence technology used; for example, a JDBC-based repository will |
|
need access to a JDBC `DataSource`; a JPA-based repository will need access to an |
|
`EntityManager`. The easiest way to accomplish this is to have this resource dependency |
|
injected using one of the `@Autowired,`, `@Inject`, `@Resource` or `@PersistenceContext` |
|
annotations. Here is an example for a JPA repository: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Repository |
|
public class JpaMovieFinder implements MovieFinder { |
|
|
|
@PersistenceContext |
|
private EntityManager entityManager; |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
If you are using the classic Hibernate APIs than you can inject the SessionFactory: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Repository |
|
public class HibernateMovieFinder implements MovieFinder { |
|
|
|
private SessionFactory sessionFactory; |
|
|
|
@Autowired |
|
public void setSessionFactory(SessionFactory sessionFactory) { |
|
this.sessionFactory = sessionFactory; |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
Last example we will show here is for typical JDBC support. You would have the |
|
`DataSource` injected into an initialization method where you would create a |
|
`JdbcTemplate` and other data access support classes like `SimpleJdbcCall` etc using |
|
this `DataSource`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Repository |
|
public class JdbcMovieFinder implements MovieFinder { |
|
|
|
private JdbcTemplate jdbcTemplate; |
|
|
|
@Autowired |
|
public void init(DataSource dataSource) { |
|
this.jdbcTemplate = new JdbcTemplate(dataSource); |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
Please see the specific coverage of each persistence technology for details on how to |
|
configure the application context to take advantage of these annotations. |
|
==== |
|
|
|
|
|
|
|
|
|
|
|
[[jdbc]] |
|
== Data access with JDBC |
|
|
|
|
|
|
|
|
|
[[jdbc-introduction]] |
|
=== Introduction to Spring Framework JDBC |
|
The value-add provided by the Spring Framework JDBC abstraction is perhaps best shown by |
|
the sequence of actions outlined in the table below. The table shows what actions Spring |
|
will take care of and which actions are the responsibility of you, the application |
|
developer. |
|
|
|
[[jdbc-who-does-what]] |
|
.Spring JDBC - who does what? |
|
|=== |
|
| Action| Spring| You |
|
|
|
| Define connection parameters. |
|
| |
|
| X |
|
|
|
| Open the connection. |
|
| X |
|
| |
|
|
|
| Specify the SQL statement. |
|
| |
|
| X |
|
|
|
| Declare parameters and provide parameter values |
|
| |
|
| X |
|
|
|
| Prepare and execute the statement. |
|
| X |
|
| |
|
|
|
| Set up the loop to iterate through the results (if any). |
|
| X |
|
| |
|
|
|
| Do the work for each iteration. |
|
| |
|
| X |
|
|
|
| Process any exception. |
|
| X |
|
| |
|
|
|
| Handle transactions. |
|
| X |
|
| |
|
|
|
| Close the connection, statement and resultset. |
|
| X |
|
| |
|
|=== |
|
|
|
The Spring Framework takes care of all the low-level details that can make JDBC such a |
|
tedious API to develop with. |
|
|
|
|
|
|
|
[[jdbc-choose-style]] |
|
==== Choosing an approach for JDBC database access |
|
You can choose among several approaches to form the basis for your JDBC database access. |
|
In addition to three flavors of the JdbcTemplate, a new SimpleJdbcInsert and |
|
SimplejdbcCall approach optimizes database metadata, and the RDBMS Object style takes a |
|
more object-oriented approach similar to that of JDO Query design. Once you start using |
|
one of these approaches, you can still mix and match to include a feature from a |
|
different approach. All approaches require a JDBC 2.0-compliant driver, and some |
|
advanced features require a JDBC 3.0 driver. |
|
|
|
* __JdbcTemplate__ is the classic Spring JDBC approach and the most popular. This |
|
"lowest level" approach and all others use a JdbcTemplate under the covers. |
|
* __NamedParameterJdbcTemplate__ wraps a `JdbcTemplate` to provide named parameters |
|
instead of the traditional JDBC "?" placeholders. This approach provides better |
|
documentation and ease of use when you have multiple parameters for an SQL statement. |
|
* __SimpleJdbcInsert and SimpleJdbcCall__ optimize database metadata to limit the amount |
|
of necessary configuration. This approach simplifies coding so that you only need to |
|
provide the name of the table or procedure and provide a map of parameters matching |
|
the column names. This only works if the database provides adequate metadata. If the |
|
database doesn't provide this metadata, you will have to provide explicit |
|
configuration of the parameters. |
|
* __RDBMS Objects including MappingSqlQuery, SqlUpdate and StoredProcedure__ requires |
|
you to create reusable and thread-safe objects during initialization of your data |
|
access layer. This approach is modeled after JDO Query wherein you define your query |
|
string, declare parameters, and compile the query. Once you do that, execute methods |
|
can be called multiple times with various parameter values passed in. |
|
|
|
|
|
|
|
[[jdbc-packages]] |
|
==== Package hierarchy |
|
The Spring Framework's JDBC abstraction framework consists of four different packages, |
|
namely `core`, `datasource`, `object`, and `support`. |
|
|
|
The `org.springframework.jdbc.core` package contains the `JdbcTemplate` class and its |
|
various callback interfaces, plus a variety of related classes. A subpackage named |
|
`org.springframework.jdbc.core.simple` contains the `SimpleJdbcInsert` and |
|
`SimpleJdbcCall` classes. Another subpackage named |
|
`org.springframework.jdbc.core.namedparam` contains the `NamedParameterJdbcTemplate` |
|
class and the related support classes. See <<jdbc-core>>, <<jdbc-advanced-jdbc>>, and |
|
<<jdbc-simple-jdbc>> |
|
|
|
The `org.springframework.jdbc.datasource` package contains a utility class for easy |
|
`DataSource` access, and various simple `DataSource` implementations that can be used |
|
for testing and running unmodified JDBC code outside of a Java EE container. A |
|
subpackage named `org.springfamework.jdbc.datasource.embedded` provides support for |
|
creating in-memory database instances using Java database engines such as HSQL and H2. |
|
See <<jdbc-connections>> and <<jdbc-embedded-database-support>> |
|
|
|
The `org.springframework.jdbc.object` package contains classes that represent RDBMS |
|
queries, updates, and stored procedures as thread safe, reusable objects. See |
|
<<jdbc-object>>.This approach is modeled by JDO, although of course objects returned by |
|
queries are "disconnected" from the database. This higher level of JDBC abstraction |
|
depends on the lower-level abstraction in the `org.springframework.jdbc.core` package. |
|
|
|
The `org.springframework.jdbc.support` package provides `SQLException` translation |
|
functionality and some utility classes. Exceptions thrown during JDBC processing are |
|
translated to exceptions defined in the `org.springframework.dao` package. This means |
|
that code using the Spring JDBC abstraction layer does not need to implement JDBC or |
|
RDBMS-specific error handling. All translated exceptions are unchecked, which gives you |
|
the option of catching the exceptions from which you can recover while allowing other |
|
exceptions to be propagated to the caller. See <<jdbc-SQLExceptionTranslator>>. |
|
|
|
|
|
|
|
|
|
[[jdbc-core]] |
|
=== Using the JDBC core classes to control basic JDBC processing and error handling |
|
|
|
|
|
|
|
[[jdbc-JdbcTemplate]] |
|
==== JdbcTemplate |
|
|
|
The `JdbcTemplate` class is the central class in the JDBC core package. It handles the |
|
creation and release of resources, which helps you avoid common errors such as |
|
forgetting to close the connection. It performs the basic tasks of the core JDBC |
|
workflow such as statement creation and execution, leaving application code to provide |
|
SQL and extract results. The `JdbcTemplate` class executes SQL queries, update |
|
statements and stored procedure calls, performs iteration over ++ResultSet++s and |
|
extraction of returned parameter values. It also catches JDBC exceptions and translates |
|
them to the generic, more informative, exception hierarchy defined in the |
|
`org.springframework.dao` package. |
|
|
|
When you use the `JdbcTemplate` for your code, you only need to implement callback |
|
interfaces, giving them a clearly defined contract. The `PreparedStatementCreator` |
|
callback interface creates a prepared statement given a `Connection` provided by this |
|
class, providing SQL and any necessary parameters. The same is true for the |
|
`CallableStatementCreator` interface, which creates callable statements. The |
|
`RowCallbackHandler` interface extracts values from each row of a `ResultSet`. |
|
|
|
The `JdbcTemplate` can be used within a DAO implementation through direct instantiation |
|
with a `DataSource` reference, or be configured in a Spring IoC container and given to |
|
DAOs as a bean reference. |
|
[NOTE] |
|
==== |
|
The `DataSource` should always be configured as a bean in the Spring IoC container. In |
|
the first case the bean is given to the service directly; in the second case it is given |
|
to the prepared template. |
|
==== |
|
|
|
All SQL issued by this class is logged at the `DEBUG` level under the category |
|
corresponding to the fully qualified class name of the template instance (typically |
|
`JdbcTemplate`, but it may be different if you are using a custom subclass of the |
|
`JdbcTemplate` class). |
|
|
|
|
|
[[jdbc-JdbcTemplate-examples]] |
|
===== Examples of JdbcTemplate class usage |
|
This section provides some examples of `JdbcTemplate` class usage. These examples are |
|
not an exhaustive list of all of the functionality exposed by the `JdbcTemplate`; see |
|
the attendant javadocs for that. |
|
|
|
[[jdbc-JdbcTemplate-examples-query]] |
|
====== Querying (SELECT) |
|
Here is a simple query for getting the number of rows in a relation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
int rowCount = this.jdbcTemplate.queryForObject("select count(*) from t_actor", Integer.class); |
|
---- |
|
|
|
A simple query using a bind variable: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
int countOfActorsNamedJoe = this.jdbcTemplate.queryForObject( |
|
"select count(*) from t_actor where first_name = ?", Integer.class, "Joe"); |
|
---- |
|
|
|
Querying for a `String`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
String lastName = this.jdbcTemplate.queryForObject( |
|
"select last_name from t_actor where id = ?", |
|
new Object[]{1212L}, String.class); |
|
---- |
|
|
|
Querying and populating a __single__ domain object: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Actor actor = this.jdbcTemplate.queryForObject( |
|
"select first_name, last_name from t_actor where id = ?", |
|
new Object[]{1212L}, |
|
new RowMapper<Actor>() { |
|
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException { |
|
Actor actor = new Actor(); |
|
actor.setFirstName(rs.getString("first_name")); |
|
actor.setLastName(rs.getString("last_name")); |
|
return actor; |
|
} |
|
}); |
|
---- |
|
|
|
Querying and populating a number of domain objects: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
List<Actor> actors = this.jdbcTemplate.query( |
|
"select first_name, last_name from t_actor", |
|
new RowMapper<Actor>() { |
|
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException { |
|
Actor actor = new Actor(); |
|
actor.setFirstName(rs.getString("first_name")); |
|
actor.setLastName(rs.getString("last_name")); |
|
return actor; |
|
} |
|
}); |
|
---- |
|
|
|
If the last two snippets of code actually existed in the same application, it would make |
|
sense to remove the duplication present in the two `RowMapper` anonymous inner classes, |
|
and extract them out into a single class (typically a `static` inner class) that can |
|
then be referenced by DAO methods as needed. For example, it may be better to write the |
|
last code snippet as follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public List<Actor> findAllActors() { |
|
return this.jdbcTemplate.query( "select first_name, last_name from t_actor", new ActorMapper()); |
|
} |
|
|
|
private static final class ActorMapper implements RowMapper<Actor> { |
|
|
|
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException { |
|
Actor actor = new Actor(); |
|
actor.setFirstName(rs.getString("first_name")); |
|
actor.setLastName(rs.getString("last_name")); |
|
return actor; |
|
} |
|
} |
|
---- |
|
|
|
[[jdbc-JdbcTemplate-examples-update]] |
|
====== Updating (INSERT/UPDATE/DELETE) with jdbcTemplate |
|
You use the `update(..)` method to perform insert, update and delete operations. |
|
Parameter values are usually provided as var args or alternatively as an object array. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
this.jdbcTemplate.update( |
|
"insert into t_actor (first_name, last_name) values (?, ?)", |
|
"Leonor", "Watling"); |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
this.jdbcTemplate.update( |
|
"update t_actor set last_name = ? where id = ?", |
|
"Banjo", 5276L); |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
this.jdbcTemplate.update( |
|
"delete from actor where id = ?", |
|
Long.valueOf(actorId)); |
|
---- |
|
|
|
[[jdbc-JdbcTemplate-examples-other]] |
|
====== Other jdbcTemplate operations |
|
You can use the `execute(..)` method to execute any arbitrary SQL, and as such the |
|
method is often used for DDL statements. It is heavily overloaded with variants taking |
|
callback interfaces, binding variable arrays, and so on. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))"); |
|
---- |
|
|
|
The following example invokes a simple stored procedure. More sophisticated stored |
|
procedure support is <<jdbc-StoredProcedure,covered later>>. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
this.jdbcTemplate.update( |
|
"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)", |
|
Long.valueOf(unionId)); |
|
---- |
|
|
|
|
|
[[jdbc-JdbcTemplate-idioms]] |
|
===== JdbcTemplate best practices |
|
|
|
Instances of the `JdbcTemplate` class are __threadsafe once configured__. This is |
|
important because it means that you can configure a single instance of a `JdbcTemplate` |
|
and then safely inject this __shared__ reference into multiple DAOs (or repositories). |
|
The `JdbcTemplate` is stateful, in that it maintains a reference to a `DataSource`, but |
|
this state is __not__ conversational state. |
|
|
|
A common practice when using the `JdbcTemplate` class (and the associated |
|
<<jdbc-NamedParameterJdbcTemplate, `NamedParameterJdbcTemplate`>> classes) is to |
|
configure a `DataSource` in your Spring configuration file, and then dependency-inject |
|
that shared `DataSource` bean into your DAO classes; the `JdbcTemplate` is created in |
|
the setter for the `DataSource`. This leads to DAOs that look in part like the following: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class JdbcCorporateEventDao implements CorporateEventDao { |
|
|
|
private JdbcTemplate jdbcTemplate; |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
**this.jdbcTemplate = new JdbcTemplate(dataSource);** |
|
} |
|
|
|
// JDBC-backed implementations of the methods on the CorporateEventDao follow... |
|
} |
|
---- |
|
|
|
The corresponding configuration might look like this. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:context="http://www.springframework.org/schema/context" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/context |
|
http://www.springframework.org/schema/context/spring-context.xsd"> |
|
|
|
<bean id="corporateEventDao" class="com.example.JdbcCorporateEventDao"> |
|
<property name="dataSource" ref="dataSource"/> |
|
</bean> |
|
|
|
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> |
|
<property name="driverClassName" value="${jdbc.driverClassName}"/> |
|
<property name="url" value="${jdbc.url}"/> |
|
<property name="username" value="${jdbc.username}"/> |
|
<property name="password" value="${jdbc.password}"/> |
|
</bean> |
|
|
|
<context:property-placeholder location="jdbc.properties"/> |
|
|
|
</beans> |
|
---- |
|
|
|
An alternative to explicit configuration is to use component-scanning and annotation |
|
support for dependency injection. In this case you annotate the class with `@Repository` |
|
(which makes it a candidate for component-scanning) and annotate the `DataSource` setter |
|
method with `@Autowired`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
**@Repository** |
|
public class JdbcCorporateEventDao implements CorporateEventDao { |
|
|
|
private JdbcTemplate jdbcTemplate; |
|
|
|
**@Autowired** |
|
public void setDataSource(DataSource dataSource) { |
|
**this.jdbcTemplate = new JdbcTemplate(dataSource);** |
|
} |
|
|
|
// JDBC-backed implementations of the methods on the CorporateEventDao follow... |
|
} |
|
---- |
|
|
|
The corresponding XML configuration file would look like the following: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:context="http://www.springframework.org/schema/context" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/context |
|
http://www.springframework.org/schema/context/spring-context.xsd"> |
|
|
|
<!-- Scans within the base package of the application for @Component classes to configure as beans --> |
|
<context:component-scan base-package="org.springframework.docs.test" /> |
|
|
|
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> |
|
<property name="driverClassName" value="${jdbc.driverClassName}"/> |
|
<property name="url" value="${jdbc.url}"/> |
|
<property name="username" value="${jdbc.username}"/> |
|
<property name="password" value="${jdbc.password}"/> |
|
</bean> |
|
|
|
<context:property-placeholder location="jdbc.properties"/> |
|
|
|
</beans> |
|
---- |
|
|
|
If you are using Spring's `JdbcDaoSupport` class, and your various JDBC-backed DAO classes |
|
extend from it, then your sub-class inherits a `setDataSource(..)` method from the |
|
`JdbcDaoSupport` class. You can choose whether to inherit from this class. The |
|
`JdbcDaoSupport` class is provided as a convenience only. |
|
|
|
Regardless of which of the above template initialization styles you choose to use (or |
|
not), it is seldom necessary to create a new instance of a `JdbcTemplate` class each |
|
time you want to execute SQL. Once configured, a `JdbcTemplate` instance is threadsafe. |
|
You may want multiple `JdbcTemplate` instances if your application accesses multiple |
|
databases, which requires multiple `DataSources`, and subsequently multiple differently |
|
configured `JdbcTemplates`. |
|
|
|
|
|
|
|
[[jdbc-NamedParameterJdbcTemplate]] |
|
==== NamedParameterJdbcTemplate |
|
|
|
The `NamedParameterJdbcTemplate` class adds support for programming JDBC statements |
|
using named parameters, as opposed to programming JDBC statements using only classic |
|
placeholder ( `'?'`) arguments. The `NamedParameterJdbcTemplate` class wraps a |
|
`JdbcTemplate`, and delegates to the wrapped `JdbcTemplate` to do much of its work. This |
|
section describes only those areas of the `NamedParameterJdbcTemplate` class that differ |
|
from the `JdbcTemplate` itself; namely, programming JDBC statements using named |
|
parameters. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// some JDBC-backed DAO class... |
|
private NamedParameterJdbcTemplate namedParameterJdbcTemplate; |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); |
|
} |
|
|
|
public int countOfActorsByFirstName(String firstName) { |
|
|
|
String sql = "select count(*) from T_ACTOR where first_name = :first_name"; |
|
|
|
SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName); |
|
|
|
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class); |
|
} |
|
---- |
|
|
|
Notice the use of the named parameter notation in the value assigned to the `sql` |
|
variable, and the corresponding value that is plugged into the `namedParameters` |
|
variable (of type `MapSqlParameterSource`). |
|
|
|
Alternatively, you can pass along named parameters and their corresponding values to a |
|
`NamedParameterJdbcTemplate` instance by using the `Map`-based style.The remaining |
|
methods exposed by the `NamedParameterJdbcOperations` and implemented by the |
|
`NamedParameterJdbcTemplate` class follow a similar pattern and are not covered here. |
|
|
|
The following example shows the use of the `Map`-based style. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// some JDBC-backed DAO class... |
|
private NamedParameterJdbcTemplate namedParameterJdbcTemplate; |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); |
|
} |
|
|
|
public int countOfActorsByFirstName(String firstName) { |
|
|
|
String sql = "select count(*) from T_ACTOR where first_name = :first_name"; |
|
|
|
Map<String, String> namedParameters = Collections.singletonMap("first_name", firstName); |
|
|
|
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class); |
|
} |
|
---- |
|
|
|
One nice feature related to the `NamedParameterJdbcTemplate` (and existing in the same |
|
Java package) is the `SqlParameterSource` interface. You have already seen an example of |
|
an implementation of this interface in one of the previous code snippet (the |
|
`MapSqlParameterSource` class). An `SqlParameterSource` is a source of named parameter |
|
values to a `NamedParameterJdbcTemplate`. The `MapSqlParameterSource` class is a very |
|
simple implementation that is simply an adapter around a `java.util.Map`, where the keys |
|
are the parameter names and the values are the parameter values. |
|
|
|
Another `SqlParameterSource` implementation is the `BeanPropertySqlParameterSource` |
|
class. This class wraps an arbitrary JavaBean (that is, an instance of a class that |
|
adheres to http://www.oracle.com/technetwork/java/javase/documentation/spec-136004.html[the |
|
JavaBean conventions]), and uses the properties of the wrapped JavaBean as the source |
|
of named parameter values. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class Actor { |
|
|
|
private Long id; |
|
private String firstName; |
|
private String lastName; |
|
|
|
public String getFirstName() { |
|
return this.firstName; |
|
} |
|
|
|
public String getLastName() { |
|
return this.lastName; |
|
} |
|
|
|
public Long getId() { |
|
return this.id; |
|
} |
|
|
|
// setters omitted... |
|
|
|
} |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// some JDBC-backed DAO class... |
|
private NamedParameterJdbcTemplate namedParameterJdbcTemplate; |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); |
|
} |
|
|
|
public int countOfActors(Actor exampleActor) { |
|
|
|
// notice how the named parameters match the properties of the above 'Actor' class |
|
String sql = "select count(*) from T_ACTOR where first_name = :firstName and last_name = :lastName"; |
|
|
|
SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(exampleActor); |
|
|
|
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class); |
|
} |
|
---- |
|
|
|
Remember that the `NamedParameterJdbcTemplate` class __wraps__ a classic `JdbcTemplate` |
|
template; if you need access to the wrapped `JdbcTemplate` instance to access |
|
functionality only present in the `JdbcTemplate` class, you can use the |
|
`getJdbcOperations()` method to access the wrapped `JdbcTemplate` through the |
|
`JdbcOperations` interface. |
|
|
|
See also <<jdbc-JdbcTemplate-idioms>> for guidelines on using the |
|
`NamedParameterJdbcTemplate` class in the context of an application. |
|
|
|
|
|
|
|
[[jdbc-SQLExceptionTranslator]] |
|
==== SQLExceptionTranslator |
|
|
|
`SQLExceptionTranslator` is an interface to be implemented by classes that can translate |
|
between `SQLExceptions` and Spring's own `org.springframework.dao.DataAccessException`, |
|
which is agnostic in regard to data access strategy. Implementations can be generic (for |
|
example, using SQLState codes for JDBC) or proprietary (for example, using Oracle error |
|
codes) for greater precision. |
|
|
|
`SQLErrorCodeSQLExceptionTranslator` is the implementation of `SQLExceptionTranslator` |
|
that is used by default. This implementation uses specific vendor codes. It is more |
|
precise than the `SQLState` implementation. The error code translations are based on |
|
codes held in a JavaBean type class called `SQLErrorCodes`. This class is created and |
|
populated by an `SQLErrorCodesFactory` which as the name suggests is a factory for |
|
creating `SQLErrorCodes` based on the contents of a configuration file named |
|
`sql-error-codes.xml`. This file is populated with vendor codes and based on the |
|
`DatabaseProductName` taken from the `DatabaseMetaData`. The codes for the actual |
|
database you are using are used. |
|
|
|
The `SQLErrorCodeSQLExceptionTranslator` applies matching rules in the following sequence: |
|
|
|
[NOTE] |
|
==== |
|
The `SQLErrorCodesFactory` is used by default to define Error codes and custom exception |
|
translations. They are looked up in a file named `sql-error-codes.xml` from the |
|
classpath and the matching `SQLErrorCodes` instance is located based on the database |
|
name from the database metadata of the database in use. |
|
==== |
|
* Any custom translation implemented by a subclass. Normally the provided concrete |
|
`SQLErrorCodeSQLExceptionTranslator` is used so this rule does not apply. It only |
|
applies if you have actually provided a subclass implementation. |
|
* Any custom implementation of the `SQLExceptionTranslator` interface that is provided |
|
as the `customSqlExceptionTranslator` property of the `SQLErrorCodes` class. |
|
* The list of instances of the `CustomSQLErrorCodesTranslation` class, provided for the |
|
`customTranslations` property of the `SQLErrorCodes` class, are searched for a match. |
|
* Error code matching is applied. |
|
* Use the fallback translator. `SQLExceptionSubclassTranslator` is the default fallback |
|
translator. If this translation is not available then the next fallback translator is |
|
the `SQLStateSQLExceptionTranslator`. |
|
|
|
You can extend `SQLErrorCodeSQLExceptionTranslator:` |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class CustomSQLErrorCodesTranslator extends SQLErrorCodeSQLExceptionTranslator { |
|
|
|
protected DataAccessException customTranslate(String task, String sql, SQLException sqlex) { |
|
if (sqlex.getErrorCode() == -12345) { |
|
return new DeadlockLoserDataAccessException(task, sqlex); |
|
} |
|
return null; |
|
} |
|
} |
|
---- |
|
|
|
In this example, the specific error code `-12345` is translated and other errors are |
|
left to be translated by the default translator implementation. To use this custom |
|
translator, it is necessary to pass it to the `JdbcTemplate` through the method |
|
`setExceptionTranslator` and to use this `JdbcTemplate` for all of the data access |
|
processing where this translator is needed. Here is an example of how this custom |
|
translator can be used: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
private JdbcTemplate jdbcTemplate; |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
|
|
// create a JdbcTemplate and set data source |
|
this.jdbcTemplate = new JdbcTemplate(); |
|
this.jdbcTemplate.setDataSource(dataSource); |
|
|
|
// create a custom translator and set the DataSource for the default translation lookup |
|
CustomSQLErrorCodesTranslator tr = new CustomSQLErrorCodesTranslator(); |
|
tr.setDataSource(dataSource); |
|
this.jdbcTemplate.setExceptionTranslator(tr); |
|
|
|
} |
|
|
|
public void updateShippingCharge(long orderId, long pct) { |
|
// use the prepared JdbcTemplate for this update |
|
this.jdbcTemplate.update("update orders" + |
|
" set shipping_charge = shipping_charge * ? / 100" + |
|
" where id = ?", pct, orderId); |
|
} |
|
---- |
|
|
|
The custom translator is passed a data source in order to look up the error codes in |
|
`sql-error-codes.xml`. |
|
|
|
|
|
|
|
[[jdbc-statements-executing]] |
|
==== Executing statements |
|
Executing an SQL statement requires very little code. You need a `DataSource` and a |
|
`JdbcTemplate`, including the convenience methods that are provided with the |
|
`JdbcTemplate`. The following example shows what you need to include for a minimal but |
|
fully functional class that creates a new table: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import javax.sql.DataSource; |
|
import org.springframework.jdbc.core.JdbcTemplate; |
|
|
|
public class ExecuteAStatement { |
|
|
|
private JdbcTemplate jdbcTemplate; |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
this.jdbcTemplate = new JdbcTemplate(dataSource); |
|
} |
|
|
|
public void doExecute() { |
|
this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))"); |
|
} |
|
} |
|
---- |
|
|
|
|
|
|
|
[[jdbc-statements-querying]] |
|
==== Running queries |
|
Some query methods return a single value. To retrieve a count or a specific value from |
|
one row, use `queryForObject(..)`. The latter converts the returned JDBC `Type` to the |
|
Java class that is passed in as an argument. If the type conversion is invalid, then an |
|
`InvalidDataAccessApiUsageException` is thrown. Here is an example that contains two |
|
query methods, one for an `int` and one that queries for a `String`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import javax.sql.DataSource; |
|
import org.springframework.jdbc.core.JdbcTemplate; |
|
|
|
public class RunAQuery { |
|
|
|
private JdbcTemplate jdbcTemplate; |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
this.jdbcTemplate = new JdbcTemplate(dataSource); |
|
} |
|
|
|
public int getCount() { |
|
return this.jdbcTemplate.queryForObject("select count(*) from mytable", Integer.class); |
|
} |
|
|
|
public String getName() { |
|
return this.jdbcTemplate.queryForObject("select name from mytable", String.class); |
|
} |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
this.dataSource = dataSource; |
|
} |
|
} |
|
---- |
|
|
|
In addition to the single result query methods, several methods return a list with an |
|
entry for each row that the query returned. The most generic method is |
|
`queryForList(..)` which returns a `List` where each entry is a `Map` with each entry in |
|
the map representing the column value for that row. If you add a method to the above |
|
example to retrieve a list of all the rows, it would look like this: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
private JdbcTemplate jdbcTemplate; |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
this.jdbcTemplate = new JdbcTemplate(dataSource); |
|
} |
|
|
|
public List<Map<String, Object>> getList() { |
|
return this.jdbcTemplate.queryForList("select * from mytable"); |
|
} |
|
---- |
|
|
|
The list returned would look something like this: |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
[{name=Bob, id=1}, {name=Mary, id=2}] |
|
---- |
|
|
|
|
|
|
|
[[jdbc-updates]] |
|
==== Updating the database |
|
The following example shows a column updated for a certain primary key. In this example, |
|
an SQL statement has placeholders for row parameters. The parameter values can be passed |
|
in as varargs or alternatively as an array of objects. Thus primitives should be wrapped |
|
in the primitive wrapper classes explicitly or using auto-boxing. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import javax.sql.DataSource; |
|
|
|
import org.springframework.jdbc.core.JdbcTemplate; |
|
|
|
public class ExecuteAnUpdate { |
|
|
|
private JdbcTemplate jdbcTemplate; |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
this.jdbcTemplate = new JdbcTemplate(dataSource); |
|
} |
|
|
|
public void setName(int id, String name) { |
|
this.jdbcTemplate.update("update mytable set name = ? where id = ?", name, id); |
|
} |
|
} |
|
---- |
|
|
|
|
|
|
|
[[jdbc-auto-genereted-keys]] |
|
==== Retrieving auto-generated keys |
|
An `update()` convenience method supports the retrieval of primary keys generated by the |
|
database. This support is part of the JDBC 3.0 standard; see Chapter 13.6 of the |
|
specification for details. The method takes a `PreparedStatementCreator` as its first |
|
argument, and this is the way the required insert statement is specified. The other |
|
argument is a `KeyHolder`, which contains the generated key on successful return from |
|
the update. There is not a standard single way to create an appropriate |
|
`PreparedStatement` (which explains why the method signature is the way it is). The |
|
following example works on Oracle but may not work on other platforms: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
final String INSERT_SQL = "insert into my_test (name) values(?)"; |
|
final String name = "Rob"; |
|
|
|
KeyHolder keyHolder = new GeneratedKeyHolder(); |
|
jdbcTemplate.update( |
|
new PreparedStatementCreator() { |
|
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException { |
|
PreparedStatement ps = connection.prepareStatement(INSERT_SQL, new String[] {"id"}); |
|
ps.setString(1, name); |
|
return ps; |
|
} |
|
}, |
|
keyHolder); |
|
|
|
// keyHolder.getKey() now contains the generated key |
|
---- |
|
|
|
|
|
|
|
|
|
[[jdbc-connections]] |
|
=== Controlling database connections |
|
|
|
|
|
|
|
[[jdbc-datasource]] |
|
==== DataSource |
|
|
|
Spring obtains a connection to the database through a `DataSource`. A `DataSource` is |
|
part of the JDBC specification and is a generalized connection factory. It allows a |
|
container or a framework to hide connection pooling and transaction management issues |
|
from the application code. As a developer, you need not know details about how to |
|
connect to the database; that is the responsibility of the administrator that sets up |
|
the datasource. You most likely fill both roles as you develop and test code, but you do |
|
not necessarily have to know how the production data source is configured. |
|
|
|
When using Spring's JDBC layer, you obtain a data source from JNDI or you configure your |
|
own with a connection pool implementation provided by a third party. Popular |
|
implementations are Apache Jakarta Commons DBCP and C3P0. Implementations in the Spring |
|
distribution are meant only for testing purposes and do not provide pooling. |
|
|
|
This section uses Spring's `DriverManagerDataSource` implementation, and several |
|
additional implementations are covered later. |
|
|
|
[NOTE] |
|
==== |
|
Only use the `DriverManagerDataSource` class should only be used for testing purposes |
|
since it does not provide pooling and will perform poorly when multiple requests for a |
|
connection are made. |
|
==== |
|
You obtain a connection with `DriverManagerDataSource` as you typically obtain a JDBC |
|
connection. Specify the fully qualified classname of the JDBC driver so that the |
|
`DriverManager` can load the driver class. Next, provide a URL that varies between JDBC |
|
drivers. (Consult the documentation for your driver for the correct value.) Then provide |
|
a username and a password to connect to the database. Here is an example of how to |
|
configure a `DriverManagerDataSource` in Java code: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
DriverManagerDataSource dataSource = new DriverManagerDataSource(); |
|
dataSource.setDriverClassName("org.hsqldb.jdbcDriver"); |
|
dataSource.setUrl("jdbc:hsqldb:hsql://localhost:"); |
|
dataSource.setUsername("sa"); |
|
dataSource.setPassword(""); |
|
---- |
|
|
|
Here is the corresponding XML configuration: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> |
|
<property name="driverClassName" value="${jdbc.driverClassName}"/> |
|
<property name="url" value="${jdbc.url}"/> |
|
<property name="username" value="${jdbc.username}"/> |
|
<property name="password" value="${jdbc.password}"/> |
|
</bean> |
|
|
|
<context:property-placeholder location="jdbc.properties"/> |
|
---- |
|
|
|
The following examples show the basic connectivity and configuration for DBCP and C3P0. |
|
To learn about more options that help control the pooling features, see the product |
|
documentation for the respective connection pooling implementations. |
|
|
|
DBCP configuration: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> |
|
<property name="driverClassName" value="${jdbc.driverClassName}"/> |
|
<property name="url" value="${jdbc.url}"/> |
|
<property name="username" value="${jdbc.username}"/> |
|
<property name="password" value="${jdbc.password}"/> |
|
</bean> |
|
|
|
<context:property-placeholder location="jdbc.properties"/> |
|
---- |
|
|
|
C3P0 configuration: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> |
|
<property name="driverClass" value="${jdbc.driverClassName}"/> |
|
<property name="jdbcUrl" value="${jdbc.url}"/> |
|
<property name="user" value="${jdbc.username}"/> |
|
<property name="password" value="${jdbc.password}"/> |
|
</bean> |
|
|
|
<context:property-placeholder location="jdbc.properties"/> |
|
---- |
|
|
|
|
|
|
|
[[jdbc-DataSourceUtils]] |
|
==== DataSourceUtils |
|
|
|
The `DataSourceUtils` class is a convenient and powerful helper class that provides |
|
`static` methods to obtain connections from JNDI and close connections if necessary. It |
|
supports thread-bound connections with, for example, `DataSourceTransactionManager`. |
|
|
|
|
|
|
|
[[jdbc-SmartDataSource]] |
|
==== SmartDataSource |
|
|
|
The `SmartDataSource` interface should be implemented by classes that can provide a |
|
connection to a relational database. It extends the `DataSource` interface to allow |
|
classes using it to query whether the connection should be closed after a given |
|
operation. This usage is efficient when you know that you will reuse a connection. |
|
|
|
|
|
|
|
[[jdbc-AbstractDataSource]] |
|
==== AbstractDataSource |
|
|
|
`AbstractDataSource` is an `abstract` base class for Spring's `DataSource` |
|
implementations that implements code that is common to all `DataSource` implementations. |
|
You extend the `AbstractDataSource` class if you are writing your own `DataSource` |
|
implementation. |
|
|
|
|
|
|
|
[[jdbc-SingleConnectionDataSource]] |
|
==== SingleConnectionDataSource |
|
|
|
The `SingleConnectionDataSource` class is an implementation of the `SmartDataSource` |
|
interface that wraps a __single__ `Connection` that is __not__ closed after each use. |
|
Obviously, this is not multi-threading capable. |
|
|
|
If any client code calls `close` in the assumption of a pooled connection, as when using |
|
persistence tools, set the `suppressClose` property to `true`. This setting returns a |
|
close-suppressing proxy wrapping the physical connection. Be aware that you will not be |
|
able to cast this to a native Oracle `Connection` or the like anymore. |
|
|
|
This is primarily a test class. For example, it enables easy testing of code outside an |
|
application server, in conjunction with a simple JNDI environment. In contrast to |
|
`DriverManagerDataSource`, it reuses the same connection all the time, avoiding |
|
excessive creation of physical connections. |
|
|
|
|
|
|
|
[[jdbc-DriverManagerDataSource]] |
|
==== DriverManagerDataSource |
|
|
|
The `DriverManagerDataSource` class is an implementation of the standard `DataSource` |
|
interface that configures a plain JDBC driver through bean properties, and returns a new |
|
`Connection` every time. |
|
|
|
This implementation is useful for test and stand-alone environments outside of a Java EE |
|
container, either as a `DataSource` bean in a Spring IoC container, or in conjunction |
|
with a simple JNDI environment. Pool-assuming `Connection.close()` calls will simply |
|
close the connection, so any `DataSource`-aware persistence code should work. However, |
|
using JavaBean-style connection pools such as `commons-dbcp` is so easy, even in a test |
|
environment, that it is almost always preferable to use such a connection pool over |
|
`DriverManagerDataSource`. |
|
|
|
|
|
|
|
[[jdbc-TransactionAwareDataSourceProxy]] |
|
==== TransactionAwareDataSourceProxy |
|
|
|
`TransactionAwareDataSourceProxy` is a proxy for a target `DataSource`, which wraps that |
|
target `DataSource` to add awareness of Spring-managed transactions. In this respect, it |
|
is similar to a transactional JNDI `DataSource` as provided by a Java EE server. |
|
|
|
[NOTE] |
|
==== |
|
It is rarely desirable to use this class, except when already existing code that must be |
|
called and passed a standard JDBC `DataSource` interface implementation. In this case, |
|
it's possible to still have this code be usable, and at the same time have this code |
|
participating in Spring managed transactions. It is generally preferable to write your |
|
own new code using the higher level abstractions for resource management, such as |
|
`JdbcTemplate` or `DataSourceUtils`. |
|
==== |
|
|
|
__(See the `TransactionAwareDataSourceProxy` javadocs for more details.)__ |
|
|
|
|
|
|
|
[[jdbc-DataSourceTransactionManager]] |
|
==== DataSourceTransactionManager |
|
|
|
The `DataSourceTransactionManager` class is a `PlatformTransactionManager` |
|
implementation for single JDBC datasources. It binds a JDBC connection from the |
|
specified data source to the currently executing thread, potentially allowing for one |
|
thread connection per data source. |
|
|
|
Application code is required to retrieve the JDBC connection through |
|
`DataSourceUtils.getConnection(DataSource)` instead of Java EE's standard |
|
`DataSource.getConnection`. It throws unchecked `org.springframework.dao` exceptions |
|
instead of checked `SQLExceptions`. All framework classes like `JdbcTemplate` use this |
|
strategy implicitly. If not used with this transaction manager, the lookup strategy |
|
behaves exactly like the common one - it can thus be used in any case. |
|
|
|
The `DataSourceTransactionManager` class supports custom isolation levels, and timeouts |
|
that get applied as appropriate JDBC statement query timeouts. To support the latter, |
|
application code must either use `JdbcTemplate` or call the |
|
`DataSourceUtils.applyTransactionTimeout(..)` method for each created statement. |
|
|
|
This implementation can be used instead of `JtaTransactionManager` in the single |
|
resource case, as it does not require the container to support JTA. Switching between |
|
both is just a matter of configuration, if you stick to the required connection lookup |
|
pattern. JTA does not support custom isolation levels! |
|
|
|
|
|
|
|
[[jdbc-NativeJdbcExtractor]] |
|
==== NativeJdbcExtractor |
|
Sometimes you need to access vendor specific JDBC methods that differ from the standard |
|
JDBC API. This can be problematic if you are running in an application server or with a |
|
`DataSource` that wraps the `Connection`, `Statement` and `ResultSet` objects with its |
|
own wrapper objects. To gain access to the native objects you can configure your |
|
`JdbcTemplate` or `OracleLobHandler` with a `NativeJdbcExtractor`. |
|
|
|
The `NativeJdbcExtractor` comes in a variety of flavors to match your execution |
|
environment: |
|
|
|
* SimpleNativeJdbcExtractor |
|
* C3P0NativeJdbcExtractor |
|
* CommonsDbcpNativeJdbcExtractor |
|
* JBossNativeJdbcExtractor |
|
* WebLogicNativeJdbcExtractor |
|
* WebSphereNativeJdbcExtractor |
|
* XAPoolNativeJdbcExtractor |
|
|
|
Usually the `SimpleNativeJdbcExtractor` is sufficient for unwrapping a `Connection` |
|
object in most environments. See the javadocs for more details. |
|
|
|
|
|
|
|
|
|
[[jdbc-advanced-jdbc]] |
|
=== JDBC batch operations |
|
Most JDBC drivers provide improved performance if you batch multiple calls to the same |
|
prepared statement. By grouping updates into batches you limit the number of round trips |
|
to the database. |
|
|
|
|
|
|
|
[[jdbc-batch-classic]] |
|
==== Basic batch operations with the JdbcTemplate |
|
You accomplish `JdbcTemplate` batch processing by implementing two methods of a special |
|
interface, `BatchPreparedStatementSetter`, and passing that in as the second parameter |
|
in your `batchUpdate` method call. Use the `getBatchSize` method to provide the size of |
|
the current batch. Use the `setValues` method to set the values for the parameters of |
|
the prepared statement. This method will be called the number of times that you |
|
specified in the `getBatchSize` call. The following example updates the actor table |
|
based on entries in a list. The entire list is used as the batch in this example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class JdbcActorDao implements ActorDao { |
|
private JdbcTemplate jdbcTemplate; |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
this.jdbcTemplate = new JdbcTemplate(dataSource); |
|
} |
|
|
|
public int[] batchUpdate(final List<Actor> actors) { |
|
int[] updateCounts = jdbcTemplate.batchUpdate("update t_actor set first_name = ?, " + |
|
"last_name = ? where id = ?", |
|
new BatchPreparedStatementSetter() { |
|
public void setValues(PreparedStatement ps, int i) throws SQLException { |
|
ps.setString(1, actors.get(i).getFirstName()); |
|
ps.setString(2, actors.get(i).getLastName()); |
|
ps.setLong(3, actors.get(i).getId().longValue()); |
|
} |
|
|
|
public int getBatchSize() { |
|
return actors.size(); |
|
} |
|
}); |
|
return updateCounts; |
|
} |
|
|
|
// ... additional methods |
|
} |
|
---- |
|
|
|
If you are processing a stream of updates or reading from a file, then you might have a |
|
preferred batch size, but the last batch might not have that number of entries. In this |
|
case you can use the `InterruptibleBatchPreparedStatementSetter` interface, which allows |
|
you to interrupt a batch once the input source is exhausted. The `isBatchExhausted` method |
|
allows you to signal the end of the batch. |
|
|
|
|
|
|
|
[[jdbc-batch-list]] |
|
==== Batch operations with a List of objects |
|
Both the `JdbcTemplate` and the `NamedParameterJdbcTemplate` provides an alternate way |
|
of providing the batch update. Instead of implementing a special batch interface, you |
|
provide all parameter values in the call as a list. The framework loops over these |
|
values and uses an internal prepared statement setter. The API varies depending on |
|
whether you use named parameters. For the named parameters you provide an array of |
|
`SqlParameterSource`, one entry for each member of the batch. You can use the |
|
`SqlParameterSource.createBatch` method to create this array, passing in either an array |
|
of JavaBeans or an array of Maps containing the parameter values. |
|
|
|
This example shows a batch update using named parameters: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class JdbcActorDao implements ActorDao { |
|
private NamedParameterTemplate namedParameterJdbcTemplate; |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); |
|
} |
|
|
|
public int[] batchUpdate(final List<Actor> actors) { |
|
SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(actors.toArray()); |
|
int[] updateCounts = namedParameterJdbcTemplate.batchUpdate( |
|
"update t_actor set first_name = :firstName, last_name = :lastName where id = :id", |
|
batch); |
|
return updateCounts; |
|
} |
|
|
|
// ... additional methods |
|
} |
|
---- |
|
|
|
For an SQL statement using the classic "?" placeholders, you pass in a list containing an |
|
object array with the update values. This object array must have one entry for each |
|
placeholder in the SQL statement, and they must be in the same order as they are defined |
|
in the SQL statement. |
|
|
|
The same example using classic JDBC "?" placeholders: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class JdbcActorDao implements ActorDao { |
|
|
|
private JdbcTemplate jdbcTemplate; |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
this.jdbcTemplate = new JdbcTemplate(dataSource); |
|
} |
|
|
|
public int[] batchUpdate(final List<Actor> actors) { |
|
List<Object[]> batch = new ArrayList<Object[]>(); |
|
for (Actor actor : actors) { |
|
Object[] values = new Object[] { |
|
actor.getFirstName(), |
|
actor.getLastName(), |
|
actor.getId()}; |
|
batch.add(values); |
|
} |
|
int[] updateCounts = jdbcTemplate.batchUpdate( |
|
"update t_actor set first_name = ?, last_name = ? where id = ?", |
|
batch); |
|
return updateCounts; |
|
} |
|
|
|
// ... additional methods |
|
|
|
} |
|
---- |
|
|
|
All of the above batch update methods return an int array containing the number of |
|
affected rows for each batch entry. This count is reported by the JDBC driver. If the |
|
count is not available, the JDBC driver returns a -2 value. |
|
|
|
|
|
|
|
[[jdbc-batch-multi]] |
|
==== Batch operations with multiple batches |
|
The last example of a batch update deals with batches that are so large that you want to |
|
break them up into several smaller batches. You can of course do this with the methods |
|
mentioned above by making multiple calls to the `batchUpdate` method, but there is now a |
|
more convenient method. This method takes, in addition to the SQL statement, a |
|
Collection of objects containing the parameters, the number of updates to make for each |
|
batch and a `ParameterizedPreparedStatementSetter` to set the values for the parameters |
|
of the prepared statement. The framework loops over the provided values and breaks the |
|
update calls into batches of the size specified. |
|
|
|
This example shows a batch update using a batch size of 100: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class JdbcActorDao implements ActorDao { |
|
|
|
private JdbcTemplate jdbcTemplate; |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
this.jdbcTemplate = new JdbcTemplate(dataSource); |
|
} |
|
|
|
public int[][] batchUpdate(final Collection<Actor> actors) { |
|
int[][] updateCounts = jdbcTemplate.batchUpdate( |
|
"update t_actor set first_name = ?, last_name = ? where id = ?", |
|
actors, |
|
100, |
|
new ParameterizedPreparedStatementSetter<Actor>() { |
|
public void setValues(PreparedStatement ps, Actor argument) throws SQLException { |
|
ps.setString(1, argument.getFirstName()); |
|
ps.setString(2, argument.getLastName()); |
|
ps.setLong(3, argument.getId().longValue()); |
|
} |
|
}); |
|
return updateCounts; |
|
} |
|
|
|
// ... additional methods |
|
|
|
} |
|
---- |
|
|
|
The batch update methods for this call returns an array of int arrays containing an array |
|
entry for each batch with an array of the number of affected rows for each update. The top |
|
level array's length indicates the number of batches executed and the second level array's |
|
length indicates the number of updates in that batch. The number of updates in each batch |
|
should be the the batch size provided for all batches except for the last one that might |
|
be less, depending on the total number of update objects provided. The update count for |
|
each update statement is the one reported by the JDBC driver. If the count is not |
|
available, the JDBC driver returns a -2 value. |
|
|
|
|
|
|
|
|
|
[[jdbc-simple-jdbc]] |
|
=== Simplifying JDBC operations with the SimpleJdbc classes |
|
The `SimpleJdbcInsert` and `SimpleJdbcCall` classes provide a simplified configuration |
|
by taking advantage of database metadata that can be retrieved through the JDBC driver. |
|
This means there is less to configure up front, although you can override or turn off |
|
the metadata processing if you prefer to provide all the details in your code. |
|
|
|
|
|
|
|
[[jdbc-simple-jdbc-insert-1]] |
|
==== Inserting data using SimpleJdbcInsert |
|
Let's start by looking at the `SimpleJdbcInsert` class with the minimal amount of |
|
configuration options. You should instantiate the `SimpleJdbcInsert` in the data access |
|
layer's initialization method. For this example, the initializing method is the |
|
`setDataSource` method. You do not need to subclass the `SimpleJdbcInsert` class; simply |
|
create a new instance and set the table name using the `withTableName` method. |
|
Configuration methods for this class follow the "fluid" style that returns the instance |
|
of the `SimpleJdbcInsert`, which allows you to chain all configuration methods. This |
|
example uses only one configuration method; you will see examples of multiple ones later. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class JdbcActorDao implements ActorDao { |
|
|
|
private JdbcTemplate jdbcTemplate; |
|
private SimpleJdbcInsert insertActor; |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
this.jdbcTemplate = new JdbcTemplate(dataSource); |
|
this.insertActor = new SimpleJdbcInsert(dataSource).withTableName("t_actor"); |
|
} |
|
|
|
public void add(Actor actor) { |
|
Map<String, Object> parameters = new HashMap<String, Object>(3); |
|
parameters.put("id", actor.getId()); |
|
parameters.put("first_name", actor.getFirstName()); |
|
parameters.put("last_name", actor.getLastName()); |
|
insertActor.execute(parameters); |
|
} |
|
|
|
// ... additional methods |
|
} |
|
---- |
|
|
|
The execute method used here takes a plain `java.utils.Map` as its only parameter. The |
|
important thing to note here is that the keys used for the Map must match the column |
|
names of the table as defined in the database. This is because we read the metadata in |
|
order to construct the actual insert statement. |
|
|
|
|
|
|
|
[[jdbc-simple-jdbc-insert-2]] |
|
==== Retrieving auto-generated keys using SimpleJdbcInsert |
|
This example uses the same insert as the preceding, but instead of passing in the id it |
|
retrieves the auto-generated key and sets it on the new Actor object. When you create |
|
the `SimpleJdbcInsert`, in addition to specifying the table name, you specify the name |
|
of the generated key column with the `usingGeneratedKeyColumns` method. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class JdbcActorDao implements ActorDao { |
|
|
|
private JdbcTemplate jdbcTemplate; |
|
private SimpleJdbcInsert insertActor; |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
this.jdbcTemplate = new JdbcTemplate(dataSource); |
|
this.insertActor = new SimpleJdbcInsert(dataSource) |
|
.withTableName("t_actor") |
|
.usingGeneratedKeyColumns("id"); |
|
} |
|
|
|
public void add(Actor actor) { |
|
Map<String, Object> parameters = new HashMap<String, Object>(2); |
|
parameters.put("first_name", actor.getFirstName()); |
|
parameters.put("last_name", actor.getLastName()); |
|
Number newId = insertActor.executeAndReturnKey(parameters); |
|
actor.setId(newId.longValue()); |
|
} |
|
|
|
// ... additional methods |
|
} |
|
---- |
|
|
|
The main difference when executing the insert by this second approach is that you do not |
|
add the id to the Map and you call the `executeAndReturnKey` method. This returns a |
|
`java.lang.Number` object with which you can create an instance of the numerical type that |
|
is used in our domain class. You cannot rely on all databases to return a specific Java |
|
class here; `java.lang.Number` is the base class that you can rely on. If you have |
|
multiple auto-generated columns, or the generated values are non-numeric, then you can |
|
use a `KeyHolder` that is returned from the `executeAndReturnKeyHolder` method. |
|
|
|
|
|
|
|
[[jdbc-simple-jdbc-insert-3]] |
|
==== Specifying columns for a SimpleJdbcInsert |
|
You can limit the columns for an insert by specifying a list of column names with the |
|
`usingColumns` method: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class JdbcActorDao implements ActorDao { |
|
|
|
private JdbcTemplate jdbcTemplate; |
|
private SimpleJdbcInsert insertActor; |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
this.jdbcTemplate = new JdbcTemplate(dataSource); |
|
this.insertActor = new SimpleJdbcInsert(dataSource) |
|
.withTableName("t_actor") |
|
.usingColumns("first_name", "last_name") |
|
.usingGeneratedKeyColumns("id"); |
|
} |
|
|
|
public void add(Actor actor) { |
|
Map<String, Object> parameters = new HashMap<String, Object>(2); |
|
parameters.put("first_name", actor.getFirstName()); |
|
parameters.put("last_name", actor.getLastName()); |
|
Number newId = insertActor.executeAndReturnKey(parameters); |
|
actor.setId(newId.longValue()); |
|
} |
|
|
|
// ... additional methods |
|
|
|
} |
|
---- |
|
|
|
The execution of the insert is the same as if you had relied on the metadata to determine |
|
which columns to use. |
|
|
|
|
|
|
|
[[jdbc-simple-jdbc-parameters]] |
|
==== Using SqlParameterSource to provide parameter values |
|
Using a `Map` to provide parameter values works fine, but it's not the most convenient |
|
class to use. Spring provides a couple of implementations of the `SqlParameterSource` |
|
interface that can be used instead.The first one is `BeanPropertySqlParameterSource`, |
|
which is a very convenient class if you have a JavaBean-compliant class that contains |
|
your values. It will use the corresponding getter method to extract the parameter |
|
values. Here is an example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class JdbcActorDao implements ActorDao { |
|
|
|
private JdbcTemplate jdbcTemplate; |
|
private SimpleJdbcInsert insertActor; |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
this.jdbcTemplate = new JdbcTemplate(dataSource); |
|
this.insertActor = new SimpleJdbcInsert(dataSource) |
|
.withTableName("t_actor") |
|
.usingGeneratedKeyColumns("id"); |
|
} |
|
|
|
public void add(Actor actor) { |
|
SqlParameterSource parameters = new BeanPropertySqlParameterSource(actor); |
|
Number newId = insertActor.executeAndReturnKey(parameters); |
|
actor.setId(newId.longValue()); |
|
} |
|
|
|
// ... additional methods |
|
|
|
} |
|
---- |
|
|
|
Another option is the `MapSqlParameterSource` that resembles a Map but provides a more |
|
convenient `addValue` method that can be chained. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class JdbcActorDao implements ActorDao { |
|
|
|
private JdbcTemplate jdbcTemplate; |
|
private SimpleJdbcInsert insertActor; |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
this.jdbcTemplate = new JdbcTemplate(dataSource); |
|
this.insertActor = new SimpleJdbcInsert(dataSource) |
|
.withTableName("t_actor") |
|
.usingGeneratedKeyColumns("id"); |
|
} |
|
|
|
public void add(Actor actor) { |
|
SqlParameterSource parameters = new MapSqlParameterSource() |
|
.addValue("first_name", actor.getFirstName()) |
|
.addValue("last_name", actor.getLastName()); |
|
Number newId = insertActor.executeAndReturnKey(parameters); |
|
actor.setId(newId.longValue()); |
|
} |
|
|
|
// ... additional methods |
|
|
|
} |
|
---- |
|
|
|
As you can see, the configuration is the same; only the executing code has to change to |
|
use these alternative input classes. |
|
|
|
|
|
|
|
[[jdbc-simple-jdbc-call-1]] |
|
==== Calling a stored procedure with SimpleJdbcCall |
|
The `SimpleJdbcCall` class leverages metadata in the database to look up names of `in` |
|
and `out` parameters, so that you do not have to declare them explicitly. You can |
|
declare parameters if you prefer to do that, or if you have parameters such as `ARRAY` |
|
or `STRUCT` that do not have an automatic mapping to a Java class. The first example |
|
shows a simple procedure that returns only scalar values in `VARCHAR` and `DATE` format |
|
from a MySQL database. The example procedure reads a specified actor entry and returns |
|
`first_name`, `last_name`, and `birth_date` columns in the form of `out` parameters. |
|
|
|
[source,sql,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
CREATE PROCEDURE read_actor ( |
|
IN in_id INTEGER, |
|
OUT out_first_name VARCHAR(100), |
|
OUT out_last_name VARCHAR(100), |
|
OUT out_birth_date DATE) |
|
BEGIN |
|
SELECT first_name, last_name, birth_date |
|
INTO out_first_name, out_last_name, out_birth_date |
|
FROM t_actor where id = in_id; |
|
END; |
|
---- |
|
|
|
The `in_id` parameter contains the `id` of the actor you are looking up. The `out` |
|
parameters return the data read from the table. |
|
|
|
The `SimpleJdbcCall` is declared in a similar manner to the `SimpleJdbcInsert`. You |
|
should instantiate and configure the class in the initialization method of your data |
|
access layer. Compared to the StoredProcedure class, you don't have to create a subclass |
|
and you don't have to declare parameters that can be looked up in the database metadata. |
|
Following is an example of a SimpleJdbcCall configuration using the above stored |
|
procedure. The only configuration option, in addition to the `DataSource`, is the name |
|
of the stored procedure. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class JdbcActorDao implements ActorDao { |
|
|
|
private JdbcTemplate jdbcTemplate; |
|
private SimpleJdbcCall procReadActor; |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
this.jdbcTemplate = new JdbcTemplate(dataSource); |
|
this.procReadActor = new SimpleJdbcCall(dataSource) |
|
.withProcedureName("read_actor"); |
|
} |
|
|
|
public Actor readActor(Long id) { |
|
SqlParameterSource in = new MapSqlParameterSource() |
|
.addValue("in_id", id); |
|
Map out = procReadActor.execute(in); |
|
Actor actor = new Actor(); |
|
actor.setId(id); |
|
actor.setFirstName((String) out.get("out_first_name")); |
|
actor.setLastName((String) out.get("out_last_name")); |
|
actor.setBirthDate((Date) out.get("out_birth_date")); |
|
return actor; |
|
} |
|
|
|
// ... additional methods |
|
|
|
} |
|
---- |
|
|
|
The code you write for the execution of the call involves creating an `SqlParameterSource` |
|
containing the IN parameter. It's important to match the name provided for the input value |
|
with that of the parameter name declared in the stored procedure. The case does not have |
|
to match because you use metadata to determine how database objects should be referred to |
|
in a stored procedure. What is specified in the source for the stored procedure is not |
|
necessarily the way it is stored in the database. Some databases transform names to all |
|
upper case while others use lower case or use the case as specified. |
|
|
|
The `execute` method takes the IN parameters and returns a Map containing any `out` |
|
parameters keyed by the name as specified in the stored procedure. In this case they are |
|
`out_first_name, out_last_name` and `out_birth_date`. |
|
|
|
The last part of the `execute` method creates an Actor instance to use to return the |
|
data retrieved. Again, it is important to use the names of the `out` parameters as they |
|
are declared in the stored procedure. Also, the case in the names of the `out` |
|
parameters stored in the results map matches that of the `out` parameter names in the |
|
database, which could vary between databases. To make your code more portable you should |
|
do a case-insensitive lookup or instruct Spring to use a `CaseInsensitiveMap` from the |
|
Jakarta Commons project. To do the latter, you create your own `JdbcTemplate` and set |
|
the `setResultsMapCaseInsensitive` property to `true`. Then you pass this customized |
|
`JdbcTemplate` instance into the constructor of your `SimpleJdbcCall`. You must include |
|
the `commons-collections.jar` in your classpath for this to work. Here is an example of |
|
this configuration: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class JdbcActorDao implements ActorDao { |
|
|
|
private SimpleJdbcCall procReadActor; |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); |
|
jdbcTemplate.setResultsMapCaseInsensitive(true); |
|
this.procReadActor = new SimpleJdbcCall(jdbcTemplate) |
|
.withProcedureName("read_actor"); |
|
} |
|
|
|
// ... additional methods |
|
|
|
} |
|
---- |
|
|
|
By taking this action, you avoid conflicts in the case used for the names of your |
|
returned `out` parameters. |
|
|
|
|
|
|
|
[[jdbc-simple-jdbc-call-2]] |
|
==== Explicitly declaring parameters to use for a SimpleJdbcCall |
|
You have seen how the parameters are deduced based on metadata, but you can declare then |
|
explicitly if you wish. You do this by creating and configuring `SimpleJdbcCall` with |
|
the `declareParameters` method, which takes a variable number of `SqlParameter` objects |
|
as input. See the next section for details on how to define an `SqlParameter`. |
|
|
|
[NOTE] |
|
==== |
|
Explicit declarations are necessary if the database you use is not a Spring-supported |
|
database. Currently Spring supports metadata lookup of stored procedure calls for the |
|
following databases: Apache Derby, DB2, MySQL, Microsoft SQL Server, Oracle, and Sybase. |
|
We also support metadata lookup of stored functions for: MySQL, Microsoft SQL Server, |
|
and Oracle. |
|
==== |
|
|
|
You can opt to declare one, some, or all the parameters explicitly. The parameter |
|
metadata is still used where you do not declare parameters explicitly. To bypass all |
|
processing of metadata lookups for potential parameters and only use the declared |
|
parameters, you call the method `withoutProcedureColumnMetaDataAccess` as part of the |
|
declaration. Suppose that you have two or more different call signatures declared for a |
|
database function. In this case you call the `useInParameterNames` to specify the list |
|
of IN parameter names to include for a given signature. |
|
|
|
The following example shows a fully declared procedure call, using the information from |
|
the preceding example. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class JdbcActorDao implements ActorDao { |
|
|
|
private SimpleJdbcCall procReadActor; |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); |
|
jdbcTemplate.setResultsMapCaseInsensitive(true); |
|
this.procReadActor = new SimpleJdbcCall(jdbcTemplate) |
|
.withProcedureName("read_actor") |
|
.withoutProcedureColumnMetaDataAccess() |
|
.useInParameterNames("in_id") |
|
.declareParameters( |
|
new SqlParameter("in_id", Types.NUMERIC), |
|
new SqlOutParameter("out_first_name", Types.VARCHAR), |
|
new SqlOutParameter("out_last_name", Types.VARCHAR), |
|
new SqlOutParameter("out_birth_date", Types.DATE) |
|
); |
|
} |
|
|
|
// ... additional methods |
|
} |
|
---- |
|
|
|
The execution and end results of the two examples are the same; this one specifies all |
|
details explicitly rather than relying on metadata. |
|
|
|
|
|
|
|
[[jdbc-params]] |
|
==== How to define SqlParameters |
|
To define a parameter for the SimpleJdbc classes and also for the RDBMS operations |
|
classes, covered in <<jdbc-object>>, you use an `SqlParameter` or one of its subclasses. |
|
You typically specify the parameter name and SQL type in the constructor. The SQL type |
|
is specified using the `java.sql.Types` constants. We have already seen declarations |
|
like: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
new SqlParameter("in_id", Types.NUMERIC), |
|
new SqlOutParameter("out_first_name", Types.VARCHAR), |
|
---- |
|
|
|
The first line with the `SqlParameter` declares an IN parameter. IN parameters can be |
|
used for both stored procedure calls and for queries using the `SqlQuery` and its |
|
subclasses covered in the following section. |
|
|
|
The second line with the `SqlOutParameter` declares an `out` parameter to be used in a |
|
stored procedure call. There is also an `SqlInOutParameter` for `InOut` parameters, |
|
parameters that provide an `IN` value to the procedure and that also return a value. |
|
|
|
[NOTE] |
|
==== |
|
Only parameters declared as `SqlParameter` and `SqlInOutParameter` will be used to |
|
provide input values. This is different from the `StoredProcedure` class, which for |
|
backwards compatibility reasons allows input values to be provided for parameters |
|
declared as `SqlOutParameter`. |
|
==== |
|
|
|
For IN parameters, in addition to the name and the SQL type, you can specify a scale for |
|
numeric data or a type name for custom database types. For `out` parameters, you can |
|
provide a `RowMapper` to handle mapping of rows returned from a `REF` cursor. Another |
|
option is to specify an `SqlReturnType` that provides an opportunity to define |
|
customized handling of the return values. |
|
|
|
|
|
|
|
[[jdbc-simple-jdbc-call-3]] |
|
==== Calling a stored function using SimpleJdbcCall |
|
You call a stored function in almost the same way as you call a stored procedure, except |
|
that you provide a function name rather than a procedure name. You use the |
|
`withFunctionName` method as part of the configuration to indicate that we want to make |
|
a call to a function, and the corresponding string for a function call is generated. A |
|
specialized execute call, `executeFunction,` is used to execute the function and it |
|
returns the function return value as an object of a specified type, which means you do |
|
not have to retrieve the return value from the results map. A similar convenience method |
|
named `executeObject` is also available for stored procedures that only have one `out` |
|
parameter. The following example is based on a stored function named `get_actor_name` |
|
that returns an actor's full name. Here is the MySQL source for this function: |
|
|
|
[source,sql,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
CREATE FUNCTION get_actor_name (in_id INTEGER) |
|
RETURNS VARCHAR(200) READS SQL DATA |
|
BEGIN |
|
DECLARE out_name VARCHAR(200); |
|
SELECT concat(first_name, ' ', last_name) |
|
INTO out_name |
|
FROM t_actor where id = in_id; |
|
RETURN out_name; |
|
END; |
|
---- |
|
|
|
To call this function we again create a `SimpleJdbcCall` in the initialization method. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class JdbcActorDao implements ActorDao { |
|
|
|
private JdbcTemplate jdbcTemplate; |
|
private SimpleJdbcCall funcGetActorName; |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
this.jdbcTemplate = new JdbcTemplate(dataSource); |
|
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); |
|
jdbcTemplate.setResultsMapCaseInsensitive(true); |
|
this.funcGetActorName = new SimpleJdbcCall(jdbcTemplate) |
|
.withFunctionName("get_actor_name"); |
|
} |
|
|
|
public String getActorName(Long id) { |
|
SqlParameterSource in = new MapSqlParameterSource() |
|
.addValue("in_id", id); |
|
String name = funcGetActorName.executeFunction(String.class, in); |
|
return name; |
|
} |
|
|
|
// ... additional methods |
|
|
|
} |
|
---- |
|
|
|
The execute method used returns a `String` containing the return value from the function |
|
call. |
|
|
|
|
|
|
|
[[jdbc-simple-jdbc-call-4]] |
|
==== Returning ResultSet/REF Cursor from a SimpleJdbcCall |
|
Calling a stored procedure or function that returns a result set is a bit tricky. Some |
|
databases return result sets during the JDBC results processing while others require an |
|
explicitly registered `out` parameter of a specific type. Both approaches need |
|
additional processing to loop over the result set and process the returned rows. With |
|
the `SimpleJdbcCall` you use the `returningResultSet` method and declare a `RowMapper` |
|
implementation to be used for a specific parameter. In the case where the result set is |
|
returned during the results processing, there are no names defined, so the returned |
|
results will have to match the order in which you declare the `RowMapper` |
|
implementations. The name specified is still used to store the processed list of results |
|
in the results map that is returned from the execute statement. |
|
|
|
The next example uses a stored procedure that takes no IN parameters and returns all |
|
rows from the t_actor table. Here is the MySQL source for this procedure: |
|
|
|
[source,sql,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
CREATE PROCEDURE read_all_actors() |
|
BEGIN |
|
SELECT a.id, a.first_name, a.last_name, a.birth_date FROM t_actor a; |
|
END; |
|
---- |
|
|
|
To call this procedure you declare the `RowMapper`. Because the class you want to map to |
|
follows the JavaBean rules, you can use a `ParameterizedBeanPropertyRowMapper` that is |
|
created by passing in the required class to map to in the `newInstance` method. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class JdbcActorDao implements ActorDao { |
|
|
|
private SimpleJdbcCall procReadAllActors; |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); |
|
jdbcTemplate.setResultsMapCaseInsensitive(true); |
|
this.procReadAllActors = new SimpleJdbcCall(jdbcTemplate) |
|
.withProcedureName("read_all_actors") |
|
.returningResultSet("actors", |
|
ParameterizedBeanPropertyRowMapper.newInstance(Actor.class)); |
|
} |
|
|
|
public List getActorsList() { |
|
Map m = procReadAllActors.execute(new HashMap<String, Object>(0)); |
|
return (List) m.get("actors"); |
|
} |
|
|
|
// ... additional methods |
|
|
|
} |
|
---- |
|
|
|
The execute call passes in an empty Map because this call does not take any parameters. |
|
The list of Actors is then retrieved from the results map and returned to the caller. |
|
|
|
|
|
|
|
|
|
[[jdbc-object]] |
|
=== Modeling JDBC operations as Java objects |
|
The `org.springframework.jdbc.object` package contains classes that allow you to access |
|
the database in a more object-oriented manner. As an example, you can execute queries |
|
and get the results back as a list containing business objects with the relational |
|
column data mapped to the properties of the business object. You can also execute stored |
|
procedures and run update, delete, and insert statements. |
|
|
|
[NOTE] |
|
==== |
|
Many Spring developers believe that the various RDBMS operation classes described below |
|
(with the exception of the <<jdbc-StoredProcedure, `StoredProcedure`>> class) can often |
|
be replaced with straight `JdbcTemplate` calls. Often it is simpler to write a DAO |
|
method that simply calls a method on a `JdbcTemplate` directly (as opposed to |
|
encapsulating a query as a full-blown class). |
|
|
|
However, if you are getting measurable value from using the RDBMS operation classes, |
|
continue using these classes. |
|
==== |
|
|
|
|
|
|
|
[[jdbc-SqlQuery]] |
|
==== SqlQuery |
|
|
|
`SqlQuery` is a reusable, threadsafe class that encapsulates an SQL query. Subclasses |
|
must implement the `newRowMapper(..)` method to provide a `RowMapper` instance that can |
|
create one object per row obtained from iterating over the `ResultSet` that is created |
|
during the execution of the query. The `SqlQuery` class is rarely used directly because |
|
the `MappingSqlQuery` subclass provides a much more convenient implementation for |
|
mapping rows to Java classes. Other implementations that extend `SqlQuery` are |
|
`MappingSqlQueryWithParameters` and `UpdatableSqlQuery`. |
|
|
|
|
|
|
|
[[jdbc-MappingSqlQuery]] |
|
==== MappingSqlQuery |
|
|
|
`MappingSqlQuery` is a reusable query in which concrete subclasses must implement the |
|
abstract `mapRow(..)` method to convert each row of the supplied `ResultSet` into an |
|
object of the type specified. The following example shows a custom query that maps the |
|
data from the `t_actor` relation to an instance of the `Actor` class. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class ActorMappingQuery extends MappingSqlQuery<Actor> { |
|
|
|
public ActorMappingQuery(DataSource ds) { |
|
super(ds, "select id, first_name, last_name from t_actor where id = ?"); |
|
super.declareParameter(new SqlParameter("id", Types.INTEGER)); |
|
compile(); |
|
} |
|
|
|
@Override |
|
protected Actor mapRow(ResultSet rs, int rowNumber) throws SQLException { |
|
Actor actor = new Actor(); |
|
actor.setId(rs.getLong("id")); |
|
actor.setFirstName(rs.getString("first_name")); |
|
actor.setLastName(rs.getString("last_name")); |
|
return actor; |
|
} |
|
|
|
} |
|
---- |
|
|
|
The class extends `MappingSqlQuery` parameterized with the `Actor` type. The constructor |
|
for this customer query takes the `DataSource` as the only parameter. In this |
|
constructor you call the constructor on the superclass with the `DataSource` and the SQL |
|
that should be executed to retrieve the rows for this query. This SQL will be used to |
|
create a `PreparedStatement` so it may contain place holders for any parameters to be |
|
passed in during execution.You must declare each parameter using the `declareParameter` |
|
method passing in an `SqlParameter`. The `SqlParameter` takes a name and the JDBC type |
|
as defined in `java.sql.Types`. After you define all parameters, you call the |
|
`compile()` method so the statement can be prepared and later executed. This class is |
|
thread-safe after it is compiled, so as long as these instances are created when the DAO |
|
is initialized they can be kept as instance variables and be reused. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
private ActorMappingQuery actorMappingQuery; |
|
|
|
@Autowired |
|
public void setDataSource(DataSource dataSource) { |
|
this.actorMappingQuery = new ActorMappingQuery(dataSource); |
|
} |
|
|
|
public Customer getCustomer(Long id) { |
|
return actorMappingQuery.findObject(id); |
|
} |
|
---- |
|
|
|
The method in this example retrieves the customer with the id that is passed in as the |
|
only parameter. Since we only want one object returned we simply call the convenience |
|
method `findObject` with the id as parameter. If we had instead a query that returned a |
|
list of objects and took additional parameters then we would use one of the execute |
|
methods that takes an array of parameter values passed in as varargs. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public List<Actor> searchForActors(int age, String namePattern) { |
|
List<Actor> actors = actorSearchMappingQuery.execute(age, namePattern); |
|
return actors; |
|
} |
|
---- |
|
|
|
|
|
|
|
[[jdbc-SqlUpdate]] |
|
==== SqlUpdate |
|
|
|
The `SqlUpdate` class encapsulates an SQL update. Like a query, an update object is |
|
reusable, and like all `RdbmsOperation` classes, an update can have parameters and is |
|
defined in SQL. This class provides a number of `update(..)` methods analogous to the |
|
`execute(..)` methods of query objects. The `SQLUpdate` class is concrete. It can be |
|
subclassed, for example, to add a custom update method, as in the following snippet |
|
where it's simply called `execute`. However, you don't have to subclass the `SqlUpdate` |
|
class since it can easily be parameterized by setting SQL and declaring parameters. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
import java.sql.Types; |
|
|
|
import javax.sql.DataSource; |
|
|
|
import org.springframework.jdbc.core.SqlParameter; |
|
import org.springframework.jdbc.object.SqlUpdate; |
|
|
|
public class UpdateCreditRating extends SqlUpdate { |
|
|
|
public UpdateCreditRating(DataSource ds) { |
|
setDataSource(ds); |
|
setSql("update customer set credit_rating = ? where id = ?"); |
|
declareParameter(new SqlParameter("creditRating", Types.NUMERIC)); |
|
declareParameter(new SqlParameter("id", Types.NUMERIC)); |
|
compile(); |
|
} |
|
|
|
/** |
|
* @param id for the Customer to be updated |
|
* @param rating the new value for credit rating |
|
* @return number of rows updated |
|
*/ |
|
public int execute(int id, int rating) { |
|
return update(rating, id); |
|
} |
|
} |
|
---- |
|
|
|
|
|
|
|
[[jdbc-StoredProcedure]] |
|
==== StoredProcedure |
|
|
|
The `StoredProcedure` class is a superclass for object abstractions of RDBMS stored |
|
procedures. This class is `abstract`, and its various `execute(..)` methods have |
|
`protected` access, preventing use other than through a subclass that offers tighter |
|
typing. |
|
|
|
The inherited `sql` property will be the name of the stored procedure in the RDBMS. |
|
|
|
To define a parameter for the `StoredProcedure` class, you use an `SqlParameter` or one |
|
of its subclasses. You must specify the parameter name and SQL type in the constructor |
|
like in the following code snippet. The SQL type is specified using the `java.sql.Types` |
|
constants. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
new SqlParameter("in_id", Types.NUMERIC), |
|
new SqlOutParameter("out_first_name", Types.VARCHAR), |
|
---- |
|
|
|
The first line with the `SqlParameter` declares an IN parameter. IN parameters can be |
|
used for both stored procedure calls and for queries using the `SqlQuery` and its |
|
subclasses covered in the following section. |
|
|
|
The second line with the `SqlOutParameter` declares an `out` parameter to be used in the |
|
stored procedure call. There is also an `SqlInOutParameter` for `I` `nOut` parameters, |
|
parameters that provide an `in` value to the procedure and that also return a value. |
|
|
|
For `i` `n` parameters, in addition to the name and the SQL type, you can specify a |
|
scale for numeric data or a type name for custom database types. For `out` parameters |
|
you can provide a `RowMapper` to handle mapping of rows returned from a REF cursor. |
|
Another option is to specify an `SqlReturnType` that enables you to define customized |
|
handling of the return values. |
|
|
|
Here is an example of a simple DAO that uses a `StoredProcedure` to call a function, |
|
`sysdate()`,which comes with any Oracle database. To use the stored procedure |
|
functionality you have to create a class that extends `StoredProcedure`. In this |
|
example, the `StoredProcedure` class is an inner class, but if you need to reuse the |
|
`StoredProcedure` you declare it as a top-level class. This example has no input |
|
parameters, but an output parameter is declared as a date type using the class |
|
`SqlOutParameter`. The `execute()` method executes the procedure and extracts the |
|
returned date from the results `Map`. The results `Map` has an entry for each declared |
|
output parameter, in this case only one, using the parameter name as the key. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import java.sql.Types; |
|
import java.util.Date; |
|
import java.util.HashMap; |
|
import java.util.Map; |
|
|
|
import javax.sql.DataSource; |
|
|
|
import org.springframework.beans.factory.annotation.Autowired; |
|
import org.springframework.jdbc.core.SqlOutParameter; |
|
import org.springframework.jdbc.object.StoredProcedure; |
|
|
|
public class StoredProcedureDao { |
|
|
|
private GetSysdateProcedure getSysdate; |
|
|
|
@Autowired |
|
public void init(DataSource dataSource) { |
|
this.getSysdate = new GetSysdateProcedure(dataSource); |
|
} |
|
|
|
public Date getSysdate() { |
|
return getSysdate.execute(); |
|
} |
|
|
|
private class GetSysdateProcedure extends StoredProcedure { |
|
|
|
private static final String SQL = "sysdate"; |
|
|
|
public GetSysdateProcedure(DataSource dataSource) { |
|
setDataSource(dataSource); |
|
setFunction(true); |
|
setSql(SQL); |
|
declareParameter(new SqlOutParameter("date", Types.DATE)); |
|
compile(); |
|
} |
|
|
|
public Date execute() { |
|
// the 'sysdate' sproc has no input parameters, so an empty Map is supplied... |
|
Map<String, Object> results = execute(new HashMap<String, Object>()); |
|
Date sysdate = (Date) results.get("date"); |
|
return sysdate; |
|
} |
|
} |
|
|
|
} |
|
---- |
|
|
|
The following example of a `StoredProcedure` has two output parameters (in this case, |
|
Oracle REF cursors). |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import oracle.jdbc.OracleTypes; |
|
import org.springframework.jdbc.core.SqlOutParameter; |
|
import org.springframework.jdbc.object.StoredProcedure; |
|
|
|
import javax.sql.DataSource; |
|
import java.util.HashMap; |
|
import java.util.Map; |
|
|
|
public class TitlesAndGenresStoredProcedure extends StoredProcedure { |
|
|
|
private static final String SPROC_NAME = "AllTitlesAndGenres"; |
|
|
|
public TitlesAndGenresStoredProcedure(DataSource dataSource) { |
|
super(dataSource, SPROC_NAME); |
|
declareParameter(new SqlOutParameter("titles", OracleTypes.CURSOR, new TitleMapper())); |
|
declareParameter(new SqlOutParameter("genres", OracleTypes.CURSOR, new GenreMapper())); |
|
compile(); |
|
} |
|
|
|
public Map<String, Object> execute() { |
|
// again, this sproc has no input parameters, so an empty Map is supplied |
|
return super.execute(new HashMap<String, Object>()); |
|
} |
|
} |
|
---- |
|
|
|
Notice how the overloaded variants of the `declareParameter(..)` method that have been |
|
used in the `TitlesAndGenresStoredProcedure` constructor are passed `RowMapper` |
|
implementation instances; this is a very convenient and powerful way to reuse existing |
|
functionality. The code for the two `RowMapper` implementations is provided below. |
|
|
|
The `TitleMapper` class maps a `ResultSet` to a `Title` domain object for each row in |
|
the supplied `ResultSet`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import org.springframework.jdbc.core.RowMapper; |
|
|
|
import java.sql.ResultSet; |
|
import java.sql.SQLException; |
|
|
|
import com.foo.domain.Title; |
|
|
|
public final class TitleMapper implements RowMapper<Title> { |
|
|
|
public Title mapRow(ResultSet rs, int rowNum) throws SQLException { |
|
Title title = new Title(); |
|
title.setId(rs.getLong("id")); |
|
title.setName(rs.getString("name")); |
|
return title; |
|
} |
|
} |
|
---- |
|
|
|
The `GenreMapper` class maps a `ResultSet` to a `Genre` domain object for each row in |
|
the supplied `ResultSet`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import org.springframework.jdbc.core.RowMapper; |
|
|
|
import java.sql.ResultSet; |
|
import java.sql.SQLException; |
|
|
|
import com.foo.domain.Genre; |
|
|
|
public final class GenreMapper implements RowMapper<Genre> { |
|
|
|
public Genre mapRow(ResultSet rs, int rowNum) throws SQLException { |
|
return new Genre(rs.getString("name")); |
|
} |
|
} |
|
---- |
|
|
|
To pass parameters to a stored procedure that has one or more input parameters in its |
|
definition in the RDBMS, you can code a strongly typed `execute(..)` method that would |
|
delegate to the superclass' untyped `execute(Map parameters)` method (which has |
|
`protected` access); for example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import oracle.jdbc.OracleTypes; |
|
import org.springframework.jdbc.core.SqlOutParameter; |
|
import org.springframework.jdbc.core.SqlParameter; |
|
import org.springframework.jdbc.object.StoredProcedure; |
|
|
|
import javax.sql.DataSource; |
|
|
|
import java.sql.Types; |
|
import java.util.Date; |
|
import java.util.HashMap; |
|
import java.util.Map; |
|
|
|
public class TitlesAfterDateStoredProcedure extends StoredProcedure { |
|
|
|
private static final String SPROC_NAME = "TitlesAfterDate"; |
|
private static final String CUTOFF_DATE_PARAM = "cutoffDate"; |
|
|
|
public TitlesAfterDateStoredProcedure(DataSource dataSource) { |
|
super(dataSource, SPROC_NAME); |
|
declareParameter(new SqlParameter(CUTOFF_DATE_PARAM, Types.DATE); |
|
declareParameter(new SqlOutParameter("titles", OracleTypes.CURSOR, new TitleMapper())); |
|
compile(); |
|
} |
|
|
|
public Map<String, Object> execute(Date cutoffDate) { |
|
Map<String, Object> inputs = new HashMap<String, Object>(); |
|
inputs.put(CUTOFF_DATE_PARAM, cutoffDate); |
|
return super.execute(inputs); |
|
} |
|
} |
|
---- |
|
|
|
|
|
|
|
|
|
[[jdbc-parameter-handling]] |
|
=== Common problems with parameter and data value handling |
|
Common problems with parameters and data values exist in the different approaches |
|
provided by the Spring Framework JDBC. |
|
|
|
|
|
|
|
[[jdbc-type-information]] |
|
==== Providing SQL type information for parameters |
|
Usually Spring determines the SQL type of the parameters based on the type of parameter |
|
passed in. It is possible to explicitly provide the SQL type to be used when setting |
|
parameter values. This is sometimes necessary to correctly set NULL values. |
|
|
|
You can provide SQL type information in several ways: |
|
|
|
* Many update and query methods of the `JdbcTemplate` take an additional parameter in |
|
the form of an `int` array. This array is used to indicate the SQL type of the |
|
corresponding parameter using constant values from the `java.sql.Types` class. Provide |
|
one entry for each parameter. |
|
* You can use the `SqlParameterValue` class to wrap the parameter value that needs this |
|
additional information.Create a new instance for each value and pass in the SQL type |
|
and parameter value in the constructor. You can also provide an optional scale |
|
parameter for numeric values. |
|
* For methods working with named parameters, use the `SqlParameterSource` classes |
|
`BeanPropertySqlParameterSource` or `MapSqlParameterSource`. They both have methods |
|
for registering the SQL type for any of the named parameter values. |
|
|
|
|
|
|
|
[[jdbc-lob]] |
|
==== Handling BLOB and CLOB objects |
|
You can store images, other binary objects, and large chunks of text. These large object |
|
are called BLOB for binary data and CLOB for character data. In Spring you can handle |
|
these large objects by using the JdbcTemplate directly and also when using the higher |
|
abstractions provided by RDBMS Objects and the `SimpleJdbc` classes. All of these |
|
approaches use an implementation of the `LobHandler` interface for the actual management |
|
of the LOB data. The `LobHandler` provides access to a `LobCreator` class, through the |
|
`getLobCreator` method, used for creating new LOB objects to be inserted. |
|
|
|
The `LobCreator/LobHandler` provides the following support for LOB input and output: |
|
|
|
* BLOB |
|
* byte[] -- getBlobAsBytes and setBlobAsBytes |
|
* InputStream -- getBlobAsBinaryStream and setBlobAsBinaryStream |
|
* CLOB |
|
* String -- getClobAsString and setClobAsString |
|
* InputStream -- getClobAsAsciiStream and setClobAsAsciiStream |
|
* Reader -- getClobAsCharacterStream and setClobAsCharacterStream |
|
|
|
The next example shows how to create and insert a BLOB. Later you will see how to read |
|
it back from the database. |
|
|
|
This example uses a `JdbcTemplate` and an implementation of the |
|
`AbstractLobCreatingPreparedStatementCallbac` `k`. It implements one method, |
|
`setValues`. This method provides a `LobCreator` that you use to set the values for the |
|
LOB columns in your SQL insert statement. |
|
|
|
For this example we assume that there is a variable, `lobHandle` `r`, that already is |
|
set to an instance of a `DefaultLobHandler`. You typically set this value through |
|
dependency injection. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
final File blobIn = new File("spring2004.jpg"); |
|
final InputStream blobIs = new FileInputStream(blobIn); |
|
final File clobIn = new File("large.txt"); |
|
final InputStream clobIs = new FileInputStream(clobIn); |
|
final InputStreamReader clobReader = new InputStreamReader(clobIs); |
|
jdbcTemplate.execute( |
|
"INSERT INTO lob_table (id, a_clob, a_blob) VALUES (?, ?, ?)", |
|
new AbstractLobCreatingPreparedStatementCallback(lobHandler) { # <1> |
|
protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException { |
|
ps.setLong(1, 1L); |
|
lobCreator.setClobAsCharacterStream(ps, 2, clobReader, (int)clobIn.length()); # <2> |
|
lobCreator.setBlobAsBinaryStream(ps, 3, blobIs, (int)blobIn.length()); # <3> |
|
} |
|
} |
|
); |
|
blobIs.close(); |
|
clobReader.close(); |
|
---- |
|
|
|
<1> Pass in the lobHandler that in this example is a plain `DefaultLobHandler` |
|
<2> Using the method `setClobAsCharacterStream`, pass in the contents of the CLOB. |
|
<3> Using the method `setBlobAsBinaryStream`, pass in the contents of the BLOB. |
|
|
|
Now it's time to read the LOB data from the database. Again, you use a `JdbcTemplate` |
|
with the same instance variable `l` `obHandler` and a reference to a `DefaultLobHandler`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
List<Map<String, Object>> l = jdbcTemplate.query("select id, a_clob, a_blob from lob_table", |
|
new RowMapper<Map<String, Object>>() { |
|
public Map<String, Object> mapRow(ResultSet rs, int i) throws SQLException { |
|
Map<String, Object> results = new HashMap<String, Object>(); |
|
String clobText = lobHandler.getClobAsString(rs, "a_clob"); # <1> |
|
results.put("CLOB", clobText); byte[] blobBytes = lobHandler.getBlobAsBytes(rs, "a_blob"); # <2> |
|
results.put("BLOB", blobBytes); return results; } }); |
|
---- |
|
|
|
<1> Using the method `getClobAsString`, retrieve the contents of the CLOB. |
|
<2> Using the method `getBlobAsBytes`, retrieve the contents of the BLOB. |
|
|
|
|
|
|
|
[[jdbc-in-clause]] |
|
==== Passing in lists of values for IN clause |
|
The SQL standard allows for selecting rows based on an expression that includes a |
|
variable list of values. A typical example would be `select * from T_ACTOR where id in |
|
(1, 2, 3)`. This variable list is not directly supported for prepared statements by the |
|
JDBC standard; you cannot declare a variable number of placeholders. You need a number |
|
of variations with the desired number of placeholders prepared, or you need to generate |
|
the SQL string dynamically once you know how many placeholders are required. The named |
|
parameter support provided in the `NamedParameterJdbcTemplate` and `JdbcTemplate` takes |
|
the latter approach. Pass in the values as a `java.util.List` of primitive objects. This |
|
list will be used to insert the required placeholders and pass in the values during the |
|
statement execution. |
|
|
|
[NOTE] |
|
==== |
|
Be careful when passing in many values. The JDBC standard does not guarantee that you |
|
can use more than 100 values for an `in` expression list. Various databases exceed this |
|
number, but they usually have a hard limit for how many values are allowed. Oracle's |
|
limit is 1000. |
|
==== |
|
|
|
In addition to the primitive values in the value list, you can create a `java.util.List` |
|
of object arrays. This list would support multiple expressions defined for the `in` |
|
clause such as `select * from T_ACTOR where (id, last_name) in ((1, 'Johnson'), (2, |
|
'Harrop'))`. This of course requires that your database supports this syntax. |
|
|
|
|
|
|
|
[[jdbc-complex-types]] |
|
==== Handling complex types for stored procedure calls |
|
When you call stored procedures you can sometimes use complex types specific to the |
|
database. To accommodate these types, Spring provides a `SqlReturnType` for handling |
|
them when they are returned from the stored procedure call and `SqlTypeValue` when they |
|
are passed in as a parameter to the stored procedure. |
|
|
|
Here is an example of returning the value of an Oracle `STRUCT` object of the user |
|
declared type `ITEM_TYPE`. The `SqlReturnType` interface has a single method named |
|
`getTypeValue` that must be implemented. This interface is used as part of the |
|
declaration of an `SqlOutParameter`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
final TestItem = new TestItem(123L, "A test item", |
|
new SimpleDateFormat("yyyy-M-d").parse("2010-12-31")); |
|
|
|
declareParameter(new SqlOutParameter("item", OracleTypes.STRUCT, "ITEM_TYPE", |
|
new SqlReturnType() { |
|
public Object getTypeValue(CallableStatement cs, int colIndx, int sqlType, String typeName) throws SQLException { |
|
STRUCT struct = (STRUCT) cs.getObject(colIndx); |
|
Object[] attr = struct.getAttributes(); |
|
TestItem item = new TestItem(); |
|
item.setId(((Number) attr[0]).longValue()); |
|
item.setDescription((String) attr[1]); |
|
item.setExpirationDate((java.util.Date) attr[2]); |
|
return item; |
|
} |
|
})); |
|
---- |
|
|
|
You use the `SqlTypeValue` to pass in the value of a Java object like `TestItem` into a |
|
stored procedure. The `SqlTypeValue` interface has a single method named |
|
`createTypeValue` that you must implement. The active connection is passed in, and you |
|
can use it to create database-specific objects such as ++StructDescriptor++s, as shown in |
|
the following example, or ++ArrayDescriptor++s. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
final TestItem = new TestItem(123L, "A test item", |
|
new SimpleDateFormat("yyyy-M-d").parse("2010-12-31")); |
|
|
|
SqlTypeValue value = new AbstractSqlTypeValue() { |
|
protected Object createTypeValue(Connection conn, int sqlType, String typeName) throws SQLException { |
|
StructDescriptor itemDescriptor = new StructDescriptor(typeName, conn); |
|
Struct item = new STRUCT(itemDescriptor, conn, |
|
new Object[] { |
|
testItem.getId(), |
|
testItem.getDescription(), |
|
new java.sql.Date(testItem.getExpirationDate().getTime()) |
|
}); |
|
return item; |
|
} |
|
}; |
|
---- |
|
|
|
This `SqlTypeValue` can now be added to the Map containing the input parameters for the |
|
execute call of the stored procedure. |
|
|
|
Another use for the `SqlTypeValue` is passing in an array of values to an Oracle stored |
|
procedure. Oracle has its own internal `ARRAY` class that must be used in this case, and |
|
you can use the `SqlTypeValue` to create an instance of the Oracle `ARRAY` and populate |
|
it with values from the Java `ARRAY`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
final Long[] ids = new Long[] {1L, 2L}; |
|
|
|
SqlTypeValue value = new AbstractSqlTypeValue() { |
|
protected Object createTypeValue(Connection conn, int sqlType, String typeName) throws SQLException { |
|
ArrayDescriptor arrayDescriptor = new ArrayDescriptor(typeName, conn); |
|
ARRAY idArray = new ARRAY(arrayDescriptor, conn, ids); |
|
return idArray; |
|
} |
|
}; |
|
---- |
|
|
|
|
|
|
|
|
|
[[jdbc-embedded-database-support]] |
|
=== Embedded database support |
|
The `org.springframework.jdbc.datasource.embedded` package provides support for embedded |
|
Java database engines. Support for http://www.hsqldb.org[HSQL], |
|
http://www.h2database.com[H2], and http://db.apache.org/derby[Derby] is provided |
|
natively. You can also use an extensible API to plug in new embedded database types and |
|
`DataSource` implementations. |
|
|
|
|
|
|
|
[[jdbc-why-embedded-database]] |
|
==== Why use an embedded database? |
|
An embedded database is useful during the development phase of a project because of its |
|
lightweight nature. Benefits include ease of configuration, quick startup time, |
|
testability, and the ability to rapidly evolve SQL during development. |
|
|
|
|
|
|
|
[[jdbc-embedded-database-xml]] |
|
==== Creating an embedded database instance using Spring XML |
|
If you want to expose an embedded database instance as a bean in a Spring |
|
ApplicationContext, use the embedded-database tag in the spring-jdbc namespace: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<jdbc:embedded-database id="dataSource"> |
|
<jdbc:script location="classpath:schema.sql"/> |
|
<jdbc:script location="classpath:test-data.sql"/> |
|
</jdbc:embedded-database> |
|
---- |
|
|
|
The preceding configuration creates an embedded HSQL database populated with SQL from |
|
schema.sql and testdata.sql resources in the classpath. The database instance is made |
|
available to the Spring container as a bean of type `javax.sql.DataSource`. This bean |
|
can then be injected into data access objects as needed. |
|
|
|
|
|
|
|
[[jdbc-embedded-database-java]] |
|
==== Creating an embedded database instance programmatically |
|
The `EmbeddedDatabaseBuilder` class provides a fluent API for constructing an embedded |
|
database programmatically. Use this when you need to create an embedded database |
|
instance in a standalone environment, such as a data access object unit test: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); |
|
EmbeddedDatabase db = builder.setType(H2).addScript("my-schema.sql").addScript("my-test-data.sql").build(); |
|
// do stuff against the db (EmbeddedDatabase extends javax.sql.DataSource) |
|
db.shutdown() |
|
---- |
|
|
|
|
|
|
|
[[jdbc-embedded-database-extension]] |
|
==== Extending the embedded database support |
|
Spring JDBC embedded database support can be extended in two ways: |
|
|
|
* Implement `EmbeddedDatabaseConfigurer` to support a new embedded database type, such |
|
as Apache Derby. |
|
* Implement `DataSourceFactory` to support a new DataSource implementation, such as a |
|
connection pool, to manage embedded database connections. |
|
|
|
You are encouraged to contribute back extensions to the Spring community at |
|
https://jira.spring.io/browse/SPR[jira.spring.io]. |
|
|
|
|
|
|
|
[[jdbc-embedded-database-using-HSQL]] |
|
==== Using HSQL |
|
Spring supports HSQL 1.8.0 and above. HSQL is the default embedded database if no type |
|
is specified explicitly. To specify HSQL explicitly, set the `type` attribute of the |
|
`embedded-database` tag to `HSQL`. If you are using the builder API, call the |
|
`setType(EmbeddedDatabaseType)` method with `EmbeddedDatabaseType.HSQL`. |
|
|
|
|
|
|
|
[[jdbc-embedded-database-using-H2]] |
|
==== Using H2 |
|
Spring supports the H2 database as well. To enable H2, set the `type` attribute of the |
|
`embedded-database` tag to `H2`. If you are using the builder API, call the |
|
`setType(EmbeddedDatabaseType)` method with `EmbeddedDatabaseType.H2`. |
|
|
|
|
|
|
|
[[jdbc-embedded-database-using-Derby]] |
|
==== Using Derby |
|
Spring also supports Apache Derby 10.5 and above. To enable Derby, set the `type` |
|
attribute of the `embedded-database` tag to `Derby`. If using the builder API, call the |
|
`setType(EmbeddedDatabaseType)` method with `EmbeddedDatabaseType.Derby`. |
|
|
|
|
|
|
|
[[jdbc-embedded-database-dao-testing]] |
|
==== Testing data access logic with an embedded database |
|
Embedded databases provide a lightweight way to test data access code. The following is |
|
a data access unit test template that uses an embedded database: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class DataAccessUnitTestTemplate { |
|
|
|
private EmbeddedDatabase db; |
|
|
|
@Before |
|
public void setUp() { |
|
// creates an HSQL in-memory database populated from default scripts |
|
// classpath:schema.sql and classpath:data.sql |
|
db = new EmbeddedDatabaseBuilder().addDefaultScripts().build(); |
|
} |
|
|
|
@Test |
|
public void testDataAccess() { |
|
JdbcTemplate template = new JdbcTemplate(db); |
|
template.query(...); |
|
} |
|
|
|
@After |
|
public void tearDown() { |
|
db.shutdown(); |
|
} |
|
|
|
} |
|
---- |
|
|
|
|
|
|
|
|
|
[[jdbc-intializing-datasource]] |
|
=== Initializing a DataSource |
|
The `org.springframework.jdbc.datasource.init` package provides support for initializing |
|
an existing `DataSource`. The embedded database support provides one option for creating |
|
and initializing a `DataSource` for an application, but sometimes you need to initialize |
|
an instance running on a server somewhere. |
|
|
|
|
|
|
|
[[jdbc-initializing-datasource-xml]] |
|
==== Initializing a database instance using Spring XML |
|
If you want to initialize a database and you can provide a reference to a DataSource |
|
bean, use the `initialize-database` tag in the `spring-jdbc` namespace: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<jdbc:initialize-database data-source="dataSource"> |
|
<jdbc:script location="classpath:com/foo/sql/db-schema.sql"/> |
|
<jdbc:script location="classpath:com/foo/sql/db-test-data.sql"/> |
|
</jdbc:initialize-database> |
|
---- |
|
|
|
The example above runs the two scripts specified against the database: the first script |
|
is a schema creation, and the second is a test data set insert. The script locations can |
|
also be patterns with wildcards in the usual ant style used for resources in Spring |
|
(e.g. `classpath*:/com/foo/**/sql/*-data.sql`). If a pattern is used the scripts are |
|
executed in lexical order of their URL or filename. |
|
|
|
The default behavior of the database initializer is to unconditionally execute the |
|
scripts provided. This will not always be what you want, for instance if running against |
|
an existing database that already has test data in it. The likelihood of accidentally |
|
deleting data is reduced by the commonest pattern (as shown above) that creates the |
|
tables first and then inserts the data - the first step will fail if the tables already |
|
exist. |
|
|
|
However, to get more control over the creation and deletion of existing data, the XML |
|
namespace provides a couple more options. The first is flag to switch the initialization |
|
on and off. This can be set according to the environment (e.g. to pull a boolean value |
|
from system properties or an environment bean), e.g. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<jdbc:initialize-database data-source="dataSource" |
|
**enabled="#{systemProperties.INITIALIZE_DATABASE}"**> |
|
<jdbc:script location="..."/> |
|
</jdbc:initialize-database> |
|
---- |
|
|
|
The second option to control what happens with existing data is to be more tolerant of |
|
failures. To this end you can control the ability of the initializer to ignore certain |
|
errors in the SQL it executes from the scripts, e.g. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<jdbc:initialize-database data-source="dataSource" **ignore-failures="DROPS"**> |
|
<jdbc:script location="..."/> |
|
</jdbc:initialize-database> |
|
---- |
|
|
|
In this example we are saying we expect that sometimes the scripts will be run against |
|
an empty database and there are some DROP statements in the scripts which would |
|
therefore fail. So failed SQL `DROP` statements will be ignored, but other failures will |
|
cause an exception. This is useful if your SQL dialect doesn't support `DROP ... IF |
|
EXISTS` (or similar) but you want to unconditionally remove all test data before |
|
re-creating it. In that case the first script is usually a set of drops, followed by a |
|
set of `CREATE` statements. |
|
|
|
The `ignore-failures` option can be set to `NONE` (the default), `DROPS` (ignore failed |
|
drops) or `ALL` (ignore all failures). |
|
|
|
If you need more control than you get from the XML namespace, you can simply use the |
|
`DataSourceInitializer` directly, and define it as a component in your application. |
|
|
|
|
|
[[jdbc-client-component-initialization]] |
|
===== Initialization of Other Components that Depend on the Database |
|
A large class of applications can just use the database initializer with no further |
|
complications: those that do not use the database until after the Spring context has |
|
started. If your application is __not__ one of those then you might need to read the |
|
rest of this section. |
|
|
|
The database initializer depends on a data source instance and runs the scripts provided |
|
in its initialization callback (c.f. `init-method` in an XML bean definition or |
|
`InitializingBean`). If other beans depend on the same data source and also use the data |
|
source in an initialization callback then there might be a problem because the data has |
|
not yet been initialized. A common example of this is a cache that initializes eagerly |
|
and loads up data from the database on application startup. |
|
|
|
To get round this issue you two options: change your cache initialization strategy to a |
|
later phase, or ensure that the database initializer is initialized first. |
|
|
|
The first option might be easy if the application is in your control, and not otherwise. |
|
Some suggestions for how to implement this are |
|
|
|
* Make the cache initialize lazily on first usage, which improves application startup time |
|
* Have your cache or a separate component that initializes the cache implement |
|
`Lifecycle` or `SmartLifecycle`. When the application context starts up a |
|
`SmartLifecycle` can be automatically started if its `autoStartup` flag is set, and a |
|
`Lifecycle` can be started manually by calling |
|
`ConfigurableApplicationContext.start()` on the enclosing context. |
|
* Use a Spring `ApplicationEvent` or similar custom observer mechanism to trigger the |
|
cache initialization. `ContextRefreshedEvent` is always published by the context when |
|
it is ready for use (after all beans have been initialized), so that is often a useful |
|
hook (this is how the `SmartLifecycle` works by default). |
|
|
|
The second option can also be easy. Some suggestions on how to implement this are |
|
|
|
* Rely on Spring BeanFactory default behavior, which is that beans are initialized in |
|
registration order. You can easily arrange that by adopting the common practice of a |
|
set of <import/> elements that order your application modules, and ensure that the |
|
database and database initialization are listed first |
|
* Separate the datasource and the business components that use it and control their |
|
startup order by putting them in separate ApplicationContext instances (e.g. parent |
|
has the datasource and child has the business components). This structure is common in |
|
Spring web applications, but can be more generally applied. |
|
|
|
|
|
|
|
|
|
|
|
[[orm]] |
|
== Object Relational Mapping (ORM) Data Access |
|
|
|
|
|
|
|
|
|
[[orm-introduction]] |
|
=== Introduction to ORM with Spring |
|
The Spring Framework supports integration with Hibernate, Java Persistence API (JPA) |
|
and Java Data Objects (JDO) for resource management, data access object |
|
(DAO) implementations, and transaction strategies. For example, for Hibernate there is |
|
first-class support with several convenient IoC features that address many typical |
|
Hibernate integration issues. You can configure all of the supported features for O/R |
|
(object relational) mapping tools through Dependency Injection. They can participate in |
|
Spring's resource and transaction management, and they comply with Spring's generic |
|
transaction and DAO exception hierarchies. The recommended integration style is to code |
|
DAOs against plain Hibernate, JPA, and JDO APIs. The older style of using Spring's DAO |
|
templates is no longer recommended; however, coverage of this style can be found in the |
|
<<classic-spring-orm>> in the appendices. |
|
|
|
Spring adds significant enhancements to the ORM layer of your choice when you create |
|
data access applications. You can leverage as much of the integration support as you |
|
wish, and you should compare this integration effort with the cost and risk of building |
|
a similar infrastructure in-house. You can use much of the ORM support as you would a |
|
library, regardless of technology, because everything is designed as a set of reusable |
|
JavaBeans. ORM in a Spring IoC container facilitates configuration and deployment. Thus |
|
most examples in this section show configuration inside a Spring container. |
|
|
|
Benefits of using the Spring Framework to create your ORM DAOs include: |
|
|
|
* __Easier testing.__ Spring's IoC approach makes it easy to swap the implementations |
|
and configuration locations of Hibernate `SessionFactory` instances, JDBC `DataSource` |
|
instances, transaction managers, and mapped object implementations (if needed). This |
|
in turn makes it much easier to test each piece of persistence-related code in |
|
isolation. |
|
* __Common data access exceptions.__ Spring can wrap exceptions from your ORM tool, |
|
converting them from proprietary (potentially checked) exceptions to a common runtime |
|
DataAccessException hierarchy. This feature allows you to handle most persistence |
|
exceptions, which are non-recoverable, only in the appropriate layers, without |
|
annoying boilerplate catches, throws, and exception declarations. You can still trap |
|
and handle exceptions as necessary. Remember that JDBC exceptions (including |
|
DB-specific dialects) are also converted to the same hierarchy, meaning that you can |
|
perform some operations with JDBC within a consistent programming model. |
|
* __General resource management.__ Spring application contexts can handle the location |
|
and configuration of Hibernate `SessionFactory` instances, JPA `EntityManagerFactory` |
|
instances, JDBC `DataSource` instances, and other related resources. This makes these |
|
values easy to manage and change. Spring offers efficient, easy, and safe handling of |
|
persistence resources. For example, related code that uses Hibernate generally needs to |
|
use the same Hibernate `Session` to ensure efficiency and proper transaction handling. |
|
Spring makes it easy to create and bind a `Session` to the current thread transparently, |
|
by exposing a current `Session` through the Hibernate `SessionFactory`. Thus Spring |
|
solves many chronic problems of typical Hibernate usage, for any local or JTA |
|
transaction environment. |
|
* __Integrated transaction management.__ You can wrap your ORM code with a declarative, |
|
aspect-oriented programming (AOP) style method interceptor either through the |
|
`@Transactional` annotation or by explicitly configuring the transaction AOP advice in |
|
an XML configuration file. In both cases, transaction semantics and exception handling |
|
(rollback, and so on) are handled for you. As discussed below, in |
|
<<orm-resource-mngmnt,Resource and transaction management>>, you can also swap various |
|
transaction managers, without affecting your ORM-related code. For example, you can |
|
swap between local transactions and JTA, with the same full services (such as |
|
declarative transactions) available in both scenarios. Additionally, JDBC-related code |
|
can fully integrate transactionally with the code you use to do ORM. This is useful |
|
for data access that is not suitable for ORM, such as batch processing and BLOB |
|
streaming, which still need to share common transactions with ORM operations. |
|
|
|
[TIP] |
|
==== |
|
For more comprehensive ORM support, including support for alternative database |
|
technologies such as MongoDB, you might want to check out the |
|
http://projects.spring.io/spring-data/[Spring Data] suite of projects. If you are |
|
a JPA user, the https://spring.io/guides/gs/accessing-data-jpa/[Getting Started Accessing |
|
Data with JPA] guide from https://spring.io provides a great introduction. |
|
==== |
|
|
|
|
|
|
|
[[orm-general]] |
|
=== General ORM integration considerations |
|
This section highlights considerations that apply to all ORM technologies. The |
|
<<orm-hibernate>> section provides more details and also show these features and |
|
configurations in a concrete context. |
|
|
|
The major goal of Spring's ORM integration is clear application layering, with any data |
|
access and transaction technology, and for loose coupling of application objects. No |
|
more business service dependencies on the data access or transaction strategy, no more |
|
hard-coded resource lookups, no more hard-to-replace singletons, no more custom service |
|
registries. One simple and consistent approach to wiring up application objects, keeping |
|
them as reusable and free from container dependencies as possible. All the individual |
|
data access features are usable on their own but integrate nicely with Spring's |
|
application context concept, providing XML-based configuration and cross-referencing of |
|
plain JavaBean instances that need not be Spring-aware. In a typical Spring application, |
|
many important objects are JavaBeans: data access templates, data access objects, |
|
transaction managers, business services that use the data access objects and transaction |
|
managers, web view resolvers, web controllers that use the business services,and so on. |
|
|
|
|
|
|
|
[[orm-resource-mngmnt]] |
|
==== Resource and transaction management |
|
Typical business applications are cluttered with repetitive resource management code. |
|
Many projects try to invent their own solutions, sometimes sacrificing proper handling |
|
of failures for programming convenience. Spring advocates simple solutions for proper |
|
resource handling, namely IoC through templating in the case of JDBC and applying AOP |
|
interceptors for the ORM technologies. |
|
|
|
The infrastructure provides proper resource handling and appropriate conversion of |
|
specific API exceptions to an unchecked infrastructure exception hierarchy. Spring |
|
introduces a DAO exception hierarchy, applicable to any data access strategy. For direct |
|
JDBC, the `JdbcTemplate` class mentioned in a previous section provides connection |
|
handling and proper conversion of `SQLException` to the `DataAccessException` hierarchy, |
|
including translation of database-specific SQL error codes to meaningful exception |
|
classes. For ORM technologies, see the next section for how to get the same exception |
|
translation benefits. |
|
|
|
When it comes to transaction management, the `JdbcTemplate` class hooks in to the Spring |
|
transaction support and supports both JTA and JDBC transactions, through respective |
|
Spring transaction managers. For the supported ORM technologies Spring offers Hibernate, |
|
JPA and JDO support through the Hibernate, JPA, and JDO transaction managers as well as |
|
JTA support. For details on transaction support, see the <<transaction>> chapter. |
|
|
|
|
|
|
|
[[orm-exception-translation]] |
|
==== Exception translation |
|
When you use Hibernate, JPA, or JDO in a DAO, you must decide how to handle the |
|
persistence technology's native exception classes. The DAO throws a subclass of a |
|
`HibernateException`, `PersistenceException` or `JDOException` depending on the |
|
technology. These exceptions are all run-time exceptions and do not have to be declared |
|
or caught. You may also have to deal with `IllegalArgumentException` and |
|
`IllegalStateException`. This means that callers can only treat exceptions as generally |
|
fatal, unless they want to depend on the persistence technology's own exception |
|
structure. Catching specific causes such as an optimistic locking failure is not |
|
possible without tying the caller to the implementation strategy. This trade off might |
|
be acceptable to applications that are strongly ORM-based and/or do not need any special |
|
exception treatment. However, Spring enables exception translation to be applied |
|
transparently through the `@Repository` annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Repository |
|
public class ProductDaoImpl implements ProductDao { |
|
|
|
// class body here... |
|
|
|
} |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<!-- Exception translation bean post processor --> |
|
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/> |
|
|
|
<bean id="myProductDao" class="product.ProductDaoImpl"/> |
|
|
|
</beans> |
|
---- |
|
|
|
The postprocessor automatically looks for all exception translators (implementations of |
|
the `PersistenceExceptionTranslator` interface) and advises all beans marked with the |
|
`@Repository` annotation so that the discovered translators can intercept and apply the |
|
appropriate translation on the thrown exceptions. |
|
|
|
In summary: you can implement DAOs based on the plain persistence technology's API and |
|
annotations, while still benefiting from Spring-managed transactions, dependency |
|
injection, and transparent exception conversion (if desired) to Spring's custom |
|
exception hierarchies. |
|
|
|
|
|
|
|
|
|
[[orm-hibernate]] |
|
=== Hibernate |
|
We will start with a coverage of http://www.hibernate.org/[Hibernate 3] in a Spring |
|
environment, using it to demonstrate the approach that Spring takes towards integrating |
|
O/R mappers. This section will cover many issues in detail and show different variations |
|
of DAO implementations and transaction demarcation. Most of these patterns can be |
|
directly translated to all other supported ORM tools. The following sections in this |
|
chapter will then cover the other ORM technologies, showing briefer examples there. |
|
|
|
[NOTE] |
|
==== |
|
As of Spring 4.0, Spring requires Hibernate 3.6 or later. |
|
==== |
|
|
|
|
|
|
|
[[orm-session-factory-setup]] |
|
==== SessionFactory setup in a Spring container |
|
|
|
To avoid tying application objects to hard-coded resource lookups, you can define |
|
resources such as a JDBC `DataSource` or a Hibernate `SessionFactory` as beans in the |
|
Spring container. Application objects that need to access resources receive references |
|
to such predefined instances through bean references, as illustrated in the DAO |
|
definition in the next section. |
|
|
|
The following excerpt from an XML application context definition shows how to set up a |
|
JDBC `DataSource` and a Hibernate `SessionFactory` on top of it: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> |
|
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/> |
|
<property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/> |
|
<property name="username" value="sa"/> |
|
<property name="password" value=""/> |
|
</bean> |
|
|
|
<bean id="mySessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> |
|
<property name="dataSource" ref="myDataSource"/> |
|
<property name="mappingResources"> |
|
<list> |
|
<value>product.hbm.xml</value> |
|
</list> |
|
</property> |
|
<property name="hibernateProperties"> |
|
<value> |
|
hibernate.dialect=org.hibernate.dialect.HSQLDialect |
|
</value> |
|
</property> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
Switching from a local Jakarta Commons DBCP `BasicDataSource` to a JNDI-located |
|
`DataSource` (usually managed by an application server) is just a matter of |
|
configuration: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<jee:jndi-lookup id="myDataSource" jndi-name="java:comp/env/jdbc/myds"/> |
|
</beans> |
|
---- |
|
|
|
You can also access a JNDI-located `SessionFactory`, using Spring's |
|
`JndiObjectFactoryBean` / `<jee:jndi-lookup>` to retrieve and expose it. However, that |
|
is typically not common outside of an EJB context. |
|
|
|
|
|
|
|
[[orm-hibernate-straight]] |
|
==== Implementing DAOs based on plain Hibernate 3 API |
|
Hibernate 3 has a feature called contextual sessions, wherein Hibernate itself manages |
|
one current `Session` per transaction. This is roughly equivalent to Spring's |
|
synchronization of one Hibernate `Session` per transaction. A corresponding DAO |
|
implementation resembles the following example, based on the plain Hibernate API: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class ProductDaoImpl implements ProductDao { |
|
|
|
private SessionFactory sessionFactory; |
|
|
|
public void setSessionFactory(SessionFactory sessionFactory) { |
|
this.sessionFactory = sessionFactory; |
|
} |
|
|
|
public Collection loadProductsByCategory(String category) { |
|
return this.sessionFactory.getCurrentSession() |
|
.createQuery("from test.Product product where product.category=?") |
|
.setParameter(0, category) |
|
.list(); |
|
} |
|
} |
|
---- |
|
|
|
This style is similar to that of the Hibernate reference documentation and examples, |
|
except for holding the `SessionFactory` in an instance variable. We strongly recommend |
|
such an instance-based setup over the old-school `static` `HibernateUtil` class from |
|
Hibernate's CaveatEmptor sample application. (In general, do not keep any resources in |
|
`static` variables unless __absolutely__ necessary.) |
|
|
|
The above DAO follows the dependency injection pattern: it fits nicely into a Spring IoC |
|
container, just as it would if coded against Spring's `HibernateTemplate`. Of course, |
|
such a DAO can also be set up in plain Java (for example, in unit tests). Simply |
|
instantiate it and call `setSessionFactory(..)` with the desired factory reference. As a |
|
Spring bean definition, the DAO would resemble the following: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<bean id="myProductDao" class="product.ProductDaoImpl"> |
|
<property name="sessionFactory" ref="mySessionFactory"/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
The main advantage of this DAO style is that it depends on Hibernate API only; no import |
|
of any Spring class is required. This is of course appealing from a non-invasiveness |
|
perspective, and will no doubt feel more natural to Hibernate developers. |
|
|
|
However, the DAO throws plain `HibernateException` (which is unchecked, so does not have |
|
to be declared or caught), which means that callers can only treat exceptions as |
|
generally fatal - unless they want to depend on Hibernate's own exception hierarchy. |
|
Catching specific causes such as an optimistic locking failure is not possible without |
|
tying the caller to the implementation strategy. This trade off might be acceptable to |
|
applications that are strongly Hibernate-based and/or do not need any special exception |
|
treatment. |
|
|
|
Fortunately, Spring's `LocalSessionFactoryBean` supports Hibernate's |
|
`SessionFactory.getCurrentSession()` method for any Spring transaction strategy, |
|
returning the current Spring-managed transactional `Session` even with |
|
`HibernateTransactionManager`. Of course, the standard behavior of that method remains |
|
the return of the current `Session` associated with the ongoing JTA transaction, if any. |
|
This behavior applies regardless of whether you are using Spring's |
|
`JtaTransactionManager`, EJB container managed transactions (CMTs), or JTA. |
|
|
|
In summary: you can implement DAOs based on the plain Hibernate 3 API, while still being |
|
able to participate in Spring-managed transactions. |
|
|
|
|
|
|
|
[[orm-hibernate-tx-declarative]] |
|
==== Declarative transaction demarcation |
|
We recommend that you use Spring's declarative transaction support, which enables you to |
|
replace explicit transaction demarcation API calls in your Java code with an AOP |
|
transaction interceptor. This transaction interceptor can be configured in a Spring |
|
container using either Java annotations or XML.This declarative transaction capability |
|
allows you to keep business services free of repetitive transaction demarcation code and |
|
to focus on adding business logic, which is the real value of your application. |
|
|
|
[NOTE] |
|
==== |
|
Prior to continuing, you are __strongly__ encouraged to read <<transaction-declarative>> |
|
if you have not done so. |
|
==== |
|
|
|
Furthermore, transaction semantics like propagation behavior and isolation level can be |
|
changed in a configuration file and do not affect the business service implementations. |
|
|
|
The following example shows how you can configure an AOP transaction interceptor, using |
|
XML, for a simple service class: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:aop="http://www.springframework.org/schema/aop" |
|
xmlns:tx="http://www.springframework.org/schema/tx" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/tx |
|
http://www.springframework.org/schema/tx/spring-tx.xsd |
|
http://www.springframework.org/schema/aop |
|
http://www.springframework.org/schema/aop/spring-aop.xsd"> |
|
|
|
<!-- SessionFactory, DataSource, etc. omitted --> |
|
|
|
<bean id="transactionManager" |
|
class="org.springframework.orm.hibernate3.HibernateTransactionManager"> |
|
<property name="sessionFactory" ref="sessionFactory"/> |
|
</bean> |
|
|
|
<aop:config> |
|
<aop:pointcut id="productServiceMethods" |
|
expression="execution(* product.ProductService.*(..))"/> |
|
<aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/> |
|
</aop:config> |
|
|
|
<tx:advice id="txAdvice" transaction-manager="myTxManager"> |
|
<tx:attributes> |
|
<tx:method name="increasePrice*" propagation="REQUIRED"/> |
|
<tx:method name="someOtherBusinessMethod" propagation="REQUIRES_NEW"/> |
|
<tx:method name="*" propagation="SUPPORTS" read-only="true"/> |
|
</tx:attributes> |
|
</tx:advice> |
|
|
|
<bean id="myProductService" class="product.SimpleProductService"> |
|
<property name="productDao" ref="myProductDao"/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
This is the service class that is advised: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class ProductServiceImpl implements ProductService { |
|
|
|
private ProductDao productDao; |
|
|
|
public void setProductDao(ProductDao productDao) { |
|
this.productDao = productDao; |
|
} |
|
|
|
// notice the absence of transaction demarcation code in this method |
|
// Spring's declarative transaction infrastructure will be demarcating |
|
// transactions on your behalf |
|
public void increasePriceOfAllProductsInCategory(final String category) { |
|
List productsToChange = this.productDao.loadProductsByCategory(category); |
|
// ... |
|
} |
|
} |
|
---- |
|
|
|
We also show an attribute-support based configuration, in the following example. You |
|
annotate the service layer with @Transactional annotations and instruct the Spring |
|
container to find these annotations and provide transactional semantics for these |
|
annotated methods. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class ProductServiceImpl implements ProductService { |
|
|
|
private ProductDao productDao; |
|
|
|
public void setProductDao(ProductDao productDao) { |
|
this.productDao = productDao; |
|
} |
|
|
|
@Transactional |
|
public void increasePriceOfAllProductsInCategory(final String category) { |
|
List productsToChange = this.productDao.loadProductsByCategory(category); |
|
// ... |
|
} |
|
|
|
@Transactional(readOnly = true) |
|
public List<Product> findAllProducts() { |
|
return this.productDao.findAllProducts(); |
|
} |
|
|
|
} |
|
---- |
|
|
|
As you can see from the following configuration example, the configuration is much |
|
simplified, compared to the XML example above, while still providing the same |
|
functionality driven by the annotations in the service layer code. All you need to |
|
provide is the TransactionManager implementation and a "<tx:annotation-driven/>" entry. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:aop="http://www.springframework.org/schema/aop" |
|
xmlns:tx="http://www.springframework.org/schema/tx" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/tx |
|
http://www.springframework.org/schema/tx/spring-tx.xsd |
|
http://www.springframework.org/schema/aop |
|
http://www.springframework.org/schema/aop/spring-aop.xsd"> |
|
|
|
<!-- SessionFactory, DataSource, etc. omitted --> |
|
|
|
<bean id="transactionManager" |
|
class="org.springframework.orm.hibernate3.HibernateTransactionManager"> |
|
<property name="sessionFactory" ref="sessionFactory"/> |
|
</bean> |
|
|
|
<tx:annotation-driven/> |
|
|
|
<bean id="myProductService" class="product.SimpleProductService"> |
|
<property name="productDao" ref="myProductDao"/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
|
|
|
|
[[orm-hibernate-tx-programmatic]] |
|
==== Programmatic transaction demarcation |
|
You can demarcate transactions in a higher level of the application, on top of such |
|
lower-level data access services spanning any number of operations. Nor do restrictions |
|
exist on the implementation of the surrounding business service; it just needs a Spring |
|
`PlatformTransactionManager`. Again, the latter can come from anywhere, but preferably |
|
as a bean reference through a `setTransactionManager(..)` method, just as the |
|
`productDAO` should be set by a `setProductDao(..)` method. The following snippets show |
|
a transaction manager and a business service definition in a Spring application context, |
|
and an example for a business method implementation: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<bean id="myTxManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> |
|
<property name="sessionFactory" ref="mySessionFactory"/> |
|
</bean> |
|
|
|
<bean id="myProductService" class="product.ProductServiceImpl"> |
|
<property name="transactionManager" ref="myTxManager"/> |
|
<property name="productDao" ref="myProductDao"/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class ProductServiceImpl implements ProductService { |
|
|
|
private TransactionTemplate transactionTemplate; |
|
private ProductDao productDao; |
|
|
|
public void setTransactionManager(PlatformTransactionManager transactionManager) { |
|
this.transactionTemplate = new TransactionTemplate(transactionManager); |
|
} |
|
|
|
public void setProductDao(ProductDao productDao) { |
|
this.productDao = productDao; |
|
} |
|
|
|
public void increasePriceOfAllProductsInCategory(final String category) { |
|
this.transactionTemplate.execute(new TransactionCallbackWithoutResult() { |
|
public void doInTransactionWithoutResult(TransactionStatus status) { |
|
List productsToChange = this.productDao.loadProductsByCategory(category); |
|
// do the price increase... |
|
} |
|
}); |
|
} |
|
} |
|
---- |
|
|
|
Spring's `TransactionInterceptor` allows any checked application exception to be thrown |
|
with the callback code, while `TransactionTemplate` is restricted to unchecked |
|
exceptions within the callback. `TransactionTemplate` triggers a rollback in case of an |
|
unchecked application exception, or if the transaction is marked rollback-only by the |
|
application (via `TransactionStatus`). `TransactionInterceptor` behaves the same way by |
|
default but allows configurable rollback policies per method. |
|
|
|
|
|
|
|
[[orm-hibernate-tx-strategies]] |
|
==== Transaction management strategies |
|
Both `TransactionTemplate` and `TransactionInterceptor` delegate the actual transaction |
|
handling to a `PlatformTransactionManager` instance, which can be a |
|
`HibernateTransactionManager` (for a single Hibernate `SessionFactory`, using a |
|
`ThreadLocal` `Session` under the hood) or a `JtaTransactionManager` (delegating to the |
|
JTA subsystem of the container) for Hibernate applications. You can even use a custom |
|
`PlatformTransactionManager` implementation. Switching from native Hibernate transaction |
|
management to JTA, such as when facing distributed transaction requirements for certain |
|
deployments of your application, is just a matter of configuration. Simply replace |
|
the Hibernate transaction manager with Spring's JTA transaction implementation. Both |
|
transaction demarcation and data access code will work without changes, because they |
|
just use the generic transaction management APIs. |
|
|
|
For distributed transactions across multiple Hibernate session factories, simply combine |
|
`JtaTransactionManager` as a transaction strategy with multiple |
|
`LocalSessionFactoryBean` definitions. Each DAO then gets one specific `SessionFactory` |
|
reference passed into its corresponding bean property. If all underlying JDBC data |
|
sources are transactional container ones, a business service can demarcate transactions |
|
across any number of DAOs and any number of session factories without special regard, as |
|
long as it is using `JtaTransactionManager` as the strategy. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<beans> |
|
|
|
<jee:jndi-lookup id="dataSource1" jndi-name="java:comp/env/jdbc/myds1"/> |
|
|
|
<jee:jndi-lookup id="dataSource2" jndi-name="java:comp/env/jdbc/myds2"/> |
|
|
|
<bean id="mySessionFactory1" |
|
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> |
|
<property name="dataSource" ref="myDataSource1"/> |
|
<property name="mappingResources"> |
|
<list> |
|
<value>product.hbm.xml</value> |
|
</list> |
|
</property> |
|
<property name="hibernateProperties"> |
|
<value> |
|
hibernate.dialect=org.hibernate.dialect.MySQLDialect |
|
hibernate.show_sql=true |
|
</value> |
|
</property> |
|
</bean> |
|
|
|
<bean id="mySessionFactory2" |
|
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> |
|
<property name="dataSource" ref="myDataSource2"/> |
|
<property name="mappingResources"> |
|
<list> |
|
<value>inventory.hbm.xml</value> |
|
</list> |
|
</property> |
|
<property name="hibernateProperties"> |
|
<value> |
|
hibernate.dialect=org.hibernate.dialect.OracleDialect |
|
</value> |
|
</property> |
|
</bean> |
|
|
|
<bean id="myTxManager" class="org.springframework.transaction.jta.JtaTransactionManager"/> |
|
|
|
<bean id="myProductDao" class="product.ProductDaoImpl"> |
|
<property name="sessionFactory" ref="mySessionFactory1"/> |
|
</bean> |
|
|
|
<bean id="myInventoryDao" class="product.InventoryDaoImpl"> |
|
<property name="sessionFactory" ref="mySessionFactory2"/> |
|
</bean> |
|
|
|
<bean id="myProductService" class="product.ProductServiceImpl"> |
|
<property name="productDao" ref="myProductDao"/> |
|
<property name="inventoryDao" ref="myInventoryDao"/> |
|
</bean> |
|
|
|
<aop:config> |
|
<aop:pointcut id="productServiceMethods" |
|
expression="execution(* product.ProductService.*(..))"/> |
|
<aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/> |
|
</aop:config> |
|
|
|
<tx:advice id="txAdvice" transaction-manager="myTxManager"> |
|
<tx:attributes> |
|
<tx:method name="increasePrice*" propagation="REQUIRED"/> |
|
<tx:method name="someOtherBusinessMethod" propagation="REQUIRES_NEW"/> |
|
<tx:method name="*" propagation="SUPPORTS" read-only="true"/> |
|
</tx:attributes> |
|
</tx:advice> |
|
|
|
</beans> |
|
---- |
|
|
|
Both `HibernateTransactionManager` and `JtaTransactionManager` allow for proper |
|
JVM-level cache handling with Hibernate, without container-specific transaction manager |
|
lookup or a JCA connector (if you are not using EJB to initiate transactions). |
|
|
|
`HibernateTransactionManager` can export the Hibernate JDBC `Connection` to plain JDBC |
|
access code, for a specific `DataSource`. This capability allows for high-level |
|
transaction demarcation with mixed Hibernate and JDBC data access completely without |
|
JTA, if you are accessing only one database. `HibernateTransactionManager` automatically |
|
exposes the Hibernate transaction as a JDBC transaction if you have set up the passed-in |
|
`SessionFactory` with a `DataSource` through the `dataSource` property of the |
|
`LocalSessionFactoryBean` class. Alternatively, you can specify explicitly the |
|
`DataSource` for which the transactions are supposed to be exposed through the |
|
`dataSource` property of the `HibernateTransactionManager` class. |
|
|
|
|
|
|
|
[[orm-hibernate-resources]] |
|
==== Comparing container-managed and locally defined resources |
|
You can switch between a container-managed JNDI `SessionFactory` and a locally defined |
|
one, without having to change a single line of application code. Whether to keep |
|
resource definitions in the container or locally within the application is mainly a |
|
matter of the transaction strategy that you use. Compared to a Spring-defined local |
|
`SessionFactory`, a manually registered JNDI `SessionFactory` does not provide any |
|
benefits. Deploying a `SessionFactory` through Hibernate's JCA connector provides the |
|
added value of participating in the Java EE server's management infrastructure, but does |
|
not add actual value beyond that. |
|
|
|
Spring's transaction support is not bound to a container. Configured with any strategy |
|
other than JTA, transaction support also works in a stand-alone or test environment. |
|
Especially in the typical case of single-database transactions, Spring's single-resource |
|
local transaction support is a lightweight and powerful alternative to JTA. When you use |
|
local EJB stateless session beans to drive transactions, you depend both on an EJB |
|
container and JTA, even if you access only a single database, and only use stateless |
|
session beans to provide declarative transactions through container-managed |
|
transactions. Also, direct use of JTA programmatically requires a Java EE environment as |
|
well. JTA does not involve only container dependencies in terms of JTA itself and of |
|
JNDI `DataSource` instances. For non-Spring, JTA-driven Hibernate transactions, you have |
|
to use the Hibernate JCA connector, or extra Hibernate transaction code with the |
|
`TransactionManagerLookup` configured for proper JVM-level caching. |
|
|
|
Spring-driven transactions can work as well with a locally defined Hibernate |
|
`SessionFactory` as they do with a local JDBC `DataSource` if they are accessing a |
|
single database. Thus you only have to use Spring's JTA transaction strategy when you |
|
have distributed transaction requirements. A JCA connector requires container-specific |
|
deployment steps, and obviously JCA support in the first place. This configuration |
|
requires more work than deploying a simple web application with local resource |
|
definitions and Spring-driven transactions. Also, you often need the Enterprise Edition |
|
of your container if you are using, for example, WebLogic Express, which does not |
|
provide JCA. A Spring application with local resources and transactions spanning one |
|
single database works in any Java EE web container (without JTA, JCA, or EJB) such as |
|
Tomcat, Resin, or even plain Jetty. Additionally, you can easily reuse such a middle |
|
tier in desktop applications or test suites. |
|
|
|
All things considered, if you do not use EJBs, stick with local `SessionFactory` setup |
|
and Spring's `HibernateTransactionManager` or `JtaTransactionManager`. You get all of |
|
the benefits, including proper transactional JVM-level caching and distributed |
|
transactions, without the inconvenience of container deployment. JNDI registration of a |
|
Hibernate `SessionFactory` through the JCA connector only adds value when used in |
|
conjunction with EJBs. |
|
|
|
|
|
|
|
[[orm-hibernate-invalid-jdbc-access-error]] |
|
==== Spurious application server warnings with Hibernate |
|
In some JTA environments with very strict `XADataSource` implementations -- currently |
|
only some WebLogic Server and WebSphere versions -- when Hibernate is configured without |
|
regard to the JTA `PlatformTransactionManager` object for that environment, it is |
|
possible for spurious warning or exceptions to show up in the application server log. |
|
These warnings or exceptions indicate that the connection being accessed is no longer |
|
valid, or JDBC access is no longer valid, possibly because the transaction is no longer |
|
active. As an example, here is an actual exception from WebLogic: |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
java.sql.SQLException: The transaction is no longer active - status: 'Committed'. No |
|
further JDBC access is allowed within this transaction. |
|
---- |
|
|
|
You resolve this warning by simply making Hibernate aware of the JTA |
|
`PlatformTransactionManager` instance, to which it will synchronize (along with Spring). |
|
You have two options for doing this: |
|
|
|
* If in your application context you are already directly obtaining the JTA |
|
`PlatformTransactionManager` object (presumably from JNDI through |
|
`JndiObjectFactoryBean` or `<jee:jndi-lookup>`) and feeding it, for example, to |
|
Spring's `JtaTransactionManager`, then the easiest way is to specify a reference to |
|
the bean defining this JTA `PlatformTransactionManager` instance as the value of the |
|
`jtaTransactionManager` property for `LocalSessionFactoryBean.` Spring then makes the |
|
object available to Hibernate. |
|
* More likely you do not already have the JTA `PlatformTransactionManager` instance, |
|
because Spring's `JtaTransactionManager` can find it itself. Thus you need to |
|
configure Hibernate to look up JTA `PlatformTransactionManager` directly. You do this |
|
by configuring an application server- specific `TransactionManagerLookup` class in the |
|
Hibernate configuration, as described in the Hibernate manual. |
|
|
|
The remainder of this section describes the sequence of events that occur with and |
|
without Hibernate's awareness of the JTA `PlatformTransactionManager`. |
|
|
|
When Hibernate is not configured with any awareness of the JTA |
|
`PlatformTransactionManager`, the following events occur when a JTA transaction commits: |
|
|
|
* The JTA transaction commits. |
|
* Spring's `JtaTransactionManager` is synchronized to the JTA transaction, so it is |
|
called back through an __afterCompletion__ callback by the JTA transaction manager. |
|
* Among other activities, this synchronization can trigger a callback by Spring to |
|
Hibernate, through Hibernate's `afterTransactionCompletion` callback (used to clear |
|
the Hibernate cache), followed by an explicit `close()` call on the Hibernate Session, |
|
which causes Hibernate to attempt to `close()` the JDBC Connection. |
|
* In some environments, this `Connection.close()` call then triggers the warning or |
|
error, as the application server no longer considers the `Connection` usable at all, |
|
because the transaction has already been committed. |
|
|
|
When Hibernate is configured with awareness of the JTA `PlatformTransactionManager`, the |
|
following events occur when a JTA transaction commits: |
|
|
|
* the JTA transaction is ready to commit. |
|
* Spring's `JtaTransactionManager` is synchronized to the JTA transaction, so the |
|
transaction is called back through a __beforeCompletion__ callback by the JTA |
|
transaction manager. |
|
* Spring is aware that Hibernate itself is synchronized to the JTA transaction, and |
|
behaves differently than in the previous scenario. Assuming the Hibernate `Session` |
|
needs to be closed at all, Spring will close it now. |
|
* The JTA transaction commits. |
|
* Hibernate is synchronized to the JTA transaction, so the transaction is called back |
|
through an __afterCompletion__ callback by the JTA transaction manager, and can |
|
properly clear its cache. |
|
|
|
|
|
|
|
|
|
[[orm-jdo]] |
|
=== JDO |
|
Spring supports the standard JDO 2.0 and 2.1 APIs as data access strategy, following the |
|
same style as the Hibernate support. The corresponding integration classes reside in the |
|
`org.springframework.orm.jdo` package. |
|
|
|
|
|
|
|
[[orm-jdo-setup]] |
|
==== PersistenceManagerFactory setup |
|
|
|
Spring provides a `LocalPersistenceManagerFactoryBean` class that allows you to define a |
|
local JDO `PersistenceManagerFactory` within a Spring application context: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<bean id="myPmf" class="org.springframework.orm.jdo.LocalPersistenceManagerFactoryBean"> |
|
<property name="configLocation" value="classpath:kodo.properties"/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
Alternatively, you can set up a `PersistenceManagerFactory` through direct instantiation |
|
of a `PersistenceManagerFactory` implementation class. A JDO `PersistenceManagerFactory` |
|
implementation class follows the JavaBeans pattern, just like a JDBC `DataSource` |
|
implementation class, which is a natural fit for a configuration that uses Spring. This |
|
setup style usually supports a Spring-defined JDBC `DataSource`, passed into the |
|
`connectionFactory` property. For example, for the open source JDO implementation |
|
DataNucleus (formerly JPOX) ( http://www.datanucleus.org/[http://www.datanucleus.org/]), |
|
this is the XML configuration of the `PersistenceManagerFactory` implementation: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> |
|
<property name="driverClassName" value="${jdbc.driverClassName}"/> |
|
<property name="url" value="${jdbc.url}"/> |
|
<property name="username" value="${jdbc.username}"/> |
|
<property name="password" value="${jdbc.password}"/> |
|
</bean> |
|
|
|
<bean id="myPmf" class="org.datanucleus.jdo.JDOPersistenceManagerFactory" destroy-method="close"> |
|
<property name="connectionFactory" ref="dataSource"/> |
|
<property name="nontransactionalRead" value="true"/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
You can also set up JDO `PersistenceManagerFactory` in the JNDI environment of a Java EE |
|
application server, usually through the JCA connector provided by the particular JDO |
|
implementation. Spring's standard `JndiObjectFactoryBean` or `<jee:jndi-lookup>` can be |
|
used to retrieve and expose such a `PersistenceManagerFactory`. However, outside an EJB |
|
context, no real benefit exists in holding the `PersistenceManagerFactory` in JNDI: only |
|
choose such a setup for a good reason. See <<orm-hibernate-resources>> for a discussion; |
|
the arguments there apply to JDO as well. |
|
|
|
|
|
|
|
[[orm-jdo-daos-straight]] |
|
==== Implementing DAOs based on the plain JDO API |
|
DAOs can also be written directly against plain JDO API, without any Spring |
|
dependencies, by using an injected `PersistenceManagerFactory`. The following is an |
|
example of a corresponding DAO implementation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class ProductDaoImpl implements ProductDao { |
|
|
|
private PersistenceManagerFactory persistenceManagerFactory; |
|
|
|
public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) { |
|
this.persistenceManagerFactory = pmf; |
|
} |
|
|
|
public Collection loadProductsByCategory(String category) { |
|
PersistenceManager pm = this.persistenceManagerFactory.getPersistenceManager(); |
|
try { |
|
Query query = pm.newQuery(Product.class, "category = pCategory"); |
|
query.declareParameters("String pCategory"); |
|
return query.execute(category); |
|
} |
|
finally { |
|
pm.close(); |
|
} |
|
} |
|
} |
|
---- |
|
|
|
Because the above DAO follows the dependency injection pattern, it fits nicely into a |
|
Spring container, just as it would if coded against Spring's `JdoTemplate`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<bean id="myProductDao" class="product.ProductDaoImpl"> |
|
<property name="persistenceManagerFactory" ref="myPmf"/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
The main problem with such DAOs is that they always get a new `PersistenceManager` from |
|
the factory. To access a Spring-managed transactional `PersistenceManager`, define a |
|
`TransactionAwarePersistenceManagerFactoryProxy` (as included in Spring) in front of |
|
your target `PersistenceManagerFactory`, then passing a reference to that proxy into |
|
your DAOs as in the following example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<bean id="myPmfProxy" |
|
class="org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy"> |
|
<property name="targetPersistenceManagerFactory" ref="myPmf"/> |
|
</bean> |
|
|
|
<bean id="myProductDao" class="product.ProductDaoImpl"> |
|
<property name="persistenceManagerFactory" ref="myPmfProxy"/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
Your data access code will receive a transactional `PersistenceManager` (if any) from |
|
the `PersistenceManagerFactory.getPersistenceManager()` method that it calls. The latter |
|
method call goes through the proxy, which first checks for a current transactional |
|
`PersistenceManager` before getting a new one from the factory. Any `close()` calls on |
|
the `PersistenceManager` are ignored in case of a transactional `PersistenceManager`. |
|
|
|
If your data access code always runs within an active transaction (or at least within |
|
active transaction synchronization), it is safe to omit the `PersistenceManager.close()` |
|
call and thus the entire `finally` block, which you might do to keep your DAO |
|
implementations concise: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class ProductDaoImpl implements ProductDao { |
|
|
|
private PersistenceManagerFactory persistenceManagerFactory; |
|
|
|
public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) { |
|
this.persistenceManagerFactory = pmf; |
|
} |
|
|
|
public Collection loadProductsByCategory(String category) { |
|
PersistenceManager pm = this.persistenceManagerFactory.getPersistenceManager(); |
|
Query query = pm.newQuery(Product.class, "category = pCategory"); |
|
query.declareParameters("String pCategory"); |
|
return query.execute(category); |
|
} |
|
} |
|
---- |
|
|
|
With such DAOs that rely on active transactions, it is recommended that you enforce |
|
active transactions through turning off |
|
`TransactionAwarePersistenceManagerFactoryProxy`'s `allowCreate` flag: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<bean id="myPmfProxy" |
|
class="org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy"> |
|
<property name="targetPersistenceManagerFactory" ref="myPmf"/> |
|
<property name="allowCreate" value="false"/> |
|
</bean> |
|
|
|
<bean id="myProductDao" class="product.ProductDaoImpl"> |
|
<property name="persistenceManagerFactory" ref="myPmfProxy"/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
The main advantage of this DAO style is that it depends on JDO API only; no import of |
|
any Spring class is required. This is of course appealing from a non-invasiveness |
|
perspective, and might feel more natural to JDO developers. |
|
|
|
However, the DAO throws plain `JDOException` (which is unchecked, so does not have to be |
|
declared or caught), which means that callers can only treat exceptions as fatal, unless |
|
you want to depend on JDO's own exception structure. Catching specific causes such as an |
|
optimistic locking failure is not possible without tying the caller to the |
|
implementation strategy. This trade off might be acceptable to applications that are |
|
strongly JDO-based and/or do not need any special exception treatment. |
|
|
|
In summary, you can DAOs based on the plain JDO API, and they can still participate in |
|
Spring-managed transactions. This strategy might appeal to you if you are already |
|
familiar with JDO. However, such DAOs throw plain `JDOException`, and you would have to |
|
convert explicitly to Spring's `DataAccessException` (if desired). |
|
|
|
|
|
|
|
[[orm-jdo-tx]] |
|
==== Transaction management |
|
[NOTE] |
|
==== |
|
You are __strongly__ encouraged to read <<transaction-declarative>> if you have not done |
|
so, to get a more detailed coverage of Spring's declarative transaction support. |
|
==== |
|
|
|
To execute service operations within transactions, you can use Spring's common |
|
declarative transaction facilities. For example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:aop="http://www.springframework.org/schema/aop" |
|
xmlns:tx="http://www.springframework.org/schema/tx" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/tx |
|
http://www.springframework.org/schema/tx/spring-tx.xsd |
|
http://www.springframework.org/schema/aop |
|
http://www.springframework.org/schema/aop/spring-aop.xsd"> |
|
|
|
<bean id="myTxManager" class="org.springframework.orm.jdo.JdoTransactionManager"> |
|
<property name="persistenceManagerFactory" ref="myPmf"/> |
|
</bean> |
|
|
|
<bean id="myProductService" class="product.ProductServiceImpl"> |
|
<property name="productDao" ref="myProductDao"/> |
|
</bean> |
|
|
|
<tx:advice id="txAdvice" transaction-manager="txManager"> |
|
<tx:attributes> |
|
<tx:method name="increasePrice*" propagation="REQUIRED"/> |
|
<tx:method name="someOtherBusinessMethod" propagation="REQUIRES_NEW"/> |
|
<tx:method name="*" propagation="SUPPORTS" read-only="true"/> |
|
</tx:attributes> |
|
</tx:advice> |
|
|
|
<aop:config> |
|
<aop:pointcut id="productServiceMethods" |
|
expression="execution(* product.ProductService.*(..))"/> |
|
<aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/> |
|
</aop:config> |
|
|
|
</beans> |
|
---- |
|
|
|
JDO requires an active transaction to modify a persistent object. The non-transactional |
|
flush concept does not exist in JDO, in contrast to Hibernate. For this reason, you need |
|
to set up the chosen JDO implementation for a specific environment. Specifically, you |
|
need to set it up explicitly for JTA synchronization, to detect an active JTA |
|
transaction itself. This is not necessary for local transactions as performed by |
|
Spring's `JdoTransactionManager`, but it is necessary to participate in JTA |
|
transactions, whether driven by Spring's `JtaTransactionManager` or by EJB CMT and plain |
|
JTA. |
|
|
|
`JdoTransactionManager` is capable of exposing a JDO transaction to JDBC access code |
|
that accesses the same JDBC `DataSource`, provided that the registered `JdoDialect` |
|
supports retrieval of the underlying JDBC `Connection`. This is the case for JDBC-based |
|
JDO 2.0 implementations by default. |
|
|
|
|
|
|
|
[[orm-jdo-dialect]] |
|
==== JdoDialect |
|
|
|
As an advanced feature, both `JdoTemplate` and `JdoTransactionManager` support a custom |
|
`JdoDialect` that can be passed into the `jdoDialect` bean property. In this scenario, |
|
the DAOs will not receive a `PersistenceManagerFactory` reference but rather a full |
|
`JdoTemplate` instance (for example, passed into the `jdoTemplate` property of |
|
`JdoDaoSupport`). Using a `JdoDialect` implementation, you can enable advanced features |
|
supported by Spring, usually in a vendor-specific manner: |
|
|
|
* Applying specific transaction semantics such as custom isolation level or transaction |
|
timeout |
|
* Retrieving the transactional JDBC `Connection` for exposure to JDBC-based DAOs |
|
* Applying query timeouts, which are automatically calculated from Spring-managed |
|
transaction timeouts |
|
* Eagerly flushing a `PersistenceManager,` to make transactional changes visible to |
|
JDBC-based data access code |
|
* Advanced translation of `JDOExceptions` to Spring `DataAccessExceptions` |
|
|
|
See the `JdoDialect` javadocs for more details on its operations and how to use them |
|
within Spring's JDO support. |
|
|
|
|
|
|
|
|
|
[[orm-jpa]] |
|
=== JPA |
|
The Spring JPA, available under the `org.springframework.orm.jpa` package, offers |
|
comprehensive support for the |
|
http://www.oracle.com/technetwork/articles/javaee/jpa-137156.html[Java Persistence |
|
API] in a similar manner to the integration with Hibernate or JDO, while being aware of |
|
the underlying implementation in order to provide additional features. |
|
|
|
|
|
|
|
[[orm-jpa-setup]] |
|
==== Three options for JPA setup in a Spring environment |
|
The Spring JPA support offers three ways of setting up the JPA `EntityManagerFactory` |
|
that will be used by the application to obtain an entity manager. |
|
|
|
|
|
[[orm-jpa-setup-lemfb]] |
|
===== LocalEntityManagerFactoryBean |
|
|
|
[NOTE] |
|
==== |
|
Only use this option in simple deployment environments such as stand-alone applications |
|
and integration tests. |
|
==== |
|
|
|
The `LocalEntityManagerFactoryBean` creates an `EntityManagerFactory` suitable for |
|
simple deployment environments where the application uses only JPA for data access. The |
|
factory bean uses the JPA `PersistenceProvider` autodetection mechanism (according to |
|
JPA's Java SE bootstrapping) and, in most cases, requires you to specify only the |
|
persistence unit name: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<bean id="myEmf" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean"> |
|
<property name="persistenceUnitName" value="myPersistenceUnit"/> |
|
</bean> |
|
</beans> |
|
---- |
|
|
|
This form of JPA deployment is the simplest and the most limited. You cannot refer to an |
|
existing JDBC `DataSource` bean definition and no support for global transactions |
|
exists. Furthermore, weaving (byte-code transformation) of persistent classes is |
|
provider-specific, often requiring a specific JVM agent to specified on startup. This |
|
option is sufficient only for stand-alone applications and test environments, for which |
|
the JPA specification is designed. |
|
|
|
|
|
[[orm-jpa-setup-jndi]] |
|
===== Obtaining an EntityManagerFactory from JNDI |
|
|
|
[NOTE] |
|
==== |
|
Use this option when deploying to a Java EE 5 server. Check your server's documentation |
|
on how to deploy a custom JPA provider into your server, allowing for a different |
|
provider than the server's default. |
|
==== |
|
|
|
Obtaining an `EntityManagerFactory` from JNDI (for example in a Java EE 5 environment), |
|
is simply a matter of changing the XML configuration: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<jee:jndi-lookup id="myEmf" jndi-name="persistence/myPersistenceUnit"/> |
|
</beans> |
|
---- |
|
|
|
This action assumes standard Java EE 5 bootstrapping: the Java EE server autodetects |
|
persistence units (in effect, `META-INF/persistence.xml` files in application jars) and |
|
`persistence-unit-ref` entries in the Java EE deployment descriptor (for example, |
|
`web.xml`) and defines environment naming context locations for those persistence units. |
|
|
|
In such a scenario, the entire persistence unit deployment, including the weaving |
|
(byte-code transformation) of persistent classes, is up to the Java EE server. The JDBC |
|
`DataSource` is defined through a JNDI location in the `META-INF/persistence.xml` file; |
|
EntityManager transactions are integrated with the server's JTA subsystem. Spring merely |
|
uses the obtained `EntityManagerFactory`, passing it on to application objects through |
|
dependency injection, and managing transactions for the persistence unit, typically |
|
through `JtaTransactionManager`. |
|
|
|
If multiple persistence units are used in the same application, the bean names of such |
|
JNDI-retrieved persistence units should match the persistence unit names that the |
|
application uses to refer to them, for example, in `@PersistenceUnit` and |
|
`@PersistenceContext` annotations. |
|
|
|
|
|
[[orm-jpa-setup-lcemfb]] |
|
===== LocalContainerEntityManagerFactoryBean |
|
|
|
[NOTE] |
|
==== |
|
Use this option for full JPA capabilities in a Spring-based application environment. |
|
This includes web containers such as Tomcat as well as stand-alone applications and |
|
integration tests with sophisticated persistence requirements. |
|
==== |
|
|
|
The `LocalContainerEntityManagerFactoryBean` gives full control over |
|
`EntityManagerFactory` configuration and is appropriate for environments where |
|
fine-grained customization is required. The `LocalContainerEntityManagerFactoryBean` |
|
creates a `PersistenceUnitInfo` instance based on the `persistence.xml` file, the |
|
supplied `dataSourceLookup` strategy, and the specified `loadTimeWeaver`. It is thus |
|
possible to work with custom data sources outside of JNDI and to control the weaving |
|
process. The following example shows a typical bean definition for a |
|
`LocalContainerEntityManagerFactoryBean`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<bean id="myEmf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> |
|
<property name="dataSource" ref="someDataSource"/> |
|
<property name="loadTimeWeaver"> |
|
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/> |
|
</property> |
|
</bean> |
|
</beans> |
|
---- |
|
|
|
The following example shows a typical `persistence.xml` file: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0"> |
|
<persistence-unit name="myUnit" transaction-type="RESOURCE_LOCAL"> |
|
<mapping-file>META-INF/orm.xml</mapping-file> |
|
<exclude-unlisted-classes/> |
|
</persistence-unit> |
|
</persistence> |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
The `<exclude-unlisted-classes/>` shortcut indicates that __no__ scanning for |
|
annotated entity classes is supposed to occur. An explicit 'true' value specified - |
|
`<exclude-unlisted-classes>true</exclude-unlisted-classes/>` - also means no scan. |
|
`<exclude-unlisted-classes>false</exclude-unlisted-classes/>` does trigger a scan; |
|
however, it is recommended to simply omit the `exclude-unlisted-classes` element |
|
if you want entity class scanning to occur. |
|
==== |
|
|
|
Using the `LocalContainerEntityManagerFactoryBean` is the most powerful JPA setup |
|
option, allowing for flexible local configuration within the application. It supports |
|
links to an existing JDBC `DataSource`, supports both local and global transactions, and |
|
so on. However, it also imposes requirements on the runtime environment, such as the |
|
availability of a weaving-capable class loader if the persistence provider demands |
|
byte-code transformation. |
|
|
|
This option may conflict with the built-in JPA capabilities of a Java EE 5 server. In a |
|
full Java EE 5 environment, consider obtaining your `EntityManagerFactory` from JNDI. |
|
Alternatively, specify a custom `persistenceXmlLocation` on your |
|
`LocalContainerEntityManagerFactoryBean` definition, for example, |
|
META-INF/my-persistence.xml, and only include a descriptor with that name in your |
|
application jar files. Because the Java EE 5 server only looks for default |
|
`META-INF/persistence.xml` files, it ignores such custom persistence units and hence |
|
avoid conflicts with a Spring-driven JPA setup upfront. (This applies to Resin 3.1, for |
|
example.) |
|
|
|
.When is load-time weaving required? |
|
**** |
|
Not all JPA providers require a JVM agent ; Hibernate is an example of one that does |
|
not. If your provider does not require an agent or you have other alternatives, such as |
|
applying enhancements at build time through a custom compiler or an ant task, the |
|
load-time weaver __should not__ be used. |
|
**** |
|
|
|
The `LoadTimeWeaver` interface is a Spring-provided class that allows JPA |
|
`ClassTransformer` instances to be plugged in a specific manner, depending whether the |
|
environment is a web container or application server. Hooking `ClassTransformers` |
|
through an |
|
http://docs.oracle.com/javase/6/docs/api/java/lang/instrument/package-summary.html[agent] |
|
typically is not efficient. The agents work against the __entire virtual machine__ and |
|
inspect __every__ class that is loaded, which is usually undesirable in a production |
|
server environment. |
|
|
|
Spring provides a number of `LoadTimeWeaver` implementations for various environments, |
|
allowing `ClassTransformer` instances to be applied only __per class loader__ and not |
|
per VM. |
|
|
|
Refer to <<aop-aj-ltw-spring>> in the AOP chapter for more insight regarding the |
|
`LoadTimeWeaver` implementations and their setup, either generic or customized to |
|
various platforms (such as Tomcat, WebLogic, GlassFish, Resin and JBoss). |
|
|
|
As described in the aforementioned section, you can configure a context-wide |
|
`LoadTimeWeaver` using the `@EnableLoadTimeWeaving` annotation of |
|
`context:load-time-weaver` XML element. Such a global weaver is picked up by all JPA |
|
`LocalContainerEntityManagerFactoryBeans` automatically. This is the preferred way of |
|
setting up a load-time weaver, delivering autodetection of the platform (WebLogic, |
|
GlassFish, Tomcat, Resin, JBoss or VM agent) and automatic propagation of the weaver to |
|
all weaver-aware beans: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<context:load-time-weaver/> |
|
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> |
|
... |
|
</bean> |
|
---- |
|
|
|
However, if needed, one can manually specify a dedicated weaver through the |
|
`loadTimeWeaver` property: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> |
|
<property name="loadTimeWeaver"> |
|
<bean class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
No matter how the LTW is configured, using this technique, JPA applications relying on |
|
instrumentation can run in the target platform (ex: Tomcat) without needing an agent. |
|
This is important especially when the hosting applications rely on different JPA |
|
implementations because the JPA transformers are applied only at class loader level and |
|
thus are isolated from each other. |
|
|
|
|
|
[[orm-jpa-multiple-pu]] |
|
===== Dealing with multiple persistence units |
|
For applications that rely on multiple persistence units locations, stored in various |
|
JARS in the classpath, for example, Spring offers the `PersistenceUnitManager` to act as |
|
a central repository and to avoid the persistence units discovery process, which can be |
|
expensive. The default implementation allows multiple locations to be specified that are |
|
parsed and later retrieved through the persistence unit name. (By default, the classpath |
|
is searched for `META-INF/persistence.xml` files.) |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<bean id="pum" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager"> |
|
<property name="persistenceXmlLocations"> |
|
<list> |
|
<value>org/springframework/orm/jpa/domain/persistence-multi.xml</value> |
|
<value>classpath:/my/package/**/custom-persistence.xml</value> |
|
<value>classpath*:META-INF/persistence.xml</value> |
|
</list> |
|
</property> |
|
<property name="dataSources"> |
|
<map> |
|
<entry key="localDataSource" value-ref="local-db"/> |
|
<entry key="remoteDataSource" value-ref="remote-db"/> |
|
</map> |
|
</property> |
|
<!-- if no datasource is specified, use this one --> |
|
<property name="defaultDataSource" ref="remoteDataSource"/> |
|
</bean> |
|
|
|
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> |
|
<property name="persistenceUnitManager" ref="pum"/> |
|
<property name="persistenceUnitName" value="myCustomUnit"/> |
|
</bean> |
|
---- |
|
|
|
The default implementation allows customization of the `PersistenceUnitInfo` instances, |
|
before they are fed to the JPA provider, declaratively through its properties, which |
|
affect __all__ hosted units, or programmatically, through the |
|
`PersistenceUnitPostProcessor`, which allows persistence unit selection. If no |
|
`PersistenceUnitManager` is specified, one is created and used internally by |
|
`LocalContainerEntityManagerFactoryBean`. |
|
|
|
|
|
|
|
[[orm-jpa-straight]] |
|
==== Implementing DAOs based on plain JPA |
|
[NOTE] |
|
==== |
|
Although `EntityManagerFactory` instances are thread-safe, `EntityManager` instances are |
|
not. The injected JPA `EntityManager` behaves like an `EntityManager` fetched from an |
|
application server's JNDI environment, as defined by the JPA specification. It delegates |
|
all calls to the current transactional `EntityManager`, if any; otherwise, it falls back |
|
to a newly created `EntityManager` per operation, in effect making its usage thread-safe. |
|
==== |
|
|
|
It is possible to write code against the plain JPA without any Spring dependencies, by |
|
using an injected `EntityManagerFactory` or `EntityManager`. Spring can understand |
|
`@PersistenceUnit` and `@PersistenceContext` annotations both at field and method level |
|
if a `PersistenceAnnotationBeanPostProcessor` is enabled. A plain JPA DAO implementation |
|
using the `@PersistenceUnit` annotation might look like this: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class ProductDaoImpl implements ProductDao { |
|
|
|
private EntityManagerFactory emf; |
|
|
|
@PersistenceUnit |
|
public void setEntityManagerFactory(EntityManagerFactory emf) { |
|
this.emf = emf; |
|
} |
|
|
|
public Collection loadProductsByCategory(String category) { |
|
EntityManager em = this.emf.createEntityManager(); |
|
try { |
|
Query query = em.createQuery("from Product as p where p.category = ?1"); |
|
query.setParameter(1, category); |
|
return query.getResultList(); |
|
} |
|
finally { |
|
if (em != null) { |
|
em.close(); |
|
} |
|
} |
|
} |
|
} |
|
---- |
|
|
|
The DAO above has no dependency on Spring and still fits nicely into a Spring |
|
application context. Moreover, the DAO takes advantage of annotations to require the |
|
injection of the default `EntityManagerFactory`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<!-- bean post-processor for JPA annotations --> |
|
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/> |
|
|
|
<bean id="myProductDao" class="product.ProductDaoImpl"/> |
|
|
|
</beans> |
|
---- |
|
|
|
As an alternative to defining a `PersistenceAnnotationBeanPostProcessor` explicitly, |
|
consider using the Spring `context:annotation-config` XML element in your application |
|
context configuration. Doing so automatically registers all Spring standard |
|
post-processors for annotation-based configuration, including |
|
`CommonAnnotationBeanPostProcessor` and so on. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<!-- post-processors for all standard config annotations --> |
|
<context:annotation-config/> |
|
|
|
<bean id="myProductDao" class="product.ProductDaoImpl"/> |
|
|
|
</beans> |
|
---- |
|
|
|
The main problem with such a DAO is that it always creates a new `EntityManager` through |
|
the factory. You can avoid this by requesting a transactional `EntityManager` (also |
|
called "shared EntityManager" because it is a shared, thread-safe proxy for the actual |
|
transactional EntityManager) to be injected instead of the factory: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class ProductDaoImpl implements ProductDao { |
|
|
|
@PersistenceContext |
|
private EntityManager em; |
|
|
|
public Collection loadProductsByCategory(String category) { |
|
Query query = em.createQuery("from Product as p where p.category = :category"); |
|
query.setParameter("category", category); |
|
return query.getResultList(); |
|
} |
|
} |
|
---- |
|
|
|
The `@PersistenceContext` annotation has an optional attribute `type`, which defaults to |
|
`PersistenceContextType.TRANSACTION`. This default is what you need to receive a shared |
|
EntityManager proxy. The alternative, `PersistenceContextType.EXTENDED`, is a completely |
|
different affair: This results in a so-called extended EntityManager, which is __not |
|
thread-safe__ and hence must not be used in a concurrently accessed component such as a |
|
Spring-managed singleton bean. Extended EntityManagers are only supposed to be used in |
|
stateful components that, for example, reside in a session, with the lifecycle of the |
|
EntityManager not tied to a current transaction but rather being completely up to the |
|
application. |
|
|
|
.Method- and field-level Injection |
|
**** |
|
Annotations that indicate dependency injections (such as `@PersistenceUnit` and |
|
`@PersistenceContext`) can be applied on field or methods inside a class, hence the |
|
expressions __method-level injection__ and __field-level injection__. Field-level |
|
annotations are concise and easier to use while method-level allows for further |
|
processing of the injected dependency. In both cases the member visibility (public, |
|
protected, private) does not matter. |
|
|
|
What about class-level annotations? |
|
|
|
On the Java EE 5 platform, they are used for dependency declaration and not for resource |
|
injection. |
|
**** |
|
|
|
The injected `EntityManager` is Spring-managed (aware of the ongoing transaction). It is |
|
important to note that even though the new DAO implementation uses method level |
|
injection of an `EntityManager` instead of an `EntityManagerFactory`, no change is |
|
required in the application context XML due to annotation usage. |
|
|
|
The main advantage of this DAO style is that it only depends on Java Persistence API; no |
|
import of any Spring class is required. Moreover, as the JPA annotations are understood, |
|
the injections are applied automatically by the Spring container. This is appealing from |
|
a non-invasiveness perspective, and might feel more natural to JPA developers. |
|
|
|
|
|
|
|
[[orm-jpa-tx]] |
|
==== Transaction Management |
|
[NOTE] |
|
==== |
|
You are __strongly__ encouraged to read <<transaction-declarative>> if you have not done |
|
so, to get a more detailed coverage of Spring's declarative transaction support. |
|
==== |
|
|
|
To execute service operations within transactions, you can use Spring's common |
|
declarative transaction facilities. For example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:aop="http://www.springframework.org/schema/aop" |
|
xmlns:tx="http://www.springframework.org/schema/tx" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/tx |
|
http://www.springframework.org/schema/tx/spring-tx.xsd |
|
http://www.springframework.org/schema/aop |
|
http://www.springframework.org/schema/aop/spring-aop.xsd"> |
|
|
|
<bean id="myTxManager" class="org.springframework.orm.jpa.JpaTransactionManager"> |
|
<property name="entityManagerFactory" ref="myEmf"/> |
|
</bean> |
|
|
|
<bean id="myProductService" class="product.ProductServiceImpl"> |
|
<property name="productDao" ref="myProductDao"/> |
|
</bean> |
|
|
|
<aop:config> |
|
<aop:pointcut id="productServiceMethods" expression="execution(* product.ProductService.*(..))"/> |
|
<aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/> |
|
</aop:config> |
|
|
|
<tx:advice id="txAdvice" transaction-manager="myTxManager"> |
|
<tx:attributes> |
|
<tx:method name="increasePrice*" propagation="REQUIRED"/> |
|
<tx:method name="someOtherBusinessMethod" propagation="REQUIRES_NEW"/> |
|
<tx:method name="*" propagation="SUPPORTS" read-only="true"/> |
|
</tx:attributes> |
|
</tx:advice> |
|
|
|
</beans> |
|
---- |
|
|
|
Spring JPA allows a configured `JpaTransactionManager` to expose a JPA transaction to |
|
JDBC access code that accesses the same JDBC `DataSource`, provided that the registered |
|
`JpaDialect` supports retrieval of the underlying JDBC `Connection`. Out of the box, |
|
Spring provides dialects for the Toplink, Hibernate and OpenJPA JPA implementations. See |
|
the next section for details on the `JpaDialect` mechanism. |
|
|
|
|
|
|
|
[[orm-jpa-dialect]] |
|
==== JpaDialect |
|
|
|
As an advanced feature `JpaTemplate`, `JpaTransactionManager` and subclasses of |
|
`AbstractEntityManagerFactoryBean` support a custom `JpaDialect`, to be passed into the |
|
`jpaDialect` bean property. In such a scenario, the DAOs do not receive an |
|
`EntityManagerFactory` reference but rather a full `JpaTemplate` instance (for example, |
|
passed into the `jpaTemplate` property of `JpaDaoSupport`). A `JpaDialect` |
|
implementation can enable some advanced features supported by Spring, usually in a |
|
vendor-specific manner: |
|
|
|
* Applying specific transaction semantics such as custom isolation level or transaction |
|
timeout) |
|
* Retrieving the transactional JDBC `Connection` for exposure to JDBC-based DAOs) |
|
* Advanced translation of `PersistenceExceptions` to Spring `DataAccessExceptions` |
|
|
|
This is particularly valuable for special transaction semantics and for advanced |
|
translation of exception. The default implementation used ( `DefaultJpaDialect`) does |
|
not provide any special capabilities and if the above features are required, you have to |
|
specify the appropriate dialect. |
|
|
|
See the `JpaDialect` javadocs for more details of its operations and how they are used |
|
within Spring's JPA support. |
|
|
|
|
|
|
|
|
|
|
|
[[oxm]] |
|
== Marshalling XML using O/X Mappers |
|
|
|
|
|
|
|
|
|
[[oxm-introduction]] |
|
=== Introduction |
|
In this chapter, we will describe Spring's Object/XML Mapping support. Object/XML |
|
Mapping, or O/X mapping for short, is the act of converting an XML document to and from |
|
an object. This conversion process is also known as XML Marshalling, or XML |
|
Serialization. This chapter uses these terms interchangeably. |
|
|
|
Within the field of O/X mapping, a __marshaller__ is responsible for serializing an |
|
object (graph) to XML. In similar fashion, an __unmarshaller__ deserializes the XML to |
|
an object graph. This XML can take the form of a DOM document, an input or output |
|
stream, or a SAX handler. |
|
|
|
Some of the benefits of using Spring for your O/X mapping needs are: |
|
|
|
|
|
|
|
==== Ease of configuration |
|
Spring's bean factory makes it easy to configure marshallers, without needing to |
|
construct JAXB context, JiBX binding factories, etc. The marshallers can be configured |
|
as any other bean in your application context. Additionally, XML Schema-based |
|
configuration is available for a number of marshallers, making the configuration even |
|
simpler. |
|
|
|
|
|
|
|
==== Consistent Interfaces |
|
Spring's O/X mapping operates through two global interfaces: the `Marshaller` and |
|
`Unmarshaller` interface. These abstractions allow you to switch O/X mapping frameworks |
|
with relative ease, with little or no changes required on the classes that do the |
|
marshalling. This approach has the additional benefit of making it possible to do XML |
|
marshalling with a mix-and-match approach (e.g. some marshalling performed using JAXB, |
|
other using XMLBeans) in a non-intrusive fashion, leveraging the strength of each |
|
technology. |
|
|
|
|
|
|
|
==== Consistent Exception Hierarchy |
|
Spring provides a conversion from exceptions from the underlying O/X mapping tool to its |
|
own exception hierarchy with the `XmlMappingException` as the root exception. As can be |
|
expected, these runtime exceptions wrap the original exception so no information is lost. |
|
|
|
|
|
|
|
|
|
[[oxm-marshaller-unmarshaller]] |
|
=== Marshaller and Unmarshaller |
|
As stated in the introduction, a __marshaller__ serializes an object to XML, and an |
|
__unmarshaller__ deserializes XML stream to an object. In this section, we will describe |
|
the two Spring interfaces used for this purpose. |
|
|
|
|
|
|
|
[[oxm-marshaller]] |
|
==== Marshaller |
|
Spring abstracts all marshalling operations behind the |
|
`org.springframework.oxm.Marshaller` interface, the main methods of which is listed |
|
below. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
public interface Marshaller { |
|
|
|
/** |
|
* Marshals the object graph with the given root into the provided Result. |
|
*/ |
|
void marshal(Object graph, Result result) throws XmlMappingException, IOException; |
|
|
|
} |
|
---- |
|
|
|
The `Marshaller` interface has one main method, which marshals the given object to a |
|
given `javax.xml.transform.Result`. Result is a tagging interface that basically |
|
represents an XML output abstraction: concrete implementations wrap various XML |
|
representations, as indicated in the table below. |
|
|
|
[[oxm-marshller-tbl]] |
|
|=== |
|
| Result implementation| Wraps XML representation |
|
|
|
| `DOMResult` |
|
| `org.w3c.dom.Node` |
|
|
|
| `SAXResult` |
|
| `org.xml.sax.ContentHandler` |
|
|
|
| `StreamResult` |
|
| `java.io.File`, `java.io.OutputStream`, or `java.io.Writer` |
|
|=== |
|
|
|
[NOTE] |
|
==== |
|
Although the `marshal()` method accepts a plain object as its first parameter, most |
|
`Marshaller` implementations cannot handle arbitrary objects. Instead, an object class |
|
must be mapped in a mapping file, marked with an annotation, registered with the |
|
marshaller, or have a common base class. Refer to the further sections in this chapter |
|
to determine how your O/X technology of choice manages this. |
|
==== |
|
|
|
|
|
|
|
[[oxm-unmarshaller]] |
|
==== Unmarshaller |
|
Similar to the `Marshaller`, there is the `org.springframework.oxm.Unmarshaller` |
|
interface. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
public interface Unmarshaller { |
|
|
|
/** |
|
* Unmarshals the given provided Source into an object graph. |
|
*/ |
|
Object unmarshal(Source source) throws XmlMappingException, IOException; |
|
} |
|
---- |
|
|
|
This interface also has one method, which reads from the given |
|
`javax.xml.transform.Source` (an XML input abstraction), and returns the object read. As |
|
with Result, Source is a tagging interface that has three concrete implementations. Each |
|
wraps a different XML representation, as indicated in the table below. |
|
|
|
[[oxm-unmarshller-tbl]] |
|
|=== |
|
| Source implementation| Wraps XML representation |
|
|
|
| `DOMSource` |
|
| `org.w3c.dom.Node` |
|
|
|
| `SAXSource` |
|
| `org.xml.sax.InputSource`, and `org.xml.sax.XMLReader` |
|
|
|
| `StreamSource` |
|
| `java.io.File`, `java.io.InputStream`, or `java.io.Reader` |
|
|=== |
|
|
|
Even though there are two separate marshalling interfaces ( `Marshaller` and |
|
`Unmarshaller`), all implementations found in Spring-WS implement both in one class. |
|
This means that you can wire up one marshaller class and refer to it both as a |
|
marshaller and an unmarshaller in your `applicationContext.xml`. |
|
|
|
|
|
|
|
[[oxm-xmlmappingexception]] |
|
==== XmlMappingException |
|
Spring converts exceptions from the underlying O/X mapping tool to its own exception |
|
hierarchy with the `XmlMappingException` as the root exception. As can be expected, |
|
these runtime exceptions wrap the original exception so no information will be lost. |
|
|
|
Additionally, the `MarshallingFailureException` and `UnmarshallingFailureException` |
|
provide a distinction between marshalling and unmarshalling operations, even though the |
|
underlying O/X mapping tool does not do so. |
|
|
|
The O/X Mapping exception hierarchy is shown in the following figure: |
|
image::images/oxm-exceptions.png[width=400] |
|
|
|
O/X Mapping exception hierarchy |
|
|
|
|
|
|
|
|
|
[[oxm-usage]] |
|
=== Using Marshaller and Unmarshaller |
|
Spring's OXM can be used for a wide variety of situations. In the following example, we |
|
will use it to marshal the settings of a Spring-managed application as an XML file. We |
|
will use a simple JavaBean to represent the settings: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class Settings { |
|
|
|
private boolean fooEnabled; |
|
|
|
public boolean isFooEnabled() { |
|
return fooEnabled; |
|
} |
|
|
|
public void setFooEnabled(boolean fooEnabled) { |
|
this.fooEnabled = fooEnabled; |
|
} |
|
} |
|
---- |
|
|
|
The application class uses this bean to store its settings. Besides a main method, the |
|
class has two methods: `saveSettings()` saves the settings bean to a file named |
|
`settings.xml`, and `loadSettings()` loads these settings again. A `main()` method |
|
constructs a Spring application context, and calls these two methods. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import java.io.FileInputStream; |
|
import java.io.FileOutputStream; |
|
import java.io.IOException; |
|
import javax.xml.transform.stream.StreamResult; |
|
import javax.xml.transform.stream.StreamSource; |
|
|
|
import org.springframework.context.ApplicationContext; |
|
import org.springframework.context.support.ClassPathXmlApplicationContext; |
|
import org.springframework.oxm.Marshaller; |
|
import org.springframework.oxm.Unmarshaller; |
|
|
|
public class Application { |
|
|
|
private static final String FILE_NAME = "settings.xml"; |
|
private Settings settings = new Settings(); |
|
private Marshaller marshaller; |
|
private Unmarshaller unmarshaller; |
|
|
|
public void setMarshaller(Marshaller marshaller) { |
|
this.marshaller = marshaller; |
|
} |
|
|
|
public void setUnmarshaller(Unmarshaller unmarshaller) { |
|
this.unmarshaller = unmarshaller; |
|
} |
|
|
|
public void saveSettings() throws IOException { |
|
FileOutputStream os = null; |
|
try { |
|
os = new FileOutputStream(FILE_NAME); |
|
this.marshaller.marshal(settings, new StreamResult(os)); |
|
} finally { |
|
if (os != null) { |
|
os.close(); |
|
} |
|
} |
|
} |
|
|
|
public void loadSettings() throws IOException { |
|
FileInputStream is = null; |
|
try { |
|
is = new FileInputStream(FILE_NAME); |
|
this.settings = (Settings) this.unmarshaller.unmarshal(new StreamSource(is)); |
|
} finally { |
|
if (is != null) { |
|
is.close(); |
|
} |
|
} |
|
} |
|
|
|
public static void main(String[] args) throws IOException { |
|
ApplicationContext appContext = |
|
new ClassPathXmlApplicationContext("applicationContext.xml"); |
|
Application application = (Application) appContext.getBean("application"); |
|
application.saveSettings(); |
|
application.loadSettings(); |
|
} |
|
} |
|
---- |
|
|
|
The `Application` requires both a `marshaller` and `unmarshaller` property to be set. We |
|
can do so using the following `applicationContext.xml`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<bean id="application" class="Application"> |
|
<property name="marshaller" ref="castorMarshaller" /> |
|
<property name="unmarshaller" ref="castorMarshaller" /> |
|
</bean> |
|
<bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller"/> |
|
</beans> |
|
---- |
|
|
|
This application context uses Castor, but we could have used any of the other marshaller |
|
instances described later in this chapter. Note that Castor does not require any further |
|
configuration by default, so the bean definition is rather simple. Also note that the |
|
`CastorMarshaller` implements both `Marshaller` and `Unmarshaller`, so we can refer to the |
|
`castorMarshaller` bean in both the `marshaller` and `unmarshaller` property of the |
|
application. |
|
|
|
This sample application produces the following `settings.xml` file: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<settings foo-enabled="false"/> |
|
---- |
|
|
|
|
|
|
|
|
|
[[oxm-schema-based-config]] |
|
=== XML Schema-based Configuration |
|
Marshallers could be configured more concisely using tags from the OXM namespace. To |
|
make these tags available, the appropriate schema has to be referenced first in the |
|
preamble of the XML configuration file. Note the 'oxm' related text below: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
**xmlns:oxm="http://www.springframework.org/schema/oxm"** xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd **http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm.xsd"**> |
|
---- |
|
|
|
Currently, the following tags are available: |
|
|
|
* <<oxm-jaxb2-xsd, `jaxb2-marshaller`>> |
|
* <<oxm-xmlbeans-xsd, `xmlbeans-marshaller`>> |
|
* <<oxm-castor-xsd, `castor-marshaller`>> |
|
* <<oxm-jibx-xsd, `jibx-marshaller`>> |
|
|
|
Each tag will be explained in its respective marshaller's section. As an example though, |
|
here is how the configuration of a JAXB2 marshaller might look like: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<oxm:jaxb2-marshaller id="marshaller" contextPath="org.springframework.ws.samples.airline.schema"/> |
|
---- |
|
|
|
|
|
|
|
|
|
[[oxm-jaxb]] |
|
=== JAXB |
|
The JAXB binding compiler translates a W3C XML Schema into one or more Java classes, a |
|
`jaxb.properties` file, and possibly some resource files. JAXB also offers a way to |
|
generate a schema from annotated Java classes. |
|
|
|
Spring supports the JAXB 2.0 API as XML marshalling strategies, following the |
|
`Marshaller` and `Unmarshaller` interfaces described in <<oxm-marshaller-unmarshaller>>. |
|
The corresponding integration classes reside in the `org.springframework.oxm.jaxb` |
|
package. |
|
|
|
|
|
|
|
[[oxm-jaxb2]] |
|
==== Jaxb2Marshaller |
|
The `Jaxb2Marshaller` class implements both the Spring `Marshaller` and `Unmarshaller` |
|
interface. It requires a context path to operate, which you can set using the |
|
`contextPath` property. The context path is a list of colon (:) separated Java package |
|
names that contain schema derived classes. It also offers a `classesToBeBound` property, |
|
which allows you to set an array of classes to be supported by the marshaller. Schema |
|
validation is performed by specifying one or more schema resource to the bean, like so: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<bean id="jaxb2Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller"> |
|
<property name="classesToBeBound"> |
|
<list> |
|
<value>org.springframework.oxm.jaxb.Flight</value> |
|
<value>org.springframework.oxm.jaxb.Flights</value> |
|
</list> |
|
</property> |
|
<property name="schema" value="classpath:org/springframework/oxm/schema.xsd"/> |
|
</bean> |
|
|
|
... |
|
|
|
</beans> |
|
---- |
|
|
|
|
|
[[oxm-jaxb2-xsd]] |
|
===== XML Schema-based Configuration |
|
The `jaxb2-marshaller` tag configures a `org.springframework.oxm.jaxb.Jaxb2Marshaller`. |
|
Here is an example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<oxm:jaxb2-marshaller id="marshaller" contextPath="org.springframework.ws.samples.airline.schema"/> |
|
---- |
|
|
|
Alternatively, the list of classes to bind can be provided to the marshaller via the |
|
`class-to-be-bound` child tag: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<oxm:jaxb2-marshaller id="marshaller"> |
|
<oxm:class-to-be-bound name="org.springframework.ws.samples.airline.schema.Airport"/> |
|
<oxm:class-to-be-bound name="org.springframework.ws.samples.airline.schema.Flight"/> |
|
... |
|
</oxm:jaxb2-marshaller> |
|
---- |
|
|
|
Available attributes are: |
|
|
|
|=== |
|
| Attribute| Description| Required |
|
|
|
| `id` |
|
| the id of the marshaller |
|
| no |
|
|
|
| `contextPath` |
|
| the JAXB Context path |
|
| no |
|
|=== |
|
|
|
|
|
|
|
|
|
[[oxm-castor]] |
|
=== Castor |
|
Castor XML mapping is an open source XML binding framework. It allows you to transform |
|
the data contained in a java object model into/from an XML document. By default, it does |
|
not require any further configuration, though a mapping file can be used to have more |
|
control over the behavior of Castor. |
|
|
|
For more information on Castor, refer to the |
|
http://castor.codehaus.org/xml-framework.html[__Castor web site__]. The Spring |
|
integration classes reside in the `org.springframework.oxm.castor` package. |
|
|
|
|
|
|
|
[[oxm-castor-marshaller]] |
|
==== CastorMarshaller |
|
As with JAXB, the `CastorMarshaller` implements both the `Marshaller` and `Unmarshaller` |
|
interface. It can be wired up as follows: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller" /> |
|
... |
|
</beans> |
|
---- |
|
|
|
|
|
|
|
[[oxm-castor-mapping]] |
|
==== Mapping |
|
Although it is possible to rely on Castor's default marshalling behavior, it might be |
|
necessary to have more control over it. This can be accomplished using a Castor mapping |
|
file. For more information, refer to http://castor.codehaus.org/xml-mapping.html[Castor |
|
XML Mapping]. |
|
|
|
The mapping can be set using the `mappingLocation` resource property, indicated below |
|
with a classpath resource. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller" > |
|
<property name="mappingLocation" value="classpath:mapping.xml" /> |
|
</bean> |
|
</beans> |
|
---- |
|
|
|
|
|
[[oxm-castor-xsd]] |
|
===== XML Schema-based Configuration |
|
The `castor-marshaller` tag configures a |
|
`org.springframework.oxm.castor.CastorMarshaller`. Here is an example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<oxm:castor-marshaller id="marshaller" mapping-location="classpath:org/springframework/oxm/castor/mapping.xml"/> |
|
---- |
|
|
|
The marshaller instance can be configured in two ways, by specifying either the location |
|
of a mapping file (through the `mapping-location` property), or by identifying Java |
|
POJOs (through the `target-class` or `target-package` properties) for which there exist |
|
corresponding XML descriptor classes. The latter way is usually used in conjunction with |
|
XML code generation from XML schemas. |
|
|
|
Available attributes are: |
|
|
|
|=== |
|
| Attribute| Description| Required |
|
|
|
| `id` |
|
| the id of the marshaller |
|
| no |
|
|
|
| `encoding` |
|
| the encoding to use for unmarshalling from XML |
|
| no |
|
|
|
| `target-class` |
|
| a Java class name for a POJO for which an XML class descriptor is available (as |
|
generated through code generation) |
|
| no |
|
|
|
| `target-package` |
|
| a Java package name that identifies a package that contains POJOs and their |
|
corresponding Castor XML descriptor classes (as generated through code generation from |
|
XML schemas) |
|
| no |
|
|
|
| `mapping-location` |
|
| location of a Castor XML mapping file |
|
| no |
|
|=== |
|
|
|
|
|
|
|
|
|
[[oxm-xmlbeans]] |
|
=== XMLBeans |
|
XMLBeans is an XML binding tool that has full XML Schema support, and offers full XML |
|
Infoset fidelity. It takes a different approach to that of most other O/X mapping |
|
frameworks, in that all classes that are generated from an XML Schema are all derived |
|
from `XmlObject`, and contain XML binding information in them. |
|
|
|
For more information on XMLBeans, refer to the http://xmlbeans.apache.org/[__XMLBeans |
|
web site __]. The Spring-WS integration classes reside in the |
|
`org.springframework.oxm.xmlbeans` package. |
|
|
|
|
|
|
|
[[oxm-xmlbeans-marshaller]] |
|
==== XmlBeansMarshaller |
|
The `XmlBeansMarshaller` implements both the `Marshaller` and `Unmarshaller` interfaces. |
|
It can be configured as follows: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<bean id="xmlBeansMarshaller" class="org.springframework.oxm.xmlbeans.XmlBeansMarshaller" /> |
|
... |
|
|
|
</beans> |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
Note that the `XmlBeansMarshaller` can only marshal objects of type `XmlObject`, and not |
|
every `java.lang.Object`. |
|
==== |
|
|
|
|
|
[[oxm-xmlbeans-xsd]] |
|
===== XML Schema-based Configuration |
|
The `xmlbeans-marshaller` tag configures a |
|
`org.springframework.oxm.xmlbeans.XmlBeansMarshaller`. Here is an example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<oxm:xmlbeans-marshaller id="marshaller"/> |
|
---- |
|
|
|
Available attributes are: |
|
|
|
|=== |
|
| Attribute| Description| Required |
|
|
|
| `id` |
|
| the id of the marshaller |
|
| no |
|
|
|
| `options` |
|
| the bean name of the XmlOptions that is to be used for this marshaller. Typically a |
|
`XmlOptionsFactoryBean` definition |
|
| no |
|
|=== |
|
|
|
|
|
|
|
|
|
[[oxm-jibx]] |
|
=== JiBX |
|
The JiBX framework offers a solution similar to that which JDO provides for ORM: a |
|
binding definition defines the rules for how your Java objects are converted to or from |
|
XML. After preparing the binding and compiling the classes, a JiBX binding compiler |
|
enhances the class files, and adds code to handle converting instances of the classes |
|
from or to XML. |
|
|
|
For more information on JiBX, refer to the http://jibx.sourceforge.net/[__JiBX web |
|
site__]. The Spring integration classes reside in the `org.springframework.oxm.jibx` |
|
package. |
|
|
|
|
|
|
|
[[oxm-jibx-marshaller]] |
|
==== JibxMarshaller |
|
The `JibxMarshaller` class implements both the `Marshaller` and `Unmarshaller` |
|
interface. To operate, it requires the name of the class to marshal in, which you can |
|
set using the `targetClass` property. Optionally, you can set the binding name using the |
|
`bindingName` property. In the next sample, we bind the `Flights` class: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<bean id="jibxFlightsMarshaller" class="org.springframework.oxm.jibx.JibxMarshaller"> |
|
<property name="targetClass">org.springframework.oxm.jibx.Flights</property> |
|
</bean> |
|
... |
|
</beans> |
|
---- |
|
|
|
A `JibxMarshaller` is configured for a single class. If you want to marshal multiple |
|
classes, you have to configure multiple ++JibxMarshaller++s with different `targetClass` |
|
property values. |
|
|
|
|
|
[[oxm-jibx-xsd]] |
|
===== XML Schema-based Configuration |
|
The `jibx-marshaller` tag configures a `org.springframework.oxm.jibx.JibxMarshaller`. |
|
Here is an example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<oxm:jibx-marshaller id="marshaller" target-class="org.springframework.ws.samples.airline.schema.Flight"/> |
|
---- |
|
|
|
Available attributes are: |
|
|
|
|=== |
|
| Attribute| Description| Required |
|
|
|
| `id` |
|
| the id of the marshaller |
|
| no |
|
|
|
| `target-class` |
|
| the target class for this marshaller |
|
| yes |
|
|
|
| `bindingName` |
|
| the binding name used by this marshaller |
|
| no |
|
|=== |
|
|
|
|
|
|
|
|
|
[[oxm-xstream]] |
|
=== XStream |
|
XStream is a simple library to serialize objects to XML and back again. It does not |
|
require any mapping, and generates clean XML. |
|
|
|
For more information on XStream, refer to the http://xstream.codehaus.org/[__XStream |
|
web site__]. The Spring integration classes reside in the |
|
`org.springframework.oxm.xstream` package. |
|
|
|
|
|
|
|
[[oxm-xstream-marshaller]] |
|
==== XStreamMarshaller |
|
The `XStreamMarshaller` does not require any configuration, and can be configured in an |
|
application context directly. To further customize the XML, you can set an__alias map__, |
|
which consists of string aliases mapped to classes: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<bean id="xstreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller"> |
|
<property name="aliases"> |
|
<props> |
|
<prop key="Flight">org.springframework.oxm.xstream.Flight</prop> |
|
</props> |
|
</property> |
|
</bean> |
|
... |
|
</beans> |
|
---- |
|
|
|
[WARNING] |
|
==== |
|
|
|
By default, XStream allows for arbitrary classes to be unmarshalled, which can result in |
|
security vulnerabilities. As such, it is __not recommended to use the |
|
`XStreamMarshaller` to unmarshal XML from external sources__ (i.e. the Web), as this can |
|
result in __security vulnerabilities__. If you do use the `XStreamMarshaller` to |
|
unmarshal XML from an external source, set the `supportedClasses` property on the |
|
`XStreamMarshaller`, like so: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="xstreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller"> |
|
<property name="supportedClasses" value="org.springframework.oxm.xstream.Flight"/> |
|
... |
|
</bean> |
|
---- |
|
|
|
This will make sure that only the registered classes are eligible for unmarshalling. |
|
|
|
Additionally, you can register |
|
{javadoc-baseurl}/org/springframework/oxm/xstream/XStreamMarshaller.html#setConverters(com.thoughtworks.xstream.converters.ConverterMatcher...)[custom |
|
converters] to make sure that only your supported classes can be unmarshalled. You might |
|
want to add a `CatchAllConverter` as the last converter in the list, in addition to |
|
converters that explicitly support the domain classes that should be supported. As a |
|
result, default XStream converters with lower priorities and possible security |
|
vulnerabilities do not get invoked. |
|
==== |
|
|
|
NOTE: Note that XStream is an XML serialization library, not a data binding library. |
|
Therefore, it has limited namespace support. As such, it is rather unsuitable for usage |
|
within Web services. |
|
|
|
|
|
|
|
|
|
|
|
|
|
[[spring-web]] |
|
= The Web |
|
[partintro] |
|
-- |
|
This part of the reference documentation covers Spring Framework's support for the |
|
presentation tier (and specifically web-based presentation tiers) including support |
|
for WebSocket-style messaging in web applications. |
|
|
|
Spring Framework's own web framework, <<mvc,Spring Web MVC>>, is covered in the |
|
first couple of chapters. Subsequent chapters are concerned with Spring Framework's |
|
integration with other web technologies, such as <<jsf,JSF>> and. |
|
|
|
Following that is coverage of Spring Framework's MVC <<portlet,portlet framework>>. |
|
|
|
The section then concludes with comprehensive coverage of the Spring Framework |
|
<<websocket>> (including <<websocket-stomp>>). |
|
|
|
* <<mvc>> |
|
* <<view>> |
|
* <<web-integration>> |
|
* <<portlet>> |
|
* <<websocket>> |
|
-- |
|
|
|
|
|
|
|
|
|
[[mvc]] |
|
== Web MVC framework |
|
|
|
|
|
|
|
|
|
[[mvc-introduction]] |
|
=== Introduction to Spring Web MVC framework |
|
The Spring Web model-view-controller (MVC) framework is designed around a |
|
`DispatcherServlet` that dispatches requests to handlers, with configurable handler |
|
mappings, view resolution, locale, time zone and theme resolution as well as support for |
|
uploading files. The default handler is based on the `@Controller` and `@RequestMapping` |
|
annotations, offering a wide range of flexible handling methods. With the introduction |
|
of Spring 3.0, the `@Controller` mechanism also allows you to create RESTful Web sites |
|
and applications, through the `@PathVariable` annotation and other features. |
|
|
|
**** |
|
"Open for extension..." |
|
A key design principle in Spring Web MVC and in Spring in general is the "__Open for |
|
extension, closed for modification__" principle. |
|
|
|
Some methods in the core classes of Spring Web MVC are marked `final`. As a developer |
|
you cannot override these methods to supply your own behavior. This has not been done |
|
arbitrarily, but specifically with this principle in mind. |
|
|
|
For an explanation of this principle, refer to __Expert Spring Web MVC and Web Flow__ by |
|
Seth Ladd and others; specifically see the section "A Look At Design," on page 117 of |
|
the first edition. Alternatively, see |
|
|
|
* http://www.objectmentor.com/resources/articles/ocp.pdf[Bob Martin, The Open-Closed |
|
Principle (PDF)] |
|
|
|
You cannot add advice to final methods when you use Spring MVC. For example, you cannot |
|
add advice to the `AbstractController.setSynchronizeOnSession()` method. Refer to |
|
<<aop-understanding-aop-proxies>> for more information on AOP proxies and why you cannot |
|
add advice to final methods. |
|
**** |
|
|
|
In Spring Web MVC you can use any object as a command or form-backing object; you do not |
|
need to implement a framework-specific interface or base class. Spring's data binding is |
|
highly flexible: for example, it treats type mismatches as validation errors that can be |
|
evaluated by the application, not as system errors. Thus you need not duplicate your |
|
business objects' properties as simple, untyped strings in your form objects simply to |
|
handle invalid submissions, or to convert the Strings properly. Instead, it is often |
|
preferable to bind directly to your business objects. |
|
|
|
Spring's view resolution is extremely flexible. A `Controller` is typically responsible |
|
for preparing a model `Map` with data and selecting a view name but it can also write |
|
directly to the response stream and complete the request. View name resolution is highly |
|
configurable through file extension or Accept header content type negotiation, through |
|
bean names, a properties file, or even a custom `ViewResolver` implementation. The model |
|
(the M in MVC) is a `Map` interface, which allows for the complete abstraction of the |
|
view technology. You can integrate directly with template based rendering technologies |
|
such as JSP, Velocity and Freemarker, or directly generate XML, JSON, Atom, and many |
|
other types of content. The model `Map` is simply transformed into an appropriate |
|
format, such as JSP request attributes, a Velocity template model. |
|
|
|
|
|
|
|
[[mvc-features]] |
|
==== Features of Spring Web MVC |
|
|
|
.Spring Web Flow |
|
**** |
|
Spring Web Flow (SWF) aims to be the best solution for the management of web application |
|
page flow. |
|
|
|
SWF integrates with existing frameworks like Spring MVC and JSF, in both Servlet and |
|
Portlet environments. If you have a business process (or processes) that would benefit |
|
from a conversational model as opposed to a purely request model, then SWF may be the |
|
solution. |
|
|
|
SWF allows you to capture logical page flows as self-contained modules that are reusable |
|
in different situations, and as such is ideal for building web application modules that |
|
guide the user through controlled navigations that drive business processes. |
|
|
|
For more information about SWF, consult the |
|
http://projects.spring.io/spring-webflow/[Spring Web Flow website]. |
|
**** |
|
|
|
Spring's web module includes many unique web support features: |
|
|
|
* __Clear separation of roles__. Each role -- controller, validator, command object, |
|
form object, model object, `DispatcherServlet`, handler mapping, view resolver, and so |
|
on -- can be fulfilled by a specialized object. |
|
* __Powerful and straightforward configuration of both framework and application classes |
|
as JavaBeans__. This configuration capability includes easy referencing across |
|
contexts, such as from web controllers to business objects and validators. |
|
* __Adaptability, non-intrusiveness, and flexibility.__ Define any controller method |
|
signature you need, possibly using one of the parameter annotations (such as |
|
@RequestParam, @RequestHeader, @PathVariable, and more) for a given scenario. |
|
* __Reusable business code, no need for duplication__. Use existing business objects |
|
as command or form objects instead of mirroring them to extend a particular framework |
|
base class. |
|
* __Customizable binding and validation__. Type mismatches as application-level |
|
validation errors that keep the offending value, localized date and number binding, |
|
and so on instead of String-only form objects with manual parsing and conversion to |
|
business objects. |
|
* __Customizable handler mapping and view resolution__. Handler mapping and view |
|
resolution strategies range from simple URL-based configuration, to sophisticated, |
|
purpose-built resolution strategies. Spring is more flexible than web MVC frameworks |
|
that mandate a particular technique. |
|
* __Flexible model transfer__. Model transfer with a name/value `Map` supports easy |
|
integration with any view technology. |
|
* __Customizable locale, time zone and theme resolution, support for JSPs with or without |
|
Spring tag library, support for JSTL, support for Velocity without the need for extra |
|
bridges, and so on.__ |
|
* __A simple yet powerful JSP tag library known as the Spring tag library that provides |
|
support for features such as data binding and themes__. The custom tags allow for |
|
maximum flexibility in terms of markup code. For information on the tag library |
|
descriptor, see the appendix entitled <<spring.tld>> |
|
* __A JSP form tag library, introduced in Spring 2.0, that makes writing forms in JSP |
|
pages much easier.__ For information on the tag library descriptor, see the appendix |
|
entitled <<spring-form.tld>> |
|
* __Beans whose lifecycle is scoped to the current HTTP request or HTTP `Session`.__ |
|
This is not a specific feature of Spring MVC itself, but rather of the |
|
`WebApplicationContext` container(s) that Spring MVC uses. These bean scopes are |
|
described in <<beans-factory-scopes-other>> |
|
|
|
|
|
|
|
[[mvc-introduction-pluggability]] |
|
==== Pluggability of other MVC implementations |
|
Non-Spring MVC implementations are preferable for some projects. Many teams expect to |
|
leverage their existing investment in skills and tools, for example with JSF. |
|
|
|
If you do not want to use Spring's Web MVC, but intend to leverage other solutions that |
|
Spring offers, you can integrate the web MVC framework of your choice with Spring |
|
easily. Simply start up a Spring root application context through its |
|
`ContextLoaderListener`, and access it through its `ServletContext` attribute (or |
|
Spring's respective helper method) from within any action object. No "plug-ins" |
|
are involved, so no dedicated integration is necessary. From the web layer's point of |
|
view, you simply use Spring as a library, with the root application context instance as |
|
the entry point. |
|
|
|
Your registered beans and Spring's services can be at your fingertips even without |
|
Spring's Web MVC. Spring does not compete with other web frameworks in this scenario. |
|
It simply addresses the many areas that the pure web MVC frameworks do not, from bean |
|
configuration to data access and transaction handling. So you can enrich your |
|
application with a Spring middle tier and/or data access tier, even if you just want |
|
to use, for example, the transaction abstraction with JDBC or Hibernate. |
|
|
|
|
|
|
|
|
|
[[mvc-servlet]] |
|
=== The DispatcherServlet |
|
|
|
Spring's web MVC framework is, like many other web MVC frameworks, request-driven, |
|
designed around a central Servlet that dispatches requests to controllers and offers |
|
other functionality that facilitates the development of web applications. Spring's |
|
`DispatcherServlet` however, does more than just that. It is completely integrated with |
|
the Spring IoC container and as such allows you to use every other feature that Spring |
|
has. |
|
|
|
The request processing workflow of the Spring Web MVC `DispatcherServlet` is illustrated |
|
in the following diagram. The pattern-savvy reader will recognize that the |
|
`DispatcherServlet` is an expression of the "Front Controller" design pattern (this is a |
|
pattern that Spring Web MVC shares with many other leading web frameworks). |
|
|
|
image::images/mvc.png[width=400] |
|
|
|
The request processing workflow in Spring Web MVC (high level) |
|
|
|
The `DispatcherServlet` is an actual `Servlet` (it inherits from the `HttpServlet` base |
|
class), and as such is declared in the `web.xml` of your web application. You need to |
|
map requests that you want the `DispatcherServlet` to handle, by using a URL mapping in |
|
the same `web.xml` file. This is standard Java EE Servlet configuration; the following |
|
example shows such a `DispatcherServlet` declaration and mapping: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<web-app> |
|
<servlet> |
|
<servlet-name>example</servlet-name> |
|
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> |
|
<load-on-startup>1</load-on-startup> |
|
</servlet> |
|
|
|
<servlet-mapping> |
|
<servlet-name>example</servlet-name> |
|
<url-pattern>/example/*</url-pattern> |
|
</servlet-mapping> |
|
|
|
</web-app> |
|
---- |
|
|
|
In the preceding example, all requests starting with `/example` will be handled by the |
|
`DispatcherServlet` instance named `example`. In a Servlet 3.0+ environment, you also |
|
have the option of configuring the Servlet container programmatically. Below is the code |
|
based equivalent of the above `web.xml` example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MyWebApplicationInitializer implements WebApplicationInitializer { |
|
|
|
@Override |
|
public void onStartup(ServletContext container) { |
|
ServletRegistration.Dynamic registration = container.addServlet("dispatcher", new DispatcherServlet()); |
|
registration.setLoadOnStartup(1); |
|
registration.addMapping("/example/*"); |
|
} |
|
|
|
} |
|
---- |
|
|
|
`WebApplicationInitializer` is an interface provided by Spring MVC that ensures your |
|
code-based configuration is detected and automatically used to initialize any Servlet 3 |
|
container. An abstract base class implementation of this interace named |
|
`AbstractDispatcherServletInitializer` makes it even easier to register the |
|
`DispatcherServlet` by simply specifying its servlet mapping. |
|
See <<mvc-container-config,Code-based Servlet container initialization>> for more details. |
|
|
|
The above is only the first step in setting up Spring Web MVC. You now need to configure |
|
the various beans used by the Spring Web MVC framework (over and above the |
|
`DispatcherServlet` itself). |
|
|
|
As detailed in <<context-introduction>>, `ApplicationContext` instances in Spring can be |
|
scoped. In the Web MVC framework, each `DispatcherServlet` has its own |
|
`WebApplicationContext`, which inherits all the beans already defined in the root |
|
`WebApplicationContext`. These inherited beans can be overridden in the servlet-specific |
|
scope, and you can define new scope-specific beans local to a given Servlet instance. |
|
|
|
.Context hierarchy in Spring Web MVC |
|
image::images/mvc-contexts.gif[width=400] |
|
|
|
Upon initialization of a `DispatcherServlet`, Spring MVC looks for a file named |
|
__[servlet-name]-servlet.xml__ in the `WEB-INF` directory of your web application and |
|
creates the beans defined there, overriding the definitions of any beans defined with |
|
the same name in the global scope. |
|
|
|
Consider the following `DispatcherServlet` Servlet configuration (in the `web.xml` file): |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<web-app> |
|
<servlet> |
|
<servlet-name>**golfing**</servlet-name> |
|
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> |
|
<load-on-startup>1</load-on-startup> |
|
</servlet> |
|
<servlet-mapping> |
|
<servlet-name>**golfing**</servlet-name> |
|
<url-pattern>/golfing/*</url-pattern> |
|
</servlet-mapping> |
|
</web-app> |
|
---- |
|
|
|
With the above Servlet configuration in place, you will need to have a file called |
|
`/WEB-INF/golfing-servlet.xml` in your application; this file will contain all of your |
|
Spring Web MVC-specific components (beans). You can change the exact location of this |
|
configuration file through a Servlet initialization parameter (see below for details). |
|
|
|
It is also possible to have just one root context for single DispatcherServlet scenarios |
|
by setting an empty contextConfigLocation servlet init parameter, as shown below: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<web-app> |
|
<context-param> |
|
<param-name>contextConfigLocation</param-name> |
|
<param-value>/WEB-INF/root-context.xml</param-value> |
|
</context-param> |
|
<servlet> |
|
<servlet-name>dispatcher</servlet-name> |
|
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> |
|
<init-param> |
|
<param-name>contextConfigLocation</param-name> |
|
<param-value></param-value> |
|
</init-param> |
|
<load-on-startup>1</load-on-startup> |
|
</servlet> |
|
<servlet-mapping> |
|
<servlet-name>dispatcher</servlet-name> |
|
<url-pattern>/*</url-pattern> |
|
</servlet-mapping> |
|
<listener> |
|
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> |
|
</listener> |
|
</web-app> |
|
---- |
|
|
|
|
|
The `WebApplicationContext` is an extension of the plain `ApplicationContext` that has |
|
some extra features necessary for web applications. It differs from a normal |
|
`ApplicationContext` in that it is capable of resolving themes (see |
|
<<mvc-themeresolver>>), and that it knows which Servlet it is associated with (by having |
|
a link to the `ServletContext`). The `WebApplicationContext` is bound in the |
|
`ServletContext`, and by using static methods on the `RequestContextUtils` class you can |
|
always look up the `WebApplicationContext` if you need access to it. |
|
|
|
|
|
|
|
[[mvc-servlet-special-bean-types]] |
|
==== Special Bean Types In the WebApplicationContext |
|
|
|
The Spring `DispatcherServlet` uses special beans to process requests and render the |
|
appropriate views. These beans are part of Spring MVC. You can choose which special |
|
beans to use by simply configuring one or more of them in the `WebApplicationContext`. |
|
However, you don't need to do that initially since Spring MVC maintains a list of |
|
default beans to use if you don't configure any. More on that in the next section. First |
|
see the table below listing the special bean types the `DispatcherServlet` relies on. |
|
|
|
[[mvc-webappctx-special-beans-tbl]] |
|
.Special bean types in the WebApplicationContext |
|
|=== |
|
| Bean type| Explanation |
|
|
|
| <<mvc-handlermapping,HandlerMapping>> |
|
| Maps incoming requests to handlers and a list of pre- and post-processors (handler |
|
interceptors) based on some criteria the details of which vary by `HandlerMapping` |
|
implementation. The most popular implementation supports annotated controllers but |
|
other implementations exists as well. |
|
|
|
| HandlerAdapter |
|
| Helps the `DispatcherServlet` to invoke a handler mapped to a request regardless of |
|
the handler is actually invoked. For example, invoking an annotated controller |
|
requires resolving various annotations. Thus the main purpose of a `HandlerAdapter` is |
|
to shield the `DispatcherServlet` from such details. |
|
|
|
| <<mvc-exceptionhandlers,HandlerExceptionResolver>> |
|
| Maps exceptions to views also allowing for more complex exception handling code. |
|
|
|
| <<mvc-viewresolver,ViewResolver>> |
|
| Resolves logical String-based view names to actual `View` types. |
|
|
|
| <<mvc-localeresolver,LocaleResolver>> & <<mvc-timezone,LocaleContextResolver>> |
|
| Resolves the locale a client is using and possibly their time zone, in order to be able |
|
to offer internationalized views |
|
|
|
| <<mvc-themeresolver,ThemeResolver>> |
|
| Resolves themes your web application can use, for example, to offer personalized layouts |
|
|
|
| <<mvc-multipart,MultipartResolver>> |
|
| Parses multi-part requests for example to support processing file uploads from HTML |
|
forms. |
|
|
|
| <<mvc-flash-attributes,FlashMapManager>> |
|
| Stores and retrieves the "input" and the "output" `FlashMap` that can be used to pass |
|
attributes from one request to another, usually across a redirect. |
|
|=== |
|
|
|
|
|
|
|
[[mvc-servlet-config]] |
|
==== Default DispatcherServlet Configuration |
|
As mentioned in the previous section for each special bean the `DispatcherServlet` |
|
maintains a list of implementations to use by default. This information is kept in the |
|
file `DispatcherServlet.properties` in the package `org.springframework.web.servlet`. |
|
|
|
All special beans have some reasonable defaults of their own. Sooner or later though |
|
you'll need to customize one or more of the properties these beans provide. For example |
|
it's quite common to configure an `InternalResourceViewResolver` settings its `prefix` |
|
property to the parent location of view files. |
|
|
|
Regardless of the details, the important concept to understand here is that once |
|
you configure a special bean such as an `InternalResourceViewResolver` in your |
|
`WebApplicationContext`, you effectively override the list of default implementations |
|
that would have been used otherwise for that special bean type. For example if you |
|
configure an `InternalResourceViewResolver`, the default list of `ViewResolver` |
|
implementations is ignored. |
|
|
|
In <<mvc-config>> you'll learn about other options for configuring Spring MVC including |
|
MVC Java config and the MVC XML namespace both of which provide a simple starting point |
|
and assume little knowledge of how Spring MVC works. Regardless of how you choose to |
|
configure your application, the concepts explained in this section are fundamental |
|
should be of help to you. |
|
|
|
|
|
|
|
[[mvc-servlet-sequence]] |
|
==== DispatcherServlet Processing Sequence |
|
After you set up a `DispatcherServlet`, and a request comes in for that specific |
|
`DispatcherServlet`, the `DispatcherServlet` starts processing the request as follows: |
|
|
|
* The `WebApplicationContext` is searched for and bound in the request as an attribute |
|
that the controller and other elements in the process can use. It is bound by default |
|
under the key `DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE`. |
|
* The locale resolver is bound to the request to enable elements in the process to |
|
resolve the locale to use when processing the request (rendering the view, preparing |
|
data, and so on). If you do not need locale resolving, you do not need it. |
|
* The theme resolver is bound to the request to let elements such as views determine |
|
which theme to use. If you do not use themes, you can ignore it. |
|
* If you specify a multipart file resolver, the request is inspected for multiparts; if |
|
multiparts are found, the request is wrapped in a `MultipartHttpServletRequest` for |
|
further processing by other elements in the process. See <<mvc-multipart>> for further |
|
information about multipart handling. |
|
* An appropriate handler is searched for. If a handler is found, the execution chain |
|
associated with the handler (preprocessors, postprocessors, and controllers) is |
|
executed in order to prepare a model or rendering. |
|
* If a model is returned, the view is rendered. If no model is returned, (may be due to |
|
a preprocessor or postprocessor intercepting the request, perhaps for security |
|
reasons), no view is rendered, because the request could already have been fulfilled. |
|
|
|
Handler exception resolvers that are declared in the `WebApplicationContext` pick up |
|
exceptions that are thrown during processing of the request. Using these exception |
|
resolvers allows you to define custom behaviors to address exceptions. |
|
|
|
The Spring `DispatcherServlet` also supports the return of the |
|
__last-modification-date__, as specified by the Servlet API. The process of determining |
|
the last modification date for a specific request is straightforward: the |
|
`DispatcherServlet` looks up an appropriate handler mapping and tests whether the |
|
handler that is found implements the __LastModified__ interface. If so, the value of the |
|
`long getLastModified(request)` method of the `LastModified` interface is returned to |
|
the client. |
|
|
|
You can customize individual `DispatcherServlet` instances by adding Servlet |
|
initialization parameters ( `init-param` elements) to the Servlet declaration in the |
|
`web.xml` file. See the following table for the list of supported parameters. |
|
|
|
[[mvc-disp-servlet-init-params-tbl]] |
|
.DispatcherServlet initialization parameters |
|
|=== |
|
| Parameter| Explanation |
|
|
|
| `contextClass` |
|
| Class that implements `WebApplicationContext`, which instantiates the context used by |
|
this Servlet. By default, the `XmlWebApplicationContext` is used. |
|
|
|
| `contextConfigLocation` |
|
| String that is passed to the context instance (specified by `contextClass`) to |
|
indicate where context(s) can be found. The string consists potentially of multiple |
|
strings (using a comma as a delimiter) to support multiple contexts. In case of |
|
multiple context locations with beans that are defined twice, the latest location |
|
takes precedence. |
|
|
|
| `namespace` |
|
| Namespace of the `WebApplicationContext`. Defaults to `[servlet-name]-servlet`. |
|
|=== |
|
|
|
|
|
|
|
|
|
[[mvc-controller]] |
|
=== Implementing Controllers |
|
Controllers provide access to the application behavior that you typically define through |
|
a service interface. Controllers interpret user input and transform it into a model that |
|
is represented to the user by the view. Spring implements a controller in a very |
|
abstract way, which enables you to create a wide variety of controllers. |
|
|
|
Spring 2.5 introduced an annotation-based programming model for MVC controllers that |
|
uses annotations such as `@RequestMapping`, `@RequestParam`, `@ModelAttribute`, and so |
|
on. This annotation support is available for both Servlet MVC and Portlet MVC. |
|
Controllers implemented in this style do not have to extend specific base classes or |
|
implement specific interfaces. Furthermore, they do not usually have direct dependencies |
|
on Servlet or Portlet APIs, although you can easily configure access to Servlet or |
|
Portlet facilities. |
|
|
|
[TIP] |
|
==== |
|
|
|
Available in the https://github.com/spring-projects/[spring-projects Org on Github], |
|
a number of web applications leverage the annotation support described in this section |
|
including __MvcShowcase__, __MvcAjax__, __MvcBasic__, __PetClinic__, __PetCare__, |
|
and others. |
|
==== |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
public class HelloWorldController { |
|
|
|
@RequestMapping("/helloWorld") |
|
public String helloWorld(Model model) { |
|
model.addAttribute("message", "Hello World!"); |
|
return "helloWorld"; |
|
} |
|
} |
|
---- |
|
|
|
As you can see, the `@Controller` and `@RequestMapping` annotations allow flexible |
|
method names and signatures. In this particular example the method accepts a `Model` and |
|
returns a view name as a `String`, but various other method parameters and return values |
|
can be used as explained later in this section. `@Controller` and `@RequestMapping` and |
|
a number of other annotations form the basis for the Spring MVC implementation. This |
|
section documents these annotations and how they are most commonly used in a Servlet |
|
environment. |
|
|
|
|
|
|
|
[[mvc-ann-controller]] |
|
==== Defining a controller with @Controller |
|
|
|
The `@Controller` annotation indicates that a particular class serves the role of |
|
a __controller__. Spring does not require you to extend any controller base class or |
|
reference the Servlet API. However, you can still reference Servlet-specific features if |
|
you need to. |
|
|
|
The `@Controller` annotation acts as a stereotype for the annotated class, indicating |
|
its role. The dispatcher scans such annotated classes for mapped methods and detects |
|
`@RequestMapping` annotations (see the next section). |
|
|
|
You can define annotated controller beans explicitly, using a standard Spring bean |
|
definition in the dispatcher's context. However, the `@Controller` stereotype also |
|
allows for autodetection, aligned with Spring general support for detecting component |
|
classes in the classpath and auto-registering bean definitions for them. |
|
|
|
To enable autodetection of such annotated controllers, you add component scanning to |
|
your configuration. Use the __spring-context__ schema as shown in the following XML |
|
snippet: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:p="http://www.springframework.org/schema/p" |
|
xmlns:context="http://www.springframework.org/schema/context" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/context |
|
http://www.springframework.org/schema/context/spring-context.xsd"> |
|
|
|
<context:component-scan base-package="org.springframework.samples.petclinic.web"/> |
|
|
|
<!-- ... --> |
|
|
|
</beans> |
|
---- |
|
|
|
|
|
|
|
[[mvc-ann-requestmapping]] |
|
==== Mapping Requests With @RequestMapping |
|
|
|
You use the `@RequestMapping` annotation to map URLs such as `/appointments` onto an |
|
entire class or a particular handler method. Typically the class-level annotation maps a |
|
specific request path (or path pattern) onto a form controller, with additional |
|
method-level annotations narrowing the primary mapping for a specific HTTP method |
|
request method ("GET", "POST", etc.) or an HTTP request parameter condition. |
|
|
|
The following example from the __Petcare__ sample shows a controller in a Spring MVC |
|
application that uses this annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
**@RequestMapping("/appointments")** |
|
public class AppointmentsController { |
|
|
|
private final AppointmentBook appointmentBook; |
|
|
|
@Autowired |
|
public AppointmentsController(AppointmentBook appointmentBook) { |
|
this.appointmentBook = appointmentBook; |
|
} |
|
|
|
**@RequestMapping(method = RequestMethod.GET)** |
|
public Map<String, Appointment> get() { |
|
return appointmentBook.getAppointmentsForToday(); |
|
} |
|
|
|
**@RequestMapping(value="/{day}", method = RequestMethod.GET)** |
|
public Map<String, Appointment> getForDay(@PathVariable @DateTimeFormat(iso=ISO.DATE) Date day, Model model) { |
|
return appointmentBook.getAppointmentsForDay(day); |
|
} |
|
|
|
**@RequestMapping(value="/new", method = RequestMethod.GET)** |
|
public AppointmentForm getNewForm() { |
|
return new AppointmentForm(); |
|
} |
|
|
|
**@RequestMapping(method = RequestMethod.POST)** |
|
public String add(@Valid AppointmentForm appointment, BindingResult result) { |
|
if (result.hasErrors()) { |
|
return "appointments/new"; |
|
} |
|
appointmentBook.addAppointment(appointment); |
|
return "redirect:/appointments"; |
|
} |
|
} |
|
---- |
|
|
|
In the example, the `@RequestMapping` is used in a number of places. The first usage is |
|
on the type (class) level, which indicates that all handling methods on this controller |
|
are relative to the `/appointments` path. The `get()` method has a further |
|
`@RequestMapping` refinement: it only accepts GET requests, meaning that an HTTP GET for |
|
`/appointments` invokes this method. The `add()` has a similar refinement, and the |
|
`getNewForm()` combines the definition of HTTP method and path into one, so that GET |
|
requests for `appointments/new` are handled by that method. |
|
|
|
The `getForDay()` method shows another usage of `@RequestMapping`: URI templates. (See |
|
<<mvc-ann-requestmapping-uri-templates,the next section >>). |
|
|
|
A `@RequestMapping` on the class level is not required. Without it, all paths are simply |
|
absolute, and not relative. The following example from the __PetClinic__ sample |
|
application shows a multi-action controller using `@RequestMapping`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
public class ClinicController { |
|
|
|
private final Clinic clinic; |
|
|
|
@Autowired |
|
public ClinicController(Clinic clinic) { |
|
this.clinic = clinic; |
|
} |
|
|
|
**@RequestMapping("/")** |
|
public void welcomeHandler() { |
|
} |
|
|
|
**@RequestMapping("/vets")** |
|
public ModelMap vetsHandler() { |
|
return new ModelMap(this.clinic.getVets()); |
|
} |
|
|
|
} |
|
---- |
|
|
|
The above example does not specify GET vs. PUT, POST, and so forth, because |
|
`@RequestMapping` maps all HTTP methods by default. Use `@RequestMapping(method=GET)` |
|
to narrow the mapping. |
|
|
|
|
|
[[mvc-ann-requestmapping-proxying]] |
|
===== ++@Controller++'s and AOP Proxying |
|
|
|
In some cases a controller may need to be decorated with an AOP proxy at runtime. |
|
One example is if you choose to have `@Transactional` annotations directly on the |
|
controller. When this is the case, for controllers specifically, we recommend |
|
using class-based proxying. This is typically the default choice with controllers. |
|
However if a controller must implement an interface that is not a Spring Context |
|
callback (e.g. `InitializingBean`, `*Aware`, etc), you may need to explicitly |
|
configure class-based proxying. For example with `<tx:annotation-driven />`, |
|
change to `<tx:annotation-driven proxy-target-class="true" />`. |
|
|
|
[[mvc-ann-requestmapping-31-vs-30]] |
|
===== New Support Classes for @RequestMapping methods in Spring MVC 3.1 |
|
Spring 3.1 introduced a new set of support classes for `@RequestMapping` methods called |
|
`RequestMappingHandlerMapping` and `RequestMappingHandlerAdapter` respectively. They are |
|
recommended for use and even required to take advantage of new features in Spring MVC |
|
3.1 and going forward. The new support classes are enabled by default by the MVC |
|
namespace and the MVC Java config but must be configured explicitly if using neither. |
|
This section describes a few important differences between the old and the new support |
|
classes. |
|
|
|
Prior to Spring 3.1, type and method-level request mappings were examined in two |
|
separate stages -- a controller was selected first by the |
|
`DefaultAnnotationHandlerMapping` and the actual method to invoke was narrowed down |
|
second by the `AnnotationMethodHandlerAdapter`. |
|
|
|
With the new support classes in Spring 3.1, the `RequestMappingHandlerMapping` is the |
|
only place where a decision is made about which method should process the request. Think |
|
of controller methods as a collection of unique endpoints with mappings for each method |
|
derived from type and method-level `@RequestMapping` information. |
|
|
|
This enables some new possibilities. For once a `HandlerInterceptor` or a |
|
`HandlerExceptionResolver` can now expect the Object-based handler to be a |
|
`HandlerMethod`, which allows them to examine the exact method, its parameters and |
|
associated annotations. The processing for a URL no longer needs to be split across |
|
different controllers. |
|
|
|
There are also several things no longer possible: |
|
|
|
* Select a controller first with a `SimpleUrlHandlerMapping` or |
|
`BeanNameUrlHandlerMapping` and then narrow the method based on `@RequestMapping` |
|
annotations. |
|
* Rely on method names as a fall-back mechanism to disambiguate between two |
|
`@RequestMapping` methods that don't have an explicit path mapping URL path but |
|
otherwise match equally, e.g. by HTTP method. In the new support classes |
|
`@RequestMapping` methods have to be mapped uniquely. |
|
* Have a single default method (without an explicit path mapping) with which requests |
|
are processed if no other controller method matches more concretely. In the new |
|
support classes if a matching method is not found a 404 error is raised. |
|
|
|
The above features are still supported with the existing support classes. However to |
|
take advantage of new Spring MVC 3.1 features you'll need to use the new support classes. |
|
|
|
|
|
[[mvc-ann-requestmapping-uri-templates]] |
|
===== URI Template Patterns |
|
__URI templates__ can be used for convenient access to selected parts of a URL in a |
|
`@RequestMapping` method. |
|
|
|
A URI Template is a URI-like string, containing one or more variable names. When you |
|
substitute values for these variables, the template becomes a URI. The |
|
http://bitworking.org/projects/URI-Templates/[proposed RFC] for URI Templates defines |
|
how a URI is parameterized. For example, the URI Template |
|
`http://www.example.com/users/{userId}` contains the variable __userId__. Assigning the |
|
value __fred__ to the variable yields `http://www.example.com/users/fred`. |
|
|
|
In Spring MVC you can use the `@PathVariable` annotation on a method argument to bind it |
|
to the value of a URI template variable: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET) |
|
public String findOwner(**@PathVariable** String ownerId, Model model) { |
|
Owner owner = ownerService.findOwner(ownerId); |
|
model.addAttribute("owner", owner); |
|
return "displayOwner"; |
|
} |
|
---- |
|
|
|
The URI Template " `/owners/{ownerId}`" specifies the variable name `ownerId`. When the |
|
controller handles this request, the value of `ownerId` is set to the value found in the |
|
appropriate part of the URI. For example, when a request comes in for `/owners/fred`, |
|
the value of `ownerId` is `fred`. |
|
|
|
[TIP] |
|
==== |
|
|
|
To process the @PathVariable annotation, Spring MVC needs to find the matching URI |
|
template variable by name. You can specify it in the annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET) |
|
public String findOwner(**@PathVariable("ownerId")** String theOwner, Model model) { |
|
// implementation omitted |
|
} |
|
---- |
|
|
|
Or if the URI template variable name matches the method argument name you can omit that |
|
detail. As long as your code is not compiled without debugging information, Spring MVC |
|
will match the method argument name to the URI template variable name: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET) |
|
public String findOwner(**@PathVariable** String ownerId, Model model) { |
|
// implementation omitted |
|
} |
|
---- |
|
==== |
|
|
|
A method can have any number of `@PathVariable` annotations: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping(value="/owners/{ownerId}/pets/{petId}", method=RequestMethod.GET) |
|
public String findPet(**@PathVariable** String ownerId, **@PathVariable** String petId, Model model) { |
|
Owner owner = ownerService.findOwner(ownerId); |
|
Pet pet = owner.getPet(petId); |
|
model.addAttribute("pet", pet); |
|
return "displayPet"; |
|
} |
|
---- |
|
|
|
When a `@PathVariable` annotation is used on a `Map<String, String>` argument, the map |
|
is populated with all URI template variables. |
|
|
|
A URI template can be assembled from type and path level __@RequestMapping__ |
|
annotations. As a result the `findPet()` method can be invoked with a URL such as |
|
`/owners/42/pets/21`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
@RequestMapping(**"/owners/{ownerId}"**) |
|
public class RelativePathUriTemplateController { |
|
|
|
@RequestMapping(**"/pets/{petId}"**) |
|
public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { |
|
// implementation omitted |
|
} |
|
|
|
} |
|
---- |
|
|
|
A `@PathVariable` argument can be of __any simple type__ such as int, long, Date, etc. |
|
Spring automatically converts to the appropriate type or throws a |
|
`TypeMismatchException` if it fails to do so. You can also register support for parsing |
|
additional data types. See <<mvc-ann-typeconversion>> and <<mvc-ann-webdatabinder>>. |
|
|
|
|
|
[[mvc-ann-requestmapping-uri-templates-regex]] |
|
===== URI Template Patterns with Regular Expressions |
|
Sometimes you need more precision in defining URI template variables. Consider the URL |
|
`"/spring-web/spring-web-3.0.5.jar"`. How do you break it down into multiple parts? |
|
|
|
The `@RequestMapping` annotation supports the use of regular expressions in URI template |
|
variables. The syntax is `{varName:regex}` where the first part defines the variable |
|
name and the second - the regular expression.For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping("/spring-web/{symbolicName:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{extension:\\.[a-z]+}") |
|
public void handle(@PathVariable String version, @PathVariable String extension) { |
|
// ... |
|
} |
|
} |
|
---- |
|
|
|
|
|
[[mvc-ann-requestmapping-patterns]] |
|
===== Path Patterns |
|
In addition to URI templates, the `@RequestMapping` annotation also supports Ant-style |
|
path patterns (for example, `/myPath/*.do`). A combination of URI template variables and |
|
Ant-style globs is also supported (e.g. `/owners/*/pets/{petId}`). |
|
|
|
|
|
[[mvc-ann-requestmapping-pattern-comparison]] |
|
===== Path Pattern Comparison |
|
When a URL matches multiple patterns, a sort is used to find the most specific match. |
|
|
|
A pattern with a lower count of URI variables and wild cards is considered more specific. |
|
For example `/hotels/{hotel}/*` has 1 URI variable and 1 wild card and is considered |
|
more specific than `/hotels/{hotel}/**` which as 1 URI variable and 2 wild cards. |
|
|
|
If two patterns have the same count, the one that is longer is considered more specific. |
|
For example `/foo/bar*` is longer and considered more specific than `/foo/*`. |
|
|
|
When two patterns have the same count and length, the pattern with fewer wild cards is considered more specific. |
|
For example `/hotels/{hotel}` is more specific than `/hotels/*`. |
|
|
|
There are also some additional special rules: |
|
|
|
* The *default mapping pattern* `/**` is less specific than any other pattern. |
|
For example `/api/{a}/{b}/{c}` is more specific. |
|
* A *prefix pattern* such as `/public/**` is less specific than any other pattern that doesn't contain double wildcards. |
|
For example `/public/path3/{a}/{b}/{c}` is more specific. |
|
|
|
For the full details see `AntPatternComparator` in `AntPathMatcher`. Note that the PathMatcher |
|
can be customized (see <<mvc-config-path-matching>> in the section on configuring Spring MVC). |
|
|
|
|
|
[[mvc-ann-requestmapping-placeholders]] |
|
===== Path Patterns with Placeholders |
|
Patterns in `@RequestMapping` annotations support ${...} placeholders against local |
|
properties and/or system properties and environment variables. This may be useful in |
|
cases where the path a controller is mapped to may need to be customized through |
|
configuration. For more information on placeholders, see the javadocs of the |
|
`PropertyPlaceholderConfigurer` class. |
|
|
|
|
|
|
|
[[mvc-ann-requestmapping-suffix-pattern-match]] |
|
===== Path Pattern Matching By Suffix |
|
By default Spring MVC automatically performs `".*"` suffix pattern matching so |
|
that a controller mapped to `/person` is also implicitly mapped to `/person.*`. |
|
This allows indicating content types via file extensions, e.g. `/person.pdf`, |
|
`/person.xml`, etc. A common pitfall however is when the last path segment of the |
|
mapping is a URI variable, e.g. `/person/{id}`. While a request for `/person/1.json` |
|
would correctly result in path variable id=1 and extension ".json", when the id |
|
naturally contains a dot, e.g. `/person/joe@email.com` the result does not match |
|
expectations. Clearly here ".com" is not a file extension. |
|
|
|
The proper way to address this is to configure Spring MVC to only do suffix pattern |
|
matching against file extensions registered for content negotiation purposes. |
|
For more on this, first see <<mvc-config-content-negotiation>> and then |
|
<<mvc-config-path-matching>> showing how to enable suffix pattern matching |
|
along with how to use registered suffix patterns only. |
|
|
|
|
|
|
|
[[mvc-ann-matrix-variables]] |
|
===== Matrix Variables |
|
The URI specification http://tools.ietf.org/html/rfc3986#section-3.3[RFC 3986] defines |
|
the possibility of including name-value pairs within path segments. There is no specific |
|
term used in the spec. The general "URI path parameters" could be applied although the |
|
more unique http://www.w3.org/DesignIssues/MatrixURIs.html["Matrix URIs"], originating |
|
from an old post by Tim Berners-Lee, is also frequently used and fairly well known. |
|
Within Spring MVC these are referred to as matrix variables. |
|
|
|
Matrix variables can appear in any path segment, each matrix variable separated with a |
|
";" (semicolon). For example: `"/cars;color=red;year=2012"`. Multiple values may be |
|
either "," (comma) separated `"color=red,green,blue"` or the variable name may be |
|
repeated `"color=red;color=green;color=blue"`. |
|
|
|
If a URL is expected to contain matrix variables, the request mapping pattern must |
|
represent them with a URI template. This ensures the request can be matched correctly |
|
regardless of whether matrix variables are present or not and in what order they are |
|
provided. |
|
|
|
Below is an example of extracting the matrix variable "q": |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// GET /pets/42;q=11;r=22 |
|
|
|
@RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET) |
|
public void findPet(@PathVariable String petId, @MatrixVariable int q) { |
|
|
|
// petId == 42 |
|
// q == 11 |
|
|
|
} |
|
---- |
|
|
|
Since all path segments may contain matrix variables, in some cases you need to be more |
|
specific to identify where the variable is expected to be: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// GET /owners/42;q=11/pets/21;q=22 |
|
|
|
@RequestMapping(value = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET) |
|
public void findPet( |
|
@MatrixVariable(value="q", pathVar="ownerId") int q1, |
|
@MatrixVariable(value="q", pathVar="petId") int q2) { |
|
|
|
// q1 == 11 |
|
// q2 == 22 |
|
|
|
} |
|
---- |
|
|
|
A matrix variable may be defined as optional and a default value specified: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// GET /pets/42 |
|
|
|
@RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET) |
|
public void findPet(@MatrixVariable(required=false, defaultValue="1") int q) { |
|
|
|
// q == 1 |
|
|
|
} |
|
---- |
|
|
|
All matrix variables may be obtained in a Map: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// GET /owners/42;q=11;r=12/pets/21;q=22;s=23 |
|
|
|
@RequestMapping(value = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET) |
|
public void findPet( |
|
@MatrixVariable Map<String, String> matrixVars, |
|
@MatrixVariable(pathVar="petId"") Map<String, String> petMatrixVars) { |
|
|
|
// matrixVars: ["q" : [11,22], "r" : 12, "s" : 23] |
|
// petMatrixVars: ["q" : 11, "s" : 23] |
|
|
|
} |
|
---- |
|
|
|
Note that to enable the use of matrix variables, you must set the |
|
`removeSemicolonContent` property of `RequestMappingHandlerMapping` to `false`. By |
|
default it is set to `true`. |
|
|
|
[TIP] |
|
==== |
|
|
|
The MVC Java config and the MVC namespace both provide options for enabling the use of |
|
matrix variables. |
|
|
|
If you are using Java config, The <<mvc-config-advanced-java, Advanced Customizations |
|
with MVC Java Config>> section describes how the `RequestMappingHandlerMapping` can |
|
be customized. |
|
|
|
In the MVC namespace, the `<mvc:annotation-driven>` element has an |
|
`enable-matrix-variables` attribute that should be set to `true`. By default it is set |
|
to `false`. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:mvc="http://www.springframework.org/schema/mvc" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/mvc |
|
http://www.springframework.org/schema/mvc/spring-mvc.xsd"> |
|
|
|
<mvc:annotation-driven enable-matrix-variables="true"/> |
|
|
|
</beans> |
|
---- |
|
==== |
|
|
|
[[mvc-ann-requestmapping-consumes]] |
|
===== Consumable Media Types |
|
You can narrow the primary mapping by specifying a list of consumable media types. The |
|
request will be matched only if the __Content-Type__ request header matches the specified |
|
media type. For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
@RequestMapping(value = "/pets", method = RequestMethod.POST, **consumes="application/json"**) |
|
public void addPet(@RequestBody Pet pet, Model model) { |
|
// implementation omitted |
|
} |
|
---- |
|
|
|
Consumable media type expressions can also be negated as in __!text/plain__ to match to |
|
all requests other than those with __Content-Type__ of __text/plain__. |
|
|
|
[TIP] |
|
==== |
|
|
|
The __consumes__ condition is supported on the type and on the method level. Unlike most |
|
other conditions, when used at the type level, method-level consumable types override |
|
rather than extend type-level consumable types. |
|
==== |
|
|
|
|
|
[[mvc-ann-requestmapping-produces]] |
|
===== Producible Media Types |
|
You can narrow the primary mapping by specifying a list of producible media types. The |
|
request will be matched only if the __Accept__ request header matches one of these |
|
values. Furthermore, use of the __produces__ condition ensures the actual content type |
|
used to generate the response respects the media types specified in the __produces__ |
|
condition. For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
@RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET, **produces="application/json"**) |
|
@ResponseBody |
|
public Pet getPet(@PathVariable String petId, Model model) { |
|
// implementation omitted |
|
} |
|
---- |
|
|
|
Just like with __consumes__, producible media type expressions can be negated as in |
|
__!text/plain__ to match to all requests other than those with an __Accept__ header |
|
value of __text/plain__. |
|
|
|
[TIP] |
|
==== |
|
The __produces__ condition is supported on the type and on the method level. Unlike most |
|
other conditions, when used at the type level, method-level producible types override |
|
rather than extend type-level producible types. |
|
==== |
|
|
|
|
|
[[mvc-ann-requestmapping-params-and-headers]] |
|
===== Request Parameters and Header Values |
|
You can narrow request matching through request parameter conditions such as |
|
`"myParam"`, `"!myParam"`, or `"myParam=myValue"`. The first two test for request |
|
parameter presence/absence and the third for a specific parameter value. Here is an |
|
example with a request parameter value condition: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
@RequestMapping("/owners/{ownerId}") |
|
public class RelativePathUriTemplateController { |
|
|
|
@RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET, **params="myParam=myValue"**) |
|
public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { |
|
// implementation omitted |
|
} |
|
|
|
} |
|
---- |
|
|
|
The same can be done to test for request header presence/absence or to match based on a |
|
specific request header value: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
@RequestMapping("/owners/{ownerId}") |
|
public class RelativePathUriTemplateController { |
|
|
|
@RequestMapping(value = "/pets", method = RequestMethod.GET, **headers="myHeader=myValue"**) |
|
public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { |
|
// implementation omitted |
|
} |
|
|
|
} |
|
---- |
|
|
|
[TIP] |
|
==== |
|
|
|
Although you can match to __Content-Type__ and __Accept__ header values using media type |
|
wild cards (for example __"content-type=text/*"__ will match to __"text/plain"__ and |
|
__"text/html"__), it is recommended to use the __consumes__ and __produces__ conditions |
|
respectively instead. They are intended specifically for that purpose. |
|
==== |
|
|
|
|
|
|
|
[[mvc-ann-methods]] |
|
==== Defining @RequestMapping handler methods |
|
|
|
An `@RequestMapping` handler method can have a very flexible signatures. The supported |
|
method arguments and return values are described in the following section. Most |
|
arguments can be used in arbitrary order with the only exception of `BindingResult` |
|
arguments. This is described in the next section. |
|
|
|
[NOTE] |
|
==== |
|
Spring 3.1 introduced a new set of support classes for `@RequestMapping` methods called |
|
`RequestMappingHandlerMapping` and `RequestMappingHandlerAdapter` respectively. They are |
|
recommended for use and even required to take advantage of new features in Spring MVC |
|
3.1 and going forward. The new support classes are enabled by default from the MVC |
|
namespace and with use of the MVC Java config but must be configured explicitly if using |
|
neither. |
|
==== |
|
|
|
|
|
[[mvc-ann-arguments]] |
|
===== Supported method argument types |
|
The following are the supported method arguments: |
|
|
|
* Request or response objects (Servlet API). Choose any specific request or response |
|
type, for example `ServletRequest` or `HttpServletRequest`. |
|
* Session object (Servlet API): of type `HttpSession`. An argument of this type enforces |
|
the presence of a corresponding session. As a consequence, such an argument is never |
|
`null`. |
|
|
|
[NOTE] |
|
==== |
|
Session access may not be thread-safe, in particular in a Servlet environment. Consider |
|
setting the ++RequestMappingHandlerAdapter++'s "synchronizeOnSession" flag to "true" if |
|
multiple requests are allowed to access a session concurrently. |
|
==== |
|
|
|
* `org.springframework.web.context.request.WebRequest` or |
|
`org.springframework.web.context.request.NativeWebRequest`. Allows for generic |
|
request parameter access as well as request/session attribute access, without ties |
|
to the native Servlet/Portlet API. |
|
* `java.util.Locale` for the current request locale, determined by the most specific |
|
locale resolver available, in effect, the configured `LocaleResolver` / |
|
`LocaleContextResolver` in an MVC environment. |
|
* `java.util.TimeZone` (Java 6+) / `java.time.ZoneId` (on Java 8) for the time zone |
|
associated with the current request, as determined by a `LocaleContextResolver`. |
|
* `java.io.InputStream` / `java.io.Reader` for access to the request's content. |
|
This value is the raw InputStream/Reader as exposed by the Servlet API. |
|
* `java.io.OutputStream` / `java.io.Writer` for generating the response's content. |
|
This value is the raw OutputStream/Writer as exposed by the Servlet API. |
|
* `org.springframework.http.HttpMethod` for the HTTP request method. |
|
* `java.security.Principal` containing the currently authenticated user. |
|
* `@PathVariable` annotated parameters for access to URI template variables. See |
|
<<mvc-ann-requestmapping-uri-templates>>. |
|
* `@MatrixVariable` annotated parameters for access to name-value pairs located in |
|
URI path segments. See <<mvc-ann-matrix-variables>>. |
|
* `@RequestParam` annotated parameters for access to specific Servlet request |
|
parameters. Parameter values are converted to the declared method argument type. |
|
See <<mvc-ann-requestparam>>. |
|
* `@RequestHeader` annotated parameters for access to specific Servlet request HTTP |
|
headers. Parameter values are converted to the declared method argument type. |
|
See <<mvc-ann-requestheader>>. |
|
* `@RequestBody` annotated parameters for access to the HTTP request body. Parameter |
|
values are converted to the declared method argument type using |
|
++HttpMessageConverter++s. See <<mvc-ann-requestbody>>. |
|
* `@RequestPart` annotated parameters for access to the content of a |
|
"multipart/form-data" request part. See <<mvc-multipart-forms-non-browsers>> and |
|
<<mvc-multipart>>. |
|
* `HttpEntity<?>` parameters for access to the Servlet request HTTP headers and |
|
contents. The request stream will be converted to the entity body using |
|
++HttpMessageConverter++s. See <<mvc-ann-httpentity>>. |
|
* `java.util.Map` / `org.springframework.ui.Model` / `org.springframework.ui.ModelMap` |
|
for enriching the implicit model that is exposed to the web view. |
|
* `org.springframework.web.servlet.mvc.support.RedirectAttributes` to specify the exact |
|
set of attributes to use in case of a redirect and also to add flash attributes |
|
(attributes stored temporarily on the server-side to make them available to the |
|
request after the redirect). `RedirectAttributes` is used instead of the implicit |
|
model if the method returns a "redirect:" prefixed view name or `RedirectView`. |
|
* Command or form objects to bind request parameters to bean properties (via setters) |
|
or directly to fields, with customizable type conversion, depending on `@InitBinder` |
|
methods and/or the HandlerAdapter configuration. See the `webBindingInitializer` |
|
property on `RequestMappingHandlerAdapter`. Such command objects along with their |
|
validation results will be exposed as model attributes by default, using the command |
|
class class name - e.g. model attribute "orderAddress" for a command object of type |
|
"some.package.OrderAddress". The `ModelAttribute` annotation can be used on a method |
|
argument to customize the model attribute name used. |
|
* `org.springframework.validation.Errors` / |
|
`org.springframework.validation.BindingResult` validation results for a preceding |
|
command or form object (the immediately preceding method argument). |
|
* `org.springframework.web.bind.support.SessionStatus` status handle for marking form |
|
processing as complete, which triggers the cleanup of session attributes that have |
|
been indicated by the `@SessionAttributes` annotation at the handler type level. |
|
* `org.springframework.web.util.UriComponentsBuilder` a builder for preparing a URL |
|
relative to the current request's host, port, scheme, context path, and the literal |
|
part of the servlet mapping. |
|
|
|
The `Errors` or `BindingResult` parameters have to follow the model object that is being |
|
bound immediately as the method signature might have more that one model object and |
|
Spring will create a separate `BindingResult` instance for each of them so the following |
|
sample won't work: |
|
|
|
.Invalid ordering of BindingResult and @ModelAttribute |
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping(method = RequestMethod.POST) |
|
public String processSubmit(**@ModelAttribute("pet") Pet pet**, Model model, **BindingResult result**) { ... } |
|
---- |
|
|
|
Note, that there is a `Model` parameter in between `Pet` and `BindingResult`. To get |
|
this working you have to reorder the parameters as follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping(method = RequestMethod.POST) |
|
public String processSubmit(**@ModelAttribute("pet") Pet pet**, **BindingResult result**, Model model) { ... } |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
JDK 1.8's `java.util.Optional` is supported as a method parameter type with annotations |
|
that have a `required` attribute (e.g. `@RequestParam`, `@RequestHeader`, etc. The use |
|
of `java.util.Optional` in those cases is equivalent to having `required=false`. |
|
==== |
|
|
|
|
|
[[mvc-ann-return-types]] |
|
===== Supported method return types |
|
The following are the supported return types: |
|
|
|
* A `ModelAndView` object, with the model implicitly enriched with command objects and |
|
the results of `@ModelAttribute` annotated reference data accessor methods. |
|
* A `Model` object, with the view name implicitly determined through a |
|
`RequestToViewNameTranslator` and the model implicitly enriched with command objects |
|
and the results of `@ModelAttribute` annotated reference data accessor methods. |
|
* A `Map` object for exposing a model, with the view name implicitly determined through |
|
a `RequestToViewNameTranslator` and the model implicitly enriched with command objects |
|
and the results of `@ModelAttribute` annotated reference data accessor methods. |
|
* A `View` object, with the model implicitly determined through command objects and |
|
`@ModelAttribute` annotated reference data accessor methods. The handler method may |
|
also programmatically enrich the model by declaring a `Model` argument (see above). |
|
* A `String` value that is interpreted as the logical view name, with the model |
|
implicitly determined through command objects and `@ModelAttribute` annotated |
|
reference data accessor methods. The handler method may also programmatically enrich |
|
the model by declaring a `Model` argument (see above). |
|
* `void` if the method handles the response itself (by writing the response content |
|
directly, declaring an argument of type `ServletResponse` / `HttpServletResponse` for |
|
that purpose) or if the view name is supposed to be implicitly determined through a |
|
`RequestToViewNameTranslator` (not declaring a response argument in the handler method |
|
signature). |
|
* If the method is annotated with `@ResponseBody`, the return type is written to the |
|
response HTTP body. The return value will be converted to the declared method argument |
|
type using ++HttpMessageConverter++s. See <<mvc-ann-responsebody>>. |
|
* An `HttpEntity<?>` or `ResponseEntity<?>` object to provide access to the Servlet |
|
response HTTP headers and contents. The entity body will be converted to the response |
|
stream using ++HttpMessageConverter++s. See <<mvc-ann-httpentity>>. |
|
* An `HttpHeaders` object to return a response with no body. |
|
* A `Callable<?>` can be returned when the application wants to produce the return value |
|
asynchronously in a thread managed by Spring MVC. |
|
* A `DeferredResult<?>` can be returned when the application wants to produce the return |
|
value from a thread of its own choosing. |
|
* A `ListenableFuture<?>` can be returned when the application wants to produce the return |
|
value from a thread of its own choosing. |
|
* Any other return type is considered to be a single model attribute to be exposed to |
|
the view, using the attribute name specified through `@ModelAttribute` at the method |
|
level (or the default attribute name based on the return type class name). The model |
|
is implicitly enriched with command objects and the results of `@ModelAttribute` |
|
annotated reference data accessor methods. |
|
|
|
|
|
[[mvc-ann-requestparam]] |
|
===== Binding request parameters to method parameters with @RequestParam |
|
|
|
Use the `@RequestParam` annotation to bind request parameters to a method parameter in |
|
your controller. |
|
|
|
The following code snippet shows the usage: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
@RequestMapping("/pets") |
|
@SessionAttributes("pet") |
|
public class EditPetForm { |
|
|
|
// ... |
|
|
|
@RequestMapping(method = RequestMethod.GET) |
|
public String setupForm(**@RequestParam("petId") int petId**, ModelMap model) { |
|
Pet pet = this.clinic.loadPet(petId); |
|
model.addAttribute("pet", pet); |
|
return "petForm"; |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
Parameters using this annotation are required by default, but you can specify that a |
|
parameter is optional by setting ++@RequestParam++'s `required` attribute to `false` |
|
(e.g., `@RequestParam(value="id", required=false)`). |
|
|
|
Type conversion is applied automatically if the target method parameter type is not |
|
`String`. See <<mvc-ann-typeconversion>>. |
|
|
|
When an `@RequestParam` annotation is used on a `Map<String, String>` or |
|
`MultiValueMap<String, String>` argument, the map is populated with all request |
|
parameters. |
|
|
|
|
|
[[mvc-ann-requestbody]] |
|
===== Mapping the request body with the @RequestBody annotation |
|
The `@RequestBody` method parameter annotation indicates that a method parameter should |
|
be bound to the value of the HTTP request body. For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping(value = "/something", method = RequestMethod.PUT) |
|
public void handle(@RequestBody String body, Writer writer) throws IOException { |
|
writer.write(body); |
|
} |
|
---- |
|
|
|
You convert the request body to the method argument by using an `HttpMessageConverter`. |
|
`HttpMessageConverter` is responsible for converting from the HTTP request message to an |
|
object and converting from an object to the HTTP response body. The |
|
`RequestMappingHandlerAdapter` supports the `@RequestBody` annotation with the following |
|
default `HttpMessageConverters`: |
|
|
|
* `ByteArrayHttpMessageConverter` converts byte arrays. |
|
* `StringHttpMessageConverter` converts strings. |
|
* `FormHttpMessageConverter` converts form data to/from a MultiValueMap<String, String>. |
|
* `SourceHttpMessageConverter` converts to/from a javax.xml.transform.Source. |
|
|
|
For more information on these converters, see <<rest-message-conversion,Message |
|
Converters>>. Also note that if using the MVC namespace or the MVC Java config, a wider |
|
range of message converters are registered by default. See <<mvc-config-enable>> for more information. |
|
|
|
If you intend to read and write XML, you will need to configure the |
|
`MarshallingHttpMessageConverter` with a specific `Marshaller` and an `Unmarshaller` |
|
implementation from the `org.springframework.oxm` package. The example below shows how |
|
to do that directly in your configuration but if your application is configured through |
|
the MVC namespace or the MVC Java config see <<mvc-config-enable>> instead. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> |
|
<property name="messageConverters"> |
|
<util:list id="beanList"> |
|
<ref bean="stringHttpMessageConverter"/> |
|
<ref bean="marshallingHttpMessageConverter"/> |
|
</util:list> |
|
</property |
|
</bean> |
|
|
|
<bean id="stringHttpMessageConverter" |
|
class="org.springframework.http.converter.StringHttpMessageConverter"/> |
|
|
|
<bean id="marshallingHttpMessageConverter" |
|
class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter"> |
|
<property name="marshaller" ref="castorMarshaller" /> |
|
<property name="unmarshaller" ref="castorMarshaller" /> |
|
</bean> |
|
|
|
<bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller"/> |
|
---- |
|
|
|
An `@RequestBody` method parameter can be annotated with `@Valid`, in which case it will |
|
be validated using the configured `Validator` instance. When using the MVC namespace or |
|
the MVC Java config, a JSR-303 validator is configured automatically assuming a JSR-303 |
|
implementation is available on the classpath. |
|
|
|
Just like with `@ModelAttribute` parameters, an `Errors` argument can be used to examine |
|
the errors. If such an argument is not declared, a `MethodArgumentNotValidException` |
|
will be raised. The exception is handled in the `DefaultHandlerExceptionResolver`, which |
|
sends a `400` error back to the client. |
|
|
|
[NOTE] |
|
==== |
|
Also see <<mvc-config-enable>> for |
|
information on configuring message converters and a validator through the MVC namespace |
|
or the MVC Java config. |
|
==== |
|
|
|
|
|
[[mvc-ann-responsebody]] |
|
===== Mapping the response body with the @ResponseBody annotation |
|
|
|
The `@ResponseBody` annotation is similar to `@RequestBody`. This annotation can be put |
|
on a method and indicates that the return type should be written straight to the HTTP |
|
response body (and not placed in a Model, or interpreted as a view name). For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping(value = "/something", method = RequestMethod.PUT) |
|
@ResponseBody |
|
public String helloWorld() { |
|
return "Hello World"; |
|
} |
|
---- |
|
|
|
The above example will result in the text `Hello World` being written to the HTTP |
|
response stream. |
|
|
|
As with `@RequestBody`, Spring converts the returned object to a response body by using |
|
an `HttpMessageConverter`. For more information on these converters, see the previous |
|
section and <<rest-message-conversion,Message Converters>>. |
|
|
|
[[mvc-ann-restcontroller]] |
|
===== Creating REST Controllers with the @RestController annotation |
|
|
|
It's a very common use case to have Controllers implement a REST API, thus serving only |
|
JSON, XML or custom MediaType content. For convenience, instead of annotating all your |
|
`@RequestMapping` methods with `@ResponseBody`, you can annotate your Controller Class |
|
with `@RestController`. |
|
|
|
{javadoc-baseurl}/org/springframework/web/bind/annotation/RestController.html[`@RestController`] |
|
is a stereotype annotation that combines `@ResponseBody` and `@Controller`. More than |
|
that, it gives more meaning to your Controller and also may carry additional semantics |
|
in future releases of the framework. |
|
|
|
As with regular ++@Controller++s, a `@RestController` may be assisted by a |
|
`@ControllerAdvice` Bean. See the <<mvc-ann-controller-advice>> section for more details. |
|
|
|
[[mvc-ann-httpentity]] |
|
===== Using HttpEntity |
|
|
|
The `HttpEntity` is similar to `@RequestBody` and `@ResponseBody`. Besides getting |
|
access to the request and response body, `HttpEntity` (and the response-specific |
|
subclass `ResponseEntity`) also allows access to the request and response headers, like |
|
so: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping("/something") |
|
public ResponseEntity<String> handle(HttpEntity<byte[]> requestEntity) throws UnsupportedEncodingException { |
|
String requestHeader = requestEntity.getHeaders().getFirst("MyRequestHeader")); |
|
byte[] requestBody = requestEntity.getBody(); |
|
|
|
// do something with request header and body |
|
|
|
HttpHeaders responseHeaders = new HttpHeaders(); |
|
responseHeaders.set("MyResponseHeader", "MyValue"); |
|
return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED); |
|
} |
|
---- |
|
|
|
The above example gets the value of the `MyRequestHeader` request header, and reads the |
|
body as a byte array. It adds the `MyResponseHeader` to the response, writes `Hello |
|
World` to the response stream, and sets the response status code to 201 (Created). |
|
|
|
As with `@RequestBody` and `@ResponseBody`, Spring uses `HttpMessageConverter` to |
|
convert from and to the request and response streams. For more information on these |
|
converters, see the previous section and <<rest-message-conversion,Message Converters>>. |
|
|
|
|
|
[[mvc-ann-modelattrib-methods]] |
|
===== Using @ModelAttribute on a method |
|
|
|
The `@ModelAttribute` annotation can be used on methods or on method arguments. This |
|
section explains its usage on methods while the next section explains its usage on |
|
method arguments. |
|
|
|
An `@ModelAttribute` on a method indicates the purpose of that method is to add one or |
|
more model attributes. Such methods support the same argument types as `@RequestMapping` |
|
methods but cannot be mapped directly to requests. Instead `@ModelAttribute` methods in |
|
a controller are invoked before `@RequestMapping` methods, within the same controller. A |
|
couple of examples: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// Add one attribute |
|
// The return value of the method is added to the model under the name "account" |
|
// You can customize the name via @ModelAttribute("myAccount") |
|
|
|
@ModelAttribute |
|
public Account addAccount(@RequestParam String number) { |
|
return accountManager.findAccount(number); |
|
} |
|
|
|
// Add multiple attributes |
|
|
|
@ModelAttribute |
|
public void populateModel(@RequestParam String number, Model model) { |
|
model.addAttribute(accountManager.findAccount(number)); |
|
// add more ... |
|
} |
|
---- |
|
|
|
`@ModelAttribute` methods are used to populate the model with commonly needed attributes |
|
for example to fill a drop-down with states or with pet types, or to retrieve a command |
|
object like Account in order to use it to represent the data on an HTML form. The latter |
|
case is further discussed in the next section. |
|
|
|
Note the two styles of `@ModelAttribute` methods. In the first, the method adds an |
|
attribute implicitly by returning it. In the second, the method accepts a `Model` and |
|
adds any number of model attributes to it. You can choose between the two styles |
|
depending on your needs. |
|
|
|
A controller can have any number of `@ModelAttribute` methods. All such methods are |
|
invoked before `@RequestMapping` methods of the same controller. |
|
|
|
`@ModelAttribute` methods can also be defined in an ++@ControllerAdvice++-annotated class |
|
and such methods apply to many controllers. See the <<mvc-ann-controller-advice>> section |
|
for more details. |
|
|
|
[TIP] |
|
==== |
|
|
|
What happens when a model attribute name is not explicitly specified? In such cases a |
|
default name is assigned to the model attribute based on its type. For example if the |
|
method returns an object of type `Account`, the default name used is "account". You can |
|
change that through the value of the `@ModelAttribute` annotation. If adding attributes |
|
directly to the `Model`, use the appropriate overloaded `addAttribute(..)` method - |
|
i.e., with or without an attribute name. |
|
==== |
|
|
|
The `@ModelAttribute` annotation can be used on `@RequestMapping` methods as well. In |
|
that case the return value of the `@RequestMapping` method is interpreted as a model |
|
attribute rather than as a view name. The view name is derived from view name |
|
conventions instead much like for methods returning void -- see <<mvc-coc-r2vnt>>. |
|
|
|
|
|
[[mvc-ann-modelattrib-method-args]] |
|
===== Using @ModelAttribute on a method argument |
|
|
|
As explained in the previous section `@ModelAttribute` can be used on methods or on |
|
method arguments. This section explains its usage on method arguments. |
|
|
|
An `@ModelAttribute` on a method argument indicates the argument should be retrieved |
|
from the model. If not present in the model, the argument should be instantiated first |
|
and then added to the model. Once present in the model, the argument's fields should be |
|
populated from all request parameters that have matching names. This is known as data |
|
binding in Spring MVC, a very useful mechanism that saves you from having to parse each |
|
form field individually. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST) |
|
public String processSubmit(**@ModelAttribute Pet pet**) { } |
|
---- |
|
|
|
Given the above example where can the Pet instance come from? There are several options: |
|
|
|
* It may already be in the model due to use of `@SessionAttributes` -- see |
|
<<mvc-ann-sessionattrib>>. |
|
* It may already be in the model due to an `@ModelAttribute` method in the same |
|
controller -- as explained in the previous section. |
|
* It may be retrieved based on a URI template variable and type converter (explained in |
|
more detail below). |
|
* It may be instantiated using its default constructor. |
|
|
|
An `@ModelAttribute` method is a common way to to retrieve an attribute from the |
|
database, which may optionally be stored between requests through the use of |
|
`@SessionAttributes`. In some cases it may be convenient to retrieve the attribute by |
|
using an URI template variable and a type converter. Here is an example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping(value="/accounts/{account}", method = RequestMethod.PUT) |
|
public String save(@ModelAttribute("account") Account account) { |
|
|
|
} |
|
---- |
|
|
|
In this example the name of the model attribute (i.e. "account") matches the name of a |
|
URI template variable. If you register `Converter<String, Account>` that can turn the |
|
`String` account value into an `Account` instance, then the above example will work |
|
without the need for an `@ModelAttribute` method. |
|
|
|
The next step is data binding. The `WebDataBinder` class matches request parameter names |
|
-- including query string parameters and form fields -- to model attribute fields by |
|
name. Matching fields are populated after type conversion (from String to the target |
|
field type) has been applied where necessary. Data binding and validation are covered in |
|
<<validation>>. Customizing the data binding process for a controller level is covered |
|
in <<mvc-ann-webdatabinder>>. |
|
|
|
As a result of data binding there may be errors such as missing required fields or type |
|
conversion errors. To check for such errors add a `BindingResult` argument immediately |
|
following the `@ModelAttribute` argument: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST) |
|
public String processSubmit(**@ModelAttribute("pet") Pet pet**, BindingResult result) { |
|
|
|
if (result.hasErrors()) { |
|
return "petForm"; |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
With a `BindingResult` you can check if errors were found in which case it's common to |
|
render the same form where the errors can be shown with the help of Spring's `<errors>` |
|
form tag. |
|
|
|
In addition to data binding you can also invoke validation using your own custom |
|
validator passing the same `BindingResult` that was used to record data binding errors. |
|
That allows for data binding and validation errors to be accumulated in one place and |
|
subsequently reported back to the user: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST) |
|
public String processSubmit(**@ModelAttribute("pet") Pet pet**, BindingResult result) { |
|
|
|
new PetValidator().validate(pet, result); |
|
if (result.hasErrors()) { |
|
return "petForm"; |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
Or you can have validation invoked automatically by adding the JSR-303 `@Valid` |
|
annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST) |
|
public String processSubmit(**@Valid @ModelAttribute("pet") Pet pet**, BindingResult result) { |
|
|
|
if (result.hasErrors()) { |
|
return "petForm"; |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
See <<validation-beanvalidation>> and <<validation>> for details on how to configure and |
|
use validation. |
|
|
|
|
|
[[mvc-ann-sessionattrib]] |
|
===== Using @SessionAttributes to store model attributes in the HTTP session between requests |
|
|
|
The type-level `@SessionAttributes` annotation declares session attributes used by a |
|
specific handler. This will typically list the names of model attributes or types of |
|
model attributes which should be transparently stored in the session or some |
|
conversational storage, serving as form-backing beans between subsequent requests. |
|
|
|
The following code snippet shows the usage of this annotation, specifying the model |
|
attribute name: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
@RequestMapping("/editPet.do") |
|
**@SessionAttributes("pet")** |
|
public class EditPetForm { |
|
// ... |
|
} |
|
---- |
|
|
|
[[mvc-ann-redirect-attributes]] |
|
===== Specifying redirect and flash attributes |
|
By default all model attributes are considered to be exposed as URI template variables |
|
in the redirect URL. Of the remaining attributes those that are primitive types or |
|
collections/arrays of primitive types are automatically appended as query parameters. |
|
|
|
In annotated controllers however the model may contain additional attributes originally |
|
added for rendering purposes (e.g. drop-down field values). To gain precise control over |
|
the attributes used in a redirect scenario, an `@RequestMapping` method can declare an |
|
argument of type `RedirectAttributes` and use it to add attributes for use in |
|
`RedirectView`. If the controller method does redirect, the content of |
|
`RedirectAttributes` is used. Otherwise the content of the default `Model` is used. |
|
|
|
The `RequestMappingHandlerAdapter` provides a flag called |
|
`"ignoreDefaultModelOnRedirect"` that can be used to indicate the content of the default |
|
`Model` should never be used if a controller method redirects. Instead the controller |
|
method should declare an attribute of type `RedirectAttributes` or if it doesn't do so |
|
no attributes should be passed on to `RedirectView`. Both the MVC namespace and the MVC |
|
Java config keep this flag set to `false` in order to maintain backwards compatibility. |
|
However, for new applications we recommend setting it to `true` |
|
|
|
The `RedirectAttributes` interface can also be used to add flash attributes. Unlike |
|
other redirect attributes, which end up in the target redirect URL, flash attributes are |
|
saved in the HTTP session (and hence do not appear in the URL). The model of the |
|
controller serving the target redirect URL automatically receives these flash attributes |
|
after which they are removed from the session. See <<mvc-flash-attributes>> for an |
|
overview of the general support for flash attributes in Spring MVC. |
|
|
|
|
|
[[mvc-ann-form-urlencoded-data]] |
|
===== Working with "application/x-www-form-urlencoded" data |
|
|
|
The previous sections covered use of `@ModelAttribute` to support form submission |
|
requests from browser clients. The same annotation is recommended for use with requests |
|
from non-browser clients as well. However there is one notable difference when it comes |
|
to working with HTTP PUT requests. Browsers can submit form data via HTTP GET or HTTP |
|
POST. Non-browser clients can also submit forms via HTTP PUT. This presents a challenge |
|
because the Servlet specification requires the `ServletRequest.getParameter*()` family |
|
of methods to support form field access only for HTTP POST, not for HTTP PUT. |
|
|
|
To support HTTP PUT and PATCH requests, the `spring-web` module provides the filter |
|
`HttpPutFormContentFilter`, which can be configured in `web.xml`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<filter> |
|
<filter-name>httpPutFormFilter</filter-name> |
|
<filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class> |
|
</filter> |
|
|
|
<filter-mapping> |
|
<filter-name>httpPutFormFilter</filter-name> |
|
<servlet-name>dispatcherServlet</servlet-name> |
|
</filter-mapping> |
|
|
|
<servlet> |
|
<servlet-name>dispatcherServlet</servlet-name> |
|
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> |
|
</servlet> |
|
---- |
|
|
|
The above filter intercepts HTTP PUT and PATCH requests with content type |
|
`application/x-www-form-urlencoded`, reads the form data from the body of the request, |
|
and wraps the `ServletRequest` in order to make the form data available through the |
|
`ServletRequest.getParameter*()` family of methods. |
|
|
|
[NOTE] |
|
==== |
|
As `HttpPutFormContentFilter` consumes the body of the request, it should not be |
|
configured for PUT or PATCH URLs that rely on other converters for |
|
`application/x-www-form-urlencoded`. This includes `@RequestBody MultiValueMap<String, |
|
String>` and `HttpEntity<MultiValueMap<String, String>>`. |
|
==== |
|
|
|
|
|
[[mvc-ann-cookievalue]] |
|
===== Mapping cookie values with the @CookieValue annotation |
|
The `@CookieValue` annotation allows a method parameter to be bound to the value of an |
|
HTTP cookie. |
|
|
|
Let us consider that the following cookie has been received with an http request: |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84 |
|
---- |
|
|
|
The following code sample demonstrates how to get the value of the `JSESSIONID` cookie: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping("/displayHeaderInfo.do") |
|
public void displayHeaderInfo(**@CookieValue("JSESSIONID")** String cookie) { |
|
//... |
|
} |
|
---- |
|
|
|
Type conversion is applied automatically if the target method parameter type is not |
|
`String`. See <<mvc-ann-typeconversion>>. |
|
|
|
This annotation is supported for annotated handler methods in Servlet and Portlet |
|
environments. |
|
|
|
|
|
[[mvc-ann-requestheader]] |
|
===== Mapping request header attributes with the @RequestHeader annotation |
|
The `@RequestHeader` annotation allows a method parameter to be bound to a request header. |
|
|
|
Here is a sample request header: |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Host localhost:8080 |
|
Accept text/html,application/xhtml+xml,application/xml;q=0.9 |
|
Accept-Language fr,en-gb;q=0.7,en;q=0.3 |
|
Accept-Encoding gzip,deflate |
|
Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 |
|
Keep-Alive 300 |
|
---- |
|
|
|
The following code sample demonstrates how to get the value of the `Accept-Encoding` and |
|
`Keep-Alive` headers: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping("/displayHeaderInfo.do") |
|
public void displayHeaderInfo(**@RequestHeader("Accept-Encoding")** String encoding, |
|
**@RequestHeader("Keep-Alive")** long keepAlive) { |
|
//... |
|
} |
|
---- |
|
|
|
Type conversion is applied automatically if the method parameter is not `String`. See |
|
<<mvc-ann-typeconversion>>. |
|
|
|
When an `@RequestHeader` annotation is used on a `Map<String, String>`, |
|
`MultiValueMap<String, String>`, or `HttpHeaders` argument, the map is populated |
|
with all header values. |
|
|
|
|
|
[TIP] |
|
==== |
|
Built-in support is available for converting a comma-separated string into an |
|
array/collection of strings or other types known to the type conversion system. For |
|
example a method parameter annotated with `@RequestHeader("Accept")` may be of type |
|
`String` but also `String[]` or `List<String>`. |
|
==== |
|
|
|
This annotation is supported for annotated handler methods in Servlet and Portlet |
|
environments. |
|
|
|
|
|
[[mvc-ann-typeconversion]] |
|
===== Method Parameters And Type Conversion |
|
String-based values extracted from the request including request parameters, path |
|
variables, request headers, and cookie values may need to be converted to the target |
|
type of the method parameter or field (e.g., binding a request parameter to a field in |
|
an `@ModelAttribute` parameter) they're bound to. If the target type is not `String`, |
|
Spring automatically converts to the appropriate type. All simple types such as int, |
|
long, Date, etc. are supported. You can further customize the conversion process through |
|
a `WebDataBinder` (see <<mvc-ann-webdatabinder>>) or by registering `Formatters` with |
|
the `FormattingConversionService` (see <<format>>). |
|
|
|
|
|
[[mvc-ann-webdatabinder]] |
|
===== Customizing WebDataBinder initialization |
|
To customize request parameter binding with PropertyEditors through Spring's |
|
`WebDataBinder`, you can use `@InitBinder`-annotated methods within your controller, |
|
`@InitBinder` methods within an `@ControllerAdvice` class, or provide a custom |
|
`WebBindingInitializer`. See the <<mvc-ann-controller-advice>> section for more details. |
|
|
|
[[mvc-ann-initbinder]] |
|
====== Customizing data binding with @InitBinder |
|
Annotating controller methods with `@InitBinder` allows you to configure web data |
|
binding directly within your controller class. `@InitBinder` identifies methods that |
|
initialize the `WebDataBinder` that will be used to populate command and form object |
|
arguments of annotated handler methods. |
|
|
|
Such init-binder methods support all arguments that `@RequestMapping` supports, except |
|
for command/form objects and corresponding validation result objects. Init-binder |
|
methods must not have a return value. Thus, they are usually declared as `void`. Typical |
|
arguments include `WebDataBinder` in combination with `WebRequest` or |
|
`java.util.Locale`, allowing code to register context-specific editors. |
|
|
|
The following example demonstrates the use of `@InitBinder` to configure a |
|
`CustomDateEditor` for all `java.util.Date` form properties. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
public class MyFormController { |
|
|
|
**@InitBinder** |
|
public void initBinder(WebDataBinder binder) { |
|
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); |
|
dateFormat.setLenient(false); |
|
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false)); |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
[[mvc-ann-webbindinginitializer]] |
|
====== Configuring a custom WebBindingInitializer |
|
|
|
To externalize data binding initialization, you can provide a custom implementation of |
|
the `WebBindingInitializer` interface, which you then enable by supplying a custom bean |
|
configuration for an `AnnotationMethodHandlerAdapter`, thus overriding the default |
|
configuration. |
|
|
|
The following example from the PetClinic application shows a configuration using a |
|
custom implementation of the `WebBindingInitializer` interface, |
|
`org.springframework.samples.petclinic.web.ClinicBindingInitializer`, which configures |
|
PropertyEditors required by several of the PetClinic controllers. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> |
|
<property name="cacheSeconds" value="0" /> |
|
<property name="webBindingInitializer"> |
|
<bean class="org.springframework.samples.petclinic.web.ClinicBindingInitializer" /> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
`@InitBinder` methods can also be defined in an ++@ControllerAdvice++-annotated class in |
|
which case they apply to matching controllers. This provides an alternative to using a |
|
`WebBindingInitializer`. See the <<mvc-ann-controller-advice>> section for more details. |
|
|
|
|
|
[[mvc-ann-lastmodified]] |
|
===== Support for the Last-Modified Response Header To Facilitate Content Caching |
|
An `@RequestMapping` method may wish to support `'Last-Modified'` HTTP requests, as |
|
defined in the contract for the Servlet API's `getLastModified` method, to facilitate |
|
content caching. This involves calculating a lastModified `long` value for a given |
|
request, comparing it against the `'If-Modified-Since'` request header value, and |
|
potentially returning a response with status code 304 (Not Modified). An annotated |
|
controller method can achieve that as follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping |
|
public String myHandleMethod(WebRequest webRequest, Model model) { |
|
|
|
long lastModified = // 1. application-specific calculation |
|
|
|
if (request.checkNotModified(lastModified)) { |
|
// 2. shortcut exit - no further processing necessary |
|
return null; |
|
} |
|
|
|
// 3. or otherwise further request processing, actually preparing content |
|
model.addAttribute(...); |
|
return "myViewName"; |
|
} |
|
---- |
|
|
|
There are two key elements to note: calling `request.checkNotModified(lastModified)` and |
|
returning `null`. The former sets the response status to 304 before it returns `true`. |
|
The latter, in combination with the former, causes Spring MVC to do no further |
|
processing of the request. |
|
|
|
[[mvc-ann-controller-advice]] |
|
===== Advising controllers with the `@ControllerAdvice` annotation |
|
The `@ControllerAdvice` annotation is a component annotation allowing implementation |
|
classes to be auto-detected through classpath scanning. It is automatically enabled when |
|
using the MVC namespace or the MVC Java config. |
|
|
|
Classes annotated with `@ControllerAdvice` can contain `@ExceptionHandler`, |
|
`@InitBinder`, and `@ModelAttribute` annotated methods, and these methods will apply to |
|
`@RequestMapping` methods across all controller hierarchies as opposed to the controller |
|
hierarchy within which they are declared. |
|
|
|
The `@ControllerAdvice` annotation can also target a subset of controllers with its |
|
attributes: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// Target all Controllers annotated with @RestController |
|
@ControllerAdvice(annotations = RestController.class) |
|
public class AnnotationAdvice {} |
|
|
|
// Target all Controllers within specific packages |
|
@ControllerAdvice("org.example.controllers") |
|
public class BasePackageAdvice {} |
|
|
|
// Target all Controllers assignable to specific classes |
|
@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class}) |
|
public class AssignableTypesAdvice {} |
|
---- |
|
|
|
Check out the |
|
{javadoc-baseurl}/org/springframework/web/bind/annotation/ControllerAdvice.html[`@ControllerAdvice` |
|
documentation] for more details. |
|
|
|
[[mvc-ann-jsonview]] |
|
===== Jackson Serialization View Support |
|
|
|
It can sometimes be useful to filter contextually the object that will be serialized to the |
|
HTTP response body. In order to provide such capability, Spring MVC has built-in support for |
|
rendering with http://wiki.fasterxml.com/JacksonJsonViews[Jackson's Serialization Views]. |
|
|
|
To use it with an `@ResponseBody` controller method or controller methods that return |
|
`ResponseEntity`, simply add the `@JsonView` annotation with a class argument specifying |
|
the view class or interface to be used: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RestController |
|
public class UserController { |
|
|
|
@RequestMapping(value = "/user", method = RequestMethod.GET) |
|
@JsonView(User.WithoutPasswordView.class) |
|
public User getUser() { |
|
return new User("eric", "7!jd#h23"); |
|
} |
|
} |
|
|
|
public class User { |
|
|
|
public interface WithoutPasswordView {}; |
|
public interface WithPasswordView extends WithoutPasswordView {}; |
|
|
|
private String username; |
|
private String password; |
|
|
|
public User() { |
|
} |
|
|
|
public User(String username, String password) { |
|
this.username = username; |
|
this.password = password; |
|
} |
|
|
|
@JsonView(WithoutPasswordView.class) |
|
public String getUsername() { |
|
return this.username; |
|
} |
|
|
|
@JsonView(WithPasswordView.class) |
|
public String getPassword() { |
|
return this.password; |
|
} |
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
Note that despite `@JsonView` allowing for more than one class to |
|
be specified, the use on a controller method is only supported with |
|
exactly one class argument. Consider the use of a composite interface |
|
if you need to enable multiple views. |
|
==== |
|
|
|
For controllers relying on view resolution, simply add the serialization view class |
|
to the model: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
public class UserController extends AbstractController { |
|
|
|
@RequestMapping(value = "/user", method = RequestMethod.GET) |
|
public String getUser(Model model) { |
|
model.addAttribute("user", new User("eric", "7!jd#h23")); |
|
model.addAttribute(JsonView.class.getName(), User.WithoutPasswordView.class); |
|
return "userView"; |
|
} |
|
} |
|
---- |
|
|
|
[[mvc-ann-jsonp]] |
|
===== Jackson JSONP Support |
|
|
|
In order to enable http://en.wikipedia.org/wiki/JSONP[JSONP] support for `@ResponseBody` |
|
and `ResponseEntity` methods, declare an `@ControllerAdvice` bean that extends |
|
`AbstractJsonpResponseBodyAdvice` as shown below where the constructor argument indicates |
|
the JSONP query parameter name(s): |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@ControllerAdvice |
|
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice { |
|
|
|
public JsonpAdvice() { |
|
super("callback"); |
|
} |
|
} |
|
---- |
|
|
|
For controllers relying on view resolution, JSONP is automatically enabled when the |
|
request has a query parameter named `jsonp` or `callback`. Those names can be |
|
customized through `jsonpParameterNames` property. |
|
|
|
|
|
[[mvc-ann-async]] |
|
==== Asynchronous Request Processing |
|
Spring MVC 3.2 introduced Servlet 3 based asynchronous request processing. Instead of |
|
returning a value, as usual, a controller method can now return a |
|
`java.util.concurrent.Callable` and produce the return value from a separate thread. |
|
Meanwhile the main Servlet container thread is released and allowed to process other |
|
requests. Spring MVC invokes the `Callable` in a separate thread with the help of a |
|
`TaskExecutor` and when the `Callable` returns, the request is dispatched back to the |
|
Servlet container to resume processing with the value returned by the `Callable`. Here |
|
is an example controller method: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping(method=RequestMethod.POST) |
|
public Callable<String> processUpload(final MultipartFile file) { |
|
|
|
return new Callable<String>() { |
|
public String call() throws Exception { |
|
// ... |
|
return "someView"; |
|
} |
|
}; |
|
|
|
} |
|
---- |
|
|
|
A second option is for the controller to return an instance of `DeferredResult`. In this |
|
case the return value will also be produced from a separate thread. However, that thread |
|
is not known to Spring MVC. For example the result may be produced in response to some |
|
external event such as a JMS message, a scheduled task, etc. Here is an example |
|
controller method: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping("/quotes") |
|
@ResponseBody |
|
public DeferredResult<String> quotes() { |
|
DeferredResult<String> deferredResult = new DeferredResult<String>(); |
|
// Save the deferredResult in in-memory queue ... |
|
return deferredResult; |
|
} |
|
|
|
// In some other thread... |
|
deferredResult.setResult(data); |
|
---- |
|
|
|
This may be difficult to understand without any knowledge of the Servlet 3 async |
|
processing feature. It would certainly help to read up on it. At a very minimum consider |
|
the following basic facts: |
|
|
|
* A `ServletRequest` can be put in asynchronous mode by calling `request.startAsync()`. |
|
The main effect of doing so is that the Servlet, as well as any Filters, can exit but |
|
the response will remain open allowing some other thread to complete processing. |
|
* The call to `request.startAsync()` returns an `AsyncContext`, which can be used for |
|
further control over async processing. For example it provides the method `dispatch`, |
|
which can be called from an application thread in order to "dispatch" the request back |
|
to the Servlet container. An async dispatch is similar to a forward except it is made |
|
from one (application) thread to another (Servlet container) thread whereas a forward |
|
occurs synchronously in the same (Servlet container) thread. |
|
* `ServletRequest` provides access to the current `DispatcherType`, which can be used to |
|
distinguish if a `Servlet` or a `Filter` is processing on the initial request |
|
processing thread and when it is processing in an async dispatch. |
|
|
|
With the above in mind, the following is the sequence of events for async request |
|
processing with a `Callable`: (1) Controller returns a `Callable`, (2) Spring MVC starts |
|
async processing and submits the `Callable` to a `TaskExecutor` for processing in a |
|
separate thread, (3) the `DispatcherServlet` and all Filter's exit the request |
|
processing thread but the response remains open, (4) the `Callable` produces a result |
|
and Spring MVC dispatches the request back to the Servlet container, (5) the |
|
`DispatcherServlet` is invoked again and processing resumes with the asynchronously |
|
produced result from the `Callable`. The exact sequencing of (2), (3), and (4) may vary |
|
depending on the speed of execution of the concurrent threads. |
|
|
|
The sequence of events for async request processing with a `DeferredResult` is the same |
|
in principal except it's up to the application to produce the asynchronous result from |
|
some thread: (1) Controller returns a `DeferredResult` and saves it in some in-memory |
|
queue or list where it can be accessed, (2) Spring MVC starts async processing, (3) the |
|
`DispatcherServlet` and all configured Filter's exit the request processing thread but |
|
the response remains open, (4) the application sets the `DeferredResult` from some |
|
thread and Spring MVC dispatches the request back to the Servlet container, (5) the |
|
`DispatcherServlet` is invoked again and processing resumes with the asynchronously |
|
produced result. |
|
|
|
Explaining the motivation for async request processing and when or why to use it are |
|
beyond the scope of this document. For further information you may wish to read |
|
https://spring.io/blog/2012/05/07/spring-mvc-3-2-preview-introducing-servlet-3-async-support[this |
|
blog post series]. |
|
|
|
|
|
[[mvc-ann-async-exceptions]] |
|
===== Exception Handling for Async Requests |
|
What happens if a `Callable` returned from a controller method raises an Exception while |
|
being executed? The effect is similar to what happens when any controller method raises |
|
an exception. It is handled by a matching `@ExceptionHandler` method in the same |
|
controller or by one of the configured `HandlerExceptionResolver` instances. |
|
|
|
[NOTE] |
|
==== |
|
Under the covers, when a `Callable` raises an Exception, Spring MVC still dispatches to |
|
the Servlet container to resume processing. The only difference is that the result of |
|
executing the `Callable` is an `Exception` that must be processed with the configured |
|
`HandlerExceptionResolver` instances. |
|
==== |
|
|
|
When using a `DeferredResult`, you have a choice of calling its `setErrorResult(Object)` |
|
method and provide an `Exception` or any other Object you'd like to use as the result. |
|
If the result is an `Exception`, it will be processed with a matching |
|
`@ExceptionHandler` method in the same controller or with any configured |
|
`HandlerExceptionResolver` instance. |
|
|
|
|
|
[[mvc-ann-async-interception]] |
|
===== Intercepting Async Requests |
|
An existing `HandlerInterceptor` can implement `AsyncHandlerInterceptor`, which provides |
|
one additional method `afterConcurrentHandlingStarted`. It is invoked after async |
|
processing starts and when the initial request processing thread is being exited. See |
|
the `AsyncHandlerInterceptor` javadocs for more details on that. |
|
|
|
Further options for async request lifecycle callbacks are provided directly on |
|
`DeferredResult`, which has the methods `onTimeout(Runnable)` and |
|
`onCompletion(Runnable)`. Those are called when the async request is about to time out |
|
or has completed respectively. The timeout event can be handled by setting the |
|
`DeferredResult` to some value. The completion callback however is final and the result |
|
can no longer be set. |
|
|
|
Similar callbacks are also available with a `Callable`. However, you will need to wrap |
|
the `Callable` in an instance of `WebAsyncTask` and then use that to register the |
|
timeout and completion callbacks. Just like with `DeferredResult`, the timeout event can |
|
be handled and a value can be returned while the completion event is final. |
|
|
|
You can also register a `CallableProcessingInterceptor` or a |
|
`DeferredResultProcessingInterceptor` globally through the MVC Java config or the MVC |
|
namespace. Those interceptors provide a full set of callbacks and apply every time a |
|
`Callable` or a `DeferredResult` is used. |
|
|
|
|
|
[[mvc-ann-async-configuration]] |
|
===== Configuration for Async Request Processing |
|
|
|
[[mvc-ann-async-configuration-servlet3]] |
|
====== Servlet 3 Async Config |
|
To use Servlet 3 async request processing, you need to update `web.xml` to version 3.0: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<web-app xmlns="http://java.sun.com/xml/ns/javaee" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
http://java.sun.com/xml/ns/javaee |
|
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" |
|
version="3.0"> |
|
|
|
... |
|
|
|
</web-app> |
|
---- |
|
|
|
The `DispatcherServlet` and any `Filter` configuration need to have the |
|
`<async-supported>true</async-supported>` sub-element. Additionally, any `Filter` that |
|
also needs to get involved in async dispatches should also be configured to support the |
|
ASYNC dispatcher type. Note that it is safe to enable the ASYNC dispatcher type for all |
|
filters provided with the Spring Framework since they will not get involved in async |
|
dispatches unless needed. |
|
|
|
[WARNING] |
|
==== |
|
Note that for some Filters it is absolutely critical to ensure they are mapped to |
|
be invoked during asynchronous dispatches. For example if a filter such as the |
|
`OpenEntityManagerInViewFilter` is responsible for releasing database connection |
|
resources and must be invoked at the end of an async request. |
|
|
|
Below is an example of a propertly configured filter: |
|
==== |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<web-app xmlns="http://java.sun.com/xml/ns/javaee" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xsi:schemaLocation=" |
|
http://java.sun.com/xml/ns/javaee |
|
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" |
|
version="3.0"> |
|
|
|
<filter> |
|
<filter-name>Spring OpenEntityManagerInViewFilter</filter-name> |
|
<filter-class>org.springframework.~.OpenEntityManagerInViewFilter</filter-class> |
|
<async-supported>true</async-supported> |
|
</filter> |
|
|
|
<filter-mapping> |
|
<filter-name>Spring OpenEntityManagerInViewFilter</filter-name> |
|
<url-pattern>/*</url-pattern> |
|
<dispatcher>REQUEST</dispatcher> |
|
<dispatcher>ASYNC</dispatcher> |
|
</filter-mapping> |
|
|
|
</web-app> |
|
|
|
---- |
|
|
|
If using Servlet 3, Java based configuration, e.g. via `WebApplicationInitializer`, |
|
you'll also need to set the "asyncSupported" flag as well as the ASYNC dispatcher type |
|
just like with `web.xml`. To simplify all this configuration, consider extending |
|
`AbstractDispatcherServletInitializer` or |
|
`AbstractAnnotationConfigDispatcherServletInitializer`, which automatically set those |
|
options and make it very easy to register `Filter` instances. |
|
|
|
[[mvc-ann-async-configuration-spring-mvc]] |
|
====== Spring MVC Async Config |
|
The MVC Java config and the MVC namespace both provide options for configuring async |
|
request processing. `WebMvcConfigurer` has the method `configureAsyncSupport` while |
|
<mvc:annotation-driven> has an <async-support> sub-element. |
|
|
|
Those allow you to configure the default timeout value to use for async requests, which |
|
if not set depends on the underlying Servlet container (e.g. 10 seconds on Tomcat). You |
|
can also configure an `AsyncTaskExecutor` to use for executing `Callable` instances |
|
returned from controller methods. It is highly recommended to configure this property |
|
since by default Spring MVC uses `SimpleAsyncTaskExecutor`. The MVC Java config and the |
|
MVC namespace also allow you to register `CallableProcessingInterceptor` and |
|
`DeferredResultProcessingInterceptor` instances. |
|
|
|
If you need to override the default timeout value for a specific `DeferredResult`, you |
|
can do so by using the appropriate class constructor. Similarly, for a `Callable`, you |
|
can wrap it in a `WebAsyncTask` and use the appropriate class constructor to customize |
|
the timeout value. The class constructor of `WebAsyncTask` also allows providing an |
|
`AsyncTaskExecutor`. |
|
|
|
|
|
|
|
[[mvc-ann-tests]] |
|
==== Testing Controllers |
|
The `spring-test` module offers first class support for testing annotated controllers. |
|
See <<spring-mvc-test-framework>>. |
|
|
|
|
|
|
|
|
|
[[mvc-handlermapping]] |
|
=== Handler mappings |
|
In previous versions of Spring, users were required to define one or more |
|
`HandlerMapping` beans in the web application context to map incoming web requests to |
|
appropriate handlers. With the introduction of annotated controllers, you generally |
|
don't need to do that because the `RequestMappingHandlerMapping` automatically looks for |
|
`@RequestMapping` annotations on all `@Controller` beans. However, do keep in mind that |
|
all `HandlerMapping` classes extending from `AbstractHandlerMapping` have the following |
|
properties that you can use to customize their behavior: |
|
|
|
* `interceptors` List of interceptors to use. ++HandlerInterceptor++s are discussed in |
|
<<mvc-handlermapping-interceptor>>. |
|
* `defaultHandler` Default handler to use, when this handler mapping does not result in |
|
a matching handler. |
|
* `order` Based on the value of the order property (see the |
|
`org.springframework.core.Ordered` interface), Spring sorts all handler mappings |
|
available in the context and applies the first matching handler. |
|
* `alwaysUseFullPath` If `true` , Spring uses the full path within the current Servlet |
|
context to find an appropriate handler. If `false` (the default), the path within the |
|
current Servlet mapping is used. For example, if a Servlet is mapped using |
|
`/testing/*` and the `alwaysUseFullPath` property is set to true, |
|
`/testing/viewPage.html` is used, whereas if the property is set to false, |
|
`/viewPage.html` is used. |
|
* `urlDecode` Defaults to `true`, as of Spring 2.5. If you prefer to compare encoded |
|
paths, set this flag to `false`. However, the `HttpServletRequest` always exposes the |
|
Servlet path in decoded form. Be aware that the Servlet path will not match when |
|
compared with encoded paths. |
|
|
|
The following example shows how to configure an interceptor: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<bean id="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"> |
|
<property name="interceptors"> |
|
<bean class="example.MyInterceptor"/> |
|
</property> |
|
</bean> |
|
<beans> |
|
---- |
|
|
|
|
|
|
|
[[mvc-handlermapping-interceptor]] |
|
==== Intercepting requests with a HandlerInterceptor |
|
|
|
Spring's handler mapping mechanism includes handler interceptors, which are useful when |
|
you want to apply specific functionality to certain requests, for example, checking for |
|
a principal. |
|
|
|
Interceptors located in the handler mapping must implement `HandlerInterceptor` from the |
|
`org.springframework.web.servlet` package. This interface defines three methods: |
|
`preHandle(..)` is called __before__ the actual handler is executed; `postHandle(..)` is |
|
called __after__ the handler is executed; and `afterCompletion(..)` is called __after |
|
the complete request has finished__. These three methods should provide enough |
|
flexibility to do all kinds of preprocessing and postprocessing. |
|
|
|
The `preHandle(..)` method returns a boolean value. You can use this method to break or |
|
continue the processing of the execution chain. When this method returns `true`, the |
|
handler execution chain will continue; when it returns false, the `DispatcherServlet` |
|
assumes the interceptor itself has taken care of requests (and, for example, rendered an |
|
appropriate view) and does not continue executing the other interceptors and the actual |
|
handler in the execution chain. |
|
|
|
Interceptors can be configured using the `interceptors` property, which is present on |
|
all `HandlerMapping` classes extending from `AbstractHandlerMapping`. This is shown in |
|
the example below: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<bean id="handlerMapping" |
|
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"> |
|
<property name="interceptors"> |
|
<list> |
|
<ref bean="officeHoursInterceptor"/> |
|
</list> |
|
</property> |
|
</bean> |
|
|
|
<bean id="officeHoursInterceptor" |
|
class="samples.TimeBasedAccessInterceptor"> |
|
<property name="openingTime" value="9"/> |
|
<property name="closingTime" value="18"/> |
|
</bean> |
|
<beans> |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package samples; |
|
|
|
public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter { |
|
|
|
private int openingTime; |
|
private int closingTime; |
|
|
|
public void setOpeningTime(int openingTime) { |
|
this.openingTime = openingTime; |
|
} |
|
|
|
public void setClosingTime(int closingTime) { |
|
this.closingTime = closingTime; |
|
} |
|
|
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, |
|
Object handler) throws Exception { |
|
Calendar cal = Calendar.getInstance(); |
|
int hour = cal.get(HOUR_OF_DAY); |
|
if (openingTime <= hour && hour < closingTime) { |
|
return true; |
|
} |
|
response.sendRedirect("http://host.com/outsideOfficeHours.html"); |
|
return false; |
|
} |
|
} |
|
---- |
|
|
|
Any request handled by this mapping is intercepted by the `TimeBasedAccessInterceptor`. |
|
If the current time is outside office hours, the user is redirected to a static HTML |
|
file that says, for example, you can only access the website during office hours. |
|
|
|
[NOTE] |
|
==== |
|
When using the `RequestMappingHandlerMapping` the actual handler is an instance of |
|
`HandlerMethod` which identifies the specific controller method that will be invoked. |
|
==== |
|
|
|
As you can see, the Spring adapter class `HandlerInterceptorAdapter` makes it easier to |
|
extend the `HandlerInterceptor` interface. |
|
|
|
[TIP] |
|
==== |
|
|
|
In the example above, the configured interceptor will apply to all requests handled with |
|
annotated controller methods. If you want to narrow down the URL paths to which an |
|
interceptor applies, you can use the MVC namespace or the MVC Java config, or declare |
|
bean instances of type `MappedInterceptor` to do that. See <<mvc-config-enable>>. |
|
==== |
|
|
|
Note that the `postHandle` method of `HandlerInterceptor` is not always ideally suited for |
|
use with `@ResponseBody` and `ResponseEntity` methods. In such cases an `HttpMessageConverter` |
|
writes to and commits the response before `postHandle` is called which makes it impossible |
|
to change the response, for example to add a header. Instead an application can implement |
|
`ResponseBodyAdvice` and either declare it as an `@ControllerAdvice` bean or configure it |
|
directly on `RequestMappingHandlerAdapter`. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[mvc-viewresolver]] |
|
=== Resolving views |
|
All MVC frameworks for web applications provide a way to address views. Spring provides |
|
view resolvers, which enable you to render models in a browser without tying you to a |
|
specific view technology. Out of the box, Spring enables you to use JSPs, Velocity |
|
templates and XSLT views, for example. See <<view>> for a discussion of how to integrate |
|
and use a number of disparate view technologies. |
|
|
|
The two interfaces that are important to the way Spring handles views are `ViewResolver` |
|
and `View`. The `ViewResolver` provides a mapping between view names and actual views. |
|
The `View` interface addresses the preparation of the request and hands the request over |
|
to one of the view technologies. |
|
|
|
|
|
|
|
[[mvc-viewresolver-resolver]] |
|
==== Resolving views with the ViewResolver interface |
|
|
|
As discussed in <<mvc-controller>>, all handler methods in the Spring Web MVC |
|
controllers must resolve to a logical view name, either explicitly (e.g., by returning a |
|
`String`, `View`, or `ModelAndView`) or implicitly (i.e., based on conventions). Views |
|
in Spring are addressed by a logical view name and are resolved by a view resolver. |
|
Spring comes with quite a few view resolvers. This table lists most of them; a couple of |
|
examples follow. |
|
|
|
[[mvc-view-resolvers-tbl]] |
|
.View resolvers |
|
|=== |
|
| ViewResolver| Description |
|
|
|
| `AbstractCachingViewResolver` |
|
| Abstract view resolver that caches views. Often views need preparation before they can |
|
be used; extending this view resolver provides caching. |
|
|
|
| `XmlViewResolver` |
|
| Implementation of `ViewResolver` that accepts a configuration file written in XML with |
|
the same DTD as Spring's XML bean factories. The default configuration file is |
|
`/WEB-INF/views.xml`. |
|
|
|
| `ResourceBundleViewResolver` |
|
| Implementation of `ViewResolver` that uses bean definitions in a `ResourceBundle`, |
|
specified by the bundle base name. Typically you define the bundle in a properties |
|
file, located in the classpath. The default file name is `views.properties`. |
|
|
|
| `UrlBasedViewResolver` |
|
| Simple implementation of the `ViewResolver` interface that effects the direct |
|
resolution of logical view names to URLs, without an explicit mapping definition. This |
|
is appropriate if your logical names match the names of your view resources in a |
|
straightforward manner, without the need for arbitrary mappings. |
|
|
|
| `InternalResourceViewResolver` |
|
| Convenient subclass of `UrlBasedViewResolver` that supports `InternalResourceView` (in |
|
effect, Servlets and JSPs) and subclasses such as `JstlView` and `TilesView`. You can |
|
specify the view class for all views generated by this resolver by using |
|
`setViewClass(..)`. See the `UrlBasedViewResolver` javadocs for details. |
|
|
|
| `VelocityViewResolver` / `FreeMarkerViewResolver` |
|
| Convenient subclass of `UrlBasedViewResolver` that supports `VelocityView` (in effect, |
|
Velocity templates) or `FreeMarkerView` ,respectively, and custom subclasses of them. |
|
|
|
| `ContentNegotiatingViewResolver` |
|
| Implementation of the `ViewResolver` interface that resolves a view based on the |
|
request file name or `Accept` header. See <<mvc-multiple-representations>>. |
|
|=== |
|
|
|
As an example, with JSP as a view technology, you can use the `UrlBasedViewResolver`. |
|
This view resolver translates a view name to a URL and hands the request over to the |
|
RequestDispatcher to render the view. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="viewResolver" |
|
class="org.springframework.web.servlet.view.UrlBasedViewResolver"> |
|
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> |
|
<property name="prefix" value="/WEB-INF/jsp/"/> |
|
<property name="suffix" value=".jsp"/> |
|
</bean> |
|
---- |
|
|
|
When returning `test` as a logical view name, this view resolver forwards the request to |
|
the `RequestDispatcher` that will send the request to `/WEB-INF/jsp/test.jsp`. |
|
|
|
When you combine different view technologies in a web application, you can use the |
|
`ResourceBundleViewResolver`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="viewResolver" |
|
class="org.springframework.web.servlet.view.ResourceBundleViewResolver"> |
|
<property name="basename" value="views"/> |
|
<property name="defaultParentView" value="parentView"/> |
|
</bean> |
|
---- |
|
|
|
The `ResourceBundleViewResolver` inspects the `ResourceBundle` identified by the |
|
basename, and for each view it is supposed to resolve, it uses the value of the property |
|
`[viewname].(class)` as the view class and the value of the property `[viewname].url` as |
|
the view url. Examples can be found in the next chapter which covers view technologies. |
|
As you can see, you can identify a parent view, from which all views in the properties |
|
file "extend". This way you can specify a default view class, for example. |
|
|
|
[NOTE] |
|
==== |
|
Subclasses of `AbstractCachingViewResolver` cache view instances that they resolve. |
|
Caching improves performance of certain view technologies. It's possible to turn off the |
|
cache by setting the `cache` property to `false`. Furthermore, if you must refresh a |
|
certain view at runtime (for example when a Velocity template is modified), you can use |
|
the `removeFromCache(String viewName, Locale loc)` method. |
|
==== |
|
|
|
|
|
|
|
[[mvc-viewresolver-chaining]] |
|
==== Chaining ViewResolvers |
|
Spring supports multiple view resolvers. Thus you can chain resolvers and, for example, |
|
override specific views in certain circumstances. You chain view resolvers by adding |
|
more than one resolver to your application context and, if necessary, by setting the |
|
`order` property to specify ordering. Remember, the higher the order property, the later |
|
the view resolver is positioned in the chain. |
|
|
|
In the following example, the chain of view resolvers consists of two resolvers, an |
|
`InternalResourceViewResolver`, which is always automatically positioned as the last |
|
resolver in the chain, and an `XmlViewResolver` for specifying Excel views. Excel views |
|
are not supported by the `InternalResourceViewResolver`. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> |
|
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> |
|
<property name="prefix" value="/WEB-INF/jsp/"/> |
|
<property name="suffix" value=".jsp"/> |
|
</bean> |
|
|
|
<bean id="excelViewResolver" class="org.springframework.web.servlet.view.XmlViewResolver"> |
|
<property name="order" value="1"/> |
|
<property name="location" value="/WEB-INF/views.xml"/> |
|
</bean> |
|
|
|
<!-- in views.xml --> |
|
|
|
<beans> |
|
<bean name="report" class="org.springframework.example.ReportExcelView"/> |
|
</beans> |
|
---- |
|
|
|
If a specific view resolver does not result in a view, Spring examines the context for |
|
other view resolvers. If additional view resolvers exist, Spring continues to inspect |
|
them until a view is resolved. If no view resolver returns a view, Spring throws a |
|
`ServletException`. |
|
|
|
The contract of a view resolver specifies that a view resolver __can__ return null to |
|
indicate the view could not be found. Not all view resolvers do this, however, because |
|
in some cases, the resolver simply cannot detect whether or not the view exists. For |
|
example, the `InternalResourceViewResolver` uses the `RequestDispatcher` internally, and |
|
dispatching is the only way to figure out if a JSP exists, but this action can only |
|
execute once. The same holds for the `VelocityViewResolver` and some others. Check the |
|
javadocs of the specific view resolver to see whether it reports non-existing views. |
|
Thus, putting an `InternalResourceViewResolver` in the chain in a place other than |
|
the last results in the chain not being fully inspected, because the |
|
`InternalResourceViewResolver` will __always__ return a view! |
|
|
|
|
|
|
|
[[mvc-redirecting]] |
|
==== Redirecting to views |
|
As mentioned previously, a controller typically returns a logical view name, which a |
|
view resolver resolves to a particular view technology. For view technologies such as |
|
JSPs that are processed through the Servlet or JSP engine, this resolution is usually |
|
handled through the combination of `InternalResourceViewResolver` and |
|
`InternalResourceView`, which issues an internal forward or include via the Servlet |
|
API's `RequestDispatcher.forward(..)` method or `RequestDispatcher.include()` method. |
|
For other view technologies, such as Velocity, XSLT, and so on, the view itself writes |
|
the content directly to the response stream. |
|
|
|
It is sometimes desirable to issue an HTTP redirect back to the client, before the view |
|
is rendered. This is desirable, for example, when one controller has been called with |
|
`POST` data, and the response is actually a delegation to another controller (for |
|
example on a successful form submission). In this case, a normal internal forward will |
|
mean that the other controller will also see the same `POST` data, which is potentially |
|
problematic if it can confuse it with other expected data. Another reason to perform a |
|
redirect before displaying the result is to eliminate the possibility of the user |
|
submitting the form data multiple times. In this scenario, the browser will first send |
|
an initial `POST`; it will then receive a response to redirect to a different URL; and |
|
finally the browser will perform a subsequent `GET` for the URL named in the redirect |
|
response. Thus, from the perspective of the browser, the current page does not reflect |
|
the result of a `POST` but rather of a `GET`. The end effect is that there is no way the |
|
user can accidentally re- `POST` the same data by performing a refresh. The refresh |
|
forces a `GET` of the result page, not a resend of the initial `POST` data. |
|
|
|
|
|
[[mvc-redirecting-redirect-view]] |
|
===== RedirectView |
|
|
|
One way to force a redirect as the result of a controller response is for the controller |
|
to create and return an instance of Spring's `RedirectView`. In this case, |
|
`DispatcherServlet` does not use the normal view resolution mechanism. Rather because it |
|
has been given the (redirect) view already, the `DispatcherServlet` simply instructs the |
|
view to do its work. |
|
|
|
The `RedirectView` issues an `HttpServletResponse.sendRedirect()` call that returns to |
|
the client browser as an HTTP redirect. By default all model attributes are considered |
|
to be exposed as URI template variables in the redirect URL. Of the remaining attributes |
|
those that are primitive types or collections/arrays of primitive types are |
|
automatically appended as query parameters. |
|
|
|
Appending primitive type attributes as query parameters may be the desired result if a |
|
model instance was prepared specifically for the redirect. However, in annotated |
|
controllers the model may contain additional attributes added for rendering purposes |
|
(e.g. drop-down field values). To avoid the possibility of having such attributes appear |
|
in the URL an annotated controller can declare an argument of type `RedirectAttributes` |
|
and use it to specify the exact attributes to make available to `RedirectView`. If the |
|
controller method decides to redirect, the content of `RedirectAttributes` is used. |
|
Otherwise the content of the model is used. |
|
|
|
Note that URI template variables from the present request are automatically made |
|
available when expanding a redirect URL and do not need to be added explicitly neither |
|
through `Model` nor `RedirectAttributes`. For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping(value = "/files/{path}", method = RequestMethod.POST) |
|
public String upload(...) { |
|
// ... |
|
return "redirect:files/{path}"; |
|
} |
|
---- |
|
|
|
If you use `RedirectView` and the view is created by the controller itself, it is |
|
recommended that you configure the redirect URL to be injected into the controller so |
|
that it is not baked into the controller but configured in the context along with the |
|
view names. The next section discusses this process. |
|
|
|
|
|
[[mvc-redirecting-redirect-prefix]] |
|
===== The redirect: prefix |
|
|
|
While the use of `RedirectView` works fine, if the controller itself creates the |
|
`RedirectView`, there is no avoiding the fact that the controller is aware that a |
|
redirection is happening. This is really suboptimal and couples things too tightly. The |
|
controller should not really care about how the response gets handled. In general it |
|
should operate only in terms of view names that have been injected into it. |
|
|
|
The special `redirect:` prefix allows you to accomplish this. If a view name is returned |
|
that has the prefix `redirect:`, the `UrlBasedViewResolver` (and all subclasses) will |
|
recognize this as a special indication that a redirect is needed. The rest of the view |
|
name will be treated as the redirect URL. |
|
|
|
The net effect is the same as if the controller had returned a `RedirectView`, but now |
|
the controller itself can simply operate in terms of logical view names. A logical view |
|
name such as `redirect:/myapp/some/resource` will redirect relative to the current |
|
Servlet context, while a name such as `redirect:http://myhost.com/some/arbitrary/path` |
|
will redirect to an absolute URL. |
|
|
|
|
|
[[mvc-redirecting-forward-prefix]] |
|
===== The forward: prefix |
|
|
|
It is also possible to use a special `forward:` prefix for view names that are |
|
ultimately resolved by `UrlBasedViewResolver` and subclasses. This creates an |
|
`InternalResourceView` (which ultimately does a `RequestDispatcher.forward()`) around |
|
the rest of the view name, which is considered a URL. Therefore, this prefix is not |
|
useful with `InternalResourceViewResolver` and `InternalResourceView` (for JSPs for |
|
example). But the prefix can be helpful when you are primarily using another view |
|
technology, but still want to force a forward of a resource to be handled by the |
|
Servlet/JSP engine. (Note that you may also chain multiple view resolvers, instead.) |
|
|
|
As with the `redirect:` prefix, if the view name with the `forward:` prefix is injected |
|
into the controller, the controller does not detect that anything special is happening |
|
in terms of handling the response. |
|
|
|
|
|
|
|
[[mvc-multiple-representations]] |
|
==== ContentNegotiatingViewResolver |
|
|
|
The `ContentNegotiatingViewResolver` does not resolve views itself but rather delegates |
|
to other view resolvers, selecting the view that resembles the representation requested |
|
by the client. Two strategies exist for a client to request a representation from the |
|
server: |
|
|
|
* Use a distinct URI for each resource, typically by using a different file extension in |
|
the URI. For example, the URI `http://www.example.com/users/fred.pdf` requests a PDF |
|
representation of the user fred, and `http://www.example.com/users/fred.xml` requests |
|
an XML representation. |
|
* Use the same URI for the client to locate the resource, but set the `Accept` HTTP |
|
request header to list the http://en.wikipedia.org/wiki/Internet_media_type[media |
|
types] that it understands. For example, an HTTP request for |
|
`http://www.example.com/users/fred` with an `Accept` header set to `application/pdf` |
|
requests a PDF representation of the user fred, while |
|
`http://www.example.com/users/fred` with an `Accept` header set to `text/xml` requests |
|
an XML representation. This strategy is known as |
|
http://en.wikipedia.org/wiki/Content_negotiation[content negotiation]. |
|
|
|
[NOTE] |
|
==== |
|
One issue with the `Accept` header is that it is impossible to set it in a web browser |
|
within HTML. For example, in Firefox, it is fixed to: |
|
|
|
[literal] |
|
[subs="verbatim"] |
|
---- |
|
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 |
|
---- |
|
|
|
For this reason it is common to see the use of a distinct URI for each representation |
|
when developing browser based web applications. |
|
==== |
|
|
|
To support multiple representations of a resource, Spring provides the |
|
`ContentNegotiatingViewResolver` to resolve a view based on the file extension or |
|
`Accept` header of the HTTP request. `ContentNegotiatingViewResolver` does not perform |
|
the view resolution itself but instead delegates to a list of view resolvers that you |
|
specify through the bean property `ViewResolvers`. |
|
|
|
The `ContentNegotiatingViewResolver` selects an appropriate `View` to handle the request |
|
by comparing the request media type(s) with the media type (also known as |
|
`Content-Type`) supported by the `View` associated with each of its `ViewResolvers`. The |
|
first `View` in the list that has a compatible `Content-Type` returns the representation |
|
to the client. If a compatible view cannot be supplied by the `ViewResolver` chain, then |
|
the list of views specified through the `DefaultViews` property will be consulted. This |
|
latter option is appropriate for singleton `Views` that can render an appropriate |
|
representation of the current resource regardless of the logical view name. The `Accept` |
|
header may include wild cards, for example `text/*`, in which case a `View` whose |
|
Content-Type was `text/xml` is a compatible match. |
|
|
|
To support the resolution of a view based on a file extension, use the |
|
`ContentNegotiatingViewResolver` bean property `mediaTypes` to specify a mapping of file |
|
extensions to media types. For more information on the algorithm used to determine the |
|
request media type, refer to the API documentation for `ContentNegotiatingViewResolver`. |
|
|
|
Here is an example configuration of a `ContentNegotiatingViewResolver:` |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"> |
|
<property name="mediaTypes"> |
|
<map> |
|
<entry key="atom" value="application/atom+xml"/> |
|
<entry key="html" value="text/html"/> |
|
<entry key="json" value="application/json"/> |
|
</map> |
|
</property> |
|
<property name="viewResolvers"> |
|
<list> |
|
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/> |
|
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> |
|
<property name="prefix" value="/WEB-INF/jsp/"/> |
|
<property name="suffix" value=".jsp"/> |
|
</bean> |
|
</list> |
|
</property> |
|
<property name="defaultViews"> |
|
<list> |
|
<bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView" /> |
|
</list> |
|
</property> |
|
</bean> |
|
|
|
<bean id="content" class="com.foo.samples.rest.SampleContentAtomView"/> |
|
---- |
|
|
|
The `InternalResourceViewResolver` handles the translation of view names and JSP pages, |
|
while the `BeanNameViewResolver` returns a view based on the name of a bean. (See |
|
"<<mvc-viewresolver-resolver,Resolving views with the ViewResolver interface>>" for more |
|
details on how Spring looks up and instantiates a view.) In this example, the `content` |
|
bean is a class that inherits from `AbstractAtomFeedView`, which returns an Atom RSS |
|
feed. For more information on creating an Atom Feed representation, see the section Atom |
|
Views. |
|
|
|
In the above configuration, if a request is made with an `.html` extension, the view |
|
resolver looks for a view that matches the `text/html` media type. The |
|
`InternalResourceViewResolver` provides the matching view for `text/html`. If the |
|
request is made with the file extension `.atom`, the view resolver looks for a view that |
|
matches the `application/atom+xml` media type. This view is provided by the |
|
`BeanNameViewResolver` that maps to the `SampleContentAtomView` if the view name |
|
returned is `content`. If the request is made with the file extension `.json`, the |
|
`MappingJackson2JsonView` instance from the `DefaultViews` list will be selected |
|
regardless of the view name. Alternatively, client requests can be made without a file |
|
extension but with the `Accept` header set to the preferred media-type, and the same |
|
resolution of request to views would occur. |
|
|
|
[NOTE] |
|
==== |
|
If `ContentNegotiatingViewResolver`'s list of ViewResolvers is not configured |
|
explicitly, it automatically uses any ViewResolvers defined in the application context. |
|
==== |
|
|
|
The corresponding controller code that returns an Atom RSS feed for a URI of the form |
|
`http://localhost/content.atom` or `http://localhost/content` with an `Accept` header of |
|
application/atom+xml is shown below. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
public class ContentController { |
|
|
|
private List<SampleContent> contentList = new ArrayList<SampleContent>(); |
|
|
|
@RequestMapping(value="/content", method=RequestMethod.GET) |
|
public ModelAndView getContent() { |
|
ModelAndView mav = new ModelAndView(); |
|
mav.setViewName("content"); |
|
mav.addObject("sampleContentList", contentList); |
|
return mav; |
|
} |
|
|
|
} |
|
---- |
|
|
|
|
|
|
|
|
|
[[mvc-flash-attributes]] |
|
=== Using flash attributes |
|
Flash attributes provide a way for one request to store attributes intended for use in |
|
another. This is most commonly needed when redirecting -- for example, the |
|
__Post/Redirect/Get__ pattern. Flash attributes are saved temporarily before the |
|
redirect (typically in the session) to be made available to the request after the |
|
redirect and removed immediately. |
|
|
|
Spring MVC has two main abstractions in support of flash attributes. `FlashMap` is used |
|
to hold flash attributes while `FlashMapManager` is used to store, retrieve, and manage |
|
`FlashMap` instances. |
|
|
|
Flash attribute support is always "on" and does not need to enabled explicitly although |
|
if not used, it never causes HTTP session creation. On each request there is an "input" |
|
`FlashMap` with attributes passed from a previous request (if any) and an "output" |
|
`FlashMap` with attributes to save for a subsequent request. Both `FlashMap` instances |
|
are accessible from anywhere in Spring MVC through static methods in |
|
`RequestContextUtils`. |
|
|
|
Annotated controllers typically do not need to work with `FlashMap` directly. Instead an |
|
`@RequestMapping` method can accept an argument of type `RedirectAttributes` and use it |
|
to add flash attributes for a redirect scenario. Flash attributes added via |
|
`RedirectAttributes` are automatically propagated to the "output" FlashMap. Similarly |
|
after the redirect attributes from the "input" `FlashMap` are automatically added to the |
|
`Model` of the controller serving the target URL. |
|
|
|
.Matching requests to flash attributes |
|
**** |
|
The concept of flash attributes exists in many other Web frameworks and has proven to be |
|
exposed sometimes to concurrency issues. This is because by definition flash attributes |
|
are to be stored until the next request. However the very "next" request may not be the |
|
intended recipient but another asynchronous request (e.g. polling or resource requests) |
|
in which case the flash attributes are removed too early. |
|
|
|
To reduce the possibility of such issues, `RedirectView` automatically "stamps" |
|
`FlashMap` instances with the path and query parameters of the target redirect URL. In |
|
turn the default `FlashMapManager` matches that information to incoming requests when |
|
looking up the "input" `FlashMap`. |
|
|
|
This does not eliminate the possibility of a concurrency issue entirely but nevertheless |
|
reduces it greatly with information that is already available in the redirect URL. |
|
Therefore the use of flash attributes is recommended mainly for redirect scenarios . |
|
**** |
|
|
|
|
|
|
|
|
|
[[mvc-uri-building]] |
|
=== Building URIs |
|
|
|
Spring MVC provides a mechanism for building and encoding a URI using |
|
`UriComponentsBuilder` and `UriComponents`. |
|
|
|
For example you can expand and encode a URI template string: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
UriComponents uriComponents = UriComponentsBuilder.fromUriString( |
|
"http://example.com/hotels/{hotel}/bookings/{booking}").build(); |
|
|
|
URI uri = uriComponents.expand("42", "21").encode().toUri(); |
|
---- |
|
|
|
Note that `UriComponents` is immutable and the `expand()` and `encode()` operations |
|
return new instances if necessary. |
|
|
|
You can also expand and encode using individual URI components: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
UriComponents uriComponents = UriComponentsBuilder.newInstance() |
|
.scheme("http").host("example.com").path("/hotels/{hotel}/bookings/{booking}").build() |
|
.expand("42", "21") |
|
.encode(); |
|
---- |
|
|
|
In a Servlet environment the `ServletUriComponentsBuilder` sub-class provides static |
|
factory methods to copy available URL information from a Servlet requests: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
HttpServletRequest request = ... |
|
|
|
// Re-use host, scheme, port, path and query string |
|
// Replace the "accountId" query param |
|
|
|
ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromRequest(request) |
|
.replaceQueryParam("accountId", "{id}").build() |
|
.expand("123") |
|
.encode(); |
|
---- |
|
|
|
Alternatively, you may choose to copy a subset of the available information up to and |
|
including the context path: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// Re-use host, port and context path |
|
// Append "/accounts" to the path |
|
|
|
ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromContextPath(request) |
|
.path("/accounts").build() |
|
---- |
|
|
|
Or in cases where the `DispatcherServlet` is mapped by name (e.g. `/main/*`), you can |
|
also have the literal part of the servlet mapping included: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// Re-use host, port, context path |
|
// Append the literal part of the servlet mapping to the path |
|
// Append "/accounts" to the path |
|
|
|
ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromServletMapping(request) |
|
.path("/accounts").build() |
|
---- |
|
|
|
[[mvc-links-to-controllers]] |
|
==== Building URIs to Controllers and methods |
|
|
|
Spring MVC provides another mechanism for building and encoding URIs that link to |
|
Controllers and methods defined within an application. |
|
{javadoc-baseurl}/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.html[`MvcUriComponentsBuilder`] |
|
extends `UriComponentsBuilder` and provides such possibilities. |
|
|
|
Given this Controller: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
@RequestMapping("/hotels/{hotel}") |
|
public class BookingController { |
|
|
|
@RequestMapping("/bookings/{booking}") |
|
public String getBooking(@PathVariable Long booking) { |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
and using the `MvcUriComponentsBuilder`, the previous example is now: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
UriComponents uriComponents = MvcUriComponentsBuilder |
|
.fromMethodName(BookingController.class, "getBooking",21).buildAndExpand(42); |
|
|
|
URI uri = uriComponents.encode().toUri(); |
|
---- |
|
|
|
The `MvcUriComponentsBuilder` can also create "mock Controllers", thus enabling to create |
|
URIs by coding against the actual Controller's API: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
UriComponents uriComponents = MvcUriComponentsBuilder |
|
.fromMethodCall(on(BookingController.class).getBooking(21)).buildAndExpand(42); |
|
|
|
URI uri = uriComponents.encode().toUri(); |
|
---- |
|
|
|
[[mvc-links-to-controllers-from-views]] |
|
==== Building URIs to Controllers and methods from views |
|
|
|
It is also useful to build links to annotated controllers from views (e.g. JSP). |
|
This can be done through a method on `MvcUriComponentsBuilder` which refers to mappings |
|
by name called `fromMappingName`. |
|
|
|
As of 4.1 every `@RequestMapping` is assigned a default name based on the |
|
capital letters of the class and the full method name. For example, the method `getFoo` in class |
|
`FooController` is assigned the name "FC#getFoo". This naming strategy is pluggable |
|
by implementing `HandlerMethodMappingNamingStrategy` and configuring it on your |
|
`RequestMappingHandlerMapping`. Furthermore the `@RequestMapping` annotation includes |
|
a name attribute that can be used to override the default strategy. |
|
|
|
[NOTE] |
|
==== |
|
The assigned request mapping names are logged at TRACE level on startup. |
|
==== |
|
|
|
The Spring JSP tag library provides a function called `mvcUrl` that can be used to |
|
prepare links to controller methods based on this mechanism. |
|
|
|
For example given: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping("/people/{id}/addresses") |
|
public class PersonAddressController { |
|
|
|
@RequestMapping("/{country}") |
|
public HttpEntity getAddress(@PathVariable String country) { ... } |
|
} |
|
---- |
|
|
|
The following JSP code can prepare a link: |
|
|
|
[source,jsp,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %> |
|
... |
|
<a href="${s:mvcUrl(''PAC#getAddress'').arg(0,''US'').buildAndExpand(''123'')}">Get Address</a> |
|
---- |
|
|
|
|
|
|
|
|
|
|
|
[[mvc-localeresolver]] |
|
=== Using locales |
|
Most parts of Spring's architecture support internationalization, just as the Spring web |
|
MVC framework does. `DispatcherServlet` enables you to automatically resolve messages |
|
using the client's locale. This is done with `LocaleResolver` objects. |
|
|
|
When a request comes in, the `DispatcherServlet` looks for a locale resolver, and if it |
|
finds one it tries to use it to set the locale. Using the `RequestContext.getLocale()` |
|
method, you can always retrieve the locale that was resolved by the locale resolver. |
|
|
|
In addition to automatic locale resolution, you can also attach an interceptor to the |
|
handler mapping (see <<mvc-handlermapping-interceptor>> for more information on handler |
|
mapping interceptors) to change the locale under specific circumstances, for example, |
|
based on a parameter in the request. |
|
|
|
Locale resolvers and interceptors are defined in the |
|
`org.springframework.web.servlet.i18n` package and are configured in your application |
|
context in the normal way. Here is a selection of the locale resolvers included in |
|
Spring. |
|
|
|
|
|
|
|
[[mvc-timezone]] |
|
==== Obtaining Time Zone Information |
|
In addition to obtaining the client's locale, it is often useful to know their time zone. |
|
The `LocaleContextResolver` interface offers an extension to `LocaleResolver` that allows |
|
resolvers to provide a richer `LocaleContext`, which may include time zone information. |
|
|
|
When available, the user's `TimeZone` can be obtained using the |
|
`RequestContext.getTimeZone()` method. Time zone information will automatically be used |
|
by Date/Time `Converter` and `Formatter` objects registered with Spring's |
|
`ConversionService`. |
|
|
|
|
|
|
|
[[mvc-localeresolver-acceptheader]] |
|
==== AcceptHeaderLocaleResolver |
|
This locale resolver inspects the `accept-language` header in the request that was sent |
|
by the client (e.g., a web browser). Usually this header field contains the locale of |
|
the client's operating system. __Note that this resolver does not support time zone |
|
information.__ |
|
|
|
|
|
|
|
[[mvc-localeresolver-cookie]] |
|
==== CookieLocaleResolver |
|
|
|
This locale resolver inspects a `Cookie` that might exist on the client to see if a |
|
`Locale` or `TimeZone` is specified. If so, it uses the specified details. Using the |
|
properties of this locale resolver, you can specify the name of the cookie as well as the |
|
maximum age. Find below an example of defining a `CookieLocaleResolver`. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"> |
|
|
|
<property name="cookieName" value="clientlanguage"/> |
|
|
|
<!-- in seconds. If set to -1, the cookie is not persisted (deleted when browser shuts down) --> |
|
<property name="cookieMaxAge" value="100000"> |
|
|
|
</bean> |
|
---- |
|
|
|
[[mvc-cookie-locale-resolver-props-tbl]] |
|
.CookieLocaleResolver properties |
|
[cols="1,1,4"] |
|
|=== |
|
| Property| Default| Description |
|
|
|
| cookieName |
|
| classname + LOCALE |
|
| The name of the cookie |
|
|
|
| cookieMaxAge |
|
| Integer.MAX_INT |
|
| The maximum time a cookie will stay persistent on the client. If -1 is specified, the |
|
cookie will not be persisted; it will only be available until the client shuts down |
|
their browser. |
|
|
|
| cookiePath |
|
| / |
|
| Limits the visibility of the cookie to a certain part of your site. When cookiePath is |
|
specified, the cookie will only be visible to that path and the paths below it. |
|
|=== |
|
|
|
|
|
|
|
[[mvc-localeresolver-session]] |
|
==== SessionLocaleResolver |
|
|
|
The `SessionLocaleResolver` allows you to retrieve `Locale` and `TimeZone` from the |
|
session that might be associated with the user's request. |
|
|
|
|
|
|
|
[[mvc-localeresolver-interceptor]] |
|
==== LocaleChangeInterceptor |
|
|
|
You can enable changing of locales by adding the `LocaleChangeInterceptor` to one of the |
|
handler mappings (see <<mvc-handlermapping>>). It will detect a parameter in the request |
|
and change the locale. It calls `setLocale()` on the `LocaleResolver` that also exists |
|
in the context. The following example shows that calls to all `*.view` resources |
|
containing a parameter named `siteLanguage` will now change the locale. So, for example, |
|
a request for the following URL, `http://www.sf.net/home.view?siteLanguage=nl` will |
|
change the site language to Dutch. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<bean id="localeChangeInterceptor" |
|
class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"> |
|
<property name="paramName" value="siteLanguage"/> |
|
</bean> |
|
|
|
<bean id="localeResolver" |
|
class="org.springframework.web.servlet.i18n.CookieLocaleResolver"/> |
|
|
|
<bean id="urlMapping" |
|
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> |
|
<property name="interceptors"> |
|
<list> |
|
<ref bean="localeChangeInterceptor"/> |
|
</list> |
|
</property> |
|
<property name="mappings"> |
|
<value>/**/*.view=someController</value> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
|
|
|
|
|
|
[[mvc-themeresolver]] |
|
=== Using themes |
|
|
|
|
|
|
|
[[mvc-themeresolver-introduction]] |
|
==== Overview of themes |
|
You can apply Spring Web MVC framework themes to set the overall look-and-feel of your |
|
application, thereby enhancing user experience. A theme is a collection of static |
|
resources, typically style sheets and images, that affect the visual style of the |
|
application. |
|
|
|
|
|
|
|
[[mvc-themeresolver-defining]] |
|
==== Defining themes |
|
To use themes in your web application, you must set up an implementation of the |
|
`org.springframework.ui.context.ThemeSource` interface. The `WebApplicationContext` |
|
interface extends `ThemeSource` but delegates its responsibilities to a dedicated |
|
implementation. By default the delegate will be an |
|
`org.springframework.ui.context.support.ResourceBundleThemeSource` implementation that |
|
loads properties files from the root of the classpath. To use a custom `ThemeSource` |
|
implementation or to configure the base name prefix of the `ResourceBundleThemeSource`, |
|
you can register a bean in the application context with the reserved name `themeSource`. |
|
The web application context automatically detects a bean with that name and uses it. |
|
|
|
When using the `ResourceBundleThemeSource`, a theme is defined in a simple properties |
|
file. The properties file lists the resources that make up the theme. Here is an example: |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
styleSheet=/themes/cool/style.css |
|
background=/themes/cool/img/coolBg.jpg |
|
---- |
|
|
|
The keys of the properties are the names that refer to the themed elements from view |
|
code. For a JSP, you typically do this using the `spring:theme` custom tag, which is |
|
very similar to the `spring:message` tag. The following JSP fragment uses the theme |
|
defined in the previous example to customize the look and feel: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> |
|
<html> |
|
<head> |
|
<link rel="stylesheet" href="<spring:theme code=''styleSheet''/>" type="text/css"/> |
|
</head> |
|
<body style="background=<spring:theme code=''background''/>"> |
|
... |
|
</body> |
|
</html> |
|
---- |
|
|
|
By default, the `ResourceBundleThemeSource` uses an empty base name prefix. As a result, |
|
the properties files are loaded from the root of the classpath. Thus you would put the |
|
`cool.properties` theme definition in a directory at the root of the classpath, for |
|
example, in `/WEB-INF/classes`. The `ResourceBundleThemeSource` uses the standard Java |
|
resource bundle loading mechanism, allowing for full internationalization of themes. For |
|
example, we could have a `/WEB-INF/classes/cool_nl.properties` that references a special |
|
background image with Dutch text on it. |
|
|
|
|
|
|
|
[[mvc-themeresolver-resolving]] |
|
==== Theme resolvers |
|
After you define themes, as in the preceding section, you decide which theme to use. The |
|
`DispatcherServlet` will look for a bean named `themeResolver` to find out which |
|
`ThemeResolver` implementation to use. A theme resolver works in much the same way as a |
|
`LocaleResolver`. It detects the theme to use for a particular request and can also |
|
alter the request's theme. The following theme resolvers are provided by Spring: |
|
|
|
[[mvc-theme-resolver-impls-tbl]] |
|
.ThemeResolver implementations |
|
[cols="1,4"] |
|
|=== |
|
| Class| Description |
|
|
|
| `FixedThemeResolver` |
|
| Selects a fixed theme, set using the `defaultThemeName` property. |
|
|
|
| `SessionThemeResolver` |
|
| The theme is maintained in the user's HTTP session. It only needs to be set once for |
|
each session, but is not persisted between sessions. |
|
|
|
| `CookieThemeResolver` |
|
| The selected theme is stored in a cookie on the client. |
|
|=== |
|
|
|
Spring also provides a `ThemeChangeInterceptor` that allows theme changes on every |
|
request with a simple request parameter. |
|
|
|
|
|
|
|
|
|
[[mvc-multipart]] |
|
=== Spring's multipart (file upload) support |
|
|
|
|
|
|
|
[[mvc-multipart-introduction]] |
|
==== Introduction |
|
Spring's built-in multipart support handles file uploads in web applications. You enable |
|
this multipart support with pluggable `MultipartResolver` objects, defined in the |
|
`org.springframework.web.multipart` package. Spring provides one `MultipartResolver` |
|
implementation for use with http://jakarta.apache.org/commons/fileupload[__Commons |
|
FileUpload__] and another for use with Servlet 3.0 multipart request parsing. |
|
|
|
By default, Spring does no multipart handling, because some developers want to handle |
|
multiparts themselves. You enable Spring multipart handling by adding a multipart |
|
resolver to the web application's context. Each request is inspected to see if it |
|
contains a multipart. If no multipart is found, the request continues as expected. If a |
|
multipart is found in the request, the `MultipartResolver` that has been declared in |
|
your context is used. After that, the multipart attribute in your request is treated |
|
like any other attribute. |
|
|
|
|
|
|
|
[[mvc-multipart-resolver-commons]] |
|
==== Using a MultipartResolver with __Commons FileUpload__ |
|
|
|
The following example shows how to use the `CommonsMultipartResolver`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="multipartResolver" |
|
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> |
|
|
|
<!-- one of the properties available; the maximum file size in bytes --> |
|
<property name="maxUploadSize" value="100000"/> |
|
|
|
</bean> |
|
---- |
|
|
|
Of course you also need to put the appropriate jars in your classpath for the multipart |
|
resolver to work. In the case of the `CommonsMultipartResolver`, you need to use |
|
`commons-fileupload.jar`. |
|
|
|
When the Spring `DispatcherServlet` detects a multi-part request, it activates the |
|
resolver that has been declared in your context and hands over the request. The resolver |
|
then wraps the current `HttpServletRequest` into a `MultipartHttpServletRequest` that |
|
supports multipart file uploads. Using the `MultipartHttpServletRequest`, you can get |
|
information about the multiparts contained by this request and actually get access to |
|
the multipart files themselves in your controllers. |
|
|
|
|
|
|
|
[[mvc-multipart-resolver-standard]] |
|
==== Using a MultipartResolver with __Servlet 3.0__ |
|
|
|
In order to use Servlet 3.0 based multipart parsing, you need to mark the |
|
`DispatcherServlet` with a `"multipart-config"` section in `web.xml`, or with a |
|
`javax.servlet.MultipartConfigElement` in programmatic Servlet registration, or in case |
|
of a custom Servlet class possibly with a `javax.servlet.annotation.MultipartConfig` |
|
annotation on your Servlet class. Configuration settings such as maximum sizes or |
|
storage locations need to be applied at that Servlet registration level as Servlet 3.0 |
|
does not allow for those settings to be done from the MultipartResolver. |
|
|
|
Once Servlet 3.0 multipart parsing has been enabled in one of the above mentioned ways |
|
you can add the `StandardServletMultipartResolver` to your Spring configuration: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="multipartResolver" |
|
class="org.springframework.web.multipart.support.StandardServletMultipartResolver"> |
|
</bean> |
|
---- |
|
|
|
|
|
|
|
[[mvc-multipart-forms]] |
|
==== Handling a file upload in a form |
|
After the `MultipartResolver` completes its job, the request is processed like any |
|
other. First, create a form with a file input that will allow the user to upload a form. |
|
The encoding attribute ( `enctype="multipart/form-data"`) lets the browser know how to |
|
encode the form as multipart request: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<html> |
|
<head> |
|
<title>Upload a file please</title> |
|
</head> |
|
<body> |
|
<h1>Please upload a file</h1> |
|
<form method="post" action="/form" enctype="multipart/form-data"> |
|
<input type="text" name="name"/> |
|
<input type="file" name="file"/> |
|
<input type="submit"/> |
|
</form> |
|
</body> |
|
</html> |
|
---- |
|
|
|
The next step is to create a controller that handles the file upload. This controller is |
|
very similar to a <<mvc-ann-controller,normal annotated `@Controller`>>, except that we |
|
use `MultipartHttpServletRequest` or `MultipartFile` in the method parameters: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
public class FileUploadController { |
|
|
|
@RequestMapping(value = "/form", method = RequestMethod.POST) |
|
public String handleFormUpload(@RequestParam("name") String name, |
|
@RequestParam("file") MultipartFile file) { |
|
|
|
if (!file.isEmpty()) { |
|
byte[] bytes = file.getBytes(); |
|
// store the bytes somewhere |
|
return "redirect:uploadSuccess"; |
|
} |
|
|
|
return "redirect:uploadFailure"; |
|
} |
|
|
|
} |
|
---- |
|
|
|
Note how the `@RequestParam` method parameters map to the input elements declared in the |
|
form. In this example, nothing is done with the `byte[]`, but in practice you can save |
|
it in a database, store it on the file system, and so on. |
|
|
|
When using Servlet 3.0 multipart parsing you can also use `javax.servlet.http.Part` for |
|
the method parameter: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
public class FileUploadController { |
|
|
|
@RequestMapping(value = "/form", method = RequestMethod.POST) |
|
public String handleFormUpload(@RequestParam("name") String name, |
|
@RequestParam("file") Part file) { |
|
|
|
InputStream inputStream = file.getInputStream(); |
|
// store bytes from uploaded file somewhere |
|
|
|
return "redirect:uploadSuccess"; |
|
} |
|
|
|
} |
|
---- |
|
|
|
|
|
|
|
[[mvc-multipart-forms-non-browsers]] |
|
==== Handling a file upload request from programmatic clients |
|
Multipart requests can also be submitted from non-browser clients in a RESTful service |
|
scenario. All of the above examples and configuration apply here as well. However, |
|
unlike browsers that typically submit files and simple form fields, a programmatic |
|
client can also send more complex data of a specific content type -- for example a |
|
multipart request with a file and second part with JSON formatted data: |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
POST /someUrl |
|
Content-Type: multipart/mixed |
|
|
|
--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp |
|
Content-Disposition: form-data; name="meta-data" |
|
Content-Type: application/json; charset=UTF-8 |
|
Content-Transfer-Encoding: 8bit |
|
|
|
{ |
|
"name": "value" |
|
} |
|
--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp |
|
Content-Disposition: form-data; name="file-data"; filename="file.properties" |
|
Content-Type: text/xml |
|
Content-Transfer-Encoding: 8bit |
|
... File Data ... |
|
---- |
|
|
|
You could access the part named "meta-data" with a `@RequestParam("meta-data") String |
|
metadata` controller method argument. However, you would probably prefer to accept a |
|
strongly typed object initialized from the JSON formatted data in the body of the |
|
request part, very similar to the way `@RequestBody` converts the body of a |
|
non-multipart request to a target object with the help of an `HttpMessageConverter`. |
|
|
|
You can use the `@RequestPart` annotation instead of the `@RequestParam` annotation for |
|
this purpose. It allows you to have the content of a specific multipart passed through |
|
an `HttpMessageConverter` taking into consideration the `'Content-Type'` header of the |
|
multipart: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping(value="/someUrl", method = RequestMethod.POST) |
|
public String onSubmit(**@RequestPart("meta-data") MetaData metadata, |
|
@RequestPart("file-data") MultipartFile file**) { |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
Notice how `MultipartFile` method arguments can be accessed with `@RequestParam` or with |
|
`@RequestPart` interchangeably. However, the `@RequestPart("meta-data") MetaData` method |
|
argument in this case is read as JSON content based on its `'Content-Type'` header and |
|
converted with the help of the `MappingJackson2HttpMessageConverter`. |
|
|
|
|
|
|
|
|
|
[[mvc-exceptionhandlers]] |
|
=== Handling exceptions |
|
|
|
|
|
|
|
[[mvc-exceptionhandlers-resolver]] |
|
==== HandlerExceptionResolver |
|
|
|
Spring `HandlerExceptionResolver` implementations deal with unexpected exceptions that |
|
occur during controller execution. A `HandlerExceptionResolver` somewhat resembles the |
|
exception mappings you can define in the web application descriptor `web.xml`. However, |
|
they provide a more flexible way to do so. For example they provide information about |
|
which handler was executing when the exception was thrown. Furthermore, a programmatic |
|
way of handling exceptions gives you more options for responding appropriately before |
|
the request is forwarded to another URL (the same end result as when you use the Servlet |
|
specific exception mappings). |
|
|
|
Besides implementing the `HandlerExceptionResolver` interface, which is only a matter of |
|
implementing the `resolveException(Exception, Handler)` method and returning a |
|
`ModelAndView`, you may also use the provided `SimpleMappingExceptionResolver` or create |
|
`@ExceptionHandler` methods. The `SimpleMappingExceptionResolver` enables you to take |
|
the class name of any exception that might be thrown and map it to a view name. This is |
|
functionally equivalent to the exception mapping feature from the Servlet API, but it is |
|
also possible to implement more finely grained mappings of exceptions from different |
|
handlers. The `@ExceptionHandler` annotation on the other hand can be used on methods |
|
that should be invoked to handle an exception. Such methods may be defined locally |
|
within an `@Controller` or may apply to many `@Controller` classes when defined within an |
|
`@ControllerAdvice` class. The following sections explain this in more detail. |
|
|
|
|
|
|
|
[[mvc-ann-exceptionhandler]] |
|
==== @ExceptionHandler |
|
|
|
The `HandlerExceptionResolver` interface and the `SimpleMappingExceptionResolver` |
|
implementations allow you to map Exceptions to specific views declaratively along with |
|
some optional Java logic before forwarding to those views. However, in some cases, |
|
especially when relying on `@ResponseBody` methods rather than on view resolution, it |
|
may be more convenient to directly set the status of the response and optionally write |
|
error content to the body of the response. |
|
|
|
You can do that with `@ExceptionHandler` methods. When declared within a controller such |
|
methods apply to exceptions raised by `@RequestMapping` methods of that contoroller (or |
|
any of its sub-classes). You can also declare an `@ExceptionHandler` method within an |
|
`@ControllerAdvice` class in which case it handles exceptions from `@RequestMapping` |
|
methods from many controllers. Below is an example of a controller-local |
|
`@ExceptionHandler` method: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
public class SimpleController { |
|
|
|
// @RequestMapping methods omitted ... |
|
|
|
@ExceptionHandler(IOException.class) |
|
public ResponseEntity<String> handleIOException(IOException ex) { |
|
// prepare responseEntity |
|
return responseEntity; |
|
} |
|
|
|
} |
|
---- |
|
|
|
The `@ExceptionHandler` value can be set to an array of Exception types. If an exception |
|
is thrown that matches one of the types in the list, then the method annotated with the |
|
matching `@ExceptionHandler` will be invoked. If the annotation value is not set then |
|
the exception types listed as method arguments are used. |
|
|
|
Much like standard controller methods annotated with a `@RequestMapping` annotation, the |
|
method arguments and return values of `@ExceptionHandler` methods can be flexible. For |
|
example, the `HttpServletRequest` can be accessed in Servlet environments and the |
|
`PortletRequest` in Portlet environments. The return type can be a `String`, which is |
|
interpreted as a view name, a `ModelAndView` object, a `ResponseEntity`, or you can also |
|
add the `@ResponseBody` to have the method return value converted with message |
|
converters and written to the response stream. |
|
|
|
|
|
|
|
[[mvc-ann-rest-spring-mvc-exceptions]] |
|
==== Handling Standard Spring MVC Exceptions |
|
Spring MVC may raise a number of exceptions while processing a request. The |
|
`SimpleMappingExceptionResolver` can easily map any exception to a default error view as |
|
needed. However, when working with clients that interpret responses in an automated way |
|
you will want to set specific status code on the response. Depending on the exception |
|
raised the status code may indicate a client error (4xx) or a server error (5xx). |
|
|
|
The `DefaultHandlerExceptionResolver` translates Spring MVC exceptions to specific error |
|
status codes. It is registered by default with the MVC namespace, the MVC Java config, |
|
and also by the the `DispatcherServlet` (i.e. when not using the MVC namespace or Java |
|
config). Listed below are some of the exceptions handled by this resolver and the |
|
corresponding status codes: |
|
|
|
|=== |
|
| Exception| HTTP Status Code |
|
|
|
| `BindException` |
|
| 400 (Bad Request) |
|
|
|
| `ConversionNotSupportedException` |
|
| 500 (Internal Server Error) |
|
|
|
| `HttpMediaTypeNotAcceptableException` |
|
| 406 (Not Acceptable) |
|
|
|
| `HttpMediaTypeNotSupportedException` |
|
| 415 (Unsupported Media Type) |
|
|
|
| `HttpMessageNotReadableException` |
|
| 400 (Bad Request) |
|
|
|
| `HttpMessageNotWritableException` |
|
| 500 (Internal Server Error) |
|
|
|
| `HttpRequestMethodNotSupportedException` |
|
| 405 (Method Not Allowed) |
|
|
|
| `MethodArgumentNotValidException` |
|
| 400 (Bad Request) |
|
|
|
| `MissingServletRequestParameterException` |
|
| 400 (Bad Request) |
|
|
|
| `MissingServletRequestPartException` |
|
| 400 (Bad Request) |
|
|
|
| `NoHandlerFoundException` |
|
| 404 (Not Found) |
|
|
|
| `NoSuchRequestHandlingMethodException` |
|
| 404 (Not Found) |
|
|
|
| `TypeMismatchException` |
|
| 400 (Bad Request) |
|
|=== |
|
|
|
The `DefaultHandlerExceptionResolver` works transparently by setting the status of the |
|
response. However, it stops short of writing any error content to the body of the |
|
response while your application may need to add developer-friendly content to every |
|
error response for example when providing a REST API. You can prepare a `ModelAndView` |
|
and render error content through view resolution -- i.e. by configuring a |
|
`ContentNegotiatingViewResolver`, `MappingJackson2JsonView`, and so on. However, you may |
|
prefer to use `@ExceptionHandler` methods instead. |
|
|
|
If you prefer to write error content via `@ExceptionHandler` methods you can extend |
|
`ResponseEntityExceptionHandler` instead. This is a convenient base for |
|
`@ControllerAdvice` classes providing an `@ExceptionHandler` method to handle standard |
|
Spring MVC exceptions and return `ResponseEntity`. That allows you to customize the |
|
response and write error content with message converters. See the |
|
`ResponseEntityExceptionHandler` javadocs for more details. |
|
|
|
|
|
|
|
[[mvc-ann-annotated-exceptions]] |
|
==== Annotating Business Exceptions With @ResponseStatus |
|
|
|
A business exception can be annotated with `@ResponseStatus`. When the exception is |
|
raised, the `ResponseStatusExceptionResolver` handles it by setting the status of the |
|
response accordingly. By default the `DispatcherServlet` registers the |
|
`ResponseStatusExceptionResolver` and it is available for use. |
|
|
|
|
|
|
|
[[mvc-ann-customer-servlet-container-error-page]] |
|
==== Customizing the Default Servlet Container Error Page |
|
When the status of the response is set to an error status code and the body of the |
|
response is empty, Servlet containers commonly render an HTML formatted error page. To |
|
customize the default error page of the container, you can declare an `<error-page>` |
|
element in `web.xml`. Up until Servlet 3, that element had to be mapped to a specific |
|
status code or exception type. Starting with Servlet 3 an error page does not need to be |
|
mapped, which effectively means the specified location customizes the default Servlet |
|
container error page. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<error-page> |
|
<location>/error</location> |
|
</error-page> |
|
---- |
|
|
|
Note that the actual location for the error page can be a JSP page or some other URL |
|
within the container including one handled through an `@Controller` method: |
|
|
|
When writing error information, the status code and the error message set on the |
|
`HttpServletResponse` can be accessed through request attributes in a controller: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
public class ErrorController { |
|
|
|
@RequestMapping(value="/error", produces="application/json") |
|
@ResponseBody |
|
public Map<String, Object> handle(HttpServletRequest request) { |
|
|
|
Map<String, Object> map = new HashMap<String, Object>(); |
|
map.put("status", request.getAttribute("javax.servlet.error.status_code")); |
|
map.put("reason", request.getAttribute("javax.servlet.error.message")); |
|
|
|
return map; |
|
} |
|
|
|
} |
|
---- |
|
|
|
or in a JSP: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<%@ page contentType="application/json" pageEncoding="UTF-8"%> |
|
{ |
|
status:<%=request.getAttribute("javax.servlet.error.status_code") %>, |
|
reason:<%=request.getAttribute("javax.servlet.error.message") %> |
|
} |
|
---- |
|
|
|
|
|
[[mvc-web-security]] |
|
=== Web Security |
|
|
|
The http://projects.spring.io/spring-security/[Spring Security] project provides features |
|
to protect web applications from malicious exploits. Check out the reference documentation in the sections on |
|
http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#csrf["CSRF protection"], |
|
http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#headers["Security Response Headers"], and also |
|
http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#mvc["Spring MVC Integration"]. |
|
Note that using Spring Security to secure the application is not necessarily required for all features. |
|
For example CSRF protection can be added simply by adding the `CsrfFilter` and |
|
`CsrfRequestDataValueProcessor` to your configuration. See the |
|
https://github.com/spring-projects/spring-mvc-showcase/commit/361adc124c05a8187b84f25e8a57550bb7d9f8e4[Spring MVC Showcase] |
|
for an example. |
|
|
|
Another option is to use a framework dedicated to Web Security. |
|
http://hdiv.org/[HDIV] is one such framework and integrates with Spring MVC. |
|
|
|
|
|
|
|
|
|
[[mvc-coc]] |
|
=== Convention over configuration support |
|
For a lot of projects, sticking to established conventions and having reasonable |
|
defaults is just what they (the projects) need, and Spring Web MVC now has explicit |
|
support for __convention over configuration__. What this means is that if you establish |
|
a set of naming conventions and suchlike, you can __substantially__ cut down on the |
|
amount of configuration that is required to set up handler mappings, view resolvers, |
|
`ModelAndView` instances, etc. This is a great boon with regards to rapid prototyping, |
|
and can also lend a degree of (always good-to-have) consistency across a codebase should |
|
you choose to move forward with it into production. |
|
|
|
Convention-over-configuration support addresses the three core areas of MVC: models, |
|
views, and controllers. |
|
|
|
|
|
|
|
[[mvc-coc-ccnhm]] |
|
==== The Controller ControllerClassNameHandlerMapping |
|
|
|
The `ControllerClassNameHandlerMapping` class is a `HandlerMapping` implementation that |
|
uses a convention to determine the mapping between request URLs and the `Controller` |
|
instances that are to handle those requests. |
|
|
|
Consider the following simple `Controller` implementation. Take special notice of the |
|
__name__ of the class. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class **ViewShoppingCartController** implements Controller { |
|
|
|
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) { |
|
// the implementation is not hugely important for this example... |
|
} |
|
|
|
} |
|
---- |
|
|
|
Here is a snippet from the corresponding Spring Web MVC configuration file: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/> |
|
|
|
<bean id="**viewShoppingCart**" class="x.y.z.ViewShoppingCartController"> |
|
<!-- inject dependencies as required... --> |
|
</bean> |
|
---- |
|
|
|
The `ControllerClassNameHandlerMapping` finds all of the various handler (or |
|
`Controller`) beans defined in its application context and strips `Controller` off the |
|
name to define its handler mappings. Thus, `ViewShoppingCartController` maps to the |
|
`/viewshoppingcart*` request URL. |
|
|
|
Let's look at some more examples so that the central idea becomes immediately familiar. |
|
(Notice all lowercase in the URLs, in contrast to camel-cased `Controller` class names.) |
|
|
|
* `WelcomeController` maps to the `/welcome*` request URL |
|
* `HomeController` maps to the `/home*` request URL |
|
* `IndexController` maps to the `/index*` request URL |
|
* `RegisterController` maps to the `/register*` request URL |
|
|
|
In the case of `MultiActionController` handler classes, the mappings generated are |
|
slightly more complex. The `Controller` names in the following examples are assumed to |
|
be `MultiActionController` implementations: |
|
|
|
* `AdminController` maps to the `/admin/*` request URL |
|
* `CatalogController` maps to the `/catalog/*` request URL |
|
|
|
If you follow the convention of naming your `Controller` implementations as |
|
`xxxController`, the `ControllerClassNameHandlerMapping` saves you the tedium of |
|
defining and maintaining a potentially __looooong__ `SimpleUrlHandlerMapping` (or |
|
suchlike). |
|
|
|
The `ControllerClassNameHandlerMapping` class extends the `AbstractHandlerMapping` base |
|
class so you can define `HandlerInterceptor` instances and everything else just as you |
|
would with many other `HandlerMapping` implementations. |
|
|
|
|
|
|
|
[[mvc-coc-modelmap]] |
|
==== The Model ModelMap (ModelAndView) |
|
|
|
The `ModelMap` class is essentially a glorified `Map` that can make adding objects that |
|
are to be displayed in (or on) a `View` adhere to a common naming convention. Consider |
|
the following `Controller` implementation; notice that objects are added to the |
|
`ModelAndView` without any associated name specified. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class DisplayShoppingCartController implements Controller { |
|
|
|
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) { |
|
|
|
List cartItems = // get a List of CartItem objects |
|
User user = // get the User doing the shopping |
|
|
|
ModelAndView mav = new ModelAndView("displayShoppingCart"); <-- the logical view name |
|
|
|
mav.addObject(cartItems); <-- look ma, no name, just the object |
|
mav.addObject(user); <-- and again ma! |
|
|
|
return mav; |
|
} |
|
} |
|
---- |
|
|
|
The `ModelAndView` class uses a `ModelMap` class that is a custom `Map` implementation |
|
that automatically generates a key for an object when an object is added to it. The |
|
strategy for determining the name for an added object is, in the case of a scalar object |
|
such as `User`, to use the short class name of the object's class. The following |
|
examples are names that are generated for scalar objects put into a `ModelMap` instance. |
|
|
|
* An `x.y.User` instance added will have the name `user` generated. |
|
* An `x.y.Registration` instance added will have the name `registration` generated. |
|
* An `x.y.Foo` instance added will have the name `foo` generated. |
|
* A `java.util.HashMap` instance added will have the name `hashMap` generated. You |
|
probably want to be explicit about the name in this case because `hashMap` is less |
|
than intuitive. |
|
* Adding `null` will result in an `IllegalArgumentException` being thrown. If the object |
|
(or objects) that you are adding could be `null`, then you will also want to be |
|
explicit about the name. |
|
|
|
.What, no automatic pluralization? |
|
**** |
|
Spring Web MVC's convention-over-configuration support does not support automatic |
|
pluralization. That is, you cannot add a `List` of `Person` objects to a `ModelAndView` |
|
and have the generated name be `people`. |
|
|
|
This decision was made after some debate, with the "Principle of Least Surprise" winning |
|
out in the end. |
|
**** |
|
|
|
The strategy for generating a name after adding a `Set` or a `List` is to peek into the |
|
collection, take the short class name of the first object in the collection, and use |
|
that with `List` appended to the name. The same applies to arrays although with arrays |
|
it is not necessary to peek into the array contents. A few examples will make the |
|
semantics of name generation for collections clearer: |
|
|
|
* An `x.y.User[]` array with zero or more `x.y.User` elements added will have the name |
|
`userList` generated. |
|
* An `x.y.Foo[]` array with zero or more `x.y.User` elements added will have the name |
|
`fooList` generated. |
|
* A `java.util.ArrayList` with one or more `x.y.User` elements added will have the name |
|
`userList` generated. |
|
* A `java.util.HashSet` with one or more `x.y.Foo` elements added will have the name |
|
`fooList` generated. |
|
* An __empty__ `java.util.ArrayList` will not be added at all (in effect, the |
|
`addObject(..)` call will essentially be a no-op). |
|
|
|
|
|
|
|
[[mvc-coc-r2vnt]] |
|
==== The View - RequestToViewNameTranslator |
|
|
|
The `RequestToViewNameTranslator` interface determines a logical `View` name when no |
|
such logical view name is explicitly supplied. It has just one implementation, the |
|
`DefaultRequestToViewNameTranslator` class. |
|
|
|
The `DefaultRequestToViewNameTranslator` maps request URLs to logical view names, as |
|
with this example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class RegistrationController implements Controller { |
|
|
|
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) { |
|
// process the request... |
|
ModelAndView mav = new ModelAndView(); |
|
// add data as necessary to the model... |
|
return mav; |
|
// notice that no View or logical view name has been set |
|
} |
|
|
|
} |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd"> |
|
|
|
<!-- this bean with the well known name generates view names for us --> |
|
<bean id="viewNameTranslator" |
|
class="org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator"/> |
|
|
|
<bean class="x.y.RegistrationController"> |
|
<!-- inject dependencies as necessary --> |
|
</bean> |
|
|
|
<!-- maps request URLs to Controller names --> |
|
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/> |
|
|
|
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> |
|
<property name="prefix" value="/WEB-INF/jsp/"/> |
|
<property name="suffix" value=".jsp"/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
Notice how in the implementation of the `handleRequest(..)` method no `View` or logical |
|
view name is ever set on the `ModelAndView` that is returned. The |
|
`DefaultRequestToViewNameTranslator` is tasked with generating a __logical view name__ |
|
from the URL of the request. In the case of the above `RegistrationController`, which is |
|
used in conjunction with the `ControllerClassNameHandlerMapping`, a request URL of |
|
`http://localhost/registration.html` results in a logical view name of `registration` |
|
being generated by the `DefaultRequestToViewNameTranslator`. This logical view name is |
|
then resolved into the `/WEB-INF/jsp/registration.jsp` view by the |
|
`InternalResourceViewResolver` bean. |
|
|
|
[TIP] |
|
==== |
|
|
|
You do not need to define a `DefaultRequestToViewNameTranslator` bean explicitly. If you |
|
like the default settings of the `DefaultRequestToViewNameTranslator`, you can rely on |
|
the Spring Web MVC `DispatcherServlet` to instantiate an instance of this class if one |
|
is not explicitly configured. |
|
==== |
|
|
|
Of course, if you need to change the default settings, then you do need to configure |
|
your own `DefaultRequestToViewNameTranslator` bean explicitly. Consult the comprehensive |
|
`DefaultRequestToViewNameTranslator` javadocs for details on the various properties |
|
that can be configured. |
|
|
|
|
|
|
|
|
|
[[mvc-etag]] |
|
=== ETag support |
|
An http://en.wikipedia.org/wiki/HTTP_ETag[ETag] (entity tag) is an HTTP response header |
|
returned by an HTTP/1.1 compliant web server used to determine change in content at a |
|
given URL. It can be considered to be the more sophisticated successor to the |
|
`Last-Modified` header. When a server returns a representation with an ETag header, the |
|
client can use this header in subsequent GETs, in an `If-None-Match` header. If the |
|
content has not changed, the server returns `304: Not Modified`. |
|
|
|
Support for ETags is provided by the Servlet filter `ShallowEtagHeaderFilter`. It is a |
|
plain Servlet Filter, and thus can be used in combination with any web framework. The |
|
`ShallowEtagHeaderFilter` filter creates so-called shallow ETags (as opposed to deep |
|
ETags, more about that later).The filter caches the content of the rendered JSP (or |
|
other content), generates an MD5 hash over that, and returns that as an ETag header in |
|
the response. The next time a client sends a request for the same resource, it uses that |
|
hash as the `If-None-Match` value. The filter detects this, renders the view again, and |
|
compares the two hashes. If they are equal, a `304` is returned. This filter will not |
|
save processing power, as the view is still rendered. The only thing it saves is |
|
bandwidth, as the rendered response is not sent back over the wire. |
|
|
|
You configure the `ShallowEtagHeaderFilter` in `web.xml`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<filter> |
|
<filter-name>etagFilter</filter-name> |
|
<filter-class>org.springframework.web.filter.ShallowEtagHeaderFilter</filter-class> |
|
</filter> |
|
|
|
<filter-mapping> |
|
<filter-name>etagFilter</filter-name> |
|
<servlet-name>petclinic</servlet-name> |
|
</filter-mapping> |
|
---- |
|
|
|
|
|
|
|
|
|
[[mvc-container-config]] |
|
=== Code-based Servlet container initialization |
|
In a Servlet 3.0+ environment, you have the option of configuring the Servlet container |
|
programmatically as an alternative or in combination with a `web.xml` file. Below is an |
|
example of registering a `DispatcherServlet`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import org.springframework.web.WebApplicationInitializer; |
|
|
|
public class MyWebApplicationInitializer implements WebApplicationInitializer { |
|
|
|
@Override |
|
public void onStartup(ServletContext container) { |
|
XmlWebApplicationContext appContext = new XmlWebApplicationContext(); |
|
appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml"); |
|
|
|
ServletRegistration.Dynamic registration = container.addServlet("dispatcher", new DispatcherServlet(appContext)); |
|
registration.setLoadOnStartup(1); |
|
registration.addMapping("/"); |
|
} |
|
|
|
} |
|
---- |
|
|
|
`WebApplicationInitializer` is an interface provided by Spring MVC that ensures your |
|
implementation is detected and automatically used to initialize any Servlet 3 container. |
|
An abstract base class implementation of `WebApplicationInitializer` named |
|
`AbstractDispatcherServletInitializer` makes it even easier to register the |
|
`DispatcherServlet` by simply overriding methods to specify the servlet mapping and the |
|
location of the `DispatcherServlet` configuration: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { |
|
|
|
@Override |
|
protected Class<?>[] getRootConfigClasses() { |
|
return null; |
|
} |
|
|
|
@Override |
|
protected Class<?>[] getServletConfigClasses() { |
|
return new Class[] { MyWebConfig.class }; |
|
} |
|
|
|
@Override |
|
protected String[] getServletMappings() { |
|
return new String[] { "/" }; |
|
} |
|
|
|
} |
|
---- |
|
|
|
The above example is for an application that uses Java-based Spring configuration. If |
|
using XML-based Spring configuration, extend directly from |
|
`AbstractDispatcherServletInitializer`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MyWebAppInitializer extends AbstractDispatcherServletInitializer { |
|
|
|
@Override |
|
protected WebApplicationContext createRootApplicationContext() { |
|
return null; |
|
} |
|
|
|
@Override |
|
protected WebApplicationContext createServletApplicationContext() { |
|
XmlWebApplicationContext cxt = new XmlWebApplicationContext(); |
|
cxt.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml"); |
|
return cxt; |
|
} |
|
|
|
@Override |
|
protected String[] getServletMappings() { |
|
return new String[] { "/" }; |
|
} |
|
|
|
} |
|
---- |
|
|
|
`AbstractDispatcherServletInitializer` also provides a convenient way to add `Filter` |
|
instances and have them automatically mapped to the `DispatcherServlet`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MyWebAppInitializer extends AbstractDispatcherServletInitializer { |
|
|
|
// ... |
|
|
|
@Override |
|
protected Filter[] getServletFilters() { |
|
return new Filter[] { new HiddenHttpMethodFilter(), new CharacterEncodingFilter() }; |
|
} |
|
|
|
} |
|
---- |
|
|
|
Each filter is added with a default name based on its concrete type and automatically |
|
mapped to the `DispatcherServlet`. |
|
|
|
The `isAsyncSupported` protected method of `AbstractDispatcherServletInitializer` |
|
provides a single place to enable async support on the `DispatcherServlet` and all |
|
filters mapped to it. By default this flag is set to `true`. |
|
|
|
|
|
|
|
|
|
[[mvc-config]] |
|
=== Configuring Spring MVC |
|
<<mvc-servlet-special-bean-types>> and <<mvc-servlet-config>> explained about Spring |
|
MVC's special beans and the default implementations used by the `DispatcherServlet`. In |
|
this section you'll learn about two additional ways of configuring Spring MVC. Namely |
|
the MVC Java config and the MVC XML namespace. |
|
|
|
The MVC Java config and the MVC namespace provide similar default configuration that |
|
overrides the `DispatcherServlet` defaults. The goal is to spare most applications from |
|
having to having to create the same configuration and also to provide higher-level |
|
constructs for configuring Spring MVC that serve as a simple starting point and require |
|
little or no prior knowledge of the underlying configuration. |
|
|
|
You can choose either the MVC Java config or the MVC namespace depending on your |
|
preference. Also as you will see further below, with the MVC Java config it is easier to |
|
see the underlying configuration as well as to make fine-grained customizations directly |
|
to the created Spring MVC beans. But let's start from the beginning. |
|
|
|
|
|
|
|
[[mvc-config-enable]] |
|
==== Enabling the MVC Java Config or the MVC XML Namespace |
|
To enable MVC Java config add the annotation `@EnableWebMvc` to one of your |
|
`@Configuration` classes: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig { |
|
|
|
} |
|
---- |
|
|
|
To achieve the same in XML use the `mvc:annotation-driven` element in your |
|
DispatcherServlet context (or in your root context if you have no DispatcherServlet |
|
context defined): |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:mvc="http://www.springframework.org/schema/mvc" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/mvc |
|
http://www.springframework.org/schema/mvc/spring-mvc.xsd"> |
|
|
|
<mvc:annotation-driven /> |
|
|
|
</beans> |
|
---- |
|
|
|
The above registers a `RequestMappingHandlerMapping`, a `RequestMappingHandlerAdapter`, |
|
and an `ExceptionHandlerExceptionResolver` (among others) in support of processing |
|
requests with annotated controller methods using annotations such as `@RequestMapping`, |
|
`@ExceptionHandler`, and others. |
|
|
|
It also enables the following: |
|
|
|
. Spring 3 style type conversion through a <<core-convert,ConversionService>> instance |
|
in addition to the JavaBeans PropertyEditors used for Data Binding. |
|
. Support for <<format,formatting>> Number fields using the `@NumberFormat` annotation |
|
through the `ConversionService`. |
|
. Support for <<format,formatting>> Date, Calendar, Long, and Joda Time fields using the |
|
`@DateTimeFormat` annotation. |
|
. Support for <<validation-mvc-jsr303,validating>> `@Controller` inputs with `@Valid`, if |
|
a JSR-303 Provider is present on the classpath. |
|
. HttpMessageConverter support for `@RequestBody` method parameters and `@ResponseBody` |
|
method return values from `@RequestMapping` or `@ExceptionHandler` methods. |
|
|
|
+ |
|
|
|
This is the complete list of HttpMessageConverters set up by mvc:annotation-driven: |
|
|
|
+ |
|
|
|
.. `ByteArrayHttpMessageConverter` converts byte arrays. |
|
.. `StringHttpMessageConverter` converts strings. |
|
.. `ResourceHttpMessageConverter` converts to/from |
|
`org.springframework.core.io.Resource` for all media types. |
|
.. `SourceHttpMessageConverter` converts to/from a `javax.xml.transform.Source`. |
|
.. `FormHttpMessageConverter` converts form data to/from a `MultiValueMap<String, |
|
String>`. |
|
.. `Jaxb2RootElementHttpMessageConverter` converts Java objects to/from XML -- added if |
|
JAXB2 is present and Jackson 2 XML extension is not present on the classpath. |
|
.. `MappingJackson2HttpMessageConverter` converts to/from JSON -- added if Jackson 2 |
|
is present on the classpath. |
|
.. `MappingJackson2XmlHttpMessageConverter` converts to/from XML -- added if |
|
https://github.com/FasterXML/jackson-dataformat-xml[Jackson 2 XML extension] is present |
|
on the classpath. |
|
.. `AtomFeedHttpMessageConverter` converts Atom feeds -- added if Rome is present on the |
|
classpath. |
|
.. `RssChannelHttpMessageConverter` converts RSS feeds -- added if Rome is present on |
|
the classpath. |
|
|
|
|
|
|
|
[[mvc-config-customize]] |
|
==== Customizing the Provided Configuration |
|
To customize the default configuration in Java you simply implement the |
|
`WebMvcConfigurer` interface or more likely extend the class `WebMvcConfigurerAdapter` |
|
and override the methods you need. Below is an example of some of the available methods |
|
to override. See |
|
{javadoc-baseurl}/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.html[`WebMvcConfigurer`] |
|
for a list of all methods and the javadocs for further details: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig extends WebMvcConfigurerAdapter { |
|
|
|
@Override |
|
protected void addFormatters(FormatterRegistry registry) { |
|
// Add formatters and/or converters |
|
} |
|
|
|
@Override |
|
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { |
|
// Configure the list of HttpMessageConverters to use |
|
} |
|
|
|
} |
|
---- |
|
|
|
To customize the default configuration of `<mvc:annotation-driven />` check what |
|
attributes and sub-elements it supports. You can view the |
|
http://schema.spring.io/mvc/spring-mvc.xsd[Spring MVC XML schema] or use the code |
|
completion feature of your IDE to discover what attributes and sub-elements are |
|
available. The sample below shows a subset of what is available: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<mvc:annotation-driven conversion-service="conversionService"> |
|
<mvc:message-converters> |
|
<bean class="org.example.MyHttpMessageConverter"/> |
|
<bean class="org.example.MyOtherHttpMessageConverter"/> |
|
</mvc:message-converters> |
|
</mvc:annotation-driven> |
|
|
|
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> |
|
<property name="formatters"> |
|
<list> |
|
<bean class="org.example.MyFormatter"/> |
|
<bean class="org.example.MyOtherFormatter"/> |
|
</list> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
|
|
|
|
[[mvc-config-interceptors]] |
|
==== Interceptors |
|
You can configure `HandlerInterceptors` or `WebRequestInterceptors` to be applied to all |
|
incoming requests or restricted to specific URL path patterns. |
|
|
|
An example of registering interceptors in Java: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig extends WebMvcConfigurerAdapter { |
|
|
|
@Override |
|
public void addInterceptors(InterceptorRegistry registry) { |
|
registry.addInterceptor(new LocaleInterceptor()); |
|
registry.addInterceptor(new ThemeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**"); |
|
registry.addInterceptor(new SecurityInterceptor()).addPathPatterns("/secure/*"); |
|
} |
|
|
|
} |
|
---- |
|
|
|
And in XML use the `<mvc:interceptors>` element: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<mvc:interceptors> |
|
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" /> |
|
<mvc:interceptor> |
|
<mvc:mapping path="/**"/> |
|
<mvc:exclude-mapping path="/admin/**"/> |
|
<bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor" /> |
|
</mvc:interceptor> |
|
<mvc:interceptor> |
|
<mvc:mapping path="/secure/*"/> |
|
<bean class="org.example.SecurityInterceptor" /> |
|
</mvc:interceptor> |
|
</mvc:interceptors> |
|
---- |
|
|
|
|
|
|
|
[[mvc-config-content-negotiation]] |
|
==== Content Negotiation |
|
You can configure how Spring MVC determines the requested media types from the client |
|
for request mapping as well as for content negotiation purposes. The available options |
|
are to check the file extension in the request URI, the "Accept" header, a request |
|
parameter, as well as to fall back on a default content type. By default, file extension |
|
in the request URI is checked first and the "Accept" header is checked next. |
|
|
|
For file extensions in the request URI, the MVC Java config and the MVC namespace, |
|
automatically register extensions such as `.json`, `.xml`, `.rss`, and `.atom` if the |
|
corresponding dependencies such as Jackson, JAXB2, or Rome are present on the classpath. |
|
Additional extensions may be not need to be registered explicitly if they can be |
|
discovered via `ServletContext.getMimeType(String)` or the __Java Activation Framework__ |
|
(see `javax.activation.MimetypesFileTypeMap`). You can register more extensions with the |
|
{javadoc-baseurl}/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.html#setUseRegisteredSuffixPatternMatch(boolean)[setUseRegisteredSuffixPatternMatch |
|
method]. |
|
|
|
The introduction of `ContentNegotiationManager` also enables selective suffix pattern |
|
matching for incoming requests. For more details, see its javadocs. |
|
|
|
Below is an example of customizing content negotiation options through the MVC Java |
|
config: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig extends WebMvcConfigurerAdapter { |
|
|
|
@Override |
|
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { |
|
configurer.favorPathExtension(false).favorParameter(true); |
|
} |
|
} |
|
---- |
|
|
|
In the MVC namespace, the `<mvc:annotation-driven>` element has a |
|
`content-negotiation-manager` attribute, which expects a `ContentNegotiationManager` |
|
that in turn can be created with a `ContentNegotiationManagerFactoryBean`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager" /> |
|
|
|
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean"> |
|
<property name="favorPathExtension" value="false" /> |
|
<property name="favorParameter" value="true" /> |
|
<property name="mediaTypes" > |
|
<value> |
|
json=application/json |
|
xml=application/xml |
|
</value> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
If not using the MVC Java config or the MVC namespace, you'll need to create an instance |
|
of `ContentNegotiationManager` and use it to configure `RequestMappingHandlerMapping` |
|
for request mapping purposes, and `RequestMappingHandlerAdapter` and |
|
`ExceptionHandlerExceptionResolver` for content negotiation purposes. |
|
|
|
Note that `ContentNegotiatingViewResolver` now can also be configured with a |
|
`ContentNegotiatingViewResolver`, so you can use one instance throughout Spring MVC. |
|
|
|
In more advanced cases, it may be useful to configure multiple |
|
`ContentNegotiationManager` instances that in turn may contain custom |
|
`ContentNegotiationStrategy` implementations. For example you could configure |
|
`ExceptionHandlerExceptionResolver` with a `ContentNegotiationManager` that always |
|
resolves the requested media type to `"application/json"`. Or you may want to plug a |
|
custom strategy that has some logic to select a default content type (e.g. either XML or |
|
JSON) if no content types were requested. |
|
|
|
|
|
|
|
[[mvc-config-view-controller]] |
|
==== View Controllers |
|
This is a shortcut for defining a `ParameterizableViewController` that immediately |
|
forwards to a view when invoked. Use it in static cases when there is no Java controller |
|
logic to execute before the view generates the response. |
|
|
|
An example of forwarding a request for `"/"` to a view called `"home"` in Java: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig extends WebMvcConfigurerAdapter { |
|
|
|
@Override |
|
public void addViewControllers(ViewControllerRegistry registry) { |
|
registry.addViewController("/").setViewName("home"); |
|
} |
|
|
|
} |
|
---- |
|
|
|
And the same in XML use the `<mvc:view-controller>` element: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<mvc:view-controller path="/" view-name="home"/> |
|
---- |
|
|
|
|
|
[[mvc-config-view-resolvers]] |
|
==== View Resolvers |
|
The MVC config simplifies the registration of view resolvers. |
|
|
|
The following is a Java config example that configures content negotiation view |
|
resolution using FreeMarker HTML templates and Jackson as a default `View` for |
|
JSON rendering: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig extends WebMvcConfigurerAdapter { |
|
|
|
@Override |
|
public void configureViewResolvers(ViewResolverRegistry registry) { |
|
registry.enableContentNegotiation(new MappingJackson2JsonView()); |
|
registry.jsp(); |
|
} |
|
|
|
} |
|
---- |
|
|
|
And the same in XML: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<mvc:view-resolvers> |
|
<mvc:content-negotiation> |
|
<mvc:default-views> |
|
<bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView" /> |
|
</mvc:default-views> |
|
</mvc:content-negotiation> |
|
<mvc:jsp /> |
|
</mvc:view-resolvers> |
|
---- |
|
|
|
Note however that FreeMarker, Velocity, Tiles, and Groovy Markup also require |
|
configuration of the underlying view technology. |
|
|
|
The MVC namespace provides dedicated elements. For example with FreeMarker: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
|
|
<mvc:view-resolvers> |
|
<mvc:content-negotiation> |
|
<mvc:default-views> |
|
<bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView" /> |
|
</mvc:default-views> |
|
</mvc:content-negotiation> |
|
<mvc:freemarker cache="false" /> |
|
</mvc:view-resolvers> |
|
|
|
<mvc:freemarker-configurer> |
|
<mvc:template-loader-path location="/freemarker" /> |
|
</mvc:freemarker-configurer> |
|
|
|
---- |
|
|
|
In Java config simply add the respective "Configurer" bean: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig extends WebMvcConfigurerAdapter { |
|
|
|
@Override |
|
public void configureViewResolvers(ViewResolverRegistry registry) { |
|
registry.enableContentNegotiation(new MappingJackson2JsonView()); |
|
registry.freeMarker().cache(false); |
|
} |
|
|
|
@Bean |
|
public FreeMarkerConfigurer freeMarkerConfigurer() { |
|
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer(); |
|
configurer.setTemplateLoaderPath("/WEB-INF/"); |
|
return configurer; |
|
} |
|
|
|
} |
|
---- |
|
|
|
|
|
|
|
[[mvc-config-static-resources]] |
|
==== Serving of Resources |
|
This option allows static resource requests following a particular URL pattern to be |
|
served by a `ResourceHttpRequestHandler` from any of a list of `Resource` locations. |
|
This provides a convenient way to serve static resources from locations other than the |
|
web application root, including locations on the classpath. The `cache-period` property |
|
may be used to set far future expiration headers (1 year is the recommendation of |
|
optimization tools such as Page Speed and YSlow) so that they will be more efficiently |
|
utilized by the client. The handler also properly evaluates the `Last-Modified` header |
|
(if present) so that a `304` status code will be returned as appropriate, avoiding |
|
unnecessary overhead for resources that are already cached by the client. For example, |
|
to serve resource requests with a URL pattern of `/resources/**` from a |
|
`public-resources` directory within the web application root you would use: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig extends WebMvcConfigurerAdapter { |
|
|
|
@Override |
|
public void addResourceHandlers(ResourceHandlerRegistry registry) { |
|
registry.addResourceHandler("/resources/**").addResourceLocations("/public-resources/"); |
|
} |
|
|
|
} |
|
---- |
|
|
|
And the same in XML: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<mvc:resources mapping="/resources/**" location="/public-resources/"/> |
|
---- |
|
|
|
To serve these resources with a 1-year future expiration to ensure maximum use of the |
|
browser cache and a reduction in HTTP requests made by the browser: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig extends WebMvcConfigurerAdapter { |
|
|
|
@Override |
|
public void addResourceHandlers(ResourceHandlerRegistry registry) { |
|
registry.addResourceHandler("/resources/**").addResourceLocations("/public-resources/").setCachePeriod(31556926); |
|
} |
|
|
|
} |
|
---- |
|
|
|
And in XML: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<mvc:resources mapping="/resources/**" location="/public-resources/" cache-period="31556926"/> |
|
---- |
|
|
|
The `mapping` attribute must be an Ant pattern that can be used by |
|
`SimpleUrlHandlerMapping`, and the `location` attribute must specify one or more valid |
|
resource directory locations. Multiple resource locations may be specified using a |
|
comma-separated list of values. The locations specified will be checked in the specified |
|
order for the presence of the resource for any given request. For example, to enable the |
|
serving of resources from both the web application root and from a known path of |
|
`/META-INF/public-web-resources/` in any jar on the classpath use: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
@EnableWebMvc |
|
@Configuration |
|
public class WebConfig extends WebMvcConfigurerAdapter { |
|
|
|
@Override |
|
public void addResourceHandlers(ResourceHandlerRegistry registry) { |
|
registry.addResourceHandler("/resources/**") |
|
.addResourceLocations("/", "classpath:/META-INF/public-web-resources/"); |
|
} |
|
|
|
} |
|
---- |
|
|
|
And in XML: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<mvc:resources mapping="/resources/**" location="/, classpath:/META-INF/public-web-resources/"/> |
|
---- |
|
|
|
When serving resources that may change when a new version of the application is |
|
deployed it is recommended that you incorporate a version string into the mapping |
|
pattern used to request the resources so that you may force clients to request the |
|
newly deployed version of your application's resources. Support for versioned URLs is |
|
built into the framework and can be enabled by configuring a resource chain |
|
on the resource handler. The chain consists of one more `ResourceResolver` |
|
instances followed by one or more `ResourceTransformer` instances. Together they |
|
can provide arbitrary resolution and transformation of resources. |
|
|
|
The built-in `VersionResourceResolver` can be configured with different strategies. |
|
For example a `FixedVersionStrategy` can use a property, a date, or other as the version. |
|
A `ContentVersionStrategy` uses an MD5 hash computed from the content of the resource |
|
(known as "fingerprinting" URLs). |
|
|
|
`ContentVersionStrategy` is a good default choice to use except in cases where |
|
it cannot be used (e.g. with JavaScript module loaders). You can configure |
|
different version strategies against different patterns as shown below. Keep in mind |
|
also that computing content-based versions is expensive and therefore resource chain |
|
caching should be enabled in production. |
|
|
|
Java config example; |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig extends WebMvcConfigurerAdapter { |
|
|
|
@Override |
|
public void addResourceHandlers(ResourceHandlerRegistry registry) { |
|
registry.addResourceHandler("/resources/**") |
|
.addResourceLocations("/public-resources/") |
|
.resourceChain(true).addResolver( |
|
new VersionResourceResolver().addContentVersionStrategy("/**")); |
|
} |
|
|
|
} |
|
---- |
|
|
|
XML example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<mvc:resources mapping="/resources/**" location="/public-resources/"> |
|
<mvc:resource-chain> |
|
<mvc:resource-cache /> |
|
<mvc:resolvers> |
|
<mvc:version-resolver> |
|
<mvc:content-version-strategy patterns="/**"/> |
|
</mvc:version-resolver> |
|
</mvc:resolvers> |
|
</mvc:resource-chain> |
|
</mvc:resources> |
|
---- |
|
|
|
In order for the above to work the application must also |
|
render URLs with versions. The easiest way to do that is to configure the |
|
`ResourceUrlEncodingFilter` which wraps the response and overrides its `encodeURL` method. |
|
This will work in JSPs, FreeMarker, Velocity, and any other view technology that calls |
|
the response `encodeURL` method. Alternatively, an application can also inject and |
|
use directly the `ResourceUrlProvider` bean, which is automatically declared with the MVC |
|
Java config and the MVC namespace. |
|
|
|
|
|
|
|
|
|
[[mvc-default-servlet-handler]] |
|
==== Falling Back On the "Default" Servlet To Serve Resources |
|
This allows for mapping the `DispatcherServlet` to "/" (thus overriding the mapping |
|
of the container's default Servlet), while still allowing static resource requests to be |
|
handled by the container's default Servlet. It configures a |
|
`DefaultServletHttpRequestHandler` with a URL mapping of "/**" and the lowest priority |
|
relative to other URL mappings. |
|
|
|
This handler will forward all requests to the default Servlet. Therefore it is important |
|
that it remains last in the order of all other URL `HandlerMappings`. That will be the |
|
case if you use `<mvc:annotation-driven>` or alternatively if you are setting up your |
|
own customized `HandlerMapping` instance be sure to set its `order` property to a value |
|
lower than that of the `DefaultServletHttpRequestHandler`, which is `Integer.MAX_VALUE`. |
|
|
|
To enable the feature using the default setup use: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig extends WebMvcConfigurerAdapter { |
|
|
|
@Override |
|
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { |
|
configurer.enable(); |
|
} |
|
|
|
} |
|
---- |
|
|
|
Or in XML: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<mvc:default-servlet-handler/> |
|
---- |
|
|
|
The caveat to overriding the "/" Servlet mapping is that the `RequestDispatcher` for the |
|
default Servlet must be retrieved by name rather than by path. The |
|
`DefaultServletHttpRequestHandler` will attempt to auto-detect the default Servlet for |
|
the container at startup time, using a list of known names for most of the major Servlet |
|
containers (including Tomcat, Jetty, GlassFish, JBoss, Resin, WebLogic, and WebSphere). |
|
If the default Servlet has been custom configured with a different name, or if a |
|
different Servlet container is being used where the default Servlet name is unknown, |
|
then the default Servlet's name must be explicitly provided as in the following example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig extends WebMvcConfigurerAdapter { |
|
|
|
@Override |
|
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { |
|
configurer.enable("myCustomDefaultServlet"); |
|
} |
|
|
|
} |
|
---- |
|
|
|
Or in XML: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<mvc:default-servlet-handler default-servlet-name="myCustomDefaultServlet"/> |
|
---- |
|
|
|
|
|
|
|
[[mvc-config-path-matching]] |
|
==== Path Matching |
|
This allows customizing various settings related to URL mapping and path matching. |
|
For details on the individual options check out the |
|
{javadoc-baseurl}/org/springframework/web/servlet/config/annotation/PathMatchConfigurer.html[PathMatchConfigurer] API. |
|
|
|
Below is an example in Java config: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig extends WebMvcConfigurerAdapter { |
|
|
|
@Override |
|
public void configurePathMatch(PathMatchConfigurer configurer) { |
|
configurer |
|
.setUseSuffixPatternMatch(true) |
|
.setUseTrailingSlashMatch(false) |
|
.setUseRegisteredSuffixPatternMatch(true) |
|
.setPathMatcher(antPathMatcher()) |
|
.setUrlPathHelper(urlPathHelper()); |
|
} |
|
|
|
@Bean |
|
public UrlPathHelper urlPathHelper() { |
|
//... |
|
} |
|
|
|
@Bean |
|
public PathMatcher antPathMatcher() { |
|
//... |
|
} |
|
|
|
} |
|
---- |
|
|
|
And the same in XML, use the `<mvc:path-matching>` element: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<mvc:annotation-driven> |
|
<mvc:path-matching |
|
suffix-pattern="true" |
|
trailing-slash="false" |
|
registered-suffixes-only="true" |
|
path-helper="pathHelper" |
|
path-matcher="pathMatcher" /> |
|
</mvc:annotation-driven> |
|
|
|
<bean id="pathHelper" class="org.example.app.MyPathHelper" /> |
|
<bean id="pathMatcher" class="org.example.app.MyPathMatcher" /> |
|
---- |
|
|
|
|
|
|
|
[[mvc-config-advanced-java]] |
|
==== Advanced Customizations with MVC Java Config |
|
As you can see from the above examples, MVC Java config and the MVC namespace provide |
|
higher level constructs that do not require deep knowledge of the underlying beans |
|
created for you. Instead it helps you to focus on your application needs. However, at |
|
some point you may need more fine-grained control or you may simply wish to understand |
|
the underlying configuration. |
|
|
|
The first step towards more fine-grained control is to see the underlying beans created |
|
for you. In MVC Java config you can see the javadocs and the `@Bean` methods in |
|
`WebMvcConfigurationSupport`. The configuration in this class is automatically imported |
|
through the `@EnableWebMvc` annotation. In fact if you open `@EnableWebMvc` you can see |
|
the `@Import` statement. |
|
|
|
The next step towards more fine-grained control is to customize a property on one of the |
|
beans created in `WebMvcConfigurationSupport` or perhaps to provide your own instance. |
|
This requires two things -- remove the `@EnableWebMvc` annotation in order to prevent |
|
the import and then extend from `DelegatingWebMvcConfiguration`, a subclass of |
|
`WebMvcConfigurationSupport`. |
|
Here is an example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
public class WebConfig extends DelegatingWebMvcConfiguration { |
|
|
|
@Override |
|
public void addInterceptors(InterceptorRegistry registry){ |
|
// ... |
|
} |
|
|
|
@Override |
|
@Bean |
|
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() { |
|
// Create or let "super" create the adapter |
|
// Then customize one of its properties |
|
} |
|
|
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
An application should have only one configuration extending `DelegatingWebMvcConfiguration` |
|
or a single `@EnableWebMvc` annotated class, since they both register the same underlying |
|
beans. |
|
|
|
Modifying beans in this way does not prevent you from using any of the higher-level |
|
constructs shown earlier in this section. `WebMvcConfigurerAdapter` subclasses and |
|
`WebMvcConfigurer` implementations are still being used. |
|
==== |
|
|
|
|
|
|
|
[[mvc-config-advanced-xml]] |
|
==== Advanced Customizations with the MVC Namespace |
|
Fine-grained control over the configuration created for you is a bit harder with the MVC |
|
namespace. |
|
|
|
If you do need to do that, rather than replicating the configuration it provides, |
|
consider configuring a `BeanPostProcessor` that detects the bean you want to customize |
|
by type and then modifying its properties as necessary. For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Component |
|
public class MyPostProcessor implements BeanPostProcessor { |
|
|
|
public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException { |
|
if (bean instanceof RequestMappingHandlerAdapter) { |
|
// Modify properties of the adapter |
|
} |
|
} |
|
|
|
} |
|
---- |
|
|
|
Note that `MyPostProcessor` needs to be included in an `<component scan />` in order for |
|
it to be detected or if you prefer you can declare it explicitly with an XML bean |
|
declaration. |
|
|
|
|
|
|
|
[[view]] |
|
== View technologies |
|
|
|
|
|
|
|
|
|
[[view-introduction]] |
|
=== Introduction |
|
One of the areas in which Spring excels is in the separation of view technologies from |
|
the rest of the MVC framework. For example, deciding to use Velocity or XSLT in place of |
|
an existing JSP is primarily a matter of configuration. This chapter covers the major |
|
view technologies that work with Spring and touches briefly on how to add new ones. This |
|
chapter assumes you are already familiar with <<mvc-viewresolver>> which covers the |
|
basics of how views in general are coupled to the MVC framework. |
|
|
|
|
|
|
|
|
|
[[view-jsp]] |
|
=== JSP & JSTL |
|
Spring provides a couple of out-of-the-box solutions for JSP and JSTL views. Using JSP |
|
or JSTL is done using a normal view resolver defined in the `WebApplicationContext`. |
|
Furthermore, of course you need to write some JSPs that will actually render the view. |
|
|
|
[NOTE] |
|
==== |
|
Setting up your application to use JSTL is a common source of error, mainly caused by |
|
confusion over the different servlet spec., JSP and JSTL version numbers, what they mean |
|
and how to declare the taglibs correctly. The article |
|
http://www.mularien.com/blog/2008/04/24/how-to-reference-and-use-jstl-in-your-web-application/[How |
|
to Reference and Use JSTL in your Web Application] provides a useful guide to the common |
|
pitfalls and how to avoid them. Note that as of Spring 3.0, the minimum supported |
|
servlet version is 2.4 (JSP 2.0 and JSTL 1.1), which reduces the scope for confusion |
|
somewhat. |
|
==== |
|
|
|
|
|
|
|
[[view-jsp-resolver]] |
|
==== View resolvers |
|
Just as with any other view technology you're integrating with Spring, for JSPs you'll |
|
need a view resolver that will resolve your views. The most commonly used view resolvers |
|
when developing with JSPs are the `InternalResourceViewResolver` and the |
|
`ResourceBundleViewResolver`. Both are declared in the `WebApplicationContext`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<!-- the ResourceBundleViewResolver --> |
|
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver"> |
|
<property name="basename" value="views"/> |
|
</bean> |
|
|
|
# And a sample properties file is uses (views.properties in WEB-INF/classes): |
|
welcome.(class)=org.springframework.web.servlet.view.JstlView |
|
welcome.url=/WEB-INF/jsp/welcome.jsp |
|
|
|
productList.(class)=org.springframework.web.servlet.view.JstlView |
|
productList.url=/WEB-INF/jsp/productlist.jsp |
|
---- |
|
|
|
As you can see, the `ResourceBundleViewResolver` needs a properties file defining the |
|
view names mapped to 1) a class and 2) a URL. With a `ResourceBundleViewResolver` you |
|
can mix different types of views using only one resolver. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> |
|
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> |
|
<property name="prefix" value="/WEB-INF/jsp/"/> |
|
<property name="suffix" value=".jsp"/> |
|
</bean> |
|
---- |
|
|
|
The `InternalResourceBundleViewResolver` can be configured for using JSPs as described |
|
above. As a best practice, we strongly encourage placing your JSP files in a directory |
|
under the `'WEB-INF'` directory, so there can be no direct access by clients. |
|
|
|
|
|
|
|
[[view-jsp-jstl]] |
|
==== 'Plain-old' JSPs versus JSTL |
|
When using the Java Standard Tag Library you must use a special view class, the |
|
`JstlView`, as JSTL needs some preparation before things such as the I18N features will |
|
work. |
|
|
|
|
|
|
|
[[view-jsp-tags]] |
|
==== Additional tags facilitating development |
|
Spring provides data binding of request parameters to command objects as described in |
|
earlier chapters. To facilitate the development of JSP pages in combination with those |
|
data binding features, Spring provides a few tags that make things even easier. All |
|
Spring tags have__HTML escaping__ features to enable or disable escaping of characters. |
|
|
|
The tag library descriptor (TLD) is included in the `spring-webmvc.jar`. Further |
|
information about the individual tags can be found in the appendix entitled |
|
<<spring.tld>>. |
|
|
|
|
|
|
|
[[view-jsp-formtaglib]] |
|
==== Using Spring's form tag library |
|
As of version 2.0, Spring provides a comprehensive set of data binding-aware tags for |
|
handling form elements when using JSP and Spring Web MVC. Each tag provides support for |
|
the set of attributes of its corresponding HTML tag counterpart, making the tags |
|
familiar and intuitive to use. The tag-generated HTML is HTML 4.01/XHTML 1.0 compliant. |
|
|
|
Unlike other form/input tag libraries, Spring's form tag library is integrated with |
|
Spring Web MVC, giving the tags access to the command object and reference data your |
|
controller deals with. As you will see in the following examples, the form tags make |
|
JSPs easier to develop, read and maintain. |
|
|
|
Let's go through the form tags and look at an example of how each tag is used. We have |
|
included generated HTML snippets where certain tags require further commentary. |
|
|
|
|
|
[[view-jsp-formtaglib-configuration]] |
|
===== Configuration |
|
The form tag library comes bundled in `spring-webmvc.jar`. The library descriptor is |
|
called `spring-form.tld`. |
|
|
|
To use the tags from this library, add the following directive to the top of your JSP |
|
page: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> |
|
---- |
|
|
|
where `form` is the tag name prefix you want to use for the tags from this library. |
|
|
|
|
|
[[view-jsp-formtaglib-formtag]] |
|
===== The form tag |
|
|
|
This tag renders an HTML 'form' tag and exposes a binding path to inner tags for |
|
binding. It puts the command object in the `PageContext` so that the command object can |
|
be accessed by inner tags. __All the other tags in this library are nested tags of the |
|
`form` tag__. |
|
|
|
Let's assume we have a domain object called `User`. It is a JavaBean with properties |
|
such as `firstName` and `lastName`. We will use it as the form backing object of our |
|
form controller which returns `form.jsp`. Below is an example of what `form.jsp` would |
|
look like: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<form:form> |
|
<table> |
|
<tr> |
|
<td>First Name:</td> |
|
<td><form:input path="firstName" /></td> |
|
</tr> |
|
<tr> |
|
<td>Last Name:</td> |
|
<td><form:input path="lastName" /></td> |
|
</tr> |
|
<tr> |
|
<td colspan="2"> |
|
<input type="submit" value="Save Changes" /> |
|
</td> |
|
</tr> |
|
</table> |
|
</form:form> |
|
---- |
|
|
|
The `firstName` and `lastName` values are retrieved from the command object placed in |
|
the `PageContext` by the page controller. Keep reading to see more complex examples of |
|
how inner tags are used with the `form` tag. |
|
|
|
The generated HTML looks like a standard form: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<form method="POST"> |
|
<table> |
|
<tr> |
|
<td>First Name:</td> |
|
<td><input name="firstName" type="text" value="Harry"/></td> |
|
</tr> |
|
<tr> |
|
<td>Last Name:</td> |
|
<td><input name="lastName" type="text" value="Potter"/></td> |
|
</tr> |
|
<tr> |
|
<td colspan="2"> |
|
<input type="submit" value="Save Changes" /> |
|
</td> |
|
</tr> |
|
</table> |
|
</form> |
|
---- |
|
|
|
The preceding JSP assumes that the variable name of the form backing object is |
|
`'command'`. If you have put the form backing object into the model under another name |
|
(definitely a best practice), then you can bind the form to the named variable like so: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<form:form commandName="user"> |
|
<table> |
|
<tr> |
|
<td>First Name:</td> |
|
<td><form:input path="firstName" /></td> |
|
</tr> |
|
<tr> |
|
<td>Last Name:</td> |
|
<td><form:input path="lastName" /></td> |
|
</tr> |
|
<tr> |
|
<td colspan="2"> |
|
<input type="submit" value="Save Changes" /> |
|
</td> |
|
</tr> |
|
</table> |
|
</form:form> |
|
---- |
|
|
|
|
|
[[view-jsp-formtaglib-inputtag]] |
|
===== The input tag |
|
|
|
This tag renders an HTML 'input' tag using the bound value and type='text' by default. |
|
For an example of this tag, see <<view-jsp-formtaglib-formtag>>. Starting with Spring |
|
3.1 you can use other types such HTML5-specific types like 'email', 'tel', 'date', and |
|
others. |
|
|
|
|
|
[[view-jsp-formtaglib-checkboxtag]] |
|
===== The checkbox tag |
|
|
|
This tag renders an HTML 'input' tag with type 'checkbox'. |
|
|
|
Let's assume our `User` has preferences such as newsletter subscription and a list of |
|
hobbies. Below is an example of the `Preferences` class: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class Preferences { |
|
|
|
private boolean receiveNewsletter; |
|
private String[] interests; |
|
private String favouriteWord; |
|
|
|
public boolean isReceiveNewsletter() { |
|
return receiveNewsletter; |
|
} |
|
|
|
public void setReceiveNewsletter(boolean receiveNewsletter) { |
|
this.receiveNewsletter = receiveNewsletter; |
|
} |
|
|
|
public String[] getInterests() { |
|
return interests; |
|
} |
|
|
|
public void setInterests(String[] interests) { |
|
this.interests = interests; |
|
} |
|
|
|
public String getFavouriteWord() { |
|
return favouriteWord; |
|
} |
|
|
|
public void setFavouriteWord(String favouriteWord) { |
|
this.favouriteWord = favouriteWord; |
|
} |
|
} |
|
---- |
|
|
|
The `form.jsp` would look like: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<form:form> |
|
<table> |
|
<tr> |
|
<td>Subscribe to newsletter?:</td> |
|
<%-- Approach 1: Property is of type java.lang.Boolean --%> |
|
<td><form:checkbox path="preferences.receiveNewsletter"/></td> |
|
</tr> |
|
|
|
<tr> |
|
<td>Interests:</td> |
|
<%-- Approach 2: Property is of an array or of type java.util.Collection --%> |
|
<td> |
|
Quidditch: <form:checkbox path="preferences.interests" value="Quidditch"/> |
|
Herbology: <form:checkbox path="preferences.interests" value="Herbology"/> |
|
Defence Against the Dark Arts: <form:checkbox path="preferences.interests" value="Defence Against the Dark Arts"/> |
|
</td> |
|
</tr> |
|
|
|
<tr> |
|
<td>Favourite Word:</td> |
|
<%-- Approach 3: Property is of type java.lang.Object --%> |
|
<td> |
|
Magic: <form:checkbox path="preferences.favouriteWord" value="Magic"/> |
|
</td> |
|
</tr> |
|
</table> |
|
</form:form> |
|
---- |
|
|
|
There are 3 approaches to the `checkbox` tag which should meet all your checkbox needs. |
|
|
|
* Approach One - When the bound value is of type `java.lang.Boolean`, the |
|
`input(checkbox)` is marked as 'checked' if the bound value is `true`. The `value` |
|
attribute corresponds to the resolved value of the `setValue(Object)` value property. |
|
* Approach Two - When the bound value is of type `array` or `java.util.Collection`, the |
|
`input(checkbox)` is marked as 'checked' if the configured `setValue(Object)` value is |
|
present in the bound `Collection`. |
|
* Approach Three - For any other bound value type, the `input(checkbox)` is marked as |
|
'checked' if the configured `setValue(Object)` is equal to the bound value. |
|
|
|
Note that regardless of the approach, the same HTML structure is generated. Below is an |
|
HTML snippet of some checkboxes: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<tr> |
|
<td>Interests:</td> |
|
<td> |
|
Quidditch: <input name="preferences.interests" type="checkbox" value="Quidditch"/> |
|
<input type="hidden" value="1" name="_preferences.interests"/> |
|
Herbology: <input name="preferences.interests" type="checkbox" value="Herbology"/> |
|
<input type="hidden" value="1" name="_preferences.interests"/> |
|
Defence Against the Dark Arts: <input name="preferences.interests" type="checkbox" value="Defence Against the Dark Arts"/> |
|
<input type="hidden" value="1" name="_preferences.interests"/> |
|
</td> |
|
</tr> |
|
---- |
|
|
|
What you might not expect to see is the additional hidden field after each checkbox. |
|
When a checkbox in an HTML page is __not__ checked, its value will not be sent to the |
|
server as part of the HTTP request parameters once the form is submitted, so we need a |
|
workaround for this quirk in HTML in order for Spring form data binding to work. The |
|
`checkbox` tag follows the existing Spring convention of including a hidden parameter |
|
prefixed by an underscore ("_") for each checkbox. By doing this, you are effectively |
|
telling Spring that "__the checkbox was visible in the form and I want my object to |
|
which the form data will be bound to reflect the state of the checkbox no matter what__". |
|
|
|
|
|
[[view-jsp-formtaglib-checkboxestag]] |
|
===== The checkboxes tag |
|
|
|
This tag renders multiple HTML 'input' tags with type 'checkbox'. |
|
|
|
Building on the example from the previous `checkbox` tag section. Sometimes you prefer |
|
not to have to list all the possible hobbies in your JSP page. You would rather provide |
|
a list at runtime of the available options and pass that in to the tag. That is the |
|
purpose of the `checkboxes` tag. You pass in an `Array`, a `List` or a `Map` containing |
|
the available options in the "items" property. Typically the bound property is a |
|
collection so it can hold multiple values selected by the user. Below is an example of |
|
the JSP using this tag: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<form:form> |
|
<table> |
|
<tr> |
|
<td>Interests:</td> |
|
<td> |
|
<%-- Property is of an array or of type java.util.Collection --%> |
|
<form:checkboxes path="preferences.interests" items="${interestList}"/> |
|
</td> |
|
</tr> |
|
</table> |
|
</form:form> |
|
---- |
|
|
|
This example assumes that the "interestList" is a `List` available as a model attribute |
|
containing strings of the values to be selected from. In the case where you use a Map, |
|
the map entry key will be used as the value and the map entry's value will be used as |
|
the label to be displayed. You can also use a custom object where you can provide the |
|
property names for the value using "itemValue" and the label using "itemLabel". |
|
|
|
|
|
[[view-jsp-formtaglib-radiobuttontag]] |
|
===== The radiobutton tag |
|
|
|
This tag renders an HTML 'input' tag with type 'radio'. |
|
|
|
A typical usage pattern will involve multiple tag instances bound to the same property |
|
but with different values. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<tr> |
|
<td>Sex:</td> |
|
<td> |
|
Male: <form:radiobutton path="sex" value="M"/> <br/> |
|
Female: <form:radiobutton path="sex" value="F"/> |
|
</td> |
|
</tr> |
|
---- |
|
|
|
|
|
[[view-jsp-formtaglib-radiobuttonstag]] |
|
===== The radiobuttons tag |
|
|
|
This tag renders multiple HTML 'input' tags with type 'radio'. |
|
|
|
Just like the `checkboxes` tag above, you might want to pass in the available options as |
|
a runtime variable. For this usage you would use the `radiobuttons` tag. You pass in an |
|
`Array`, a `List` or a `Map` containing the available options in the "items" property. |
|
In the case where you use a Map, the map entry key will be used as the value and the map |
|
entry's value will be used as the label to be displayed. You can also use a custom |
|
object where you can provide the property names for the value using "itemValue" and the |
|
label using "itemLabel". |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<tr> |
|
<td>Sex:</td> |
|
<td><form:radiobuttons path="sex" items="${sexOptions}"/></td> |
|
</tr> |
|
---- |
|
|
|
|
|
[[view-jsp-formtaglib-passwordtag]] |
|
===== The password tag |
|
|
|
This tag renders an HTML 'input' tag with type 'password' using the bound value. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<tr> |
|
<td>Password:</td> |
|
<td> |
|
<form:password path="password" /> |
|
</td> |
|
</tr> |
|
---- |
|
|
|
Please note that by default, the password value is __not__ shown. If you do want the |
|
password value to be shown, then set the value of the `'showPassword'` attribute to |
|
true, like so. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<tr> |
|
<td>Password:</td> |
|
<td> |
|
<form:password path="password" value="^76525bvHGq" showPassword="true" /> |
|
</td> |
|
</tr> |
|
---- |
|
|
|
|
|
[[view-jsp-formtaglib-selecttag]] |
|
===== The select tag |
|
|
|
This tag renders an HTML 'select' element. It supports data binding to the selected |
|
option as well as the use of nested `option` and `options` tags. |
|
|
|
Let's assume a `User` has a list of skills. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<tr> |
|
<td>Skills:</td> |
|
<td><form:select path="skills" items="${skills}"/></td> |
|
</tr> |
|
---- |
|
|
|
If the `User's` skill were in Herbology, the HTML source of the 'Skills' row would look |
|
like: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<tr> |
|
<td>Skills:</td> |
|
<td> |
|
<select name="skills" multiple="true"> |
|
<option value="Potions">Potions</option> |
|
<option value="Herbology" selected="selected">Herbology</option> |
|
<option value="Quidditch">Quidditch</option> |
|
</select> |
|
</td> |
|
</tr> |
|
---- |
|
|
|
|
|
[[view-jsp-formtaglib-optiontag]] |
|
===== The option tag |
|
|
|
This tag renders an HTML 'option'. It sets 'selected' as appropriate based on the bound |
|
value. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<tr> |
|
<td>House:</td> |
|
<td> |
|
<form:select path="house"> |
|
<form:option value="Gryffindor"/> |
|
<form:option value="Hufflepuff"/> |
|
<form:option value="Ravenclaw"/> |
|
<form:option value="Slytherin"/> |
|
</form:select> |
|
</td> |
|
</tr> |
|
---- |
|
|
|
If the `User's` house was in Gryffindor, the HTML source of the 'House' row would look |
|
like: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<tr> |
|
<td>House:</td> |
|
<td> |
|
<select name="house"> |
|
<option value="Gryffindor" selected="selected">Gryffindor</option> |
|
<option value="Hufflepuff">Hufflepuff</option> |
|
<option value="Ravenclaw">Ravenclaw</option> |
|
<option value="Slytherin">Slytherin</option> |
|
</select> |
|
</td> |
|
</tr> |
|
---- |
|
|
|
|
|
[[view-jsp-formtaglib-optionstag]] |
|
===== The options tag |
|
|
|
This tag renders a list of HTML 'option' tags. It sets the 'selected' attribute as |
|
appropriate based on the bound value. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<tr> |
|
<td>Country:</td> |
|
<td> |
|
<form:select path="country"> |
|
<form:option value="-" label="--Please Select"/> |
|
<form:options items="${countryList}" itemValue="code" itemLabel="name"/> |
|
</form:select> |
|
</td> |
|
</tr> |
|
---- |
|
|
|
If the `User` lived in the UK, the HTML source of the 'Country' row would look like: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<tr> |
|
<td>Country:</td> |
|
<td> |
|
<select name="country"> |
|
<option value="-">--Please Select</option> |
|
<option value="AT">Austria</option> |
|
<option value="UK" selected="selected">United Kingdom</option> |
|
<option value="US">United States</option> |
|
</select> |
|
</td> |
|
</tr> |
|
---- |
|
|
|
As the example shows, the combined usage of an `option` tag with the `options` tag |
|
generates the same standard HTML, but allows you to explicitly specify a value in the |
|
JSP that is for display only (where it belongs) such as the default string in the |
|
example: "-- Please Select". |
|
|
|
The `items` attribute is typically populated with a collection or array of item objects. |
|
`itemValue` and `itemLabel` simply refer to bean properties of those item objects, if |
|
specified; otherwise, the item objects themselves will be stringified. Alternatively, |
|
you may specify a `Map` of items, in which case the map keys are interpreted as option |
|
values and the map values correspond to option labels. If `itemValue` and/or `itemLabel` |
|
happen to be specified as well, the item value property will apply to the map key and |
|
the item label property will apply to the map value. |
|
|
|
|
|
[[view-jsp-formtaglib-textAreatag]] |
|
===== The textarea tag |
|
|
|
This tag renders an HTML 'textarea'. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<tr> |
|
<td>Notes:</td> |
|
<td><form:textarea path="notes" rows="3" cols="20" /></td> |
|
<td><form:errors path="notes" /></td> |
|
</tr> |
|
---- |
|
|
|
|
|
[[view-jsp-formtaglib-hiddeninputtag]] |
|
===== The hidden tag |
|
|
|
This tag renders an HTML 'input' tag with type 'hidden' using the bound value. To submit |
|
an unbound hidden value, use the HTML `input` tag with type 'hidden'. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<form:hidden path="house" /> |
|
|
|
---- |
|
|
|
If we choose to submit the 'house' value as a hidden one, the HTML would look like: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<input name="house" type="hidden" value="Gryffindor"/> |
|
|
|
---- |
|
|
|
|
|
[[view-jsp-formtaglib-errorstag]] |
|
===== The errors tag |
|
|
|
This tag renders field errors in an HTML 'span' tag. It provides access to the errors |
|
created in your controller or those that were created by any validators associated with |
|
your controller. |
|
|
|
Let's assume we want to display all error messages for the `firstName` and `lastName` |
|
fields once we submit the form. We have a validator for instances of the `User` class |
|
called `UserValidator`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class UserValidator implements Validator { |
|
|
|
public boolean supports(Class candidate) { |
|
return User.class.isAssignableFrom(candidate); |
|
} |
|
|
|
public void validate(Object obj, Errors errors) { |
|
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "required", "Field is required."); |
|
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", "required", "Field is required."); |
|
} |
|
} |
|
---- |
|
|
|
The `form.jsp` would look like: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<form:form> |
|
<table> |
|
<tr> |
|
<td>First Name:</td> |
|
<td><form:input path="firstName" /></td> |
|
<%-- Show errors for firstName field --%> |
|
<td><form:errors path="firstName" /></td> |
|
</tr> |
|
|
|
<tr> |
|
<td>Last Name:</td> |
|
<td><form:input path="lastName" /></td> |
|
<%-- Show errors for lastName field --%> |
|
<td><form:errors path="lastName" /></td> |
|
</tr> |
|
<tr> |
|
<td colspan="3"> |
|
<input type="submit" value="Save Changes" /> |
|
</td> |
|
</tr> |
|
</table> |
|
</form:form> |
|
---- |
|
|
|
If we submit a form with empty values in the `firstName` and `lastName` fields, this is |
|
what the HTML would look like: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<form method="POST"> |
|
<table> |
|
<tr> |
|
<td>First Name:</td> |
|
<td><input name="firstName" type="text" value=""/></td> |
|
<%-- Associated errors to firstName field displayed --%> |
|
<td><span name="firstName.errors">Field is required.</span></td> |
|
</tr> |
|
|
|
<tr> |
|
<td>Last Name:</td> |
|
<td><input name="lastName" type="text" value=""/></td> |
|
<%-- Associated errors to lastName field displayed --%> |
|
<td><span name="lastName.errors">Field is required.</span></td> |
|
</tr> |
|
<tr> |
|
<td colspan="3"> |
|
<input type="submit" value="Save Changes" /> |
|
</td> |
|
</tr> |
|
</table> |
|
</form> |
|
---- |
|
|
|
What if we want to display the entire list of errors for a given page? The example below |
|
shows that the `errors` tag also supports some basic wildcarding functionality. |
|
|
|
* `path="*"` - displays all errors |
|
* `path="lastName"` - displays all errors associated with the `lastName` field |
|
* if `path` is omitted - object errors only are displayed |
|
|
|
The example below will display a list of errors at the top of the page, followed by |
|
field-specific errors next to the fields: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<form:form> |
|
<form:errors path="*" cssClass="errorBox" /> |
|
<table> |
|
<tr> |
|
<td>First Name:</td> |
|
<td><form:input path="firstName" /></td> |
|
<td><form:errors path="firstName" /></td> |
|
</tr> |
|
<tr> |
|
<td>Last Name:</td> |
|
<td><form:input path="lastName" /></td> |
|
<td><form:errors path="lastName" /></td> |
|
</tr> |
|
<tr> |
|
<td colspan="3"> |
|
<input type="submit" value="Save Changes" /> |
|
</td> |
|
</tr> |
|
</table> |
|
</form:form> |
|
---- |
|
|
|
The HTML would look like: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<form method="POST"> |
|
<span name="*.errors" class="errorBox">Field is required.<br/>Field is required.</span> |
|
<table> |
|
<tr> |
|
<td>First Name:</td> |
|
<td><input name="firstName" type="text" value=""/></td> |
|
<td><span name="firstName.errors">Field is required.</span></td> |
|
</tr> |
|
|
|
<tr> |
|
<td>Last Name:</td> |
|
<td><input name="lastName" type="text" value=""/></td> |
|
<td><span name="lastName.errors">Field is required.</span></td> |
|
</tr> |
|
<tr> |
|
<td colspan="3"> |
|
<input type="submit" value="Save Changes" /> |
|
</td> |
|
</tr> |
|
</form> |
|
---- |
|
|
|
|
|
[[rest-method-conversion]] |
|
===== HTTP Method Conversion |
|
A key principle of REST is the use of the Uniform Interface. This means that all |
|
resources (URLs) can be manipulated using the same four HTTP methods: GET, PUT, POST, |
|
and DELETE. For each method, the HTTP specification defines the exact semantics. For |
|
instance, a GET should always be a safe operation, meaning that is has no side effects, |
|
and a PUT or DELETE should be idempotent, meaning that you can repeat these operations |
|
over and over again, but the end result should be the same. While HTTP defines these |
|
four methods, HTML only supports two: GET and POST. Fortunately, there are two possible |
|
workarounds: you can either use JavaScript to do your PUT or DELETE, or simply do a POST |
|
with the 'real' method as an additional parameter (modeled as a hidden input field in an |
|
HTML form). This latter trick is what Spring's `HiddenHttpMethodFilter` does. This |
|
filter is a plain Servlet Filter and therefore it can be used in combination with any |
|
web framework (not just Spring MVC). Simply add this filter to your web.xml, and a POST |
|
with a hidden _method parameter will be converted into the corresponding HTTP method |
|
request. |
|
|
|
To support HTTP method conversion the Spring MVC form tag was updated to support setting |
|
the HTTP method. For example, the following snippet taken from the updated Petclinic |
|
sample |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<form:form method="delete"> |
|
<p class="submit"><input type="submit" value="Delete Pet"/></p> |
|
</form:form> |
|
---- |
|
|
|
This will actually perform an HTTP POST, with the 'real' DELETE method hidden behind a |
|
request parameter, to be picked up by the `HiddenHttpMethodFilter`, as defined in |
|
web.xml: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<filter> |
|
<filter-name>httpMethodFilter</filter-name> |
|
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> |
|
</filter> |
|
|
|
<filter-mapping> |
|
<filter-name>httpMethodFilter</filter-name> |
|
<servlet-name>petclinic</servlet-name> |
|
</filter-mapping> |
|
---- |
|
|
|
The corresponding `@Controller` method is shown below: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping(method = RequestMethod.DELETE) |
|
public String deletePet(@PathVariable int ownerId, @PathVariable int petId) { |
|
this.clinic.deletePet(petId); |
|
return "redirect:/owners/" + ownerId; |
|
} |
|
---- |
|
|
|
|
|
[[view-jsp-formtaglib-html5]] |
|
===== HTML5 Tags |
|
Starting with Spring 3, the Spring form tag library allows entering dynamic attributes, |
|
which means you can enter any HTML5 specific attributes. |
|
|
|
In Spring 3.1, the form input tag supports entering a type attribute other than 'text'. |
|
This is intended to allow rendering new HTML5 specific input types such as 'email', |
|
'date', 'range', and others. Note that entering type='text' is not required since 'text' |
|
is the default type. |
|
|
|
|
|
|
|
|
|
[[view-tiles]] |
|
=== Tiles |
|
It is possible to integrate Tiles - just as any other view technology - in web |
|
applications using Spring. The following describes in a broad way how to do this. |
|
|
|
|
|
[NOTE] |
|
==== |
|
This section focuses on Spring's support for Tiles v3 in the |
|
`org.springframework.web.servlet.view.tiles3` package. |
|
==== |
|
|
|
|
|
[[view-tiles-dependencies]] |
|
==== Dependencies |
|
To be able to use Tiles, you have to add a dependency on Tiles version 3.0.1 or higher |
|
and http://tiles.apache.org/framework/dependency-management.html[its transitive dependencies] |
|
to your project. |
|
|
|
|
|
[[view-tiles-integrate]] |
|
==== How to integrate Tiles |
|
To be able to use Tiles, you have to configure it using files containing definitions |
|
(for basic information on definitions and other Tiles concepts, please have a look at |
|
http://tiles.apache.org[]). In Spring this is done using the `TilesConfigurer`. Have a |
|
look at the following piece of example ApplicationContext configuration: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer"> |
|
<property name="definitions"> |
|
<list> |
|
<value>/WEB-INF/defs/general.xml</value> |
|
<value>/WEB-INF/defs/widgets.xml</value> |
|
<value>/WEB-INF/defs/administrator.xml</value> |
|
<value>/WEB-INF/defs/customer.xml</value> |
|
<value>/WEB-INF/defs/templates.xml</value> |
|
</list> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
As you can see, there are five files containing definitions, which are all located in |
|
the `'WEB-INF/defs'` directory. At initialization of the `WebApplicationContext`, the |
|
files will be loaded and the definitions factory will be initialized. After that has |
|
been done, the Tiles includes in the definition files can be used as views within your |
|
Spring web application. To be able to use the views you have to have a `ViewResolver` |
|
just as with any other view technology used with Spring. Below you can find two |
|
possibilities, the `UrlBasedViewResolver` and the `ResourceBundleViewResolver`. |
|
|
|
You can specify locale specific Tiles definitions by adding an underscore and then |
|
the locale. For example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer"> |
|
<property name="definitions"> |
|
<list> |
|
<value>/WEB-INF/defs/tiles.xml</value> |
|
<value>/WEB-INF/defs/tiles_fr_FR.xml</value> |
|
</list> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
With this configuration, `tiles_fr_FR.xml` will be used for requests with the `fr_FR` locale, |
|
and `tiles.xml` will be used by default. |
|
|
|
[NOTE] |
|
==== |
|
Since underscores are used to indicate locales, it is recommended to avoid using |
|
them otherwise in the file names for Tiles definitions. |
|
==== |
|
|
|
|
|
[[view-tiles-url]] |
|
===== UrlBasedViewResolver |
|
|
|
The `UrlBasedViewResolver` instantiates the given `viewClass` for each view it has to |
|
resolve. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver"> |
|
<property name="viewClass" value="org.springframework.web.servlet.view.tiles3.TilesView"/> |
|
</bean> |
|
---- |
|
|
|
|
|
[[view-tiles-resource]] |
|
===== ResourceBundleViewResolver |
|
|
|
The `ResourceBundleViewResolver` has to be provided with a property file containing |
|
viewnames and viewclasses the resolver can use: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver"> |
|
<property name="basename" value="views"/> |
|
</bean> |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
... |
|
welcomeView.(class)=org.springframework.web.servlet.view.tiles3.TilesView |
|
welcomeView.url=welcome (this is the name of a Tiles definition) |
|
|
|
vetsView.(class)=org.springframework.web.servlet.view.tiles3.TilesView |
|
vetsView.url=vetsView (again, this is the name of a Tiles definition) |
|
|
|
findOwnersForm.(class)=org.springframework.web.servlet.view.JstlView |
|
findOwnersForm.url=/WEB-INF/jsp/findOwners.jsp |
|
... |
|
---- |
|
|
|
As you can see, when using the `ResourceBundleViewResolver`, you can easily mix |
|
different view technologies. |
|
|
|
Note that the `TilesView` class supports JSTL (the JSP Standard Tag Library) out of the |
|
box. |
|
|
|
|
|
[[view-tiles-preparer]] |
|
===== SimpleSpringPreparerFactory and SpringBeanPreparerFactory |
|
|
|
As an advanced feature, Spring also supports two special Tiles `PreparerFactory` |
|
implementations. Check out the Tiles documentation for details on how to use |
|
`ViewPreparer` references in your Tiles definition files. |
|
|
|
Specify `SimpleSpringPreparerFactory` to autowire ViewPreparer instances based on |
|
specified preparer classes, applying Spring's container callbacks as well as applying |
|
configured Spring BeanPostProcessors. If Spring's context-wide annotation-config has |
|
been activated, annotations in ViewPreparer classes will be automatically detected and |
|
applied. Note that this expects preparer __classes__ in the Tiles definition files, just |
|
like the default `PreparerFactory` does. |
|
|
|
Specify `SpringBeanPreparerFactory` to operate on specified preparer __names__ instead |
|
of classes, obtaining the corresponding Spring bean from the DispatcherServlet's |
|
application context. The full bean creation process will be in the control of the Spring |
|
application context in this case, allowing for the use of explicit dependency injection |
|
configuration, scoped beans etc. Note that you need to define one Spring bean definition |
|
per preparer name (as used in your Tiles definitions). |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer"> |
|
<property name="definitions"> |
|
<list> |
|
<value>/WEB-INF/defs/general.xml</value> |
|
<value>/WEB-INF/defs/widgets.xml</value> |
|
<value>/WEB-INF/defs/administrator.xml</value> |
|
<value>/WEB-INF/defs/customer.xml</value> |
|
<value>/WEB-INF/defs/templates.xml</value> |
|
</list> |
|
</property> |
|
|
|
<!-- resolving preparer names as Spring bean definition names --> |
|
<property name="preparerFactoryClass" |
|
value="org.springframework.web.servlet.view.tiles3.SpringBeanPreparerFactory"/> |
|
|
|
</bean> |
|
---- |
|
|
|
|
|
|
|
|
|
[[view-velocity]] |
|
=== Velocity & FreeMarker |
|
http://velocity.apache.org[Velocity] and http://www.freemarker.org[FreeMarker] are two |
|
templating languages that can be used as view technologies within Spring MVC |
|
applications. The languages are quite similar and serve similar needs and so are |
|
considered together in this section. For semantic and syntactic differences between the |
|
two languages, see the http://www.freemarker.org[FreeMarker] web site. |
|
|
|
|
|
|
|
[[view-velocity-dependencies]] |
|
==== Dependencies |
|
Your web application will need to include `velocity-1.x.x.jar` or `freemarker-2.x.jar` |
|
in order to work with Velocity or FreeMarker respectively and `commons-collections.jar` |
|
is required for Velocity. Typically they are included in the `WEB-INF/lib` folder where |
|
they are guaranteed to be found by a Java EE server and added to the classpath for your |
|
application. It is of course assumed that you already have the `spring-webmvc.jar` in |
|
your `'WEB-INF/lib'` directory too! If you make use of Spring's 'dateToolAttribute' or |
|
'numberToolAttribute' in your Velocity views, you will also need to include the |
|
`velocity-tools-generic-1.x.jar` |
|
|
|
|
|
|
|
[[view-velocity-contextconfig]] |
|
==== Context configuration |
|
A suitable configuration is initialized by adding the relevant configurer bean |
|
definition to your `'*-servlet.xml'` as shown below: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<!-- |
|
This bean sets up the Velocity environment for us based on a root path for templates. |
|
Optionally, a properties file can be specified for more control over the Velocity |
|
environment, but the defaults are pretty sane for file based template loading. |
|
--> |
|
<bean id="velocityConfig" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer"> |
|
<property name="resourceLoaderPath" value="/WEB-INF/velocity/"/> |
|
</bean> |
|
|
|
<!-- |
|
View resolvers can also be configured with ResourceBundles or XML files. If you need |
|
different view resolving based on Locale, you have to use the resource bundle resolver. |
|
--> |
|
<bean id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver"> |
|
<property name="cache" value="true"/> |
|
<property name="prefix" value=""/> |
|
<property name="suffix" value=".vm"/> |
|
</bean> |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<!-- freemarker config --> |
|
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"> |
|
<property name="templateLoaderPath" value="/WEB-INF/freemarker/"/> |
|
</bean> |
|
|
|
<!-- |
|
View resolvers can also be configured with ResourceBundles or XML files. If you need |
|
different view resolving based on Locale, you have to use the resource bundle resolver. |
|
--> |
|
<bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"> |
|
<property name="cache" value="true"/> |
|
<property name="prefix" value=""/> |
|
<property name="suffix" value=".ftl"/> |
|
</bean> |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
For non web-apps add a `VelocityConfigurationFactoryBean` or a |
|
`FreeMarkerConfigurationFactoryBean` to your application context definition file. |
|
==== |
|
|
|
|
|
|
|
[[view-velocity-createtemplates]] |
|
==== Creating templates |
|
Your templates need to be stored in the directory specified by the `*Configurer` bean |
|
shown above. This document does not cover details of creating templates for the two |
|
languages - please see their relevant websites for information. If you use the view |
|
resolvers highlighted, then the logical view names relate to the template file names in |
|
similar fashion to `InternalResourceViewResolver` for JSP's. So if your controller |
|
returns a ModelAndView object containing a view name of "welcome" then the resolvers |
|
will look for the `/WEB-INF/freemarker/welcome.ftl` or `/WEB-INF/velocity/welcome.vm` |
|
template as appropriate. |
|
|
|
|
|
|
|
[[view-velocity-advancedconfig]] |
|
==== Advanced configuration |
|
The basic configurations highlighted above will be suitable for most application |
|
requirements, however additional configuration options are available for when unusual or |
|
advanced requirements dictate. |
|
|
|
|
|
[[view-velocity-example-velocityproperties]] |
|
===== velocity.properties |
|
This file is completely optional, but if specified, contains the values that are passed |
|
to the Velocity runtime in order to configure velocity itself. Only required for |
|
advanced configurations, if you need this file, specify its location on the |
|
`VelocityConfigurer` bean definition above. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="velocityConfig" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer"> |
|
<property name="configLocation" value="/WEB-INF/velocity.properties"/> |
|
</bean> |
|
---- |
|
|
|
Alternatively, you can specify velocity properties directly in the bean definition for |
|
the Velocity config bean by replacing the "configLocation" property with the following |
|
inline properties. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="velocityConfig" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer"> |
|
<property name="velocityProperties"> |
|
<props> |
|
<prop key="resource.loader">file</prop> |
|
<prop key="file.resource.loader.class"> |
|
org.apache.velocity.runtime.resource.loader.FileResourceLoader |
|
</prop> |
|
<prop key="file.resource.loader.path">${webapp.root}/WEB-INF/velocity</prop> |
|
<prop key="file.resource.loader.cache">false</prop> |
|
</props> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
Refer to the |
|
{javadoc-baseurl}/org/springframework/ui/velocity/VelocityEngineFactory.html[API |
|
documentation] for Spring configuration of Velocity, or the Velocity documentation for |
|
examples and definitions of the `'velocity.properties'` file itself. |
|
|
|
|
|
[[views-freemarker]] |
|
===== FreeMarker |
|
FreeMarker 'Settings' and 'SharedVariables' can be passed directly to the FreeMarker |
|
`Configuration` object managed by Spring by setting the appropriate bean properties on |
|
the `FreeMarkerConfigurer` bean. The `freemarkerSettings` property requires a |
|
`java.util.Properties` object and the `freemarkerVariables` property requires a |
|
`java.util.Map`. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"> |
|
<property name="templateLoaderPath" value="/WEB-INF/freemarker/"/> |
|
<property name="freemarkerVariables"> |
|
<map> |
|
<entry key="xml_escape" value-ref="fmXmlEscape"/> |
|
</map> |
|
</property> |
|
</bean> |
|
|
|
<bean id="fmXmlEscape" class="freemarker.template.utility.XmlEscape"/> |
|
---- |
|
|
|
See the FreeMarker documentation for details of settings and variables as they apply to |
|
the `Configuration` object. |
|
|
|
|
|
|
|
[[view-velocity-forms]] |
|
==== Bind support and form handling |
|
Spring provides a tag library for use in JSP's that contains (amongst other things) a |
|
`<spring:bind/>` tag. This tag primarily enables forms to display values from form |
|
backing objects and to show the results of failed validations from a `Validator` in the |
|
web or business tier. From version 1.1, Spring now has support for the same |
|
functionality in both Velocity and FreeMarker, with additional convenience macros for |
|
generating form input elements themselves. |
|
|
|
|
|
[[view-bind-macros]] |
|
===== The bind macros |
|
A standard set of macros are maintained within the `spring-webmvc.jar` file for both |
|
languages, so they are always available to a suitably configured application. |
|
|
|
Some of the macros defined in the Spring libraries are considered internal (private) but |
|
no such scoping exists in the macro definitions making all macros visible to calling |
|
code and user templates. The following sections concentrate only on the macros you need |
|
to be directly calling from within your templates. If you wish to view the macro code |
|
directly, the files are called spring.vm / spring.ftl and are in the packages |
|
`org.springframework.web.servlet.view.velocity` or |
|
`org.springframework.web.servlet.view.freemarker` respectively. |
|
|
|
|
|
[[view-simple-binding]] |
|
===== Simple binding |
|
In your html forms (vm / ftl templates) that act as the 'formView' for a Spring form |
|
controller, you can use code similar to the following to bind to field values and |
|
display error messages for each input field in similar fashion to the JSP equivalent. |
|
Note that the name of the command object is "command" by default, but can be overridden |
|
in your MVC configuration by setting the 'commandName' bean property on your form |
|
controller. Example code is shown below for the `personFormV` and `personFormF` views |
|
configured earlier; |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<!-- velocity macros are automatically available --> |
|
<html> |
|
... |
|
<form action="" method="POST"> |
|
Name: |
|
#springBind( "command.name" ) |
|
<input type="text" |
|
name="${status.expression}" |
|
value="$!status.value" /><br> |
|
#foreach($error in $status.errorMessages) <b>$error</b> <br> #end |
|
<br> |
|
... |
|
<input type="submit" value="submit"/> |
|
</form> |
|
... |
|
</html> |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<!-- freemarker macros have to be imported into a namespace. We strongly |
|
recommend sticking to 'spring' --> |
|
<#import "/spring.ftl" as spring /> |
|
<html> |
|
... |
|
<form action="" method="POST"> |
|
Name: |
|
<@spring.bind "command.name" /> |
|
<input type="text" |
|
name="${spring.status.expression}" |
|
value="${spring.status.value?default("")}" /><br> |
|
<#list spring.status.errorMessages as error> <b>${error}</b> <br> </#list> |
|
<br> |
|
... |
|
<input type="submit" value="submit"/> |
|
</form> |
|
... |
|
</html> |
|
---- |
|
|
|
`#springBind` / `<@spring.bind>` requires a 'path' argument which consists of the name |
|
of your command object (it will be 'command' unless you changed it in your |
|
FormController properties) followed by a period and the name of the field on the command |
|
object you wish to bind to. Nested fields can be used too such as |
|
"command.address.street". The `bind` macro assumes the default HTML escaping behavior |
|
specified by the ServletContext parameter `defaultHtmlEscape` in web.xml |
|
|
|
The optional form of the macro called `#springBindEscaped` / `<@spring.bindEscaped>` |
|
takes a second argument and explicitly specifies whether HTML escaping should be used in |
|
the status error messages or values. Set to true or false as required. Additional form |
|
handling macros simplify the use of HTML escaping and these macros should be used |
|
wherever possible. They are explained in the next section. |
|
|
|
|
|
[[views-form-macros]] |
|
===== Form input generation macros |
|
Additional convenience macros for both languages simplify both binding and form |
|
generation (including validation error display). It is never necessary to use these |
|
macros to generate form input fields, and they can be mixed and matched with simple HTML |
|
or calls direct to the spring bind macros highlighted previously. |
|
|
|
The following table of available macros show the VTL and FTL definitions and the |
|
parameter list that each takes. |
|
|
|
[[views-macros-defs-tbl]] |
|
.Table of macro definitions |
|
[cols="3,1,1"] |
|
|=== |
|
| macro| VTL definition| FTL definition |
|
|
|
| **message** (output a string from a resource bundle based on the code parameter) |
|
| #springMessage($code) |
|
| <@spring.message code/> |
|
|
|
| **messageText** (output a string from a resource bundle based on the code parameter, |
|
falling back to the value of the default parameter) |
|
| #springMessageText($code $text) |
|
| <@spring.messageText code, text/> |
|
|
|
| **url** (prefix a relative URL with the application's context root) |
|
| #springUrl($relativeUrl) |
|
| <@spring.url relativeUrl/> |
|
|
|
| **formInput** (standard input field for gathering user input) |
|
| #springFormInput($path $attributes) |
|
| <@spring.formInput path, attributes, fieldType/> |
|
|
|
| **formHiddenInput *** (hidden input field for submitting non-user input) |
|
| #springFormHiddenInput($path $attributes) |
|
| <@spring.formHiddenInput path, attributes/> |
|
|
|
| **formPasswordInput** * (standard input field for gathering passwords. Note that no |
|
value will ever be populated in fields of this type) |
|
| #springFormPasswordInput($path $attributes) |
|
| <@spring.formPasswordInput path, attributes/> |
|
|
|
| **formTextarea** (large text field for gathering long, freeform text input) |
|
| #springFormTextarea($path $attributes) |
|
| <@spring.formTextarea path, attributes/> |
|
|
|
| **formSingleSelect** (drop down box of options allowing a single required value to be |
|
selected) |
|
| #springFormSingleSelect( $path $options $attributes) |
|
| <@spring.formSingleSelect path, options, attributes/> |
|
|
|
| **formMultiSelect** (a list box of options allowing the user to select 0 or more values) |
|
| #springFormMultiSelect($path $options $attributes) |
|
| <@spring.formMultiSelect path, options, attributes/> |
|
|
|
| **formRadioButtons** (a set of radio buttons allowing a single selection to be made |
|
from the available choices) |
|
| #springFormRadioButtons($path $options $separator $attributes) |
|
| <@spring.formRadioButtons path, options separator, attributes/> |
|
|
|
| **formCheckboxes** (a set of checkboxes allowing 0 or more values to be selected) |
|
| #springFormCheckboxes($path $options $separator $attributes) |
|
| <@spring.formCheckboxes path, options, separator, attributes/> |
|
|
|
| **formCheckbox** (a single checkbox) |
|
| #springFormCheckbox($path $attributes) |
|
| <@spring.formCheckbox path, attributes/> |
|
|
|
| **showErrors** (simplify display of validation errors for the bound field) |
|
| #springShowErrors($separator $classOrStyle) |
|
| <@spring.showErrors separator, classOrStyle/> |
|
|=== |
|
|
|
* In FTL (FreeMarker), these two macros are not actually required as you can use the |
|
normal `formInput` macro, specifying ' `hidden`' or ' `password`' as the value for the |
|
`fieldType` parameter. |
|
|
|
The parameters to any of the above macros have consistent meanings: |
|
|
|
* path: the name of the field to bind to (ie "command.name") |
|
* options: a Map of all the available values that can be selected from in the input |
|
field. The keys to the map represent the values that will be POSTed back from the form |
|
and bound to the command object. Map objects stored against the keys are the labels |
|
displayed on the form to the user and may be different from the corresponding values |
|
posted back by the form. Usually such a map is supplied as reference data by the |
|
controller. Any Map implementation can be used depending on required behavior. For |
|
strictly sorted maps, a `SortedMap` such as a `TreeMap` with a suitable Comparator may |
|
be used and for arbitrary Maps that should return values in insertion order, use a |
|
`LinkedHashMap` or a `LinkedMap` from commons-collections. |
|
* separator: where multiple options are available as discreet elements (radio buttons or |
|
checkboxes), the sequence of characters used to separate each one in the list (ie |
|
"<br>"). |
|
* attributes: an additional string of arbitrary tags or text to be included within the |
|
HTML tag itself. This string is echoed literally by the macro. For example, in a |
|
textarea field you may supply attributes as 'rows="5" cols="60"' or you could pass |
|
style information such as 'style="border:1px solid silver"'. |
|
* classOrStyle: for the showErrors macro, the name of the CSS class that the span tag |
|
wrapping each error will use. If no information is supplied (or the value is empty) |
|
then the errors will be wrapped in <b></b> tags. |
|
|
|
Examples of the macros are outlined below some in FTL and some in VTL. Where usage |
|
differences exist between the two languages, they are explained in the notes. |
|
|
|
[[views-form-macros-input]] |
|
====== Input Fields |
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<!-- the Name field example from above using form macros in VTL --> |
|
... |
|
Name: |
|
#springFormInput("command.name" "")<br> |
|
#springShowErrors("<br>" "")<br> |
|
---- |
|
|
|
The formInput macro takes the path parameter (command.name) and an additional attributes |
|
parameter which is empty in the example above. The macro, along with all other form |
|
generation macros, performs an implicit spring bind on the path parameter. The binding |
|
remains valid until a new bind occurs so the showErrors macro doesn't need to pass the |
|
path parameter again - it simply operates on whichever field a bind was last created for. |
|
|
|
The showErrors macro takes a separator parameter (the characters that will be used to |
|
separate multiple errors on a given field) and also accepts a second parameter, this |
|
time a class name or style attribute. Note that FreeMarker is able to specify default |
|
values for the attributes parameter, unlike Velocity, and the two macro calls above |
|
could be expressed as follows in FTL: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<@spring.formInput "command.name"/> |
|
<@spring.showErrors "<br>"/> |
|
---- |
|
|
|
Output is shown below of the form fragment generating the name field, and displaying a |
|
validation error after the form was submitted with no value in the field. Validation |
|
occurs through Spring's Validation framework. |
|
|
|
The generated HTML looks like this: |
|
|
|
[source,jsp,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Name: |
|
<input type="text" name="name" value=""> |
|
<br> |
|
<b>required</b> |
|
<br> |
|
<br> |
|
---- |
|
|
|
The formTextarea macro works the same way as the formInput macro and accepts the same |
|
parameter list. Commonly, the second parameter (attributes) will be used to pass style |
|
information or rows and cols attributes for the textarea. |
|
|
|
[[views-form-macros-select]] |
|
====== Selection Fields |
|
Four selection field macros can be used to generate common UI value selection inputs in |
|
your HTML forms. |
|
|
|
* formSingleSelect |
|
* formMultiSelect |
|
* formRadioButtons |
|
* formCheckboxes |
|
|
|
Each of the four macros accepts a Map of options containing the value for the form |
|
field, and the label corresponding to that value. The value and the label can be the |
|
same. |
|
|
|
An example of radio buttons in FTL is below. The form backing object specifies a default |
|
value of 'London' for this field and so no validation is necessary. When the form is |
|
rendered, the entire list of cities to choose from is supplied as reference data in the |
|
model under the name 'cityMap'. |
|
|
|
[source,jsp,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
... |
|
Town: |
|
<@spring.formRadioButtons "command.address.town", cityMap, "" /><br><br> |
|
---- |
|
|
|
This renders a line of radio buttons, one for each value in `cityMap` using the |
|
separator "". No additional attributes are supplied (the last parameter to the macro is |
|
missing). The cityMap uses the same String for each key-value pair in the map. The map's |
|
keys are what the form actually submits as POSTed request parameters, map values are the |
|
labels that the user sees. In the example above, given a list of three well known cities |
|
and a default value in the form backing object, the HTML would be |
|
|
|
[source,jsp,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Town: |
|
<input type="radio" name="address.town" value="London">London</input> |
|
<input type="radio" name="address.town" value="Paris" checked="checked">Paris</input> |
|
<input type="radio" name="address.town" value="New York">New York</input> |
|
---- |
|
|
|
If your application expects to handle cities by internal codes for example, the map of |
|
codes would be created with suitable keys like the example below. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
protected Map referenceData(HttpServletRequest request) throws Exception { |
|
Map cityMap = new LinkedHashMap(); |
|
cityMap.put("LDN", "London"); |
|
cityMap.put("PRS", "Paris"); |
|
cityMap.put("NYC", "New York"); |
|
|
|
Map m = new HashMap(); |
|
m.put("cityMap", cityMap); |
|
return m; |
|
} |
|
---- |
|
|
|
The code would now produce output where the radio values are the relevant codes but the |
|
user still sees the more user friendly city names. |
|
|
|
[source,jsp,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Town: |
|
<input type="radio" name="address.town" value="LDN">London</input> |
|
<input type="radio" name="address.town" value="PRS" checked="checked">Paris</input> |
|
<input type="radio" name="address.town" value="NYC">New York</input> |
|
---- |
|
|
|
|
|
[[views-form-macros-html-escaping]] |
|
===== HTML escaping and XHTML compliance |
|
Default usage of the form macros above will result in HTML tags that are HTML 4.01 |
|
compliant and that use the default value for HTML escaping defined in your web.xml as |
|
used by Spring's bind support. In order to make the tags XHTML compliant or to override |
|
the default HTML escaping value, you can specify two variables in your template (or in |
|
your model where they will be visible to your templates). The advantage of specifying |
|
them in the templates is that they can be changed to different values later in the |
|
template processing to provide different behavior for different fields in your form. |
|
|
|
To switch to XHTML compliance for your tags, specify a value of 'true' for a |
|
model/context variable named xhtmlCompliant: |
|
|
|
[source,jsp,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
## for Velocity.. |
|
#set($springXhtmlCompliant = true) |
|
|
|
<#-- for FreeMarker --> |
|
<#assign xhtmlCompliant = true in spring> |
|
---- |
|
|
|
Any tags generated by the Spring macros will now be XHTML compliant after processing |
|
this directive. |
|
|
|
In similar fashion, HTML escaping can be specified per field: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<#-- until this point, default HTML escaping is used --> |
|
|
|
<#assign htmlEscape = true in spring> |
|
<#-- next field will use HTML escaping --> |
|
<@spring.formInput "command.name" /> |
|
|
|
<#assign htmlEscape = false in spring> |
|
<#-- all future fields will be bound with HTML escaping off --> |
|
---- |
|
|
|
|
|
|
|
|
|
[[view-xslt]] |
|
=== XSLT |
|
XSLT is a transformation language for XML and is popular as a view technology within web |
|
applications. XSLT can be a good choice as a view technology if your application |
|
naturally deals with XML, or if your model can easily be converted to XML. The following |
|
section shows how to produce an XML document as model data and have it transformed with |
|
XSLT in a Spring Web MVC application. |
|
|
|
|
|
|
|
[[view-xslt-firstwords]] |
|
==== My First Words |
|
This example is a trivial Spring application that creates a list of words in the |
|
`Controller` and adds them to the model map. The map is returned along with the view |
|
name of our XSLT view. See <<mvc-controller>> for details of Spring Web MVC's |
|
`Controller` interface. The XSLT view will turn the list of words into a simple XML |
|
document ready for transformation. |
|
|
|
|
|
[[view-xslt-beandefs]] |
|
===== Bean definitions |
|
Configuration is standard for a simple Spring application. The dispatcher servlet config |
|
file contains a reference to a `ViewResolver`, URL mappings and a single controller |
|
bean... |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="homeController"class="xslt.HomeController"/> |
|
---- |
|
|
|
... that encapsulates our word generation logic. |
|
|
|
|
|
[[view-xslt-controllercode]] |
|
===== Standard MVC controller code |
|
The controller logic is encapsulated in a subclass of `AbstractController`, with the |
|
handler method being defined like so... |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
protected ModelAndView handleRequestInternal(HttpServletRequest request, |
|
HttpServletResponse response) throws Exception { |
|
|
|
Map map = new HashMap(); |
|
List wordList = new ArrayList(); |
|
|
|
wordList.add("hello"); |
|
wordList.add("world"); |
|
|
|
map.put("wordList", wordList); |
|
|
|
return new ModelAndView("home", map); |
|
} |
|
---- |
|
|
|
So far we've done nothing that's XSLT specific. The model data has been created in the |
|
same way as you would for any other Spring MVC application. Depending on the |
|
configuration of the application now, that list of words could be rendered by JSP/JSTL |
|
by having them added as request attributes, or they could be handled by Velocity by |
|
adding the object to the `VelocityContext`. In order to have XSLT render them, they of |
|
course have to be converted into an XML document somehow. There are software packages |
|
available that will automatically 'domify' an object graph, but within Spring, you have |
|
complete flexibility to create the DOM from your model in any way you choose. This |
|
prevents the transformation of XML playing too great a part in the structure of your |
|
model data which is a danger when using tools to manage the domification process. |
|
|
|
|
|
[[view-xslt-subclassing]] |
|
===== Convert the model data to XML |
|
In order to create a DOM document from our list of words or any other model data, we |
|
must subclass the (provided) |
|
`org.springframework.web.servlet.view.xslt.AbstractXsltView` class. In doing so, we must |
|
also typically implement the abstract method `createXsltSource(..)` method. The first |
|
parameter passed to this method is our model map. Here's the complete listing of the |
|
`HomePage` class in our trivial word application: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package xslt; |
|
|
|
// imports omitted for brevity |
|
|
|
public class HomePage extends AbstractXsltView { |
|
|
|
protected Source createXsltSource(Map model, String rootName, |
|
HttpServletRequest request, HttpServletResponse response) throws Exception { |
|
|
|
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); |
|
Element root = document.createElement(rootName); |
|
|
|
List words = (List) model.get("wordList"); |
|
for (Iterator it = words.iterator(); it.hasNext();) { |
|
String nextWord = (String) it.next(); |
|
Element wordNode = document.createElement("word"); |
|
Text textNode = document.createTextNode(nextWord); |
|
wordNode.appendChild(textNode); |
|
root.appendChild(wordNode); |
|
} |
|
return new DOMSource(root); |
|
} |
|
|
|
} |
|
---- |
|
|
|
A series of parameter name/value pairs can optionally be defined by your subclass which |
|
will be added to the transformation object. The parameter names must match those defined |
|
in your XSLT template declared with `<xsl:param |
|
name="myParam">defaultValue</xsl:param>`. To specify the parameters, override the |
|
`getParameters()` method of the `AbstractXsltView` class and return a `Map` of the |
|
name/value pairs. If your parameters need to derive information from the current |
|
request, you can override the `getParameters(HttpServletRequest request)` method instead. |
|
|
|
|
|
[[view-xslt-viewdefinitions]] |
|
===== Defining the view properties |
|
The views.properties file (or equivalent xml definition if you're using an XML based |
|
view resolver as we did in the Velocity examples above) looks like this for the one-view |
|
application that is 'My First Words': |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
home.(class)=xslt.HomePage |
|
home.stylesheetLocation=/WEB-INF/xsl/home.xslt |
|
home.root=words |
|
---- |
|
|
|
Here, you can see how the view is tied in with the `HomePage` class just written which |
|
handles the model domification in the first property `'.(class)'`. The |
|
`'stylesheetLocation'` property points to the XSLT file which will handle the XML |
|
transformation into HTML for us and the final property `'.root'` is the name that will |
|
be used as the root of the XML document. This gets passed to the `HomePage` class above |
|
in the second parameter to the `createXsltSource(..)` method(s). |
|
|
|
|
|
[[view-xslt-transforming]] |
|
===== Document transformation |
|
Finally, we have the XSLT code used for transforming the above document. As shown in the |
|
above `'views.properties'` file, the stylesheet is called `'home.xslt'` and it lives in |
|
the war file in the `'WEB-INF/xsl'` directory. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="utf-8"?> |
|
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> |
|
|
|
<xsl:output method="html" omit-xml-declaration="yes"/> |
|
|
|
<xsl:template match="/"> |
|
<html> |
|
<head><title>Hello!</title></head> |
|
<body> |
|
<h1>My First Words</h1> |
|
<xsl:apply-templates/> |
|
</body> |
|
</html> |
|
</xsl:template> |
|
|
|
<xsl:template match="word"> |
|
<xsl:value-of select="."/><br/> |
|
</xsl:template> |
|
|
|
</xsl:stylesheet> |
|
---- |
|
|
|
|
|
|
|
[[view-xslt-summary]] |
|
==== Summary |
|
A summary of the files discussed and their location in the WAR file is shown in the |
|
simplified WAR structure below. |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ProjectRoot |
|
| |
|
+- WebContent |
|
| |
|
+- WEB-INF |
|
| |
|
+- classes |
|
| | |
|
| +- xslt |
|
| | | |
|
| | +- HomePageController.class |
|
| | +- HomePage.class |
|
| | |
|
| +- views.properties |
|
| |
|
+- lib |
|
| | |
|
| +- spring-*.jar |
|
| |
|
+- xsl |
|
| | |
|
| +- home.xslt |
|
| |
|
+- frontcontroller-servlet.xml |
|
---- |
|
|
|
You will also need to ensure that an XML parser and an XSLT engine are available on the |
|
classpath. JDK 1.4 provides them by default, and most Java EE containers will also make |
|
them available by default, but it's a possible source of errors to be aware of. |
|
|
|
|
|
|
|
|
|
[[view-document]] |
|
=== Document views (PDF/Excel) |
|
|
|
|
|
|
|
[[view-document-intro]] |
|
==== Introduction |
|
Returning an HTML page isn't always the best way for the user to view the model output, |
|
and Spring makes it simple to generate a PDF document or an Excel spreadsheet |
|
dynamically from the model data. The document is the view and will be streamed from the |
|
server with the correct content type to (hopefully) enable the client PC to run their |
|
spreadsheet or PDF viewer application in response. |
|
|
|
In order to use Excel views, you need to add the 'poi' library to your classpath, and |
|
for PDF generation, the iText library. |
|
|
|
|
|
|
|
[[view-document-config]] |
|
==== Configuration and setup |
|
Document based views are handled in an almost identical fashion to XSLT views, and the |
|
following sections build upon the previous one by demonstrating how the same controller |
|
used in the XSLT example is invoked to render the same model as both a PDF document and |
|
an Excel spreadsheet (which can also be viewed or manipulated in Open Office). |
|
|
|
|
|
[[view-document-configviews]] |
|
===== Document view definitions |
|
First, let's amend the views.properties file (or xml equivalent) and add a simple view |
|
definition for both document types. The entire file now looks like this with the XSLT |
|
view shown from earlier: |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
home.(class)=xslt.HomePage |
|
home.stylesheetLocation=/WEB-INF/xsl/home.xslt |
|
home.root=words |
|
|
|
xl.(class)=excel.HomePage |
|
|
|
pdf.(class)=pdf.HomePage |
|
---- |
|
|
|
__If you want to start with a template spreadsheet or a fillable PDF form to add your |
|
model data to, specify the location as the 'url' property in the view definition__ |
|
|
|
|
|
[[view-document-configcontroller]] |
|
===== Controller code |
|
The controller code we'll use remains exactly the same from the XSLT example earlier |
|
other than to change the name of the view to use. Of course, you could be clever and |
|
have this selected based on a URL parameter or some other logic - proof that Spring |
|
really is very good at decoupling the views from the controllers! |
|
|
|
|
|
[[view-document-configsubclasses]] |
|
===== Subclassing for Excel views |
|
Exactly as we did for the XSLT example, we'll subclass suitable abstract classes in |
|
order to implement custom behavior in generating our output documents. For Excel, this |
|
involves writing a subclass of |
|
`org.springframework.web.servlet.view.document.AbstractExcelView` (for Excel files |
|
generated by POI) or `org.springframework.web.servlet.view.document.AbstractJExcelView` |
|
(for JExcelApi-generated Excel files) and implementing the `buildExcelDocument()` method. |
|
|
|
Here's the complete listing for our POI Excel view which displays the word list from the |
|
model map in consecutive rows of the first column of a new spreadsheet: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package excel; |
|
|
|
// imports omitted for brevity |
|
|
|
public class HomePage extends AbstractExcelView { |
|
|
|
protected void buildExcelDocument(Map model, HSSFWorkbook wb, HttpServletRequest req, |
|
HttpServletResponse resp) throws Exception { |
|
|
|
HSSFSheet sheet; |
|
HSSFRow sheetRow; |
|
HSSFCell cell; |
|
|
|
// Go to the first sheet |
|
// getSheetAt: only if wb is created from an existing document |
|
// sheet = wb.getSheetAt(0); |
|
sheet = wb.createSheet("Spring"); |
|
sheet.setDefaultColumnWidth((short) 12); |
|
|
|
// write a text at A1 |
|
cell = getCell(sheet, 0, 0); |
|
setText(cell, "Spring-Excel test"); |
|
|
|
List words = (List) model.get("wordList"); |
|
for (int i=0; i < words.size(); i++) { |
|
cell = getCell(sheet, 2+i, 0); |
|
setText(cell, (String) words.get(i)); |
|
} |
|
} |
|
|
|
} |
|
---- |
|
|
|
And the following is a view generating the same Excel file, now using JExcelApi: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package excel; |
|
|
|
// imports omitted for brevity |
|
|
|
public class HomePage extends AbstractJExcelView { |
|
|
|
protected void buildExcelDocument(Map model, WritableWorkbook wb, |
|
HttpServletRequest request, HttpServletResponse response) throws Exception { |
|
|
|
WritableSheet sheet = wb.createSheet("Spring", 0); |
|
|
|
sheet.addCell(new Label(0, 0, "Spring-Excel test")); |
|
|
|
List words = (List) model.get("wordList"); |
|
for (int i = 0; i < words.size(); i++) { |
|
sheet.addCell(new Label(2+i, 0, (String) words.get(i))); |
|
} |
|
} |
|
} |
|
---- |
|
|
|
Note the differences between the APIs. We've found that the JExcelApi is somewhat more |
|
intuitive, and furthermore, JExcelApi has slightly better image-handling capabilities. |
|
There have been memory problems with large Excel files when using JExcelApi however. |
|
|
|
If you now amend the controller such that it returns `xl` as the name of the view ( |
|
`return new ModelAndView("xl", map);`) and run your application again, you should find |
|
that the Excel spreadsheet is created and downloaded automatically when you request the |
|
same page as before. |
|
|
|
|
|
[[view-document-configsubclasspdf]] |
|
===== Subclassing for PDF views |
|
The PDF version of the word list is even simpler. This time, the class extends |
|
`org.springframework.web.servlet.view.document.AbstractPdfView` and implements the |
|
`buildPdfDocument()` method as follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package pdf; |
|
|
|
// imports omitted for brevity |
|
|
|
public class PDFPage extends AbstractPdfView { |
|
|
|
protected void buildPdfDocument(Map model, Document doc, PdfWriter writer, |
|
HttpServletRequest req, HttpServletResponse resp) throws Exception { |
|
List words = (List) model.get("wordList"); |
|
for (int i=0; i<words.size(); i++) { |
|
doc.add( new Paragraph((String) words.get(i))); |
|
} |
|
} |
|
|
|
} |
|
---- |
|
|
|
Once again, amend the controller to return the `pdf` view with `return new |
|
ModelAndView("pdf", map);`, and reload the URL in your application. This time a PDF |
|
document should appear listing each of the words in the model map. |
|
|
|
|
|
|
|
|
|
[[view-jasper-reports]] |
|
=== JasperReports |
|
JasperReports ( http://jasperreports.sourceforge.net[]) is a powerful open-source |
|
reporting engine that supports the creation of report designs using an easily understood |
|
XML file format. JasperReports is capable of rendering reports in four different |
|
formats: CSV, Excel, HTML and PDF. |
|
|
|
|
|
|
|
[[view-jasper-reports-dependencies]] |
|
==== Dependencies |
|
Your application will need to include the latest release of JasperReports, which at the |
|
time of writing was 0.6.1. JasperReports itself depends on the following projects: |
|
|
|
* BeanShell |
|
* Commons BeanUtils |
|
* Commons Collections |
|
* Commons Digester |
|
* Commons Logging |
|
* iText |
|
* POI |
|
|
|
JasperReports also requires a JAXP compliant XML parser. |
|
|
|
|
|
|
|
[[view-jasper-reports-configuration]] |
|
==== Configuration |
|
To configure JasperReports views in your Spring container configuration you need to |
|
define a `ViewResolver` to map view names to the appropriate view class depending on |
|
which format you want your report rendered in. |
|
|
|
|
|
[[view-jasper-reports-configuration-resolver]] |
|
===== Configuring the ViewResolver |
|
|
|
Typically, you will use the `ResourceBundleViewResolver` to map view names to view |
|
classes and files in a properties file. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver"> |
|
<property name="basename" value="views"/> |
|
</bean> |
|
---- |
|
|
|
Here we've configured an instance of the `ResourceBundleViewResolver` class that will |
|
look for view mappings in the resource bundle with base name `views`. (The content of |
|
this file is described in the next section.) |
|
|
|
|
|
[[view-jasper-reports-configuration-views]] |
|
===== Configuring the Views |
|
|
|
The Spring Framework contains five different `View` implementations for JasperReports, |
|
four of which correspond to one of the four output formats supported by JasperReports, |
|
and one that allows for the format to be determined at runtime: |
|
|
|
[[view-jasper-reports-configuration-views-classes]] |
|
.JasperReports View classes |
|
|=== |
|
| Class Name| Render Format |
|
|
|
| `JasperReportsCsvView` |
|
| CSV |
|
|
|
| `JasperReportsHtmlView` |
|
| HTML |
|
|
|
| `JasperReportsPdfView` |
|
| PDF |
|
|
|
| `JasperReportsXlsView` |
|
| Microsoft Excel |
|
|
|
| `JasperReportsMultiFormatView` |
|
| The view is <<view-jasper-reports-configuration-multiformat-view,decided upon at |
|
runtime>> |
|
|=== |
|
|
|
Mapping one of these classes to a view name and a report file is a matter of adding the |
|
appropriate entries in the resource bundle configured in the previous section as shown |
|
here: |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
simpleReport.(class)=org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView |
|
simpleReport.url=/WEB-INF/reports/DataSourceReport.jasper |
|
---- |
|
|
|
Here you can see that the view with name `simpleReport` is mapped to the |
|
`JasperReportsPdfView` class, causing the output of this report to be rendered in PDF |
|
format. The `url` property of the view is set to the location of the underlying report |
|
file. |
|
|
|
|
|
[[view-jasper-reports-configuration-report-files]] |
|
===== About Report Files |
|
JasperReports has two distinct types of report file: the design file, which has a |
|
`.jrxml` extension, and the compiled report file, which has a `.jasper` extension. |
|
Typically, you use the JasperReports Ant task to compile your `.jrxml` design file into |
|
a `.jasper` file before deploying it into your application. With the Spring Framework |
|
you can map either of these files to your report file and the framework will take care |
|
of compiling the `.jrxml` file on the fly for you. You should note that after a `.jrxml` |
|
file is compiled by the Spring Framework, the compiled report is cached for the lifetime |
|
of the application. Thus, to make changes to the file you will need to restart your |
|
application. |
|
|
|
|
|
[[view-jasper-reports-configuration-multiformat-view]] |
|
===== Using JasperReportsMultiFormatView |
|
|
|
The `JasperReportsMultiFormatView` allows for the report format to be specified at |
|
runtime. The actual rendering of the report is delegated to one of the other |
|
JasperReports view classes - the `JasperReportsMultiFormatView` class simply adds a |
|
wrapper layer that allows for the exact implementation to be specified at runtime. |
|
|
|
The `JasperReportsMultiFormatView` class introduces two concepts: the format key and the |
|
discriminator key. The `JasperReportsMultiFormatView` class uses the mapping key to look |
|
up the actual view implementation class, and it uses the format key to lookup up the |
|
mapping key. From a coding perspective you add an entry to your model with the format |
|
key as the key and the mapping key as the value, for example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public ModelAndView handleSimpleReportMulti(HttpServletRequest request, |
|
HttpServletResponse response) throws Exception { |
|
|
|
String uri = request.getRequestURI(); |
|
String format = uri.substring(uri.lastIndexOf(".") + 1); |
|
|
|
Map model = getModel(); |
|
model.put("format", format); |
|
|
|
return new ModelAndView("simpleReportMulti", model); |
|
|
|
} |
|
---- |
|
|
|
In this example, the mapping key is determined from the extension of the request URI and |
|
is added to the model under the default format key: `format`. If you wish to use a |
|
different format key then you can configure this using the `formatKey` property of the |
|
`JasperReportsMultiFormatView` class. |
|
|
|
By default the following mapping key mappings are configured in |
|
`JasperReportsMultiFormatView`: |
|
|
|
[[view-jasper-reports-configuration-multiformat-view-mappings]] |
|
.JasperReportsMultiFormatView Default Mapping Key Mappings |
|
|=== |
|
| Mapping Key| View Class |
|
|
|
| csv |
|
| `JasperReportsCsvView` |
|
|
|
| html |
|
| `JasperReportsHtmlView` |
|
|
|
| pdf |
|
| `JasperReportsPdfView` |
|
|
|
| xls |
|
| `JasperReportsXlsView` |
|
|=== |
|
|
|
So in the example above a request to URI /foo/myReport.pdf would be mapped to the |
|
`JasperReportsPdfView` class. You can override the mapping key to view class mappings |
|
using the `formatMappings` property of `JasperReportsMultiFormatView`. |
|
|
|
|
|
|
|
[[view-jasper-reports-model]] |
|
==== Populating the ModelAndView |
|
|
|
In order to render your report correctly in the format you have chosen, you must supply |
|
Spring with all of the data needed to populate your report. For JasperReports this means |
|
you must pass in all report parameters along with the report datasource. Report |
|
parameters are simple name/value pairs and can be added to the `Map` for your model as |
|
you would add any name/value pair. |
|
|
|
When adding the datasource to the model you have two approaches to choose from. The |
|
first approach is to add an instance of `JRDataSource` or a `Collection` type to the |
|
model `Map` under any arbitrary key. Spring will then locate this object in the model |
|
and treat it as the report datasource. For example, you may populate your model like so: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
private Map getModel() { |
|
Map model = new HashMap(); |
|
Collection beanData = getBeanData(); |
|
model.put("myBeanData", beanData); |
|
return model; |
|
} |
|
---- |
|
|
|
The second approach is to add the instance of `JRDataSource` or `Collection` under a |
|
specific key and then configure this key using the `reportDataKey` property of the view |
|
class. In both cases Spring will wrap instances of `Collection` in a |
|
`JRBeanCollectionDataSource` instance. For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
private Map getModel() { |
|
Map model = new HashMap(); |
|
Collection beanData = getBeanData(); |
|
Collection someData = getSomeData(); |
|
model.put("myBeanData", beanData); |
|
model.put("someData", someData); |
|
return model; |
|
} |
|
---- |
|
|
|
Here you can see that two `Collection` instances are being added to the model. To ensure |
|
that the correct one is used, we simply modify our view configuration as appropriate: |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
simpleReport.(class)=org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView |
|
simpleReport.url=/WEB-INF/reports/DataSourceReport.jasper |
|
simpleReport.reportDataKey=myBeanData |
|
---- |
|
|
|
Be aware that when using the first approach, Spring will use the first instance of |
|
`JRDataSource` or `Collection` that it encounters. If you need to place multiple |
|
instances of `JRDataSource` or `Collection` into the model you need to use the second |
|
approach. |
|
|
|
|
|
|
|
[[view-jasper-reports-subreports]] |
|
==== Working with Sub-Reports |
|
JasperReports provides support for embedded sub-reports within your master report files. |
|
There are a wide variety of mechanisms for including sub-reports in your report files. |
|
The easiest way is to hard code the report path and the SQL query for the sub report |
|
into your design files. The drawback of this approach is obvious: the values are |
|
hard-coded into your report files reducing reusability and making it harder to modify |
|
and update report designs. To overcome this you can configure sub-reports declaratively, |
|
and you can include additional data for these sub-reports directly from your controllers. |
|
|
|
|
|
[[view-jasper-reports-subreports-config-reports]] |
|
===== Configuring Sub-Report Files |
|
To control which sub-report files are included in a master report using Spring, your |
|
report file must be configured to accept sub-reports from an external source. To do this |
|
you declare a parameter in your report file like so: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<parameter name="ProductsSubReport" class="net.sf.jasperreports.engine.JasperReport"/> |
|
---- |
|
|
|
Then, you define your sub-report to use this sub-report parameter: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<subreport> |
|
<reportElement isPrintRepeatedValues="false" x="5" y="25" width="325" |
|
height="20" isRemoveLineWhenBlank="true" backcolor="#ffcc99"/> |
|
<subreportParameter name="City"> |
|
<subreportParameterExpression><![CDATA[$F{city}]]></subreportParameterExpression> |
|
</subreportParameter> |
|
<dataSourceExpression><![CDATA[$P{SubReportData}]]></dataSourceExpression> |
|
<subreportExpression class="net.sf.jasperreports.engine.JasperReport"> |
|
<![CDATA[$P{ProductsSubReport}]]></subreportExpression> |
|
</subreport> |
|
---- |
|
|
|
This defines a master report file that expects the sub-report to be passed in as an |
|
instance of `net.sf.jasperreports.engine.JasperReports` under the parameter |
|
`ProductsSubReport`. When configuring your Jasper view class, you can instruct Spring to |
|
load a report file and pass it into the JasperReports engine as a sub-report using the |
|
`subReportUrls` property: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<property name="subReportUrls"> |
|
<map> |
|
<entry key="ProductsSubReport" value="/WEB-INF/reports/subReportChild.jrxml"/> |
|
</map> |
|
</property> |
|
---- |
|
|
|
Here, the key of the `Map` corresponds to the name of the sub-report parameter in the |
|
report design file, and the entry is the URL of the report file. Spring will load this |
|
report file, compiling it if necessary, and pass it into the JasperReports engine under |
|
the given key. |
|
|
|
|
|
[[view-jasper-reports-subreports-config-datasources]] |
|
===== Configuring Sub-Report Data Sources |
|
This step is entirely optional when using Spring to configure your sub-reports. If you |
|
wish, you can still configure the data source for your sub-reports using static queries. |
|
However, if you want Spring to convert data returned in your `ModelAndView` into |
|
instances of `JRDataSource` then you need to specify which of the parameters in your |
|
`ModelAndView` Spring should convert. To do this, configure the list of parameter names |
|
using the `subReportDataKeys` property of your chosen view class: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<property name="subReportDataKeys" value="SubReportData"/> |
|
---- |
|
|
|
Here, the key you supply __must__ correspond to both the key used in your `ModelAndView` |
|
and the key used in your report design file. |
|
|
|
|
|
|
|
[[view-jasper-reports-exporter-parameters]] |
|
==== Configuring Exporter Parameters |
|
If you have special requirements for exporter configuration -- perhaps you want a |
|
specific page size for your PDF report -- you can configure these exporter parameters |
|
declaratively in your Spring configuration file using the `exporterParameters` property |
|
of the view class. The `exporterParameters` property is typed as a `Map`. In your |
|
configuration the key of an entry should be the fully-qualified name of a static field |
|
that contains the exporter parameter definition, and the value of an entry should be the |
|
value you want to assign to the parameter. An example of this is shown below: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="htmlReport" class="org.springframework.web.servlet.view.jasperreports.JasperReportsHtmlView"> |
|
<property name="url" value="/WEB-INF/reports/simpleReport.jrxml"/> |
|
<property name="exporterParameters"> |
|
<map> |
|
<entry key="net.sf.jasperreports.engine.export.JRHtmlExporterParameter.HTML_FOOTER"> |
|
<value>Footer by Spring! |
|
</td><td width="50%">&nbsp; </td></tr> |
|
</table></body></html> |
|
</value> |
|
</entry> |
|
</map> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
Here you can see that the `JasperReportsHtmlView` is configured with an exporter |
|
parameter for `net.sf.jasperreports.engine.export.JRHtmlExporterParameter.HTML_FOOTER` |
|
which will output a footer in the resulting HTML. |
|
|
|
|
|
|
|
|
|
[[view-feeds]] |
|
=== Feed Views |
|
Both `AbstractAtomFeedView` and `AbstractRssFeedView` inherit from the base class |
|
`AbstractFeedView` and are used to provide Atom and RSS Feed views respectfully. They |
|
are based on java.net's https://rome.dev.java.net[ROME] project and are located in the |
|
package `org.springframework.web.servlet.view.feed`. |
|
|
|
`AbstractAtomFeedView` requires you to implement the `buildFeedEntries()` method and |
|
optionally override the `buildFeedMetadata()` method (the default implementation is |
|
empty), as shown below. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class SampleContentAtomView extends AbstractAtomFeedView { |
|
|
|
@Override |
|
protected void buildFeedMetadata(Map<String, Object> model, |
|
Feed feed, HttpServletRequest request) { |
|
// implementation omitted |
|
} |
|
|
|
@Override |
|
protected List<Entry> buildFeedEntries(Map<String, Object> model, |
|
HttpServletRequest request, HttpServletResponse response) throws Exception { |
|
// implementation omitted |
|
} |
|
|
|
} |
|
---- |
|
|
|
Similar requirements apply for implementing `AbstractRssFeedView`, as shown below. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class SampleContentAtomView extends AbstractRssFeedView { |
|
|
|
@Override |
|
protected void buildFeedMetadata(Map<String, Object> model, |
|
Channel feed, HttpServletRequest request) { |
|
// implementation omitted |
|
} |
|
|
|
@Override |
|
protected List<Item> buildFeedItems(Map<String, Object> model, |
|
HttpServletRequest request, HttpServletResponse response) throws Exception { |
|
// implementation omitted |
|
} |
|
|
|
} |
|
---- |
|
|
|
The `buildFeedItems()` and `buildFeedEntires()` methods pass in the HTTP request in case |
|
you need to access the Locale. The HTTP response is passed in only for the setting of |
|
cookies or other HTTP headers. The feed will automatically be written to the response |
|
object after the method returns. |
|
|
|
For an example of creating an Atom view please refer to Alef Arendsen's Spring Team Blog |
|
https://spring.io/blog/2009/03/16/adding-an-atom-view-to-an-application-using-spring-s-rest-support[entry]. |
|
|
|
|
|
|
|
|
|
[[view-xml-marshalling]] |
|
=== XML Marshalling View |
|
The `MarshallingView` uses an XML `Marshaller` defined in the `org.springframework.oxm` |
|
package to render the response content as XML. The object to be marshalled can be set |
|
explicitly using `MarhsallingView`'s `modelKey` bean property. Alternatively, the view |
|
will iterate over all model properties and marshal the first type that is supported |
|
by the `Marshaller`. For more information on the functionality in the |
|
`org.springframework.oxm` package refer to the chapter <<oxm,Marshalling XML using O/X |
|
Mappers>>. |
|
|
|
|
|
|
|
|
|
[[view-json-mapping]] |
|
=== JSON Mapping View |
|
The `MappingJackson2JsonView` uses the Jackson library's `ObjectMapper` to render the response |
|
content as JSON. By default, the entire contents of the model map (with the exception of |
|
framework-specific classes) will be encoded as JSON. For cases where the contents of the |
|
map need to be filtered, users may specify a specific set of model attributes to encode |
|
via the `RenderedAttributes` property. The `extractValueFromSingleKeyModel` property may |
|
also be used to have the value in single-key models extracted and serialized directly |
|
rather than as a map of model attributes. |
|
|
|
JSON mapping can be customized as needed through the use of Jackson's provided |
|
annotations. When further control is needed, a custom `ObjectMapper` can be injected |
|
through the `ObjectMapper` property for cases where custom JSON |
|
serializers/deserializers need to be provided for specific types. |
|
|
|
http://en.wikipedia.org/wiki/JSONP[JSONP] is supported and automatically enabled when |
|
the request has a query parameter named `jsonp` or `callback`. The JSONP query parameter |
|
name(s) could be customized through the `jsonpParameterNames` property. |
|
|
|
|
|
|
|
|
|
[[view-xml-mapping]] |
|
=== XML Mapping View |
|
The `MappingJackson2XmlView` uses the |
|
https://github.com/FasterXML/jackson-dataformat-xml[Jackson XML extension]'s `XmlMapper` |
|
to render the response content as XML. If the model contains multiples entries, the |
|
object to be serialized should be set explicitly using `MappingJackson2XmlView`'s |
|
`modelKey` bean property. If the model contains a single entry, it will be serialized |
|
automatically. |
|
|
|
XML mapping can be customized as needed through the use of JAXB or Jackson's provided |
|
annotations. When further control is needed, a custom `XmlMapper` can be injected |
|
through the `ObjectMapper` property for cases where custom XML |
|
serializers/deserializers need to be provided for specific types. |
|
|
|
|
|
|
|
|
|
|
|
[[web-integration]] |
|
== Integrating with other web frameworks |
|
|
|
|
|
|
|
|
|
[[intro]] |
|
=== Introduction |
|
|
|
.Spring Web Flow |
|
**** |
|
Spring Web Flow (SWF) aims to be the best solution for the management of web application |
|
page flow. |
|
|
|
SWF integrates with existing frameworks like Spring MVC and JSF, in both Servlet and |
|
Portlet environments. If you have a business process (or processes) that would benefit |
|
from a conversational model as opposed to a purely request model, then SWF may be the |
|
solution. |
|
|
|
SWF allows you to capture logical page flows as self-contained modules that are reusable |
|
in different situations, and as such is ideal for building web application modules that |
|
guide the user through controlled navigations that drive business processes. |
|
|
|
For more information about SWF, consult the |
|
http://projects.spring.io/spring-webflow/[Spring Web Flow website]. |
|
**** |
|
|
|
This chapter details Spring's integration with third party web frameworks, such as |
|
http://www.oracle.com/technetwork/java/javaee/javaserverfaces-139869.html[JSF]. |
|
|
|
One of the core value propositions of the Spring Framework is that of enabling |
|
__choice__. In a general sense, Spring does not force one to use or buy into any |
|
particular architecture, technology, or methodology (although it certainly recommends |
|
some over others). This freedom to pick and choose the architecture, technology, or |
|
methodology that is most relevant to a developer and their development team is |
|
arguably most evident in the web area, where Spring provides its own web framework |
|
(<<mvc,Spring MVC>>), while at the same time providing integration with a number of |
|
popular third party web frameworks. This allows one to continue to leverage any and all |
|
of the skills one may have acquired in a particular web framework such as JSF, while |
|
at the same time being able to enjoy the benefits afforded by Spring in other areas such |
|
as data access, declarative transaction management, and flexible configuration and |
|
application assembly. |
|
|
|
Having dispensed with the woolly sales patter (c.f. the previous paragraph), the |
|
remainder of this chapter will concentrate upon the meaty details of integrating your |
|
favorite web framework with Spring. One thing that is often commented upon by developers |
|
coming to Java from other languages is the seeming super-abundance of web frameworks |
|
available in Java. There are indeed a great number of web frameworks in the Java space; |
|
in fact there are far too many to cover with any semblance of detail in a single |
|
chapter. This chapter thus picks four of the more popular web frameworks in Java, |
|
starting with the Spring configuration that is common to all of the supported web |
|
frameworks, and then detailing the specific integration options for each supported web |
|
framework. |
|
|
|
[NOTE] |
|
==== |
|
Please note that this chapter does not attempt to explain how to use any of the |
|
supported web frameworks. For example, if you want to use JSF for the presentation |
|
layer of your web application, the assumption is that you are already familiar with |
|
JSF itself. If you need further details about any of the supported web frameworks |
|
themselves, please do consult <<web-integration-resources>> at the end of this chapter. |
|
==== |
|
|
|
|
|
|
|
|
|
[[web-integration-common]] |
|
=== Common configuration |
|
Before diving into the integration specifics of each supported web framework, let us |
|
first take a look at the Spring configuration that is __not__ specific to any one web |
|
framework. (This section is equally applicable to Spring's own web framework, Spring |
|
MVC.) |
|
|
|
One of the concepts (for want of a better word) espoused by (Spring's) lightweight |
|
application model is that of a layered architecture. Remember that in a 'classic' |
|
layered architecture, the web layer is but one of many layers; it serves as one of the |
|
entry points into a server side application and it delegates to service objects |
|
(facades) defined in a service layer to satisfy business specific (and |
|
presentation-technology agnostic) use cases. In Spring, these service objects, any other |
|
business-specific objects, data access objects, etc. exist in a distinct 'business |
|
context', which contains __no__ web or presentation layer objects (presentation objects |
|
such as Spring MVC controllers are typically configured in a distinct 'presentation |
|
context'). This section details how one configures a Spring container (a |
|
`WebApplicationContext`) that contains all of the 'business beans' in one's application. |
|
|
|
On to specifics: all that one need do is to declare a |
|
{javadoc-baseurl}/org/springframework/web/context/ContextLoaderListener.html[`ContextLoaderListener`] |
|
in the standard Java EE servlet `web.xml` file of one's web application, and add a |
|
`contextConfigLocation`<context-param/> section (in the same file) that defines which |
|
set of Spring XML configuration files to load. |
|
|
|
Find below the <listener/> configuration: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<listener> |
|
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> |
|
</listener> |
|
---- |
|
|
|
Find below the <context-param/> configuration: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<context-param> |
|
<param-name>contextConfigLocation</param-name> |
|
<param-value>/WEB-INF/applicationContext*.xml</param-value> |
|
</context-param> |
|
---- |
|
|
|
If you don't specify the `contextConfigLocation` context parameter, the |
|
`ContextLoaderListener` will look for a file called `/WEB-INF/applicationContext.xml` to |
|
load. Once the context files are loaded, Spring creates a |
|
{javadoc-baseurl}/org/springframework/web/context/WebApplicationContext.html[`WebApplicationContext`] |
|
object based on the bean definitions and stores it in the `ServletContext` of the web |
|
application. |
|
|
|
All Java web frameworks are built on top of the Servlet API, and so one can use the |
|
following code snippet to get access to this 'business context' `ApplicationContext` |
|
created by the `ContextLoaderListener`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext); |
|
---- |
|
|
|
The |
|
{javadoc-baseurl}/org/springframework/web/context/support/WebApplicationContextUtils.html[`WebApplicationContextUtils`] |
|
class is for convenience, so you don't have to remember the name of the `ServletContext` |
|
attribute. Its __getWebApplicationContext()__ method will return `null` if an object |
|
doesn't exist under the `WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE` |
|
key. Rather than risk getting `NullPointerExceptions` in your application, it's better |
|
to use the `getRequiredWebApplicationContext()` method. This method throws an exception |
|
when the `ApplicationContext` is missing. |
|
|
|
Once you have a reference to the `WebApplicationContext`, you can retrieve beans by |
|
their name or type. Most developers retrieve beans by name and then cast them to one of |
|
their implemented interfaces. |
|
|
|
Fortunately, most of the frameworks in this section have simpler ways of looking up |
|
beans. Not only do they make it easy to get beans from a Spring container, but they also |
|
allow you to use dependency injection on their controllers. Each web framework section |
|
has more detail on its specific integration strategies. |
|
|
|
|
|
|
|
|
|
[[jsf]] |
|
=== JavaServer Faces 1.2 |
|
JavaServer Faces (JSF) is the JCP's standard component-based, event-driven web user |
|
interface framework. As of Java EE 5, it is an official part of the Java EE umbrella. |
|
|
|
For a popular JSF runtime as well as for popular JSF component libraries, check out the |
|
http://myfaces.apache.org/[Apache MyFaces project]. The MyFaces project also provides |
|
common JSF extensions such as http://myfaces.apache.org/orchestra/[MyFaces Orchestra]: |
|
a Spring-based JSF extension that provides rich conversation scope support. |
|
|
|
[NOTE] |
|
==== |
|
Spring Web Flow 2.0 provides rich JSF support through its newly established Spring Faces |
|
module, both for JSF-centric usage (as described in this section) and for Spring-centric |
|
usage (using JSF views within a Spring MVC dispatcher). Check out the |
|
http://projects.spring.io/spring-webflow[Spring Web Flow website] for details! |
|
==== |
|
|
|
The key element in Spring's JSF integration is the JSF `ELResolver` mechanism. |
|
|
|
[[jsf-springbeanfaceselresolver]] |
|
==== SpringBeanFacesELResolver (JSF 1.2+) |
|
`SpringBeanFacesELResolver` is a JSF 1.2 compliant `ELResolver` implementation, |
|
integrating with the standard Unified EL as used by JSF 1.2 and JSP 2.1. Like |
|
`SpringBeanVariableResolver`, it delegates to the Spring's 'business context' |
|
`WebApplicationContext` __first__, then to the default resolver of the underlying JSF |
|
implementation. |
|
|
|
Configuration-wise, simply define `SpringBeanFacesELResolver` in your JSF 1.2 |
|
__faces-context.xml__ file: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<faces-config> |
|
<application> |
|
<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver> |
|
... |
|
</application> |
|
</faces-config> |
|
---- |
|
|
|
|
|
[[jsf-facescontextutils]] |
|
==== FacesContextUtils |
|
A custom `VariableResolver` works well when mapping one's properties to beans |
|
in __faces-config.xml__, but at times one may need to grab a bean explicitly. The |
|
{javadoc-baseurl}/org/springframework/web/jsf/FacesContextUtils.html[`FacesContextUtils`] |
|
class makes this easy. It is similar to `WebApplicationContextUtils`, except that it |
|
takes a `FacesContext` parameter rather than a `ServletContext` parameter. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ApplicationContext ctx = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance()); |
|
---- |
|
|
|
|
|
|
|
[[struts]] |
|
=== Apache Struts 2.x |
|
Invented by Craig McClanahan, http://struts.apache.org[Struts] is an open source project |
|
hosted by the Apache Software Foundation. At the time, it greatly simplified the |
|
JSP/Servlet programming paradigm and won over many developers who were using proprietary |
|
frameworks. It simplified the programming model, it was open source (and thus free as in |
|
beer), and it had a large community, which allowed the project to grow and become popular |
|
among Java web developers. |
|
|
|
Check out the Struts |
|
https://struts.apache.org/release/2.3.x/docs/spring-plugin.html[Spring Plugin] for the |
|
built-in Spring integration shipped with Struts. |
|
|
|
|
|
|
|
[[tapestry]] |
|
=== Tapestry 5.x |
|
From the http://tapestry.apache.org/[Tapestry homepage]: |
|
|
|
Tapestry is a "__Component oriented framework for creating dynamic, robust, |
|
highly scalable web applications in Java.__" |
|
|
|
While Spring has its own <<mvc,powerful web layer>>, there are a number of unique |
|
advantages to building an enterprise Java application using a combination of Tapestry |
|
for the web user interface and the Spring container for the lower layers. |
|
|
|
For more information, check out Tapestry's dedicated |
|
https://tapestry.apache.org/integrating-with-spring-framework.html[integration module for |
|
Spring]. |
|
|
|
|
|
|
|
[[web-integration-resources]] |
|
=== Further Resources |
|
Find below links to further resources about the various web frameworks described in this |
|
chapter. |
|
|
|
* The http://www.oracle.com/technetwork/java/javaee/javaserverfaces-139869.html[JSF] homepage |
|
* The http://struts.apache.org/[Struts] homepage |
|
* The http://tapestry.apache.org/[Tapestry] homepage |
|
|
|
|
|
|
|
|
|
|
|
[[portlet]] |
|
== Portlet MVC Framework |
|
|
|
|
|
|
|
|
|
[[portlet-introduction]] |
|
=== Introduction |
|
|
|
.JSR-168 The Java Portlet Specification |
|
**** |
|
For more general information about portlet development, please review a whitepaper from |
|
Oracle entitled |
|
http://www.oracle.com/technetwork/java/index-raji-test-141933.html["Introduction |
|
to JSR 168"], and of course the |
|
http://jcp.org/aboutJava/communityprocess/final/jsr168/[JSR-168 Specification] itself. |
|
**** |
|
|
|
In addition to supporting conventional (servlet-based) Web development, Spring also |
|
supports JSR-168 Portlet development. As much as possible, the Portlet MVC framework is |
|
a mirror image of the Web MVC framework, and also uses the same underlying view |
|
abstractions and integration technology. So, be sure to review the chapters entitled |
|
<<mvc>> and <<view>> before continuing with this chapter. |
|
|
|
[NOTE] |
|
==== |
|
Bear in mind that while the concepts of Spring MVC are the same in Spring Portlet MVC, |
|
there are some notable differences created by the unique workflow of JSR-168 portlets. |
|
==== |
|
|
|
The main way in which portlet workflow differs from servlet workflow is that the request |
|
to the portlet can have two distinct phases: the action phase and the render phase. The |
|
action phase is executed only once and is where any 'backend' changes or actions occur, |
|
such as making changes in a database. The render phase then produces what is displayed |
|
to the user each time the display is refreshed. The critical point here is that for a |
|
single overall request, the action phase is executed only once, but the render phase may |
|
be executed multiple times. This provides (and requires) a clean separation between the |
|
activities that modify the persistent state of your system and the activities that |
|
generate what is displayed to the user. |
|
|
|
.Spring Web Flow |
|
**** |
|
Spring Web Flow (SWF) aims to be the best solution for the management of web application |
|
page flow. |
|
|
|
SWF integrates with existing frameworks like Spring MVC and JSF, in both Servlet and |
|
Portlet environments. If you have a business process (or processes) that would benefit |
|
from a conversational model as opposed to a purely request model, then SWF may be the |
|
solution. |
|
|
|
SWF allows you to capture logical page flows as self-contained modules that are reusable |
|
in different situations, and as such is ideal for building web application modules that |
|
guide the user through controlled navigations that drive business processes. |
|
|
|
For more information about SWF, consult the Spring Web Flow website. |
|
**** |
|
|
|
The dual phases of portlet requests are one of the real strengths of the JSR-168 |
|
specification. For example, dynamic search results can be updated routinely on the |
|
display without the user explicitly rerunning the search. Most other portlet MVC |
|
frameworks attempt to completely hide the two phases from the developer and make it look |
|
as much like traditional servlet development as possible - we think this approach |
|
removes one of the main benefits of using portlets. So, the separation of the two phases |
|
is preserved throughout the Spring Portlet MVC framework. The primary manifestation of |
|
this approach is that where the servlet version of the MVC classes will have one method |
|
that deals with the request, the portlet version of the MVC classes will have two |
|
methods that deal with the request: one for the action phase and one for the render |
|
phase. For example, where the servlet version of `AbstractController` has the |
|
`handleRequestInternal(..)` method, the portlet version of `AbstractController` has |
|
`handleActionRequestInternal(..)` and `handleRenderRequestInternal(..)` methods. |
|
|
|
The framework is designed around a `DispatcherPortlet` that dispatches requests to |
|
handlers, with configurable handler mappings and view resolution, just as the |
|
`DispatcherServlet` in the web framework does. File upload is also supported in the same |
|
way. |
|
|
|
Locale resolution and theme resolution are not supported in Portlet MVC - these areas |
|
are in the purview of the portal/portlet container and are not appropriate at the Spring |
|
level. However, all mechanisms in Spring that depend on the locale (such as |
|
internationalization of messages) will still function properly because |
|
`DispatcherPortlet` exposes the current locale in the same way as `DispatcherServlet`. |
|
|
|
|
|
|
|
[[portlet-introduction-controller]] |
|
==== Controllers - The C in MVC |
|
The default handler is still a very simple `Controller` interface, offering just two |
|
methods: |
|
|
|
* `void handleActionRequest(request,response)` |
|
* `ModelAndView handleRenderRequest(request,response)` |
|
|
|
The framework also includes most of the same controller implementation hierarchy, such |
|
as `AbstractController`, `SimpleFormController`, and so on. Data binding, command object |
|
usage, model handling, and view resolution are all the same as in the servlet framework. |
|
|
|
|
|
|
|
[[portlet-introduction-view]] |
|
==== Views - The V in MVC |
|
All the view rendering capabilities of the servlet framework are used directly via a |
|
special bridge servlet named `ViewRendererServlet`. By using this servlet, the portlet |
|
request is converted into a servlet request and the view can be rendered using the |
|
entire normal servlet infrastructure. This means all the existing renderers, such as |
|
JSP, Velocity, etc., can still be used within the portlet. |
|
|
|
|
|
|
|
[[portlet-introduction-scope]] |
|
==== Web-scoped beans |
|
Spring Portlet MVC supports beans whose lifecycle is scoped to the current HTTP request |
|
or HTTP `Session` (both normal and global). This is not a specific feature of Spring |
|
Portlet MVC itself, but rather of the `WebApplicationContext` container(s) that Spring |
|
Portlet MVC uses. These bean scopes are described in detail in |
|
<<beans-factory-scopes-other>> |
|
|
|
|
|
|
|
|
|
[[portlet-dispatcher]] |
|
=== The DispatcherPortlet |
|
|
|
Portlet MVC is a request-driven web MVC framework, designed around a portlet that |
|
dispatches requests to controllers and offers other functionality facilitating the |
|
development of portlet applications. Spring's `DispatcherPortlet` however, does more |
|
than just that. It is completely integrated with the Spring `ApplicationContext` and |
|
allows you to use every other feature Spring has. |
|
|
|
Like ordinary portlets, the `DispatcherPortlet` is declared in the `portlet.xml` file of |
|
your web application: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<portlet> |
|
<portlet-name>sample</portlet-name> |
|
<portlet-class>org.springframework.web.portlet.DispatcherPortlet</portlet-class> |
|
<supports> |
|
<mime-type>text/html</mime-type> |
|
<portlet-mode>view</portlet-mode> |
|
</supports> |
|
<portlet-info> |
|
<title>Sample Portlet</title> |
|
</portlet-info> |
|
</portlet> |
|
---- |
|
|
|
The `DispatcherPortlet` now needs to be configured. |
|
|
|
In the Portlet MVC framework, each `DispatcherPortlet` has its own |
|
`WebApplicationContext`, which inherits all the beans already defined in the Root |
|
`WebApplicationContext`. These inherited beans can be overridden in the portlet-specific |
|
scope, and new scope-specific beans can be defined local to a given portlet instance. |
|
|
|
The framework will, on initialization of a `DispatcherPortlet`, look for a file named |
|
`[portlet-name]-portlet.xml` in the `WEB-INF` directory of your web application and |
|
create the beans defined there (overriding the definitions of any beans defined with the |
|
same name in the global scope). |
|
|
|
The config location used by the `DispatcherPortlet` can be modified through a portlet |
|
initialization parameter (see below for details). |
|
|
|
The Spring `DispatcherPortlet` has a few special beans it uses, in order to be able to |
|
process requests and render the appropriate views. These beans are included in the |
|
Spring framework and can be configured in the `WebApplicationContext`, just as any other |
|
bean would be configured. Each of those beans is described in more detail below. Right |
|
now, we'll just mention them, just to let you know they exist and to enable us to go on |
|
talking about the `DispatcherPortlet`. For most of the beans, defaults are provided so |
|
you don't have to worry about configuring them. |
|
|
|
[[portlet-webappctx-special-beans-tbl]] |
|
.Special beans in the WebApplicationContext |
|
[cols="1,4"] |
|
|=== |
|
| Expression| Explanation |
|
|
|
| handler mapping(s) |
|
| (<<portlet-handlermapping>>) a list of pre- and post-processors and controllers that |
|
will be executed if they match certain criteria (for instance a matching portlet mode |
|
specified with the controller) |
|
|
|
| controller(s) |
|
| (<<portlet-controller>>) the beans providing the actual functionality (or at least, |
|
access to the functionality) as part of the MVC triad |
|
|
|
| view resolver |
|
| (<<portlet-viewresolver>>) capable of resolving view names to view definitions |
|
|
|
| multipart resolver |
|
| (<<portlet-multipart>>) offers functionality to process file uploads from HTML forms |
|
|
|
| handler exception resolver |
|
| (<<portlet-exceptionresolver>>) offers functionality to map exceptions to views or |
|
implement other more complex exception handling code |
|
|=== |
|
|
|
When a `DispatcherPortlet` is setup for use and a request comes in for that specific |
|
`DispatcherPortlet`, it starts processing the request. The list below describes the |
|
complete process a request goes through if handled by a `DispatcherPortlet`: |
|
|
|
. The locale returned by `PortletRequest.getLocale()` is bound to the request to let |
|
elements in the process resolve the locale to use when processing the request (rendering |
|
the view, preparing data, etc.). |
|
. If a multipart resolver is specified and this is an `ActionRequest`, the request is |
|
inspected for multiparts and if they are found, it is wrapped in a |
|
`MultipartActionRequest` for further processing by other elements in the process. (See |
|
<<portlet-multipart>> for further information about multipart handling). |
|
. An appropriate handler is searched for. If a handler is found, the execution chain |
|
associated with the handler (pre-processors, post-processors, controllers) will be |
|
executed in order to prepare a model. |
|
. If a model is returned, the view is rendered, using the view resolver that has been |
|
configured with the `WebApplicationContext`. If no model is returned (which could be due |
|
to a pre- or post-processor intercepting the request, for example, for security |
|
reasons), no view is rendered, since the request could already have been fulfilled. |
|
|
|
Exceptions that are thrown during processing of the request get picked up by any of the |
|
handler exception resolvers that are declared in the `WebApplicationContext`. Using |
|
these exception resolvers you can define custom behavior in case such exceptions get |
|
thrown. |
|
|
|
You can customize Spring's `DispatcherPortlet` by adding context parameters in the |
|
`portlet.xml` file or portlet init-parameters. The possibilities are listed below. |
|
|
|
[[portlet-dpp-init-params]] |
|
.DispatcherPortlet initialization parameters |
|
[cols="1,4"] |
|
|=== |
|
| Parameter| Explanation |
|
|
|
| `contextClass` |
|
| Class that implements `WebApplicationContext`, which will be used to instantiate the |
|
context used by this portlet. If this parameter isn't specified, the |
|
`XmlPortletApplicationContext` will be used. |
|
|
|
| `contextConfigLocation` |
|
| String which is passed to the context instance (specified by `contextClass`) to |
|
indicate where context(s) can be found. The String is potentially split up into |
|
multiple Strings (using a comma as a delimiter) to support multiple contexts (in case |
|
of multiple context locations, for beans that are defined twice, the latest takes |
|
precedence). |
|
|
|
| `namespace` |
|
| The namespace of the `WebApplicationContext`. Defaults to `[portlet-name]-portlet`. |
|
|
|
| `viewRendererUrl` |
|
| The URL at which `DispatcherPortlet` can access an instance of `ViewRendererServlet` |
|
(see <<portlet-viewservlet>>). |
|
|=== |
|
|
|
|
|
|
|
|
|
[[portlet-viewservlet]] |
|
=== The ViewRendererServlet |
|
|
|
The rendering process in Portlet MVC is a bit more complex than in Web MVC. In order to |
|
reuse all the <<view,view technologies>> from Spring Web MVC, we must convert the |
|
`PortletRequest` / `PortletResponse` to `HttpServletRequest` / `HttpServletResponse` and |
|
then call the `render` method of the `View`. To do this, `DispatcherPortlet` uses a |
|
special servlet that exists for just this purpose: the `ViewRendererServlet`. |
|
|
|
In order for `DispatcherPortlet` rendering to work, you must declare an instance of the |
|
`ViewRendererServlet` in the `web.xml` file for your web application as follows: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<servlet> |
|
<servlet-name>ViewRendererServlet</servlet-name> |
|
<servlet-class>org.springframework.web.servlet.ViewRendererServlet</servlet-class> |
|
</servlet> |
|
|
|
<servlet-mapping> |
|
<servlet-name>ViewRendererServlet</servlet-name> |
|
<url-pattern>/WEB-INF/servlet/view</url-pattern> |
|
</servlet-mapping> |
|
---- |
|
|
|
To perform the actual rendering, `DispatcherPortlet` does the following: |
|
|
|
. Binds the `WebApplicationContext` to the request as an attribute under the same |
|
`WEB_APPLICATION_CONTEXT_ATTRIBUTE` key that `DispatcherServlet` uses. |
|
. Binds the `Model` and `View` objects to the request to make them available to the |
|
`ViewRendererServlet`. |
|
. Constructs a `PortletRequestDispatcher` and performs an `include` using the `/WEB- |
|
INF/servlet/view` URL that is mapped to the `ViewRendererServlet`. |
|
|
|
The `ViewRendererServlet` is then able to call the `render` method on the `View` with |
|
the appropriate arguments. |
|
|
|
The actual URL for the `ViewRendererServlet` can be changed using `DispatcherPortlet`'s |
|
`viewRendererUrl` configuration parameter. |
|
|
|
|
|
|
|
|
|
[[portlet-controller]] |
|
=== Controllers |
|
The controllers in Portlet MVC are very similar to the Web MVC Controllers, and porting |
|
code from one to the other should be simple. |
|
|
|
The basis for the Portlet MVC controller architecture is the |
|
`org.springframework.web.portlet.mvc.Controller` interface, which is listed below. |
|
|
|
[source,java,indent=0] |
|
---- |
|
public interface Controller { |
|
|
|
/** |
|
* Process the render request and return a ModelAndView object which the |
|
* DispatcherPortlet will render. |
|
*/ |
|
ModelAndView handleRenderRequest(RenderRequest request, |
|
RenderResponse response) throws Exception; |
|
|
|
/** |
|
* Process the action request. There is nothing to return. |
|
*/ |
|
void handleActionRequest(ActionRequest request, |
|
ActionResponse response) throws Exception; |
|
|
|
} |
|
---- |
|
|
|
As you can see, the Portlet `Controller` interface requires two methods that handle the |
|
two phases of a portlet request: the action request and the render request. The action |
|
phase should be capable of handling an action request, and the render phase should be |
|
capable of handling a render request and returning an appropriate model and view. While |
|
the `Controller` interface is quite abstract, Spring Portlet MVC offers several |
|
controllers that already contain a lot of the functionality you might need; most of |
|
these are very similar to controllers from Spring Web MVC. The `Controller` interface |
|
just defines the most common functionality required of every controller: handling an |
|
action request, handling a render request, and returning a model and a view. |
|
|
|
|
|
|
|
[[portlet-controller-abstractcontroller]] |
|
==== AbstractController and PortletContentGenerator |
|
|
|
Of course, just a `Controller` interface isn't enough. To provide a basic |
|
infrastructure, all of Spring Portlet MVC's ++Controller++s inherit from |
|
`AbstractController`, a class offering access to Spring's `ApplicationContext` and |
|
control over caching. |
|
|
|
[[portlet-ac-features]] |
|
.Features offered by the AbstractController |
|
[cols="1,4"] |
|
|=== |
|
| Parameter| Explanation |
|
|
|
| `requireSession` |
|
| Indicates whether or not this `Controller` requires a session to do its work. This |
|
feature is offered to all controllers. If a session is not present when such a |
|
controller receives a request, the user is informed using a `SessionRequiredException`. |
|
|
|
| `synchronizeSession` |
|
| Use this if you want handling by this controller to be synchronized on the user's |
|
session. To be more specific, the extending controller will override the |
|
`handleRenderRequestInternal(..)` and `handleActionRequestInternal(..)` methods, which |
|
will be synchronized on the user's session if you specify this variable. |
|
|
|
| `renderWhenMinimized` |
|
| If you want your controller to actually render the view when the portlet is in a |
|
minimized state, set this to true. By default, this is set to false so that portlets |
|
that are in a minimized state don't display any content. |
|
|
|
| `cacheSeconds` |
|
| When you want a controller to override the default cache expiration defined for the |
|
portlet, specify a positive integer here. By default it is set to `-1`, which does not |
|
change the default caching. Setting it to `0` will ensure the result is never cached. |
|
|=== |
|
|
|
The `requireSession` and `cacheSeconds` properties are declared on the |
|
`PortletContentGenerator` class, which is the superclass of `AbstractController`) but |
|
are included here for completeness. |
|
|
|
When using the `AbstractController` as a base class for your controllers (which is not |
|
recommended since there are a lot of other controllers that might already do the job for |
|
you) you only have to override either the `handleActionRequestInternal(ActionRequest, |
|
ActionResponse)` method or the `handleRenderRequestInternal(RenderRequest, |
|
RenderResponse)` method (or both), implement your logic, and return a `ModelAndView` |
|
object (in the case of `handleRenderRequestInternal`). |
|
|
|
The default implementations of both `handleActionRequestInternal(..)` and |
|
`handleRenderRequestInternal(..)` throw a `PortletException`. This is consistent with |
|
the behavior of `GenericPortlet` from the JSR- 168 Specification API. So you only need |
|
to override the method that your controller is intended to handle. |
|
|
|
Here is short example consisting of a class and a declaration in the web application |
|
context. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package samples; |
|
|
|
import javax.portlet.RenderRequest; |
|
import javax.portlet.RenderResponse; |
|
|
|
import org.springframework.web.portlet.mvc.AbstractController; |
|
import org.springframework.web.portlet.ModelAndView; |
|
|
|
public class SampleController extends AbstractController { |
|
|
|
public ModelAndView handleRenderRequestInternal(RenderRequest request, RenderResponse response) { |
|
ModelAndView mav = new ModelAndView("foo"); |
|
mav.addObject("message", "Hello World!"); |
|
return mav; |
|
} |
|
|
|
} |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="sampleController" class="samples.SampleController"> |
|
<property name="cacheSeconds" value="120"/> |
|
</bean> |
|
---- |
|
|
|
The class above and the declaration in the web application context is all you need |
|
besides setting up a handler mapping (see <<portlet-handlermapping>>) to get this very |
|
simple controller working. |
|
|
|
|
|
|
|
[[portlet-controller-simple]] |
|
==== Other simple controllers |
|
Although you can extend `AbstractController`, Spring Portlet MVC provides a number of |
|
concrete implementations which offer functionality that is commonly used in simple MVC |
|
applications. |
|
|
|
The `ParameterizableViewController` is basically the same as the example above, except |
|
for the fact that you can specify the view name that it will return in the web |
|
application context (no need to hard-code the view name). |
|
|
|
The `PortletModeNameViewController` uses the current mode of the portlet as the view |
|
name. So, if your portlet is in View mode (i.e. `PortletMode.VIEW`) then it uses "view" |
|
as the view name. |
|
|
|
|
|
|
|
[[portlet-controller-command]] |
|
==== Command Controllers |
|
Spring Portlet MVC has the exact same hierarchy of __command controllers__ as Spring Web |
|
MVC. They provide a way to interact with data objects and dynamically bind parameters |
|
from the `PortletRequest` to the data object specified. Your data objects don't have to |
|
implement a framework-specific interface, so you can directly manipulate your persistent |
|
objects if you desire. Let's examine what command controllers are available, to get an |
|
overview of what you can do with them: |
|
|
|
* `AbstractCommandController` - a command controller you can use to create your own |
|
command controller, capable of binding request parameters to a data object you |
|
specify. This class does not offer form functionality, it does however offer |
|
validation features and lets you specify in the controller itself what to do with the |
|
command object that has been filled with the parameters from the request. |
|
* `AbstractFormController` - an abstract controller offering form submission support. |
|
Using this controller you can model forms and populate them using a command object you |
|
retrieve in the controller. After a user has filled the form, `AbstractFormController` |
|
binds the fields, validates, and hands the object back to the controller to take |
|
appropriate action. Supported features are: invalid form submission (resubmission), |
|
validation, and normal form workflow. You implement methods to determine which views |
|
are used for form presentation and success. Use this controller if you need forms, but |
|
don't want to specify what views you're going to show the user in the application |
|
context. |
|
* `SimpleFormController` - a concrete `AbstractFormController` that provides even more |
|
support when creating a form with a corresponding command object. The |
|
`SimpleFormController` lets you specify a command object, a viewname for the form, a |
|
viewname for the page you want to show the user when form submission has succeeded, |
|
and more. |
|
* `AbstractWizardFormController` -- a concrete `AbstractFormController` that provides a |
|
wizard-style interface for editing the contents of a command object across multiple |
|
display pages. Supports multiple user actions: finish, cancel, or page change, all of |
|
which are easily specified in request parameters from the view. |
|
|
|
These command controllers are quite powerful, but they do require a detailed |
|
understanding of how they operate in order to use them efficiently. Carefully review the |
|
javadocs for this entire hierarchy and then look at some sample implementations before |
|
you start using them. |
|
|
|
|
|
|
|
[[portlet-controller-wrapping]] |
|
==== PortletWrappingController |
|
|
|
Instead of developing new controllers, it is possible to use existing portlets and map |
|
requests to them from a `DispatcherPortlet`. Using the `PortletWrappingController`, you |
|
can instantiate an existing `Portlet` as a `Controller` as follows: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="myPortlet" class="org.springframework.web.portlet.mvc.PortletWrappingController"> |
|
<property name="portletClass" value="sample.MyPortlet"/> |
|
<property name="portletName" value="my-portlet"/> |
|
<property name="initParameters"> |
|
<value>config=/WEB-INF/my-portlet-config.xml</value> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
This can be very valuable since you can then use interceptors to pre-process and |
|
post-process requests going to these portlets. Since JSR-168 does not support any kind |
|
of filter mechanism, this is quite handy. For example, this can be used to wrap the |
|
Hibernate `OpenSessionInViewInterceptor` around a MyFaces JSF Portlet. |
|
|
|
|
|
|
|
|
|
[[portlet-handlermapping]] |
|
=== Handler mappings |
|
Using a handler mapping you can map incoming portlet requests to appropriate handlers. |
|
There are some handler mappings you can use out of the box, for example, the |
|
`PortletModeHandlerMapping`, but let's first examine the general concept of a |
|
`HandlerMapping`. |
|
|
|
Note: We are intentionally using the term "Handler" here instead of "Controller". |
|
`DispatcherPortlet` is designed to be used with other ways to process requests than just |
|
Spring Portlet MVC's own Controllers. A Handler is any Object that can handle portlet |
|
requests. Controllers are an example of Handlers, and they are of course the default. To |
|
use some other framework with `DispatcherPortlet`, a corresponding implementation of |
|
`HandlerAdapter` is all that is needed. |
|
|
|
The functionality a basic `HandlerMapping` provides is the delivering of a |
|
`HandlerExecutionChain`, which must contain the handler that matches the incoming |
|
request, and may also contain a list of handler interceptors that are applied to the |
|
request. When a request comes in, the `DispatcherPortlet` will hand it over to the |
|
handler mapping to let it inspect the request and come up with an appropriate |
|
`HandlerExecutionChain`. Then the `DispatcherPortlet` will execute the handler and |
|
interceptors in the chain (if any). These concepts are all exactly the same as in Spring |
|
Web MVC. |
|
|
|
The concept of configurable handler mappings that can optionally contain interceptors |
|
(executed before or after the actual handler was executed, or both) is extremely |
|
powerful. A lot of supporting functionality can be built into a custom `HandlerMapping`. |
|
Think of a custom handler mapping that chooses a handler not only based on the portlet |
|
mode of the request coming in, but also on a specific state of the session associated |
|
with the request. |
|
|
|
In Spring Web MVC, handler mappings are commonly based on URLs. Since there is really no |
|
such thing as a URL within a Portlet, we must use other mechanisms to control mappings. |
|
The two most common are the portlet mode and a request parameter, but anything available |
|
to the portlet request can be used in a custom handler mapping. |
|
|
|
The rest of this section describes three of Spring Portlet MVC's most commonly used |
|
handler mappings. They all extend `AbstractHandlerMapping` and share the following |
|
properties: |
|
|
|
* `interceptors`: The list of interceptors to use. ++HandlerInterceptor++s are discussed |
|
in <<portlet-handlermapping-interceptor>>. |
|
* `defaultHandler`: The default handler to use, when this handler mapping does not |
|
result in a matching handler. |
|
* `order`: Based on the value of the order property (see the |
|
`org.springframework.core.Ordered` interface), Spring will sort all handler mappings |
|
available in the context and apply the first matching handler. |
|
* `lazyInitHandlers`: Allows for lazy initialization of singleton handlers (prototype |
|
handlers are always lazily initialized). Default value is false. This property is |
|
directly implemented in the three concrete Handlers. |
|
|
|
|
|
|
|
[[portlet-handlermapping-portletmode]] |
|
==== PortletModeHandlerMapping |
|
|
|
This is a simple handler mapping that maps incoming requests based on the current mode |
|
of the portlet (e.g. 'view', 'edit', 'help'). An example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="org.springframework.web.portlet.handler.PortletModeHandlerMapping"> |
|
<property name="portletModeMap"> |
|
<map> |
|
<entry key="view" value-ref="viewHandler"/> |
|
<entry key="edit" value-ref="editHandler"/> |
|
<entry key="help" value-ref="helpHandler"/> |
|
</map> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
|
|
|
|
[[portlet-handlermapping-parameter]] |
|
==== ParameterHandlerMapping |
|
|
|
If we need to navigate around to multiple controllers without changing portlet mode, the |
|
simplest way to do this is with a request parameter that is used as the key to control |
|
the mapping. |
|
|
|
`ParameterHandlerMapping` uses the value of a specific request parameter to control the |
|
mapping. The default name of the parameter is `'action'`, but can be changed using the |
|
`'parameterName'` property. |
|
|
|
The bean configuration for this mapping will look something like this: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="org.springframework.web.portlet.handler.ParameterHandlerMapping"> |
|
<property name="parameterMap"> |
|
<map> |
|
<entry key="add" value-ref="addItemHandler"/> |
|
<entry key="edit" value-ref="editItemHandler"/> |
|
<entry key="delete" value-ref="deleteItemHandler"/> |
|
</map> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
|
|
|
|
[[portlet-handlermapping-portletmodeparameter]] |
|
==== PortletModeParameterHandlerMapping |
|
|
|
The most powerful built-in handler mapping, `PortletModeParameterHandlerMapping` |
|
combines the capabilities of the two previous ones to allow different navigation within |
|
each portlet mode. |
|
|
|
Again the default name of the parameter is "action", but can be changed using the |
|
`parameterName` property. |
|
|
|
By default, the same parameter value may not be used in two different portlet modes. |
|
This is so that if the portal itself changes the portlet mode, the request will no |
|
longer be valid in the mapping. This behavior can be changed by setting the |
|
`allowDupParameters` property to true. However, this is not recommended. |
|
|
|
The bean configuration for this mapping will look something like this: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="org.springframework.web.portlet.handler.PortletModeParameterHandlerMapping"> |
|
<property name="portletModeParameterMap"> |
|
<map> |
|
<entry key="view"> <!-- 'view' portlet mode --> |
|
<map> |
|
<entry key="add" value-ref="addItemHandler"/> |
|
<entry key="edit" value-ref="editItemHandler"/> |
|
<entry key="delete" value-ref="deleteItemHandler"/> |
|
</map> |
|
</entry> |
|
<entry key="edit"> <!-- 'edit' portlet mode --> |
|
<map> |
|
<entry key="prefs" value-ref="prefsHandler"/> |
|
<entry key="resetPrefs" value-ref="resetPrefsHandler"/> |
|
</map> |
|
</entry> |
|
</map> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
This mapping can be chained ahead of a `PortletModeHandlerMapping`, which can then |
|
provide defaults for each mode and an overall default as well. |
|
|
|
|
|
|
|
[[portlet-handlermapping-interceptor]] |
|
==== Adding HandlerInterceptors |
|
|
|
Spring's handler mapping mechanism has a notion of handler interceptors, which can be |
|
extremely useful when you want to apply specific functionality to certain requests, for |
|
example, checking for a principal. Again Spring Portlet MVC implements these concepts in |
|
the same way as Web MVC. |
|
|
|
Interceptors located in the handler mapping must implement `HandlerInterceptor` from the |
|
`org.springframework.web.portlet` package. Just like the servlet version, this interface |
|
defines three methods: one that will be called before the actual handler will be |
|
executed ( `preHandle`), one that will be called after the handler is executed ( |
|
`postHandle`), and one that is called after the complete request has finished ( |
|
`afterCompletion`). These three methods should provide enough flexibility to do all |
|
kinds of pre- and post- processing. |
|
|
|
The `preHandle` method returns a boolean value. You can use this method to break or |
|
continue the processing of the execution chain. When this method returns `true`, the |
|
handler execution chain will continue. When it returns `false`, the `DispatcherPortlet` |
|
assumes the interceptor itself has taken care of requests (and, for example, rendered an |
|
appropriate view) and does not continue executing the other interceptors and the actual |
|
handler in the execution chain. |
|
|
|
The `postHandle` method is only called on a `RenderRequest`. The `preHandle` and |
|
`afterCompletion` methods are called on both an `ActionRequest` and a `RenderRequest`. |
|
If you need to execute logic in these methods for just one type of request, be sure to |
|
check what kind of request it is before processing it. |
|
|
|
|
|
|
|
[[portlet-handlermapping-interceptoradapter]] |
|
==== HandlerInterceptorAdapter |
|
|
|
As with the servlet package, the portlet package has a concrete implementation of |
|
`HandlerInterceptor` called `HandlerInterceptorAdapter`. This class has empty versions |
|
of all the methods so that you can inherit from this class and implement just one or two |
|
methods when that is all you need. |
|
|
|
|
|
|
|
[[portlet-handlermapping-parameterinterceptor]] |
|
==== ParameterMappingInterceptor |
|
|
|
The portlet package also has a concrete interceptor named `ParameterMappingInterceptor` |
|
that is meant to be used directly with `ParameterHandlerMapping` and |
|
`PortletModeParameterHandlerMapping`. This interceptor will cause the parameter that is |
|
being used to control the mapping to be forwarded from an `ActionRequest` to the |
|
subsequent `RenderRequest`. This will help ensure that the `RenderRequest` is mapped to |
|
the same Handler as the `ActionRequest`. This is done in the `preHandle` method of the |
|
interceptor, so you can still modify the parameter value in your handler to change where |
|
the `RenderRequest` will be mapped. |
|
|
|
Be aware that this interceptor is calling `setRenderParameter` on the `ActionResponse`, |
|
which means that you cannot call `sendRedirect` in your handler when using this |
|
interceptor. If you need to do external redirects then you will either need to forward |
|
the mapping parameter manually or write a different interceptor to handle this for you. |
|
|
|
|
|
|
|
|
|
[[portlet-viewresolver]] |
|
=== Views and resolving them |
|
As mentioned previously, Spring Portlet MVC directly reuses all the view technologies |
|
from Spring Web MVC. This includes not only the various `View` implementations |
|
themselves, but also the `ViewResolver` implementations. For more information, refer to |
|
<<view>> and <<mvc-viewresolver>> respectively. |
|
|
|
A few items on using the existing `View` and `ViewResolver` implementations are worth |
|
mentioning: |
|
|
|
* Most portals expect the result of rendering a portlet to be an HTML fragment. So, |
|
things like JSP/JSTL, Velocity, FreeMarker, and XSLT all make sense. But it is |
|
unlikely that views that return other document types will make any sense in a portlet |
|
context. |
|
* There is no such thing as an HTTP redirect from within a portlet (the |
|
`sendRedirect(..)` method of `ActionResponse` cannot be used to stay within the |
|
portal). So, `RedirectView` and use of the `'redirect:'` prefix will __not__ work |
|
correctly from within Portlet MVC. |
|
* It may be possible to use the `'forward:'` prefix from within Portlet MVC. However, |
|
remember that since you are in a portlet, you have no idea what the current URL looks |
|
like. This means you cannot use a relative URL to access other resources in your web |
|
application and that you will have to use an absolute URL. |
|
|
|
Also, for JSP development, the new Spring Taglib and the new Spring Form Taglib both |
|
work in portlet views in exactly the same way that they work in servlet views. |
|
|
|
|
|
|
|
|
|
[[portlet-multipart]] |
|
=== Multipart (file upload) support |
|
Spring Portlet MVC has built-in multipart support to handle file uploads in portlet |
|
applications, just like Web MVC does. The design for the multipart support is done with |
|
pluggable `PortletMultipartResolver` objects, defined in the |
|
`org.springframework.web.portlet.multipart` package. Spring provides a |
|
`PortletMultipartResolver` for use with |
|
http://jakarta.apache.org/commons/fileupload[Commons FileUpload]. How uploading files is |
|
supported will be described in the rest of this section. |
|
|
|
By default, no multipart handling will be done by Spring Portlet MVC, as some developers |
|
will want to handle multiparts themselves. You will have to enable it yourself by adding |
|
a multipart resolver to the web application's context. After you have done that, |
|
`DispatcherPortlet` will inspect each request to see if it contains a multipart. If no |
|
multipart is found, the request will continue as expected. However, if a multipart is |
|
found in the request, the `PortletMultipartResolver` that has been declared in your |
|
context will be used. After that, the multipart attribute in your request will be |
|
treated like any other attribute. |
|
|
|
[NOTE] |
|
==== |
|
Any configured `PortletMultipartResolver` bean __must__ have the following id (or name): |
|
" `portletMultipartResolver`". If you have defined your `PortletMultipartResolver` with |
|
any other name, then the `DispatcherPortlet` will __not__ find your |
|
`PortletMultipartResolver`, and consequently no multipart support will be in effect. |
|
==== |
|
|
|
|
|
|
|
[[portlet-multipart-resolver]] |
|
==== Using the PortletMultipartResolver |
|
|
|
The following example shows how to use the `CommonsPortletMultipartResolver`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="portletMultipartResolver" |
|
class="org.springframework.web.portlet.multipart.CommonsPortletMultipartResolver"> |
|
<!-- one of the properties available; the maximum file size in bytes --> |
|
<property name="maxUploadSize" value="100000"/> |
|
</bean> |
|
---- |
|
|
|
Of course you also need to put the appropriate jars in your classpath for the multipart |
|
resolver to work. In the case of the `CommonsMultipartResolver`, you need to use |
|
`commons-fileupload.jar`. Be sure to use at least version 1.1 of Commons FileUpload as |
|
previous versions do not support JSR-168 Portlet applications. |
|
|
|
Now that you have seen how to set Portlet MVC up to handle multipart requests, let's |
|
talk about how to actually use it. When `DispatcherPortlet` detects a multipart request, |
|
it activates the resolver that has been declared in your context and hands over the |
|
request. What the resolver then does is wrap the current `ActionRequest` in a |
|
`MultipartActionRequest` that has support for multipart file uploads. Using the |
|
`MultipartActionRequest` you can get information about the multiparts contained by this |
|
request and actually get access to the multipart files themselves in your controllers. |
|
|
|
Note that you can only receive multipart file uploads as part of an `ActionRequest`, not |
|
as part of a `RenderRequest`. |
|
|
|
|
|
|
|
[[portlet-multipart-forms]] |
|
==== Handling a file upload in a form |
|
After the `PortletMultipartResolver` has finished doing its job, the request will be |
|
processed like any other. To use the `PortletMultipartResolver`, create a form with an |
|
upload field (see example below), then let Spring bind the file onto your form (backing |
|
object). To actually let the user upload a file, we have to create a (JSP/HTML) form: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<h1>Please upload a file</h1> |
|
<form method="post" action="<portlet:actionURL/>" enctype="multipart/form-data"> |
|
<input type="file" name="file"/> |
|
<input type="submit"/> |
|
</form> |
|
---- |
|
|
|
As you can see, we've created a field named "file" that matches the property of the bean |
|
that holds the `byte[]` array. Furthermore we've added the encoding attribute ( |
|
`enctype="multipart/form-data"`), which is necessary to let the browser know how to |
|
encode the multipart fields (do not forget this!). |
|
|
|
Just as with any other property that's not automagically convertible to a string or |
|
primitive type, to be able to put binary data in your objects you have to register a |
|
custom editor with the `PortletRequestDataBinder`. There are a couple of editors |
|
available for handling files and setting the results on an object. There's a |
|
`StringMultipartFileEditor` capable of converting files to Strings (using a user-defined |
|
character set), and there is a `ByteArrayMultipartFileEditor` which converts files to |
|
byte arrays. They function analogous to the `CustomDateEditor`. |
|
|
|
So, to be able to upload files using a form, declare the resolver, a mapping to a |
|
controller that will process the bean, and the controller itself. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="portletMultipartResolver" |
|
class="org.springframework.web.portlet.multipart.CommonsPortletMultipartResolver"/> |
|
|
|
<bean class="org.springframework.web.portlet.handler.PortletModeHandlerMapping"> |
|
<property name="portletModeMap"> |
|
<map> |
|
<entry key="view" value-ref="fileUploadController"/> |
|
</map> |
|
</property> |
|
</bean> |
|
|
|
<bean id="fileUploadController" class="examples.FileUploadController"> |
|
<property name="commandClass" value="examples.FileUploadBean"/> |
|
<property name="formView" value="fileuploadform"/> |
|
<property name="successView" value="confirmation"/> |
|
</bean> |
|
---- |
|
|
|
After that, create the controller and the actual class to hold the file property. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class FileUploadController extends SimpleFormController { |
|
|
|
public void onSubmitAction(ActionRequest request, ActionResponse response, |
|
Object command, BindException errors) throws Exception { |
|
|
|
// cast the bean |
|
FileUploadBean bean = (FileUploadBean) command; |
|
|
|
// let's see if there's content there |
|
byte[] file = bean.getFile(); |
|
if (file == null) { |
|
// hmm, that's strange, the user did not upload anything |
|
} |
|
|
|
// do something with the file here |
|
} |
|
|
|
protected void initBinder(PortletRequest request, |
|
PortletRequestDataBinder binder) throws Exception { |
|
// to actually be able to convert Multipart instance to byte[] |
|
// we have to register a custom editor |
|
binder.registerCustomEditor(byte[].class, new ByteArrayMultipartFileEditor()); |
|
// now Spring knows how to handle multipart object and convert |
|
} |
|
|
|
} |
|
|
|
public class FileUploadBean { |
|
|
|
private byte[] file; |
|
|
|
public void setFile(byte[] file) { |
|
this.file = file; |
|
} |
|
|
|
public byte[] getFile() { |
|
return file; |
|
} |
|
|
|
} |
|
---- |
|
|
|
As you can see, the `FileUploadBean` has a property of type `byte[]` that holds the |
|
file. The controller registers a custom editor to let Spring know how to actually |
|
convert the multipart objects the resolver has found to properties specified by the |
|
bean. In this example, nothing is done with the `byte[]` property of the bean itself, |
|
but in practice you can do whatever you want (save it in a database, mail it to |
|
somebody, etc). |
|
|
|
An equivalent example in which a file is bound straight to a String-typed property on a |
|
form backing object might look like this: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class FileUploadController extends SimpleFormController { |
|
|
|
public void onSubmitAction(ActionRequest request, ActionResponse response, |
|
Object command, BindException errors) throws Exception { |
|
|
|
// cast the bean |
|
FileUploadBean bean = (FileUploadBean) command; |
|
|
|
// let's see if there's content there |
|
String file = bean.getFile(); |
|
if (file == null) { |
|
// hmm, that's strange, the user did not upload anything |
|
} |
|
|
|
// do something with the file here |
|
} |
|
|
|
protected void initBinder(PortletRequest request, |
|
PortletRequestDataBinder binder) throws Exception { |
|
|
|
// to actually be able to convert Multipart instance to a String |
|
// we have to register a custom editor |
|
binder.registerCustomEditor(String.class, new StringMultipartFileEditor()); |
|
// now Spring knows how to handle multipart objects and convert |
|
} |
|
} |
|
|
|
public class FileUploadBean { |
|
|
|
private String file; |
|
|
|
public void setFile(String file) { |
|
this.file = file; |
|
} |
|
|
|
public String getFile() { |
|
return file; |
|
} |
|
} |
|
---- |
|
|
|
Of course, this last example only makes (logical) sense in the context of uploading a |
|
plain text file (it wouldn't work so well in the case of uploading an image file). |
|
|
|
The third (and final) option is where one binds directly to a `MultipartFile` property |
|
declared on the (form backing) object's class. In this case one does not need to |
|
register any custom property editor because there is no type conversion to be performed. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class FileUploadController extends SimpleFormController { |
|
|
|
public void onSubmitAction(ActionRequest request, ActionResponse response, |
|
Object command, BindException errors) throws Exception { |
|
|
|
// cast the bean |
|
FileUploadBean bean = (FileUploadBean) command; |
|
|
|
// let's see if there's content there |
|
MultipartFile file = bean.getFile(); |
|
if (file == null) { |
|
// hmm, that's strange, the user did not upload anything |
|
} |
|
|
|
// do something with the file here |
|
} |
|
} |
|
|
|
public class FileUploadBean { |
|
|
|
private MultipartFile file; |
|
|
|
public void setFile(MultipartFile file) { |
|
this.file = file; |
|
} |
|
|
|
public MultipartFile getFile() { |
|
return file; |
|
} |
|
|
|
} |
|
---- |
|
|
|
|
|
|
|
|
|
[[portlet-exceptionresolver]] |
|
=== Handling exceptions |
|
Just like Servlet MVC, Portlet MVC provides ++HandlerExceptionResolver++s to ease the |
|
pain of unexpected exceptions that occur while your request is being processed by a |
|
handler that matched the request. Portlet MVC also provides a portlet-specific, concrete |
|
`SimpleMappingExceptionResolver` that enables you to take the class name of any |
|
exception that might be thrown and map it to a view name. |
|
|
|
|
|
|
|
|
|
[[portlet-annotation]] |
|
=== Annotation-based controller configuration |
|
Spring 2.5 introduced an annotation-based programming model for MVC controllers, using |
|
annotations such as `@RequestMapping`, `@RequestParam`, `@ModelAttribute`, etc. This |
|
annotation support is available for both Servlet MVC and Portlet MVC. Controllers |
|
implemented in this style do not have to extend specific base classes or implement |
|
specific interfaces. Furthermore, they do not usually have direct dependencies on |
|
Servlet or Portlet API's, although they can easily get access to Servlet or Portlet |
|
facilities if desired. |
|
|
|
The following sections document these annotations and how they are most commonly used in |
|
a Portlet environment. |
|
|
|
|
|
|
|
[[portlet-ann-setup]] |
|
==== Setting up the dispatcher for annotation support |
|
__`@RequestMapping` will only be processed if a corresponding `HandlerMapping` (for |
|
type level annotations) and/or `HandlerAdapter` (for method level annotations) is |
|
present in the dispatcher.__ This is the case by default in both `DispatcherServlet` and |
|
`DispatcherPortlet`. |
|
|
|
However, if you are defining custom `HandlerMappings` or `HandlerAdapters`, then you |
|
need to make sure that a corresponding custom `DefaultAnnotationHandlerMapping` and/or |
|
`AnnotationMethodHandlerAdapter` is defined as well - provided that you intend to use |
|
`@RequestMapping`. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xsi:schemaLocation="http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd"> |
|
|
|
<bean class="org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping"/> |
|
|
|
<bean class="org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter"/> |
|
|
|
// ... (controller bean definitions) ... |
|
|
|
</beans> |
|
---- |
|
|
|
Defining a `DefaultAnnotationHandlerMapping` and/or `AnnotationMethodHandlerAdapter` |
|
explicitly also makes sense if you would like to customize the mapping strategy, e.g. |
|
specifying a custom `WebBindingInitializer` (see below). |
|
|
|
|
|
|
|
[[portlet-ann-controller]] |
|
==== Defining a controller with @Controller |
|
|
|
The `@Controller` annotation indicates that a particular class serves the role of a |
|
__controller__. There is no need to extend any controller base class or reference the |
|
Portlet API. You are of course still able to reference Portlet-specific features if you |
|
need to. |
|
|
|
The basic purpose of the `@Controller` annotation is to act as a stereotype for the |
|
annotated class, indicating its role. The dispatcher will scan such annotated classes |
|
for mapped methods, detecting `@RequestMapping` annotations (see the next section). |
|
|
|
Annotated controller beans may be defined explicitly, using a standard Spring bean |
|
definition in the dispatcher's context. However, the `@Controller` stereotype also |
|
allows for autodetection, aligned with Spring 2.5's general support for detecting |
|
component classes in the classpath and auto-registering bean definitions for them. |
|
|
|
To enable autodetection of such annotated controllers, you have to add component |
|
scanning to your configuration. This is easily achieved by using the __spring-context__ |
|
schema as shown in the following XML snippet: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:p="http://www.springframework.org/schema/p" |
|
xmlns:context="http://www.springframework.org/schema/context" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/context |
|
http://www.springframework.org/schema/context/spring-context.xsd"> |
|
|
|
<context:component-scan base-package="org.springframework.samples.petportal.portlet"/> |
|
|
|
// ... |
|
|
|
</beans> |
|
---- |
|
|
|
|
|
|
|
[[portlet-ann-requestmapping]] |
|
==== Mapping requests with @RequestMapping |
|
|
|
The `@RequestMapping` annotation is used to map portlet modes like 'VIEW'/'EDIT' onto an |
|
entire class or a particular handler method. Typically the type-level annotation maps a |
|
specific mode (or mode plus parameter condition) onto a form controller, with additional |
|
method-level annotations 'narrowing' the primary mapping for specific portlet request |
|
parameters. |
|
|
|
[TIP] |
|
==== |
|
|
|
`@RequestMapping` at the type level may be used for plain implementations of the |
|
`Controller` interface as well. In this case, the request processing code would follow |
|
the traditional `handle(Action|Render)Request` signature, while the controller's mapping |
|
would be expressed through an `@RequestMapping` annotation. This works for pre-built |
|
`Controller` base classes, such as `SimpleFormController`, too. |
|
|
|
In the following discussion, we'll focus on controllers that are based on annotated |
|
handler methods. |
|
==== |
|
|
|
The following is an example of a form controller from the PetPortal sample application |
|
using this annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
@RequestMapping("EDIT") |
|
@SessionAttributes("site") |
|
public class PetSitesEditController { |
|
|
|
private Properties petSites; |
|
|
|
public void setPetSites(Properties petSites) { |
|
this.petSites = petSites; |
|
} |
|
|
|
@ModelAttribute("petSites") |
|
public Properties getPetSites() { |
|
return this.petSites; |
|
} |
|
|
|
@RequestMapping // default (action=list) |
|
public String showPetSites() { |
|
return "petSitesEdit"; |
|
} |
|
|
|
@RequestMapping(params = "action=add") // render phase |
|
public String showSiteForm(Model model) { |
|
// Used for the initial form as well as for redisplaying with errors. |
|
if (!model.containsAttribute("site")) { |
|
model.addAttribute("site", new PetSite()); |
|
} |
|
|
|
return "petSitesAdd"; |
|
} |
|
|
|
@RequestMapping(params = "action=add") // action phase |
|
public void populateSite(@ModelAttribute("site") PetSite petSite, |
|
BindingResult result, SessionStatus status, ActionResponse response) { |
|
new PetSiteValidator().validate(petSite, result); |
|
if (!result.hasErrors()) { |
|
this.petSites.put(petSite.getName(), petSite.getUrl()); |
|
status.setComplete(); |
|
response.setRenderParameter("action", "list"); |
|
} |
|
} |
|
|
|
@RequestMapping(params = "action=delete") |
|
public void removeSite(@RequestParam("site") String site, ActionResponse response) { |
|
this.petSites.remove(site); |
|
response.setRenderParameter("action", "list"); |
|
} |
|
} |
|
---- |
|
|
|
|
|
|
|
[[portlet-ann-requestmapping-arguments]] |
|
==== Supported handler method arguments |
|
Handler methods which are annotated with `@RequestMapping` are allowed to have very |
|
flexible signatures. They may have arguments of the following types, in arbitrary order |
|
(except for validation results, which need to follow right after the corresponding |
|
command object, if desired): |
|
|
|
* Request and/or response objects (Portlet API). You may choose any specific |
|
request/response type, e.g. PortletRequest / ActionRequest / RenderRequest. An |
|
explicitly declared action/render argument is also used for mapping specific request |
|
types onto a handler method (in case of no other information given that differentiates |
|
between action and render requests). |
|
* Session object (Portlet API): of type PortletSession. An argument of this type will |
|
enforce the presence of a corresponding session. As a consequence, such an argument |
|
will never be `null`. |
|
* `org.springframework.web.context.request.WebRequest` or |
|
`org.springframework.web.context.request.NativeWebRequest`. Allows for generic request |
|
parameter access as well as request/session attribute access, without ties to the |
|
native Servlet/Portlet API. |
|
* `java.util.Locale` for the current request locale (the portal locale in a Portlet |
|
environment). |
|
* `java.util.TimeZone` / `java.time.ZoneId` for the current request time zone. |
|
* `java.io.InputStream` / `java.io.Reader` for access to the request's content. This |
|
will be the raw InputStream/Reader as exposed by the Portlet API. |
|
* `java.io.OutputStream` / `java.io.Writer` for generating the response's content. This |
|
will be the raw OutputStream/Writer as exposed by the Portlet API. |
|
* `@RequestParam` annotated parameters for access to specific Portlet request |
|
parameters. Parameter values will be converted to the declared method argument type. |
|
* `java.util.Map` / `org.springframework.ui.Model` / `org.springframework.ui.ModelMap` |
|
for enriching the implicit model that will be exposed to the web view. |
|
* Command/form objects to bind parameters to: as bean properties or fields, with |
|
customizable type conversion, depending on `@InitBinder` methods and/or the |
|
HandlerAdapter configuration - see the " `webBindingInitializer`" property on |
|
`AnnotationMethodHandlerAdapter`. Such command objects along with their validation |
|
results will be exposed as model attributes, by default using the non-qualified |
|
command class name in property notation (e.g. "orderAddress" for type |
|
"mypackage.OrderAddress"). Specify a parameter-level `ModelAttribute` annotation for |
|
declaring a specific model attribute name. |
|
* `org.springframework.validation.Errors` / |
|
`org.springframework.validation.BindingResult` validation results for a preceding |
|
command/form object (the immediate preceding argument). |
|
* `org.springframework.web.bind.support.SessionStatus` status handle for marking form |
|
processing as complete (triggering the cleanup of session attributes that have been |
|
indicated by the `@SessionAttributes` annotation at the handler type level). |
|
|
|
The following return types are supported for handler methods: |
|
|
|
* A `ModelAndView` object, with the model implicitly enriched with command objects and |
|
the results of `@ModelAttribute` annotated reference data accessor methods. |
|
* A `Model` object, with the view name implicitly determined through a |
|
`RequestToViewNameTranslator` and the model implicitly enriched with command objects |
|
and the results of `@ModelAttribute` annotated reference data accessor methods. |
|
* A `Map` object for exposing a model, with the view name implicitly determined through |
|
a `RequestToViewNameTranslator` and the model implicitly enriched with command objects |
|
and the results of `@ModelAttribute` annotated reference data accessor methods. |
|
* A `View` object, with the model implicitly determined through command objects and |
|
`@ModelAttribute` annotated reference data accessor methods. The handler method may |
|
also programmatically enrich the model by declaring a `Model` argument (see above). |
|
* A `String` value which is interpreted as view name, with the model implicitly |
|
determined through command objects and `@ModelAttribute` annotated reference data |
|
accessor methods. The handler method may also programmatically enrich the model by |
|
declaring a `Model` argument (see above). |
|
* `void` if the method handles the response itself (e.g. by writing the response content |
|
directly). |
|
* Any other return type will be considered a single model attribute to be exposed to the |
|
view, using the attribute name specified through `@ModelAttribute` at the method level |
|
(or the default attribute name based on the return type's class name otherwise). The |
|
model will be implicitly enriched with command objects and the results of |
|
`@ModelAttribute` annotated reference data accessor methods. |
|
|
|
|
|
|
|
[[portlet-ann-requestparam]] |
|
==== Binding request parameters to method parameters with @RequestParam |
|
|
|
The `@RequestParam` annotation is used to bind request parameters to a method parameter |
|
in your controller. |
|
|
|
The following code snippet from the PetPortal sample application shows the usage: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
@RequestMapping("EDIT") |
|
@SessionAttributes("site") |
|
public class PetSitesEditController { |
|
|
|
// ... |
|
|
|
public void removeSite(@RequestParam("site") String site, ActionResponse response) { |
|
this.petSites.remove(site); |
|
response.setRenderParameter("action", "list"); |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
Parameters using this annotation are required by default, but you can specify that a |
|
parameter is optional by setting `@RequestParam`'s `required` attribute to `false` |
|
(e.g., `@RequestParam(value="id", required=false)`). |
|
|
|
|
|
|
|
[[portlet-ann-modelattrib]] |
|
==== Providing a link to data from the model with @ModelAttribute |
|
|
|
`@ModelAttribute` has two usage scenarios in controllers. When placed on a method |
|
parameter, `@ModelAttribute` is used to map a model attribute to the specific, annotated |
|
method parameter (see the `populateSite()` method below). This is how the controller |
|
gets a reference to the object holding the data entered in the form. In addition, the |
|
parameter can be declared as the specific type of the form backing object rather than as |
|
a generic `java.lang.Object`, thus increasing type safety. |
|
|
|
`@ModelAttribute` is also used at the method level to provide __reference data__ for the |
|
model (see the `getPetSites()` method below). For this usage the method signature can |
|
contain the same types as documented above for the `@RequestMapping` annotation. |
|
|
|
[NOTE] |
|
==== |
|
`@ModelAttribute` annotated methods will be executed __before__ the chosen |
|
`@RequestMapping` annotated handler method. They effectively pre-populate the implicit |
|
model with specific attributes, often loaded from a database. Such an attribute can then |
|
already be accessed through `@ModelAttribute` annotated handler method parameters in the |
|
chosen handler method, potentially with binding and validation applied to it. |
|
==== |
|
|
|
The following code snippet shows these two usages of this annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
@RequestMapping("EDIT") |
|
@SessionAttributes("site") |
|
public class PetSitesEditController { |
|
|
|
// ... |
|
|
|
@ModelAttribute("petSites") |
|
public Properties getPetSites() { |
|
return this.petSites; |
|
} |
|
|
|
@RequestMapping(params = "action=add") // action phase |
|
public void populateSite( @ModelAttribute("site") PetSite petSite, BindingResult result, SessionStatus status, ActionResponse response) { |
|
new PetSiteValidator().validate(petSite, result); |
|
if (!result.hasErrors()) { |
|
this.petSites.put(petSite.getName(), petSite.getUrl()); |
|
status.setComplete(); |
|
response.setRenderParameter("action", "list"); |
|
} |
|
} |
|
} |
|
---- |
|
|
|
|
|
|
|
[[portlet-ann-sessionattrib]] |
|
==== Specifying attributes to store in a Session with @SessionAttributes |
|
|
|
The type-level `@SessionAttributes` annotation declares session attributes used by a |
|
specific handler. This will typically list the names of model attributes or types of |
|
model attributes which should be transparently stored in the session or some |
|
conversational storage, serving as form-backing beans between subsequent requests. |
|
|
|
The following code snippet shows the usage of this annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
@RequestMapping("EDIT") |
|
@SessionAttributes("site") |
|
public class PetSitesEditController { |
|
// ... |
|
} |
|
---- |
|
|
|
|
|
|
|
[[portlet-ann-webdatabinder]] |
|
==== Customizing WebDataBinder initialization |
|
|
|
To customize request parameter binding with PropertyEditors, etc. via Spring's |
|
`WebDataBinder`, you can either use `@InitBinder`-annotated methods within your |
|
controller or externalize your configuration by providing a custom |
|
`WebBindingInitializer`. |
|
|
|
|
|
[[portlet-ann-initbinder]] |
|
===== Customizing data binding with @InitBinder |
|
|
|
Annotating controller methods with `@InitBinder` allows you to configure web data |
|
binding directly within your controller class. `@InitBinder` identifies methods which |
|
initialize the `WebDataBinder` which will be used for populating command and form object |
|
arguments of annotated handler methods. |
|
|
|
Such init-binder methods support all arguments that `@RequestMapping` supports, except |
|
for command/form objects and corresponding validation result objects. Init-binder |
|
methods must not have a return value. Thus, they are usually declared as `void`. Typical |
|
arguments include `WebDataBinder` in combination with `WebRequest` or |
|
`java.util.Locale`, allowing code to register context-specific editors. |
|
|
|
The following example demonstrates the use of `@InitBinder` for configuring a |
|
`CustomDateEditor` for all `java.util.Date` form properties. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
public class MyFormController { |
|
|
|
@InitBinder |
|
public void initBinder(WebDataBinder binder) { |
|
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); |
|
dateFormat.setLenient(false); |
|
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false)); |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
|
|
[[portlet-ann-webbindinginitializer]] |
|
===== Configuring a custom WebBindingInitializer |
|
|
|
To externalize data binding initialization, you can provide a custom implementation of |
|
the `WebBindingInitializer` interface, which you then enable by supplying a custom bean |
|
configuration for an `AnnotationMethodHandlerAdapter`, thus overriding the default |
|
configuration. |
|
|
|
|
|
|
|
|
|
[[portlet-deployment]] |
|
=== Portlet application deployment |
|
The process of deploying a Spring Portlet MVC application is no different than deploying |
|
any JSR-168 Portlet application. However, this area is confusing enough in general that |
|
it is worth talking about here briefly. |
|
|
|
Generally, the portal/portlet container runs in one webapp in your servlet container and |
|
your portlets run in another webapp in your servlet container. In order for the portlet |
|
container webapp to make calls into your portlet webapp it must make cross-context calls |
|
to a well-known servlet that provides access to the portlet services defined in your |
|
`portlet.xml` file. |
|
|
|
The JSR-168 specification does not specify exactly how this should happen, so each |
|
portlet container has its own mechanism for this, which usually involves some kind of |
|
"deployment process" that makes changes to the portlet webapp itself and then registers |
|
the portlets within the portlet container. |
|
|
|
At a minimum, the `web.xml` file in your portlet webapp is modified to inject the |
|
well-known servlet that the portlet container will call. In some cases a single servlet |
|
will service all portlets in the webapp, in other cases there will be an instance of the |
|
servlet for each portlet. |
|
|
|
Some portlet containers will also inject libraries and/or configuration files into the |
|
webapp as well. The portlet container must also make its implementation of the Portlet |
|
JSP Tag Library available to your webapp. |
|
|
|
The bottom line is that it is important to understand the deployment needs of your |
|
target portal and make sure they are met (usually by following the automated deployment |
|
process it provides). Be sure to carefully review the documentation from your portal for |
|
this process. |
|
|
|
Once you have deployed your portlet, review the resulting `web.xml` file for sanity. |
|
Some older portals have been known to corrupt the definition of the |
|
`ViewRendererServlet`, thus breaking the rendering of your portlets. |
|
|
|
|
|
|
|
|
|
|
|
[[websocket]] |
|
== WebSocket Support |
|
This part of the reference documentation covers Spring Framework's support for |
|
WebSocket-style messaging in web applications including use of STOMP as an |
|
application level WebSocket sub-protocol. |
|
|
|
<<websocket-intro>> establishes a frame of mind in which to think about |
|
WebSocket, covering adoption challenges, design considerations, and thoughts on |
|
when it is a good fit. |
|
|
|
<<websocket-server>> reviews the Spring WebSocket API on the server-side, while |
|
<<websocket-fallback>> explains the SockJS protocol and shows how to configure |
|
and use it. |
|
|
|
<<websocket-stomp-overview>> introduces the STOMP messaging protocol. |
|
<<websocket-stomp-enable>> demonstrates how to configure STOMP support in Spring. |
|
<<websocket-stomp-handle-annotations>> and the following sections explain how to |
|
write annotated message handling methods, send messages, choose message broker |
|
options, as well as work with the special "user" destinations. Finally, |
|
<<websocket-stomp-testing>> lists three approaches to testing STOMP/WebSocket |
|
applications. |
|
|
|
|
|
|
|
[[websocket-intro]] |
|
=== Introduction |
|
The WebSocket protocol http://tools.ietf.org/html/rfc6455[RFC 6455] defines an important |
|
new capability for web applications: full-duplex, two-way communication between client |
|
and server. It is an exciting new capability on the heels of a long history of |
|
techniques to make the web more interactive including Java Applets, XMLHttpRequest, |
|
Adobe Flash, ActiveXObject, various Comet techniques, server-sent events, and others. |
|
|
|
A proper introduction to the WebSocket protocol is beyond the scope of this |
|
document. At a minimum however it's important to understand that HTTP is used only for |
|
the initial handshake, which relies on a mechanism built into HTTP to request |
|
a protocol upgrade (or in this case a protocol switch) to which the server can respond with |
|
HTTP status 101 (switching protocols) if it agrees. Assuming the handshake succeeds |
|
the TCP socket underlying the HTTP upgrade request remains open and both client and |
|
server can use it to send messages to each other. |
|
|
|
Spring Framework 4 includes a new `spring-websocket` module with comprehensive |
|
WebSocket support. It is compatible with the Java WebSocket API standard |
|
(http://jcp.org/en/jsr/detail?id=356[JSR-356]) |
|
and also provides additional value-add as explained in the rest of the introduction. |
|
|
|
|
|
|
|
[[websocket-into-fallback-options]] |
|
==== WebSocket Fallback Options |
|
An important challenge to adoption is the lack of support for WebSocket in some |
|
browsers. Notably the first Internet Explorer version to support WebSocket is |
|
version 10 (see http://caniuse.com/websockets for support by browser versions). |
|
Furthermore, some restrictive proxies may be configured in ways that either |
|
preclude the attempt to do an HTTP upgrade or otherwise break connection after |
|
some time because it has remained opened for too long. A good overview on this |
|
topic from Peter Lubbers is available in the InfoQ article |
|
http://www.infoq.com/articles/Web-Sockets-Proxy-Servers["How HTML5 Web Sockets Interact With Proxy Servers"]. |
|
|
|
Therefore to build a WebSocket application today, fallback options are required in |
|
order to simulate the WebSocket API where necessary. The Spring Framework provides |
|
such transparent fallback options based on the https://github.com/sockjs/sockjs-protocol[SockJS protocol]. |
|
These options can be enabled through configuration and do not require modifying the |
|
application otherwise. |
|
|
|
|
|
|
|
[[websocket-intro-architecture]] |
|
==== A Messaging Architecture |
|
Aside from short-to-midterm adoption challenges, using WebSocket |
|
brings up important design considerations that are important to recognize |
|
early on, especially in contrast to what we know about building web applications today. |
|
|
|
Today REST is a widely accepted, understood, and supported |
|
architecture for building web applications. It is an architecture that relies |
|
on having many URLs (__nouns__), a handful of HTTP methods (__verbs__), and |
|
other principles such as using hypermedia (__links__), remaining stateless, etc. |
|
|
|
By contrast a WebSocket application may use a single URL only for the |
|
initial HTTP handshake. All messages thereafter share and flow on the |
|
same TCP connection. This points to an entirely different, asynchronous, |
|
event-driven, messaging architecture. One that is much closer |
|
to traditional messaging applications (e.g. JMS, AMQP). |
|
|
|
Spring Framework 4 includes a new `spring-messaging` module with key |
|
abstractions from the |
|
http://projects.spring.io/spring-integration/[Spring Integration] project |
|
such as `Message`, `MessageChannel`, `MessageHandler`, and others that can serve as |
|
a foundation for such a messaging architecture. The module also includes a |
|
set of annotations for mapping messages to methods, similar to the Spring MVC |
|
annotation based programming model. |
|
|
|
|
|
|
|
[[websocket-intro-sub-protocol]] |
|
==== Sub-Protocol Support in WebSocket |
|
WebSocket does imply a __messaging architecture__ but does not mandate the |
|
use of any specific __messaging protocol__. It is a very thin layer over TCP |
|
that transforms a stream of bytes into a stream of messages |
|
(either text or binary) and not much more. It is up to applications |
|
to interpret the meaning of a message. |
|
|
|
Unlike HTTP, which is an application-level protocol, in the WebSocket protocol |
|
there is simply not enough information in an incoming message for a framework |
|
or container to know how to route it or process it. Therefore WebSocket is arguably |
|
too low level for anything but a very trivial application. It can be done, but |
|
it will likely lead to creating a framework on top. This is comparable to how |
|
most web applications today are written using a web framework rather than the |
|
Servlet API alone. |
|
|
|
For this reason the WebSocket RFC defines the use of |
|
http://tools.ietf.org/html/rfc6455#section-1.9[sub-protocols]. |
|
During the handshake, the client and server can use the header |
|
`Sec-WebSocket-Protocol` to agree on a sub-protocol, i.e. a higher, application-level |
|
protocol to use. The use of a sub-protocol is not required, but |
|
even if not used, applications will still need to choose a message |
|
format that both the client and server can understand. That format can be custom, |
|
framework-specific, or a standard messaging protocol. |
|
|
|
The Spring Framework provides support for using |
|
http://stomp.github.io/stomp-specification-1.2.html#Abstract[STOMP] -- a simple, messaging protocol |
|
originally created for use in scripting languages with frames inspired |
|
by HTTP. STOMP is widely support and well suited for use over |
|
WebSocket and over the web. |
|
|
|
|
|
|
|
[[websocket-intro-when-to-use]] |
|
==== Should I Use WebSocket? |
|
With all the design considerations surrounding the use of WebSocket, it is |
|
reasonable to ask, "When is it appropriate to use?". |
|
|
|
The best fit for WebSocket is in web applications where the client and |
|
server need to exchange events at high frequency and with low latency. Prime |
|
candidates include, but are not limited to, applications in finance, games, |
|
collaboration, and others. Such applications are both very sensitive to time |
|
delays and also need to exchange a wide variety of messages at a high |
|
frequency. |
|
|
|
For other application types, however, this may not be the case. |
|
For example, a news or social feed that shows breaking news as it becomes |
|
available may be perfectly okay with simple polling once every few minutes. |
|
Here latency is important, but it is acceptable if the news takes a |
|
few minutes to appear. |
|
|
|
Even in cases where latency is crucial, if the volume of messages is |
|
relatively low (e.g. monitoring network failures) the use of |
|
https://spring.io/blog/2012/05/08/spring-mvc-3-2-preview-techniques-for-real-time-updates[long polling] |
|
should be considered as a relatively simple alternative that |
|
works reliably and is comparable in terms of efficiency (again assuming the volume of |
|
messages is relatively low). |
|
|
|
It is the combination of both low latency and high frequency of messages that can make |
|
the use of the WebSocket protocol critical. Even in such applications, |
|
the choice remains whether all client-server |
|
communication should be done through WebSocket messages as opposed to using |
|
HTTP and REST. The answer is going to vary by application; however, it is likely |
|
that some functionality may be exposed over both WebSocket and as a REST API in |
|
order to provide clients with alternatives. Furthermore, a REST API call may need |
|
to broadcast a message to interested clients connected via WebSocket. |
|
|
|
The Spring Framework allows `@Controller` and `@RestController` classes to have both |
|
HTTP request handling and WebSocket message handling methods. |
|
Furthermore, a Spring MVC request handling method, or any application |
|
method for that matter, can easily broadcast a message to all interested |
|
WebSocket clients or to a specific user. |
|
|
|
|
|
|
|
|
|
[[websocket-server]] |
|
=== WebSocket API |
|
The Spring Framework provides a WebSocket API designed to adapt to various WebSocket engines. |
|
Currently the list includes WebSocket runtimes such as Tomcat 7.0.47+, Jetty 9.1+, |
|
GlassFish 4.1+, WebLogic 12.1.3+, and Undertow 1.0+ (and WildFly 8.0+). Additional support |
|
may be added as more WebSocket runtimes become available. |
|
|
|
[NOTE] |
|
==== |
|
As explained in the <<websocket-intro-sub-protocol,introduction>>, direct use of a |
|
WebSocket API is too low level for applications -- until assumptions are made about the |
|
format of a message there is little a framework can do to interpret messages or route |
|
them via annotations. This is why applications should consider using a sub-protocol |
|
and Spring's <<websocket-stomp,STOMP over WebSocket>> support. |
|
|
|
When using a higher level protocol, the details of the WebSocket API become less |
|
relevant, much like the details of TCP communication are not exposed to applications |
|
when using HTTP. Nevertheless this section covers the details of using WebSocket |
|
directly. |
|
==== |
|
|
|
|
|
|
|
[[websocket-server-handler]] |
|
==== Create and Configure a WebSocketHandler |
|
Creating a WebSocket server is as simple as implementing `WebSocketHandler` or more |
|
likely extending either `TextWebSocketHandler` or `BinaryWebSocketHandler`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import org.springframework.web.socket.WebSocketHandler; |
|
import org.springframework.web.socket.WebSocketSession; |
|
import org.springframework.web.socket.TextMessage; |
|
|
|
public class MyHandler extends TextWebSocketHandler { |
|
|
|
@Override |
|
public void handleTextMessage(WebSocketSession session, TextMessage message) { |
|
// ... |
|
} |
|
|
|
} |
|
---- |
|
|
|
There is dedicated WebSocket Java-config and XML namespace support for mapping the above |
|
WebSocket handler to a specific URL: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import org.springframework.web.socket.config.annotation.EnableWebSocket; |
|
import org.springframework.web.socket.config.annotation.WebSocketConfigurer; |
|
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; |
|
|
|
@Configuration |
|
@EnableWebSocket |
|
public class WebSocketConfig implements WebSocketConfigurer { |
|
|
|
@Override |
|
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { |
|
registry.addHandler(myHandler(), "/myHandler"); |
|
} |
|
|
|
@Bean |
|
public WebSocketHandler myHandler() { |
|
return new MyHandler(); |
|
} |
|
|
|
} |
|
---- |
|
|
|
XML configuration equivalent: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes,attributes"] |
|
---- |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:websocket="http://www.springframework.org/schema/websocket" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/websocket |
|
http://www.springframework.org/schema/websocket/spring-websocket.xsd"> |
|
|
|
<websocket:handlers> |
|
<websocket:mapping path="/myHandler" handler="myHandler"/> |
|
</websocket:handlers> |
|
|
|
<bean id="myHandler" class="org.springframework.samples.MyHandler"/> |
|
|
|
</beans> |
|
---- |
|
|
|
The above is for use in Spring MVC applications and should be included in the |
|
configuration of a <<mvc-serlvet,DispatcherServlet>>. However, Spring's WebSocket |
|
support does not depend on Spring MVC. It is relatively simple to integrate a `WebSocketHandler` |
|
into other HTTP serving environments with the help of |
|
{javadoc-baseurl}/org/springframework/web/socket/server/support/WebSocketHttpRequestHandler.html[WebSocketHttpRequestHandler]. |
|
|
|
|
|
|
|
[[websocket-server-handshake]] |
|
==== Customizing the WebSocket Handshake |
|
The easiest way to customize the initial HTTP WebSocket handshake request is through |
|
a `HandshakeInterceptor`, which exposes "before" and "after" the handshake methods. |
|
Such an interceptor can be used to preclude the handshake or to make any attributes |
|
available to the `WebSocketSession`. For example, there is a built-in interceptor |
|
for passing HTTP session attributes to the WebSocket session: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebSocket |
|
public class WebSocketConfig implements WebSocketConfigurer { |
|
|
|
@Override |
|
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { |
|
registry.addHandler(new MyHandler(), "/myHandler") |
|
.addInterceptors(new HttpSessionHandshakeInterceptor()); |
|
} |
|
|
|
} |
|
---- |
|
|
|
And the XML configuration equivalent: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes,attributes"] |
|
---- |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:websocket="http://www.springframework.org/schema/websocket" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/websocket |
|
http://www.springframework.org/schema/websocket/spring-websocket.xsd"> |
|
|
|
<websocket:handlers> |
|
<websocket:mapping path="/myHandler" handler="myHandler"/> |
|
<websocket:handshake-interceptors> |
|
<bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"/> |
|
</websocket:handshake-interceptors> |
|
</websocket:handlers> |
|
|
|
<bean id="myHandler" class="org.springframework.samples.MyHandler"/> |
|
|
|
</beans> |
|
---- |
|
|
|
A more advanced option is to extend the `DefaultHandshakeHandler` that performs |
|
the steps of the WebSocket handshake, including validating the client origin, |
|
negotiating a sub-protocol, and others. An application may also need to use this |
|
option if it needs to configure a custom `RequestUpgradeStrategy` in order to |
|
adapt to a WebSocket server engine and version that is not yet supported |
|
(also see <<websocket-server-deployment>> for more on this subject). |
|
Both the Java-config and XML namespace make it possible to configure a custom |
|
`HandshakeHandler`. |
|
|
|
|
|
|
|
[[websocket-server-decorators]] |
|
==== WebSocketHandler Decoration |
|
Spring provides a `WebSocketHandlerDecorator` base class that can be used to decorate |
|
a `WebSocketHandler` with additional behavior. Logging and exception handling |
|
implementations are provided and added by default when using the WebSocket Java-config |
|
or XML namespace. The `ExceptionWebSocketHandlerDecorator` catches all uncaught |
|
exceptions arising from any WebSocketHandler method and closes the WebSocket |
|
session with status `1011` that indicates a server error. |
|
|
|
|
|
|
|
[[websocket-server-deployment]] |
|
==== Deployment Considerations |
|
The Spring WebSocket API is easy to integrate into a Spring MVC application where |
|
the `DispatcherServlet` serves both HTTP WebSocket handshake as well as other |
|
HTTP requests. It is also easy to integrate into other HTTP processing scenarios |
|
by invoking `WebSocketHttpRequestHandler`. This is convenient and easy to |
|
understand. However, special considerations apply with regards to JSR-356 runtimes. |
|
|
|
The Java WebSocket API (JSR-356) provides two deployment mechanisms. The first |
|
involves a Servlet container classpath scan (Servlet 3 feature) at startup; and |
|
the other is a registration API to use at Servlet container initialization. |
|
Neither of these mechanism makes it possible to use a single "front controller" |
|
for all HTTP processing -- including WebSocket handshake and all other HTTP |
|
requests -- such as Spring MVC's `DispatcherServlet`. |
|
|
|
This is a significant limitation of JSR-356 that Spring's WebSocket support |
|
addresses by providing a server-specific `RequestUpgradeStrategy` even when |
|
running in a JSR-356 runtime. |
|
|
|
[NOTE] |
|
==== |
|
A request to overcome the above limitation in the Java WebSocket API has been |
|
created and can be followed at |
|
https://java.net/jira/browse/WEBSOCKET_SPEC-211[WEBSOCKET_SPEC-211]. |
|
Also note that Tomcat and Jetty already provide native API alternatives that |
|
makes it easy to overcome the limitation. We are hopeful that more servers |
|
will follow their example regardless of when it is addressed in the |
|
Java WebSocket API. |
|
==== |
|
|
|
A secondary consideration is that Servlet containers with JSR-356 support are expected |
|
to perform a `ServletContainerInitializer` (SCI) scan that can slow down application |
|
startup, in some cases dramatically. If a significant impact is observed after an |
|
upgrade to a Servlet container version with JSR-356 support, it should |
|
be possible to selectively enable or disable web fragments (and SCI scanning) |
|
through the use of the `<absolute-ordering />` element in `web.xml`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes,attributes"] |
|
---- |
|
<web-app xmlns="http://java.sun.com/xml/ns/javaee" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xsi:schemaLocation=" |
|
http://java.sun.com/xml/ns/javaee |
|
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" |
|
version="3.0"> |
|
|
|
<absolute-ordering/> |
|
|
|
</web-app> |
|
---- |
|
|
|
You can then selectively enable web fragments by name, such as Spring's own |
|
`SpringServletContainerInitializer` that provides support for the Servlet 3 |
|
Java initialization API, if required: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes,attributes"] |
|
---- |
|
<web-app xmlns="http://java.sun.com/xml/ns/javaee" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xsi:schemaLocation=" |
|
http://java.sun.com/xml/ns/javaee |
|
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" |
|
version="3.0"> |
|
|
|
<absolute-ordering> |
|
<name>spring_web</name> |
|
</absolute-ordering> |
|
|
|
</web-app> |
|
---- |
|
|
|
[[websocket-server-runtime-configuration]] |
|
==== Configuring the WebSocket Engine |
|
|
|
Each underlying WebSocket engine exposes configuration properties that control |
|
runtime characteristics such as the size of message buffer sizes, idle timeout, |
|
and others. |
|
|
|
For Tomcat, WildFly, and GlassFish add a `ServletServerContainerFactoryBean` to your |
|
WebSocket Java config: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebSocket |
|
public class WebSocketConfig implements WebSocketConfigurer { |
|
|
|
@Bean |
|
public ServletServerContainerFactoryBean createWebSocketContainer() { |
|
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean(); |
|
container.setMaxTextMessageBufferSize(8192); |
|
container.setMaxBinaryMessageBufferSize(8192); |
|
return container; |
|
} |
|
|
|
} |
|
---- |
|
|
|
or WebSocket XML namespace: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes,attributes"] |
|
---- |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:websocket="http://www.springframework.org/schema/websocket" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/websocket |
|
http://www.springframework.org/schema/websocket/spring-websocket.xsd"> |
|
|
|
<bean class="org.springframework...ServletServerContainerFactoryBean"> |
|
<property name="maxTextMessageBufferSize" value="8192"/> |
|
<property name="maxBinaryMessageBufferSize" value="8192"/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
For client side WebSocket configuration, you should use `WebSocketContainerFactoryBean` |
|
(XML) or `ContainerProvider.getWebSocketContainer()` (Java config). |
|
==== |
|
|
|
For Jetty, you'll need to supply a pre-configured Jetty `WebSocketServerFactory` and plug |
|
that into Spring's `DefaultHandshakeHandler` through your WebSocket Java config: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebSocket |
|
public class WebSocketConfig implements WebSocketConfigurer { |
|
|
|
@Override |
|
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { |
|
registry.addHandler(echoWebSocketHandler(), |
|
"/echo").setHandshakeHandler(handshakeHandler()); |
|
} |
|
|
|
@Bean |
|
public DefaultHandshakeHandler handshakeHandler() { |
|
|
|
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER); |
|
policy.setInputBufferSize(8192); |
|
policy.setIdleTimeout(600000); |
|
|
|
return new DefaultHandshakeHandler( |
|
new JettyRequestUpgradeStrategy(new WebSocketServerFactory(policy))); |
|
} |
|
|
|
} |
|
---- |
|
|
|
or WebSocket XML namespace: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes,attributes"] |
|
---- |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:websocket="http://www.springframework.org/schema/websocket" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/websocket |
|
http://www.springframework.org/schema/websocket/spring-websocket.xsd"> |
|
|
|
<websocket:handlers> |
|
<websocket:mapping path="/echo" handler="echoHandler"/> |
|
<websocket:handshake-handler ref="handshakeHandler"/> |
|
</websocket:handlers> |
|
|
|
<bean id="handshakeHandler" class="org.springframework...DefaultHandshakeHandler"> |
|
<constructor-arg ref="upgradeStrategy"/> |
|
</bean> |
|
|
|
<bean id="upgradeStrategy" class="org.springframework...JettyRequestUpgradeStrategy"> |
|
<constructor-arg ref="serverFactory"/> |
|
</bean> |
|
|
|
<bean id="serverFactory" class="org.eclipse.jetty...WebSocketServerFactory"> |
|
<constructor-arg> |
|
<bean class="org.eclipse.jetty...WebSocketPolicy"> |
|
<constructor-arg value="SERVER"/> |
|
<property name="inputBufferSize" value="8092"/> |
|
<property name="idleTimeout" value="600000"/> |
|
</bean> |
|
</constructor-arg> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
[[websocket-server-allowed-origins]] |
|
==== Configuring allowed origins |
|
|
|
As of Spring Framework 4.1.5, the default behavior for WebSocket and SockJS is to accept |
|
only _same origin_ requests. It is also possible to allow _all_ or a specified list of origins. |
|
This check is mostly designed for browser clients. There is nothing preventing other types |
|
of clients from modifying the `Origin` header value (see |
|
https://tools.ietf.org/html/rfc6454[RFC 6454: The Web Origin Concept] for more details). |
|
|
|
The 3 possible behaviors are: |
|
|
|
* Allow only same origin requests (default): in this mode, when SockJS is enabled, the |
|
Iframe HTTP response header `X-Frame-Options` is set to `SAMEORIGIN`, and JSONP |
|
transport is disabled since it does not allow to check the origin of a request. |
|
As a consequence, IE6 and IE7 are not supported when this mode is enabled. |
|
* Allow a specified list of origins: each provided _allowed origin_ must start with `http://` |
|
or `https://`. In this mode, when SockJS is enabled, both IFrame and JSONP based |
|
transports are disabled. As a consequence, IE6 through IE9 are not supported when this |
|
mode is enabled. |
|
* Allow all origins: to enable this mode, you should provide `*` as the allowed origin |
|
value. In this mode, all transports are available. |
|
|
|
WebSocket and SockJS allowed origins can be configured as shown bellow: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import org.springframework.web.socket.config.annotation.EnableWebSocket; |
|
import org.springframework.web.socket.config.annotation.WebSocketConfigurer; |
|
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; |
|
|
|
@Configuration |
|
@EnableWebSocket |
|
public class WebSocketConfig implements WebSocketConfigurer { |
|
|
|
@Override |
|
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { |
|
registry.addHandler(myHandler(), "/myHandler").setAllowedOrigins("http://mydomain.com"); |
|
} |
|
|
|
@Bean |
|
public WebSocketHandler myHandler() { |
|
return new MyHandler(); |
|
} |
|
|
|
} |
|
---- |
|
|
|
XML configuration equivalent: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes,attributes"] |
|
---- |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:websocket="http://www.springframework.org/schema/websocket" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/websocket |
|
http://www.springframework.org/schema/websocket/spring-websocket.xsd"> |
|
|
|
<websocket:handlers allowed-origins="http://mydomain.com"> |
|
<websocket:mapping path="/myHandler" handler="myHandler" /> |
|
</websocket:handlers> |
|
|
|
<bean id="myHandler" class="org.springframework.samples.MyHandler"/> |
|
|
|
</beans> |
|
---- |
|
|
|
|
|
[[websocket-fallback]] |
|
=== SockJS Fallback Options |
|
As explained in the <<websocket-into-fallback-options,introduction>>, WebSocket is not |
|
supported in all browsers yet and may be precluded by restrictive network proxies. |
|
This is why Spring provides fallback options that emulate the WebSocket API as close |
|
as possible based on the https://github.com/sockjs/sockjs-protocol[SockJS protocol] |
|
(version 0.3.3). |
|
|
|
[[websocket-fallback-sockjs-overview]] |
|
==== Overview of SockJS |
|
|
|
The goal of SockJS is to let applications use a WebSocket API but fall back to |
|
non-WebSocket alternatives when necessary at runtime, i.e. without the need to |
|
change application code. |
|
|
|
SockJS consists of: |
|
|
|
* The https://github.com/sockjs/sockjs-protocol[SockJS protocol] |
|
defined in the form of executable |
|
http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html[narrated tests]. |
|
* The https://github.com/sockjs/sockjs-client/tree/v0.3.4[SockJS JavaScript client] - a client library for use in browsers. |
|
* SockJS server implementations including one in the Spring Framework `spring-websocket` module. |
|
* As of 4.1 `spring-websocket` also provides a SockJS Java client. |
|
|
|
SockJS is designed for use in browsers. It goes to great lengths |
|
to support a wide range of browser versions using a variety of techniques. |
|
For the full list of SockJS transport types and browsers see the |
|
https://github.com/sockjs/sockjs-client/tree/v0.3.4[SockJS client] page. Transports |
|
fall in 3 general categories: WebSocket, HTTP Streaming, and HTTP Long Polling. |
|
For an overview of these categories see |
|
https://spring.io/blog/2012/05/08/spring-mvc-3-2-preview-techniques-for-real-time-updates/[this blog post]. |
|
|
|
The SockJS client begins by sending `"GET /info"` to |
|
obtain basic information from the server. After that it must decide what transport |
|
to use. If possible WebSocket is used. If not, in most browsers |
|
there is at least one HTTP streaming option and if not then HTTP (long) |
|
polling is used. |
|
|
|
All transport requests have the following URL structure: |
|
---- |
|
http://host:port/myApp/myEndpoint/{server-id}/{session-id}/{transport} |
|
---- |
|
|
|
* `{server-id}` - useful for routing requests in a cluster but not used otherwise. |
|
* `{session-id}` - correlates HTTP requests belonging to a SockJS session. |
|
* `{transport}` - indicates the transport type, e.g. "websocket", "xhr-streaming", etc. |
|
|
|
The WebSocket transport needs only a single HTTP request to do the WebSocket handshake. |
|
All messages thereafter are exchanged on that socket. |
|
|
|
HTTP transports require more requests. Ajax/XHR streaming for example relies on |
|
one long-running request for server-to-client messages and additional HTTP POST |
|
requests for client-to-server messages. Long polling is similar except it |
|
ends the current request after each server-to-client send. |
|
|
|
SockJS adds minimal message framing. For example the server sends the letter +o+ |
|
("open" frame) initially, messages are sent as +a["message1","message2"]+ |
|
(JSON-encoded array), the letter +h+ ("heartbeat" frame) if no messages flow |
|
for 25 seconds by default, and the letter +c+ ("close" frame) to close the session. |
|
|
|
To learn more, run an example in a browser and watch the HTTP requests. |
|
The SockJS client allows fixing the list of transports so it is possible to |
|
see each transport one at a time. The SockJS client also provides a debug flag |
|
which enables helpful messages in the browser console. On the server side enable |
|
`TRACE` logging for `org.springframework.web.socket`. |
|
For even more detail refer to the SockJS protocol |
|
http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html[narrated test]. |
|
|
|
|
|
[[websocket-fallback-sockjs-enable]] |
|
==== Enable SockJS |
|
SockJS is easy to enable through Java configuration: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebSocket |
|
public class WebSocketConfig implements WebSocketConfigurer { |
|
|
|
@Override |
|
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { |
|
registry.addHandler(myHandler(), "/myHandler").withSockJS(); |
|
} |
|
|
|
@Bean |
|
public WebSocketHandler myHandler() { |
|
return new MyHandler(); |
|
} |
|
|
|
} |
|
---- |
|
|
|
and the XML configuration equivalent: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes,attributes"] |
|
---- |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:websocket="http://www.springframework.org/schema/websocket" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/websocket |
|
http://www.springframework.org/schema/websocket/spring-websocket.xsd"> |
|
|
|
<websocket:handlers> |
|
<websocket:mapping path="/myHandler" handler="myHandler"/> |
|
<websocket:sockjs/> |
|
</websocket:handlers> |
|
|
|
<bean id="myHandler" class="org.springframework.samples.MyHandler"/> |
|
|
|
</beans> |
|
---- |
|
|
|
The above is for use in Spring MVC applications and should be included in the |
|
configuration of a <<mvc-serlvet,DispatcherServlet>>. However, Spring's WebSocket |
|
and SockJS support does not depend on Spring MVC. It is relatively simple to |
|
integrate into other HTTP serving environments with the help of |
|
{javadoc-baseurl}/org/springframework/web/socket/sockjs/support/SockJsHttpRequestHandler.html[SockJsHttpRequestHandler]. |
|
|
|
On the browser side, applications can use the |
|
https://github.com/sockjs/sockjs-client/tree/v0.3.4[sockjs-client] (version 0.3.x) that |
|
emulates the W3C WebSocket API and communicates with the server to select the best |
|
transport option depending on the browser it's running in. Review the |
|
https://github.com/sockjs/sockjs-client/tree/v0.3.4[sockjs-client] page and the list of |
|
transport types supported by browser. The client also provides several |
|
configuration options, for example, to specify which transports to include. |
|
|
|
[[websocket-fallback-xhr-vs-iframe]] |
|
==== HTTP Streaming in IE 8, 9: Ajax/XHR vs IFrame |
|
|
|
Internet Explorer 8 and 9 are and will remain common for some time. They are |
|
a key reason for having SockJS. This section covers important |
|
considerations about running in those browsers. |
|
|
|
The SockJS client supports Ajax/XHR streaming in IE 8 and 9 via Microsoft's |
|
http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx[XDomainRequest]. |
|
That works across domains but does not support sending cookies. |
|
Cookies are very often essential for Java applications. |
|
However since the SockJS client can be used with many server |
|
types (not just Java ones), it needs to know whether cookies matter. |
|
If so the SockJS client prefers Ajax/XHR for streaming or otherwise it |
|
relies on a iframe-based technique. |
|
|
|
The very first `"/info"` request from the SockJS client is a request for |
|
information that can influence the client's choice of transports. |
|
One of those details is whether the server application relies on cookies, |
|
e.g. for authentication purposes or clustering with sticky sessions. |
|
Spring's SockJS support includes a property called `sessionCookieNeeded`. |
|
It is enabled by default since most Java applications rely on the `JSESSIONID` |
|
cookie. If your application does not need it, you can turn off this option |
|
and the SockJS client should choose `xdr-streaming` in IE 8 and 9. |
|
|
|
If you do use an iframe-based transport, and in any case, it is good to know |
|
that browsers can be instructed to block the use of IFrames on a given page by |
|
setting the HTTP response header `X-Frame-Options` to `DENY`, |
|
`SAMEORIGIN`, or `ALLOW-FROM <origin>`. This is used to prevent |
|
https://www.owasp.org/index.php/Clickjacking[clickjacking]. |
|
|
|
[NOTE] |
|
==== |
|
Spring Security 3.2+ provides support for setting `X-Frame-Options` on every |
|
response. By default the Spring Security Java config sets it to `DENY`. |
|
In 3.2 the Spring Security XML namespace does not set that header by default |
|
but may be configured to do so, and in the future it may set it by default. |
|
|
|
See http://docs.spring.io/spring-security/site/docs/3.2.2.RELEASE/reference/htmlsingle/#headers[Section 7.1. "Default Security Headers"] |
|
of the Spring Security documentation for details on how to configure the |
|
setting of the `X-Frame-Options` header. You may also check or watch |
|
https://jira.spring.io/browse/SEC-2501[SEC-2501] for additional background. |
|
==== |
|
|
|
If your application adds the `X-Frame-Options` response header (as it should!) |
|
and relies on an iframe-based transport, you will need to set the header value to |
|
`SAMEORIGIN` or `ALLOW-FROM <origin>`. Along with that the Spring SockJS |
|
support also needs to know the location of the SockJS client because it is loaded |
|
from the iframe. By default the iframe is set to download the SockJS client |
|
from a CDN location. It is a good idea to configure this option to |
|
a URL from the same origin as the application. |
|
|
|
In Java config this can be done as shown below. The XML namespace provides a |
|
similar option via the `<websocket:sockjs>` element: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebSocket |
|
public class WebSocketConfig implements WebSocketConfigurer { |
|
|
|
@Override |
|
public void registerStompEndpoints(StompEndpointRegistry registry) { |
|
registry.addEndpoint("/portfolio").withSockJS() |
|
.setClientLibraryUrl("http://localhost:8080/myapp/js/sockjs-client.js"); |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
During initial development, do enable the SockJS client `devel` mode that prevents |
|
the browser from caching SockJS requests (like the iframe) that would otherwise |
|
be cached. For details on how to enable it see the |
|
https://github.com/sockjs/sockjs-client/tree/v0.3.4[SockJS client] page. |
|
==== |
|
|
|
[[websocket-fallback-sockjs-heartbeat]] |
|
==== Heartbeat Messages |
|
|
|
The SockJS protocol requires servers to send heartbeat messages to preclude proxies |
|
from concluding a connection is hung. The Spring SockJS configuration has a property |
|
called `heartbeatTime` that can be used to customize the frequency. By default a |
|
heartbeat is sent after 25 seconds assuming no other messages were sent on that |
|
connection. This 25 seconds value is in line with the following |
|
http://tools.ietf.org/html/rfc6202[IETF recommendation] for public Internet applications. |
|
|
|
[NOTE] |
|
==== |
|
When using STOMP over WebSocket/SockJS, if the STOMP client and server negotiate |
|
heartbeats to be exchanged, the SockJS heartbeats are disabled. |
|
==== |
|
|
|
The Spring SockJS support also allows configuring the `TaskScheduler` to use |
|
for scheduling heartbeats tasks. The task scheduler is backed by a thread pool |
|
with default settings based on the number of available processors. Applications |
|
should consider customizing the settings according to their specific needs. |
|
|
|
[[websocket-fallback-sockjs-servlet3-async]] |
|
==== Servlet 3 Async Requests |
|
|
|
HTTP streaming and HTTP long polling SockJS transports require a connection to remain |
|
open longer than usual. For an overview of these techniques see |
|
https://spring.io/blog/2012/05/08/spring-mvc-3-2-preview-techniques-for-real-time-updates/[this blog post]. |
|
|
|
In Servlet containers this is done through Servlet 3 async support that |
|
allows exiting the Servlet container thread processing a request and continuing |
|
to write to the response from another thread. |
|
|
|
A specific issue is that the Servlet API does not provide notifications for a client |
|
that has gone away, see https://java.net/jira/browse/SERVLET_SPEC-44[SERVLET_SPEC-44]. |
|
However, Servlet containers raise an exception on subsequent attempts to write |
|
to the response. Since Spring's SockJS Service supports sever-sent heartbeats (every |
|
25 seconds by default), that means a client disconnect is usually detected within that |
|
time period or earlier if messages are sent more frequently. |
|
|
|
[NOTE] |
|
==== |
|
As a result network IO failures may occur simply because a client has disconnected, which |
|
can fill the log with unnecessary stack traces. Spring makes a best effort to identify |
|
such network failures that represent client disconnects (specific to each server) and log |
|
a minimal message using the dedicated log category `DISCONNECTED_CLIENT_LOG_CATEGORY` |
|
defined in `AbstractSockJsSession`. If you need to see the stack traces, set that |
|
log category to TRACE. |
|
==== |
|
|
|
[[websocket-fallback-cors]] |
|
==== CORS Headers for SockJS |
|
|
|
If you allow cross-origin requests (see <<websocket-server-allowed-origins>>), the SockJS protocol |
|
uses CORS for cross-domain support in the XHR streaming and polling transports. Therefore |
|
CORS headers are added automatically unless the presence of CORS headers in the response |
|
is detected. So if an application is already configured to provide CORS support, e.g. |
|
through a Servlet Filter, Spring's SockJsService will skip this part. |
|
|
|
It is also possible to disable the addition of these CORS headers via the |
|
`suppressCors` property in Spring's SockJsService. |
|
|
|
The following is the list of headers and values expected by SockJS: |
|
|
|
* `"Access-Control-Allow-Origin"` - initialized from the value of the "Origin" request header. |
|
* `"Access-Control-Allow-Credentials"` - always set to `true`. |
|
* `"Access-Control-Request-Headers"` - initialized from values from the equivalent request header. |
|
* `"Access-Control-Allow-Methods"` - the HTTP methods a transport supports (see `TransportType` enum). |
|
* `"Access-Control-Max-Age"` - set to 31536000 (1 year). |
|
|
|
For the exact implementation see `addCorsHeaders` in `AbstractSockJsService` as well |
|
as the `TransportType` enum in the source code. |
|
|
|
Alternatively if the CORS configuration allows it consider excluding URLs with the |
|
SockJS endpoint prefix thus letting Spring's `SockJsService` handle it. |
|
|
|
|
|
[[websocket-fallback-sockjs-client]] |
|
==== SockJS Client |
|
|
|
A SockJS Java client is provided in order to connect to remote SockJS endpoints without |
|
using a browser. This can be especially useful when there is a need for bidirectional |
|
communication between 2 servers over a public network, i.e. where network proxies may |
|
preclude the use of the WebSocket protocol. A SockJS Java client is also very useful |
|
for testing purposes, for example to simulate a large number of concurrent users. |
|
|
|
The SockJS Java client supports the "websocket", "xhr-streaming", and "xhr-polling" |
|
transports. The remaining ones only make sense for use in a browser. |
|
|
|
The `WebSocketTransport` can be configured with: |
|
|
|
* `StandardWebSocketClient` in a JSR-356 runtime |
|
* `JettyWebSocketClient` using the Jetty 9+ native WebSocket API |
|
* Any implementation of Spring's `WebSocketClient` |
|
|
|
An `XhrTransport` by definition supports both "xhr-streaming" and "xhr-polling" since |
|
from a client perspective there is no difference other than in the URL used to connect |
|
to the server. At present there are two implementations: |
|
|
|
* `RestTemplateXhrTransport` uses Spring's `RestTemplate` for HTTP requests. |
|
* `JettyXhrTransport` uses Jetty's `HttpClient` for HTTP requests. |
|
|
|
The example below shows how to create a SockJS client and connect to a SockJS endpoint: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
List<Transport> transports = new ArrayList<>(2); |
|
transports.add(new WebSocketTransport(StandardWebSocketClient())); |
|
transports.add(new RestTemplateXhrTransport()); |
|
|
|
SockJsClient sockJsClient = new SockJsClient(transports); |
|
sockJsClient.doHandshake(new MyWebSocketHandler(), "ws://example.com:8080/sockjs"); |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
SockJS uses JSON formatted arrays for messages. By default Jackson 2 is used and needs |
|
to be on the classpath. Alternatively you can configure a custom implementation of |
|
`SockJsMessageCodec` and configure it on the `SockJsClient`. |
|
==== |
|
|
|
To use the SockJsClient for simulating a large number of concurrent users you will |
|
need to configure the underlying HTTP client (for XHR transports) to allow a sufficient |
|
number of connections and threads. For example with Jetty: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
HttpClient jettyHttpClient = new HttpClient(); |
|
jettyHttpClient.setMaxConnectionsPerDestination(1000); |
|
jettyHttpClient.setExecutor(new QueuedThreadPool(1000)); |
|
---- |
|
|
|
Consider also customizing these server-side SockJS related properties (see Javadoc for details): |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
public class WebSocketConfig extends WebSocketMessageBrokerConfigurationSupport { |
|
|
|
@Override |
|
public void registerStompEndpoints(StompEndpointRegistry registry) { |
|
registry.addEndpoint("/sockjs").withSockJS() |
|
.setStreamBytesLimit(512 * 1024) |
|
.setHttpMessageCacheSize(1000) |
|
.setDisconnectDelay(30 * 1000); |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
|
|
|
|
|
|
[[websocket-stomp]] |
|
=== STOMP Over WebSocket Messaging Architecture |
|
The WebSocket protocol defines two main types of messages -- text and binary -- |
|
but leaves their content undefined. Instead it's expected that the client and |
|
server may agree on using a sub-protocol, i.e. a higher-level protocol that defines |
|
the message content. Using a sub-protocol is optional but either way the client |
|
and server both need to understand how to interpret messages. |
|
|
|
|
|
|
|
[[websocket-stomp-overview]] |
|
==== Overview of STOMP |
|
http://stomp.github.io/stomp-specification-1.2.html#Abstract[STOMP] is a simple |
|
text-oriented messaging protocol that was originally created for scripting languages |
|
(such as Ruby, Python, and Perl) to connect to enterprise message brokers. It is |
|
designed to address a subset of commonly used patterns in messaging protocols. STOMP |
|
can be used over any reliable 2-way streaming network protocol such as TCP and WebSocket. |
|
|
|
STOMP is a frame based protocol with frames modeled on HTTP. This is the |
|
structure of a frame: |
|
|
|
---- |
|
COMMAND |
|
header1:value1 |
|
header2:value2 |
|
|
|
Body^@ |
|
---- |
|
|
|
For example, a client can use the +SEND+ command to send a message or the |
|
+SUBSCRIBE+ command to express interest in receiving messages. Both of these commands |
|
require a +"destination"+ header that indicates where to send a message, or likewise |
|
what to subscribe to. |
|
|
|
Here is an example of a client sending a request to buy stock shares: |
|
|
|
---- |
|
SEND |
|
destination:/queue/trade |
|
content-type:application/json |
|
content-length:44 |
|
|
|
{"action":"BUY","ticker":"MMM","shares",44}^@ |
|
---- |
|
|
|
Here is an example of a client subscribing to receive stock quotes: |
|
---- |
|
SUBSCRIBE |
|
id:sub-1 |
|
destination:/topic/price.stock.* |
|
|
|
^@ |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
The meaning of a destination is intentionally left opaque in the STOMP spec. It can |
|
be any string, and it's entirely up to STOMP servers to define the semantics and |
|
the syntax of the destinations that they support. It is very common, however, for |
|
destinations to be path-like strings where `"/topic/.."` implies publish-subscribe |
|
(__one-to-many__) and `"/queue/"` implies point-to-point (__one-to-one__) message |
|
exchanges. |
|
==== |
|
|
|
STOMP servers can use the +MESSAGE+ command to broadcast messages to all subscribers. |
|
Here is an example of a server sending a stock quote to a subscribed client: |
|
|
|
---- |
|
MESSAGE |
|
message-id:nxahklf6-1 |
|
subscription:sub-1 |
|
destination:/topic/price.stock.MMM |
|
|
|
{"ticker":"MMM","price":129.45}^@ |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
It is important to know that a server cannot send unsolicited messages. All messages |
|
from a server must be in response to a specific client subscription, and the |
|
+"subscription-id"+ header of the server message must match the +"id"+ header of the |
|
client subscription. |
|
==== |
|
|
|
The above overview is intended to provide the most basic understanding of the |
|
STOMP protocol. It is recommended to review the protocol |
|
http://stomp.github.io/stomp-specification-1.2.html[specification], which is |
|
easy to follow and manageable in terms of size. |
|
|
|
The following summarizes the benefits for an application of using STOMP over WebSocket: |
|
|
|
* Standard message format |
|
* Application-level protocol with support for common messaging patterns |
|
* Client-side support, e.g. https://github.com/jmesnil/stomp-websocket[stomp.js], https://github.com/cujojs/msgs[msgs.js] |
|
* The ability to interpret, route, and process messages on both the client and server-side |
|
* The option to plug in a message broker -- RabbitMQ, ActiveMQ, many others -- to broadcast messages (explained later) |
|
|
|
Most importantly the use of STOMP (vs plain WebSocket) enables the Spring Framework |
|
to provide a programming model for application-level use in the same way that |
|
Spring MVC provides a programming model based on HTTP. |
|
|
|
|
|
|
|
[[websocket-stomp-enable]] |
|
==== Enable STOMP over WebSocket |
|
The Spring Framework provides support for using STOMP over WebSocket through |
|
the +spring-messaging+ and +spring-websocket+ modules. It's easy to enable it. |
|
|
|
Here is an example of configuring a STOMP WebSocket endpoint with SockJS fallback |
|
options. The endpoint is available for clients to connect to a URL path `/app/portfolio`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; |
|
import org.springframework.web.socket.config.annotation.StompEndpointRegistry; |
|
|
|
@Configuration |
|
@EnableWebSocketMessageBroker |
|
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { |
|
|
|
@Override |
|
public void configureMessageBroker(MessageBrokerRegistry config) { |
|
config.setApplicationDestinationPrefixes("/app"); |
|
config.enableSimpleBroker("/queue", "/topic"); |
|
} |
|
|
|
@Override |
|
public void registerStompEndpoints(StompEndpointRegistry registry) { |
|
registry.addEndpoint("/portfolio").withSockJS(); |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
XML configuration equivalent: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes,attributes"] |
|
---- |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:websocket="http://www.springframework.org/schema/websocket" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/websocket |
|
http://www.springframework.org/schema/websocket/spring-websocket.xsd"> |
|
|
|
<websocket:message-broker application-destination-prefix="/app"> |
|
<websocket:stomp-endpoint path="/portfolio"> |
|
<websocket:sockjs/> |
|
</websocket:stomp-endpoint> |
|
<websocket:simple-broker prefix="/queue, /topic"/> |
|
... |
|
</websocket:message-broker> |
|
|
|
</beans> |
|
---- |
|
|
|
On the browser side, a client might connect as follows using |
|
https://github.com/jmesnil/stomp-websocket[stomp.js] and the |
|
https://github.com/sockjs/sockjs-client[sockjs-client]: |
|
|
|
[source,javascript,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
var socket = new SockJS("/spring-websocket-portfolio/portfolio"); |
|
var stompClient = Stomp.over(socket); |
|
|
|
stompClient.connect({}, function(frame) { |
|
} |
|
---- |
|
|
|
Or if connecting via WebSocket (without SockJS): |
|
|
|
[source,javascript,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
var socket = new WebSocket("/spring-websocket-portfolio/portfolio"); |
|
var stompClient = Stomp.over(socket); |
|
|
|
stompClient.connect({}, function(frame) { |
|
} |
|
---- |
|
|
|
Note that the `stompClient` above does not need to specify `login` and `passcode` headers. |
|
Even if it did, they would be ignored, or rather overridden, on the server side. See the |
|
sections <<websocket-stomp-handle-broker-relay-configure>> and |
|
<<websocket-stomp-authentication>> for more information on authentication. |
|
|
|
|
|
[[websocket-stomp-message-flow]] |
|
==== Flow of Messages |
|
|
|
When a STOMP endpoint is configured, the Spring application acts as the STOMP broker |
|
to connected clients. It handles incoming messages and sends messages back. |
|
This section provides a big picture overview of how messages flow inside the application. |
|
|
|
The `spring-messaging` module contains a number of abstractions that originated in the |
|
https://spring.io/spring-integration[Spring Integration] project and are intended |
|
for use as building blocks in messaging applications: |
|
|
|
* {javadoc-baseurl}/org/springframework/messaging/Message.html[Message] -- |
|
a message with headers and a payload. |
|
* {javadoc-baseurl}/org/springframework/messaging/MessageHandler.html[MessageHandler] -- |
|
a contract for handling a message. |
|
* {javadoc-baseurl}/org/springframework/messaging/MessageChannel.html[MessageChannel] -- |
|
a contract for sending a message enabling loose coupling between senders and receivers. |
|
* {javadoc-baseurl}/org/springframework/messaging/SubscribableChannel.html[SubscribableChannel] -- |
|
extends `MessageChannel` and sends messages to registered `MessageHandler` subscribers. |
|
* {javadoc-baseurl}/org/springframework/messaging/support/ExecutorSubscribableChannel.html[ExecutorSubscribableChannel] -- |
|
a concrete implementation of `SubscribableChannel` that can deliver messages |
|
asynchronously via a thread pool. |
|
|
|
The provided STOMP over WebSocket config, both Java and XML, uses the above to |
|
assemble a concrete message flow including the following 3 channels: |
|
|
|
* `"clientInboundChannel"` -- for messages from WebSocket clients. Every incoming |
|
WebSocket message carrying a STOMP frame is sent through this channel. |
|
* `"clientOutboundChannel"` -- for messages to WebSocket clients. Every outgoing |
|
STOMP message from the broker is sent through this channel before getting sent |
|
to a client's WebSocket session. |
|
* `"brokerChannel"` -- for messages to the broker from within the application. |
|
Every message sent from the application to the broker passes through this channel. |
|
|
|
Messages on the `"clientInboundChannel"` can flow to annotated |
|
methods for application handling (e.g. a stock trade execution request) or can |
|
be forwarded to the broker (e.g. client subscribing for stock quotes). |
|
The STOMP destination is used for simple prefix-based routing. For example |
|
the "/app" prefix could route messages to annotated methods while the "/topic" |
|
and "/queue" prefixes could route messages to the broker. |
|
|
|
When a message-handling annotated method has a return type, its return |
|
value is sent as the payload of a Spring `Message` to the `"brokerChannel"`. |
|
The broker in turn broadcasts the message to clients. Sending a message |
|
to a destination can also be done from anywhere in the application with |
|
the help of a messaging template. For example, an HTTP POST handling method |
|
can broadcast a message to connected clients, or a service component may |
|
periodically broadcast stock quotes. |
|
|
|
Below is a simple example to illustrate the flow of messages: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebSocketMessageBroker |
|
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { |
|
|
|
@Override |
|
public void registerStompEndpoints(StompEndpointRegistry registry) { |
|
registry.addEndpoint("/portfolio"); |
|
} |
|
|
|
@Override |
|
public void configureMessageBroker(MessageBrokerRegistry registry) { |
|
registry.setApplicationDestinationPrefixes("/app"); |
|
registry.enableSimpleBroker("/topic"); |
|
} |
|
|
|
} |
|
|
|
@Controller |
|
public class GreetingController { |
|
|
|
@MessageMapping("/greeting") { |
|
public String handle(String greeting) { |
|
return "[" + getTimestamp() + ": " + greeting; |
|
} |
|
|
|
} |
|
|
|
---- |
|
|
|
The following explains the message flow for the above example: |
|
|
|
* WebSocket clients connect to the WebSocket endpoint at "/portfolio". |
|
* Subscriptions to "/topic/greeting" pass through the "clientInboundChannel" |
|
and are forwarded to the broker. |
|
* Greetings sent to "/app/greeting" pass through the "clientInboundChannel" |
|
and are forwarded to the `GreetingController`. The controller adds the current |
|
time, and the return value is passed through the "brokerChannel" as a message |
|
to "/topic/greeting" (destination is selected based on a convention but can be |
|
overridden via `@SendTo`). |
|
* The broker in turn broadcasts messages to subscribers, and they pass through |
|
the `"clientOutboundChannel"`. |
|
|
|
The next section provides more details on annotated methods including the |
|
kinds of arguments and return values supported. |
|
|
|
|
|
|
|
[[websocket-stomp-handle-annotations]] |
|
==== Annotation Message Handling |
|
|
|
The `@MessageMapping` annotation is supported on methods of `@Controller` classes. |
|
It can be used for mapping methods to message destinations and can also be combined |
|
with the type-level `@MessageMapping` for expressing shared mappings across all |
|
annotated methods within a controller. |
|
|
|
By default destination mappings are treated as Ant-style, slash-separated, path |
|
patterns, e.g. "/foo*", "/foo/**". etc. They can also contain template variables, |
|
e.g. "/foo/{id}" that can then be referenced via `@DestinationVariable`-annotated |
|
method arguments. |
|
|
|
[NOTE] |
|
==== |
|
Applications can also use dot-separated destinations (vs slash). |
|
See <<websocket-stomp-destination-separator>>. |
|
==== |
|
|
|
The following method arguments are supported for `@MessageMapping` methods: |
|
|
|
* `Message` method argument to get access to the complete message being processed. |
|
* `@Payload`-annotated argument for access to the payload of a message, converted with |
|
a `org.springframework.messaging.converter.MessageConverter`. |
|
The presence of the annotation is not required since it is assumed by default. |
|
Payload method arguments annotated with validation annotations (like `@Validated`) will |
|
be subject to JSR-303 validation. |
|
* `@Header`-annotated arguments for access to a specific header value along with |
|
type conversion using an `org.springframework.core.convert.converter.Converter` |
|
if necessary. |
|
* `@Headers`-annotated method argument that must also be assignable to `java.util.Map` |
|
for access to all headers in the message. |
|
* `MessageHeaders` method argument for getting access to a map of all headers. |
|
* `MessageHeaderAccessor`, `SimpMessageHeaderAccessor`, or `StompHeaderAccessor` |
|
for access to headers via typed accessor methods. |
|
* `@DestinationVariable`-annotated arguments for access to template |
|
variables extracted from the message destination. Values will be converted to |
|
the declared method argument type as necessary. |
|
* `java.security.Principal` method arguments reflecting the user logged in at |
|
the time of the WebSocket HTTP handshake. |
|
|
|
The return value from an `@MessageMapping` method is converted with a |
|
`org.springframework.messaging.converter.MessageConverter` and used as the body |
|
of a new message that is then sent, by default, to the `"brokerChannel"` with |
|
the same destination as the client message but using the prefix `"/topic"` by |
|
default. An `@SendTo` message level annotation can be used to specify any |
|
other destination instead. |
|
|
|
An `@SubscribeMapping` annotation can also be used to map subscription requests |
|
to `@Controller` methods. It is supported on the method level, but can also be |
|
combined with a type level `@MessageMapping` annotation that expresses shared |
|
mappings across all message handling methods within the same controller. |
|
|
|
By default the return value from an `@SubscribeMapping` method is sent as a |
|
message directly back to the connected client and does not pass through the |
|
broker. This is useful for implementing request-reply message interactions; for |
|
example, to fetch application data when the application UI is being initialized. |
|
Or alternatively an `@SubscribeMapping` method can be annotated with `@SendTo` |
|
in which case the resulting message is sent to the `"brokerChannel"` using |
|
the specified target destination. |
|
|
|
|
|
[[websocket-stomp-handle-send]] |
|
==== Sending Messages |
|
|
|
What if you want to send messages to connected clients from any part of the |
|
application? Any application component can send messages to the `"brokerChannel"`. |
|
The easiest way to do that is to have a `SimpMessagingTemplate` injected, and |
|
use it to send messages. Typically it should be easy to have it injected by |
|
type, for example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
public class GreetingController { |
|
|
|
private SimpMessagingTemplate template; |
|
|
|
@Autowired |
|
public GreetingController(SimpMessagingTemplate template) { |
|
this.template = template; |
|
} |
|
|
|
@RequestMapping(value="/greetings", method=POST) |
|
public void greet(String greeting) { |
|
String text = "[" + getTimestamp() + "]:" + greeting; |
|
this.template.convertAndSend("/topic/greetings", text); |
|
} |
|
|
|
} |
|
---- |
|
|
|
But it can also be qualified by its name "brokerMessagingTemplate" if another |
|
bean of the same type exists. |
|
|
|
|
|
[[websocket-stomp-handle-simple-broker]] |
|
==== Simple Broker |
|
|
|
The built-in, simple message broker handles subscription requests from clients, |
|
stores them in memory, and broadcasts messages to connected clients with matching |
|
destinations. The broker supports path-like destinations, including subscriptions |
|
to Ant-style destination patterns. |
|
|
|
[NOTE] |
|
==== |
|
Applications can also use dot-separated destinations (vs slash). |
|
See <<websocket-stomp-destination-separator>>. |
|
==== |
|
|
|
|
|
|
|
|
|
[[websocket-stomp-handle-broker-relay]] |
|
==== Full-Featured Broker |
|
|
|
The simple broker is great for getting started but supports only a subset of |
|
STOMP commands (e.g. no acks, receipts, etc.), relies on a simple message |
|
sending loop, and is not suitable for clustering. As an alternative, applications |
|
can upgrade to using a full-featured message broker. |
|
|
|
Check the STOMP documentation for your message broker of choice (e.g. |
|
http://www.rabbitmq.com/stomp.html[RabbitMQ], |
|
http://activemq.apache.org/stomp.html[ActiveMQ], etc.), install the broker, |
|
and run it with STOMP support enabled. Then enable the STOMP broker relay in the |
|
Spring configuration instead of the simple broker. |
|
|
|
Below is example configuration that enables a full-featured broker: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebSocketMessageBroker |
|
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { |
|
|
|
@Override |
|
public void registerStompEndpoints(StompEndpointRegistry registry) { |
|
registry.addEndpoint("/portfolio").withSockJS(); |
|
} |
|
|
|
@Override |
|
public void configureMessageBroker(MessageBrokerRegistry registry) { |
|
registry.enableStompBrokerRelay("/topic", "/queue"); |
|
registry.setApplicationDestinationPrefixes("/app"); |
|
} |
|
|
|
} |
|
---- |
|
|
|
XML configuration equivalent: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes,attributes"] |
|
---- |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:websocket="http://www.springframework.org/schema/websocket" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/websocket |
|
http://www.springframework.org/schema/websocket/spring-websocket.xsd"> |
|
|
|
<websocket:message-broker application-destination-prefix="/app"> |
|
<websocket:stomp-endpoint path="/portfolio" /> |
|
<websocket:sockjs/> |
|
</websocket:stomp-endpoint> |
|
<websocket:stomp-broker-relay prefix="/topic,/queue" /> |
|
</websocket:message-broker> |
|
|
|
</beans> |
|
---- |
|
|
|
The "STOMP broker relay" in the above configuration is a Spring |
|
http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/messaging/MessageHandler.html[MessageHandler] |
|
that handles messages by forwarding them to an external message broker. |
|
To do so it establishes TCP connections to the broker, forwards all |
|
messages to it, and then forwards all messages received |
|
from the broker to clients through their WebSocket sessions. Essentially |
|
it acts as a "relay" that forwards messages in both directions. |
|
|
|
[NOTE] |
|
==== |
|
Please add a dependency on `org.projectreactor:reactor-net` for TCP connection management. |
|
==== |
|
|
|
Furthermore, application components (e.g. HTTP request handling methods, |
|
business services, etc.) can also send messages to the broker relay, as described |
|
in <<websocket-stomp-handle-send>>, in order to broadcast messages to |
|
subscribed WebSocket clients. |
|
|
|
In effect, the broker relay enables robust and scalable message broadcasting. |
|
|
|
[[websocket-stomp-handle-broker-relay-configure]] |
|
==== Connections To Full-Featured Broker |
|
|
|
A STOMP broker relay maintains a single "system" TCP connection to the broker. |
|
This connection is used for messages originating from the server-side application |
|
only, not for receiving messages. You can configure the STOMP credentials |
|
for this connection, i.e. the STOMP frame `login` and `passcode` headers. This |
|
is exposed in both the XML namespace and the Java config as the |
|
++systemLogin++/++systemPasscode++ properties with default values ++guest++/++guest++. |
|
|
|
The STOMP broker relay also creates a separate TCP connection for every connected |
|
WebSocket client. You can configure the STOMP credentials to use for all TCP |
|
connections created on behalf of clients. This is exposed in both the XML namespace |
|
and the Java config as the ++clientLogin++/++clientPasscode++ properties with default |
|
values ++guest++/++guest++. |
|
|
|
[NOTE] |
|
==== |
|
The STOMP broker relay always sets the `login` and `passcode` headers on every `CONNECT` |
|
frame that it forwards to the broker on behalf of clients. Therefore WebSocket clients |
|
need not set those headers; they will be ignored. As the following section explains, |
|
instead WebSocket clients should rely on HTTP authentication to protect the WebSocket |
|
endpoint and establish the client identity. |
|
==== |
|
|
|
The STOMP broker relay also sends and receives heartbeats to and from the message |
|
broker over the "system" TCP connection. You can configure the intervals for sending |
|
and receiving heartbeats (10 seconds each by default). If connectivity to the broker |
|
is lost, the broker relay will continue to try to reconnect, every 5 seconds, |
|
until it succeeds. |
|
|
|
[NOTE] |
|
==== |
|
A Spring bean can implement `ApplicationListener<BrokerAvailabilityEvent>` in order |
|
to receive notifications when the "system" connection to the broker is lost and |
|
re-established. For example a Stock Quote service broadcasting stock quotes can |
|
stop trying to send messages when there is no active "system" connection. |
|
==== |
|
|
|
The STOMP broker relay can also be configured with a `virtualHost` property. |
|
The value of this property will be set as the `host` header of every `CONNECT` frame |
|
and may be useful for example in a cloud environment where the actual host to which |
|
the TCP connection is established is different from the host providing the |
|
cloud-based STOMP service. |
|
|
|
[[websocket-stomp-destination-separator]] |
|
==== Using Dot as Separator in `@MessageMapping` Destinations |
|
|
|
Although slash-separated path patterns are familiar to web developers, in messaging |
|
it is common to use a "." as the separator, for example in the names of topics, queues, |
|
exchanges, etc. Applications can also switch to using "." (dot) instead of "/" (slash) |
|
as the separator in `@MessageMapping` mappings by configuring a custom `AntPathMatcher`. |
|
|
|
In Java config: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebSocketMessageBroker |
|
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { |
|
|
|
// ... |
|
|
|
@Override |
|
public void configureMessageBroker(MessageBrokerRegistry registry) { |
|
registry.enableStompBrokerRelay("/queue/", "/topic/"); |
|
registry.setApplicationDestinationPrefixes("/app"); |
|
registry.setPathMatcher(new AntPathMatcher(".")); |
|
} |
|
|
|
} |
|
---- |
|
|
|
In XML config: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes,attributes"] |
|
---- |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:websocket="http://www.springframework.org/schema/websocket" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/websocket |
|
http://www.springframework.org/schema/websocket/spring-websocket.xsd"> |
|
|
|
<websocket:message-broker application-destination-prefix="/app" path-matcher="pathMatcher"> |
|
<websocket:stomp-endpoint path="/stomp" /> |
|
<websocket:simple-broker prefix="/topic, /queue"/> |
|
</websocket:message-broker> |
|
|
|
<bean id="pathMatcher" class="org.springframework.util.AntPathMatcher"> |
|
<constructor-arg index="0" value="." /> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
And below is a simple example to illustrate a controller with "." separator: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
@MessageMapping("foo") |
|
public class FooController { |
|
|
|
@MessageMapping("bar.{baz}") |
|
public void handleBaz(@DestinationVariable String baz) { |
|
} |
|
|
|
} |
|
---- |
|
|
|
If the application prefix is set to "/app" then the foo method is effectively mapped to "/app/foo.bar.{baz}". |
|
|
|
|
|
|
|
|
|
[[websocket-stomp-authentication]] |
|
==== Authentication |
|
|
|
In a WebSocket-style application it is often useful to know who sent a message. |
|
Therefore some form of authentication is needed to establish the user identity |
|
and associate it with the current session. |
|
|
|
Existing Web applications already use HTTP based authentication. |
|
For example Spring Security can secure the HTTP URLs of the application as usual. |
|
Since a WebSocket session begins with an HTTP handshake, that means URLs mapped |
|
to STOMP/WebSocket are already automatically protected and require authentication. |
|
Moreover the page that opens the WebSocket connection is itself likely protected |
|
and so by the time of the actual handshake, the user should have been authenticated. |
|
|
|
When a WebSocket handshake is made and a new WebSocket session is created, |
|
Spring's WebSocket support automatically propagates the `java.security.Principal` |
|
from the HTTP request to the WebSocket session. After that every message flowing |
|
through the application on that WebSocket session is enriched with |
|
the user information. It's present in the message as a header. |
|
Controller methods can access the current user by adding a method argument of |
|
type `javax.security.Principal`. |
|
|
|
Note that even though the STOMP `CONNECT` frame has "login" and "passcode" headers |
|
that can be used for authentication, Spring's STOMP WebSocket support ignores them |
|
and currently expects users to have been authenticated already via HTTP. |
|
|
|
In some cases it may be useful to assign an identity to a WebSocket session even |
|
when the user has not been formally authenticated. For example, a mobile app might |
|
assign some identity to anonymous users, perhaps based on geographical location. |
|
The do that currently, an application can sub-class `DefaultHandshakeHandler` |
|
and override the `determineUser` method. The custom handshake handler can then |
|
be plugged in (see examples in <<websocket-server-deployment>>). |
|
|
|
|
|
|
|
[[websocket-stomp-user-destination]] |
|
==== User Destinations |
|
|
|
An application can send messages targeting a specific user, and Spring's STOMP support |
|
recognizes destinations prefixed with `"/user/"` for this purpose. |
|
For example, a client might subscribe to the destination `"/user/queue/position-updates"`. |
|
This destination will be handled by the `UserDestinationMessageHandler` and |
|
transformed into a destination unique to the user session, |
|
e.g. `"/queue/position-updates-user123"`. This provides the convenience of subscribing |
|
to a generically named destination while at the same time ensuring no collisions |
|
with other users subscribing to the same destination so that each user can receive |
|
unique stock position updates. |
|
|
|
On the sending side messages can be sent to a destination such as |
|
`"/user/{username}/queue/position-updates"`, which in turn will be translated |
|
by the `UserDestinationMessageHandler` into one or more destinations, one for each |
|
session associated with the user. This allows any component within the application to |
|
send messages targeting a specific user without necessarily knowing anything more |
|
than their name and the generic destination. This is also supported through an |
|
annotation as well as a messaging template. |
|
|
|
For example, a message-handling method can send messages to the user associated with |
|
the message being handled through the `@SendToUser` annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
public class PortfolioController { |
|
|
|
@MessageMapping("/trade") |
|
@SendToUser("/queue/position-updates") |
|
public TradeResult executeTrade(Trade trade, Principal principal) { |
|
// ... |
|
return tradeResult; |
|
} |
|
} |
|
---- |
|
|
|
If the user has more than one session, by default all of the sessions subscribed |
|
to the given destination are targeted. However sometimes, it may be necessary to |
|
target only the session that sent the message being handled. This can be done by |
|
setting the `broadcast` attribute to false, for example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
public class MyController { |
|
|
|
@MessageMapping("/action") |
|
public void handleAction() throws Exception{ |
|
// raise MyBusinessException here |
|
} |
|
|
|
@MessageExceptionHandler |
|
@SendToUser(value="/queue/errors", broadcast=false) |
|
public ApplicationError handleException(MyBusinessException exception) { |
|
// ... |
|
return appError; |
|
} |
|
} |
|
---- |
|
|
|
|
|
[NOTE] |
|
==== |
|
While user destinations generally imply an authenticated user, it isn't required |
|
strictly. A WebSocket session that is not associated with an authenticated user |
|
can subscribe to a user destination. In such cases the `@SendToUser` annotation |
|
will behave exactly the same as with `broadcast=false`, i.e. targeting only the |
|
session that sent the message being handled. |
|
==== |
|
|
|
It is also possible to send a message to user destinations from any application |
|
component by injecting the `SimpMessagingTemplate` created by the Java config or |
|
XML namespace, for example (the bean name is `"brokerMessagingTemplate"` if required |
|
for qualification with `@Qualifier`): |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Service |
|
public class TradeServiceImpl implements TradeService { |
|
|
|
private final SimpMessagingTemplate messagingTemplate; |
|
|
|
@Autowired |
|
public TradeServiceImpl(SimpMessagingTemplate messagingTemplate) { |
|
this.messagingTemplate = messagingTemplate; |
|
} |
|
|
|
// ... |
|
|
|
public void afterTradeExecuted(Trade trade) { |
|
this.messagingTemplate.convertAndSendToUser( |
|
trade.getUserName(), "/queue/position-updates", trade.getResult()); |
|
} |
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
When using user destinations with an external message broker, check the broker |
|
documentation on how to manage inactive queues, so that when the user session is |
|
over, all unique user queues are removed. For example, RabbitMQ creates auto-delete |
|
queues when destinations like `/exchange/amq.direct/position-updates` are used. |
|
So in that case the client could subscribe to `/user/exchange/amq.direct/position-updates`. |
|
Similarly, ActiveMQ has |
|
http://activemq.apache.org/delete-inactive-destinations.html[configuration options] |
|
for purging inactive destinations. |
|
==== |
|
|
|
|
|
|
|
|
|
[[websocket-stomp-appplication-context-events]] |
|
==== Listening To ApplicationContext Events and Intercepting Messages |
|
|
|
Several `ApplicationContext` events (listed below) are published and can be |
|
received by implementing Spring's `ApplicationListener` interface. |
|
|
|
* `BrokerAvailabilityEvent` -- indicates when the broker becomes available/unavailable. |
|
While the "simple" broker becomes available immediately on startup and remains so while |
|
the application is running, the STOMP "broker relay" may lose its connection |
|
to the full featured broker, for example if the broker is restarted. The broker relay |
|
has reconnect logic and will re-establish the "system" connection to the broker |
|
when it comes back, hence this event is published whenever the state changes from connected |
|
to disconnected and vice versa. Components using the `SimpMessagingTemplate` should |
|
subscribe to this event and avoid sending messages at times when the broker is not |
|
available. In any case they should be prepared to handle `MessageDeliveryException` |
|
when sending a message. |
|
* `SessionConnectEvent` -- published when a new STOMP CONNECT is received |
|
indicating the start of a new client session. The event contains the message representing the |
|
connect including the session id, user information (if any), and any custom headers the client |
|
may have sent. This is useful for tracking client sessions. Components subscribed |
|
to this event can wrap the contained message using `SimpMessageHeaderAccessor` or |
|
`StompMessageHeaderAccessor`. |
|
* `SessionConnectedEvent` -- published shortly after a `SessionConnectEvent` when the |
|
broker has sent a STOMP CONNECTED frame in response to the CONNECT. At this point the |
|
STOMP session can be considered fully established. |
|
* `SessionSubscribeEvent` -- published when a new STOMP SUBSCRIBE is received. |
|
* `SessionUnsubscribeEvent` -- published when a new STOMP UNSUBSCRIBE is received. |
|
* `SessionDisconnectEvent` -- published when a STOMP session ends. The DISCONNECT may |
|
have been sent from the client, or it may also be automatically generated when the |
|
WebSocket session is closed. In some cases this event may be published more than once |
|
per session. Components should be idempotent with regard to multiple disconnect events. |
|
|
|
[NOTE] |
|
==== |
|
When using a full-featured broker, the STOMP "broker relay" automatically reconnects the |
|
"system" connection in case the broker becomes temporarily unavailable. Client connections |
|
however are not automatically reconnected. Assuming heartbeats are enabled, the client |
|
will typically notice the broker is not responding within 10 seconds. Clients need to |
|
implement their own reconnect logic. |
|
==== |
|
|
|
Furthermore, an application can directly intercept every incoming and outgoing message by |
|
registering a `ChannelInterceptor` on the respective message channel. For example |
|
to intercept inbound messages: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebSocketMessageBroker |
|
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { |
|
|
|
@Override |
|
public void configureClientInboundChannel(ChannelRegistration registration) { |
|
registration.setInterceptors(new MyChannelInterceptor()); |
|
} |
|
} |
|
---- |
|
|
|
A custom `ChannelInterceptor` can extend the empty method base class |
|
`ChannelInterceptorAdapter` and use `StompHeaderAccessor` or `SimpMessageHeaderAccessor` |
|
to access information about the message. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MyChannelInterceptor extends ChannelInterceptorAdapter { |
|
|
|
@Override |
|
public Message<?> preSend(Message<?> message, MessageChannel channel) { |
|
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message); |
|
StompCommand command = accessor.getStompCommand(); |
|
// ... |
|
return message; |
|
} |
|
} |
|
---- |
|
|
|
|
|
|
|
[[websocket-stomp-websocket-scope]] |
|
==== WebSocket Scope |
|
|
|
Each WebSocket session has a map of attributes. The map is attached as a header to |
|
inbound client messages and may be accessed from a controller method, for example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
public class MyController { |
|
|
|
@MessageMapping("/action") |
|
public void handle(SimpMessageHeaderAccessor headerAccessor) { |
|
Map<String, Object> attrs = headerAccessor.getSessionAttributes(); |
|
// ... |
|
} |
|
} |
|
---- |
|
|
|
It is also possible to declare a Spring-managed bean in the `"websocket"` scope. |
|
WebSocket-scoped beans can be injected into controllers and any channel interceptors |
|
registered on the "clientInboundChannel". Those are typically singletons and live |
|
longer than any individual WebSocket session. Therefore you will need to use a |
|
scope proxy mode for WebSocket-scoped beans: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Component |
|
@Scope(value="websocket", proxyMode = ScopedProxyMode.TARGET_CLASS) |
|
public class MyBean { |
|
|
|
@PostConstruct |
|
public void init() { |
|
// Invoked after dependencies injected |
|
} |
|
|
|
// ... |
|
|
|
@PreDestroy |
|
public void destroy() { |
|
// Invoked when the WebSocket session ends |
|
} |
|
} |
|
|
|
@Controller |
|
public class MyController { |
|
|
|
private final MyBean myBean; |
|
|
|
@Autowired |
|
public MyController(MyBean myBean) { |
|
this.myBean = myBean; |
|
} |
|
|
|
@MessageMapping("/action") |
|
public void handle() { |
|
// this.myBean from the current WebSocket session |
|
} |
|
} |
|
---- |
|
|
|
As with any custom scope, Spring initializes a new `MyBean` instance the first |
|
time it is accessed from the controller and stores the instance in the WebSocket |
|
session attributes. The same instance is returned subsequently until the session |
|
ends. WebSocket-scoped beans will have all Spring lifecycle methods invoked as |
|
shown in the examples above. |
|
|
|
|
|
|
|
[[websocket-stomp-configuration-performance]] |
|
==== Configuration and Performance |
|
|
|
There is no silver bullet when it comes to performance. Many factors may |
|
affect it including the size of messages, the volume, whether application |
|
methods perform work that requires blocking, as well as external factors |
|
such as network speed and others. The goal of this section is to provide |
|
an overview of the available configuration options along with some thoughts |
|
on how to reason about scaling. |
|
|
|
In a messaging application messages are passed through channels for asynchronous |
|
executions backed by thread pools. Configuring such an application requires |
|
good knowledge of the channels and the flow of messages. Therefore it is |
|
recommended to review <<websocket-stomp-message-flow>>. |
|
|
|
The obvious place to start is to configure the thread pools backing the |
|
`"clientInboundChannel"` and the `"clientOutboundChannel"`. By default both |
|
are configured at twice the number of available processors. |
|
|
|
If the handling of messages in annotated methods is mainly CPU bound then the |
|
number of threads for the `"clientInboundChannel"` should remain close to the |
|
number of processors. If the work they do is more IO bound and requires blocking |
|
or waiting on a database or other external system then the thread pool size |
|
will need to be increased. |
|
|
|
[NOTE] |
|
==== |
|
`ThreadPoolExecutor` has 3 important properties. Those are the core and |
|
the max thread pool size as well as the capacity for the queue to store |
|
tasks for which there are no available threads. |
|
|
|
A common point of confusion is that configuring the core pool size (e.g. 10) |
|
and max pool size (e.g. 20) results in a thread pool with 10 to 20 threads. |
|
In fact if the capacity is left at its default value of Integer.MAX_VALUE |
|
then the thread pool will never increase beyond the core pool size since |
|
all additional tasks will be queued. |
|
|
|
Please review the Javadoc of `ThreadPoolExecutor` to learn how these |
|
properties work and understand the various queuing strategies. |
|
==== |
|
|
|
On the `"clientOutboundChannel"` side it is all about sending messages to WebSocket |
|
clients. If clients are on a fast network then the number of threads should |
|
remain close to the number of available processors. If they are slow or on |
|
low bandwidth they will take longer to consume messages and put a burden on the |
|
thread pool. Therefore increasing the thread pool size will be necessary. |
|
|
|
While the workload for the "clientInboundChannel" is possible to predict -- |
|
after all it is based on what the application does -- how to configure the |
|
"clientOutboundChannel" is harder as it is based on factors beyond |
|
the control of the application. For this reason there are two additional |
|
properties related to the sending of messages. Those are the `"sendTimeLimit"` |
|
and the `"sendBufferSizeLimit"`. Those are used to configure how long a |
|
send is allowed to take and how much data can be buffered when sending |
|
messages to a client. |
|
|
|
The general idea is that at any given time only a single thread may be used |
|
to send to a client. All additional messages meanwhile get buffered and you |
|
can use these properties to decide how long sending a message is allowed to |
|
take and how much data can be buffered in the mean time. Please review the |
|
Javadoc and documentation of the XML schema for this configuration for |
|
important additional details. |
|
|
|
Here is example configuration: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebSocketMessageBroker |
|
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { |
|
|
|
@Override |
|
public void configureWebSocketTransport(WebSocketTransportRegistration registration) { |
|
registration.setSendTimeLimit(15 * 1000).setSendBufferSizeLimit(512 * 1024); |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes,attributes"] |
|
---- |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:websocket="http://www.springframework.org/schema/websocket" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/websocket |
|
http://www.springframework.org/schema/websocket/spring-websocket.xsd"> |
|
|
|
<websocket:message-broker> |
|
<websocket:transport send-timeout="15000" send-buffer-size="524288" /> |
|
<!-- ... --> |
|
</websocket:message-broker> |
|
|
|
</beans> |
|
---- |
|
|
|
The WebSocket transport configuration shown above can also be used to configure the |
|
maximum allowed size for incoming STOMP messages. Although in theory a WebSocket |
|
message can be almost unlimited in size, in practice WebSocket servers impose |
|
limits -- for example, 8K on Tomcat and 64K on Jetty. For this reason STOMP clients |
|
such as stomp.js split larger STOMP messages at 16K boundaries and send them as |
|
multiple WebSocket messages thus requiring the server to buffer and re-assemble. |
|
|
|
Spring's STOMP over WebSocket support does this so applications can configure the |
|
maximum size for STOMP messages irrespective of WebSocket server specific message |
|
sizes. Do keep in mind that the WebSocket message size will be automatically |
|
adjusted if necessary to ensure they can carry 16K WebSocket messages at a |
|
minimum. |
|
|
|
Here is example configuration: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebSocketMessageBroker |
|
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { |
|
|
|
@Override |
|
public void configureWebSocketTransport(WebSocketTransportRegistration registration) { |
|
registration.setMessageSizeLimit(128 * 1024); |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes,attributes"] |
|
---- |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:websocket="http://www.springframework.org/schema/websocket" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/websocket |
|
http://www.springframework.org/schema/websocket/spring-websocket.xsd"> |
|
|
|
<websocket:message-broker> |
|
<websocket:transport message-size="131072" /> |
|
<!-- ... --> |
|
</websocket:message-broker> |
|
|
|
</beans> |
|
---- |
|
|
|
An important point about scaling is using multiple application instances. |
|
Currently it is not possible to do that with the simple broker. |
|
However when using a full-featured broker such as RabbitMQ, each application |
|
instance connects to the broker and messages broadcast from one application |
|
instance can be broadcast through the broker to WebSocket clients connected |
|
through any other application instances. |
|
|
|
|
|
|
|
[[websocket-stomp-stats]] |
|
==== Runtime Monitoring |
|
|
|
When using `@EnableWebSocketMessageBroker` or `<websocket:message-broker>` key |
|
infrastructure components automatically gather stats and counters that provide |
|
important insight into the internal state of the application. The configuration |
|
also declares a bean of type `WebSocketMessageBrokerStats` that gathers all |
|
available information in one place and by default logs it at `INFO` level once |
|
every 30 minutes. This bean can be exported to JMX through Spring's |
|
`MBeanExporter` for viewing at runtime, for example through JDK's `jconsole`. |
|
Below is a summary of the available information. |
|
|
|
Client WebSocket Sessions:: |
|
Current::: indicates how many client sessions there are |
|
currently with the count further broken down by WebSocket vs HTTP |
|
streaming and polling SockJS sessions. |
|
Total::: indicates how many total sessions have been established. |
|
Abnormally Closed::: |
|
Connect Failures:::: these are sessions that got established but were |
|
closed after not having received any messages within 60 seconds. This is |
|
usually an indication of proxy or network issues. |
|
Send Limit Exceeded:::: sessions closed after exceeding the configured send |
|
timeout or the send buffer limits which can occur with slow clients |
|
(see previous section). |
|
Transport Errors:::: sessions closed after a transport error such as |
|
failure to read or write to a WebSocket connection or |
|
HTTP request/response. |
|
STOMP Frames::: the total number of CONNECT, CONNECTED, and DISCONNECT frames |
|
processed indicating how many clients connected on the STOMP level. Note that |
|
the DISCONNECT count may be lower when sessions get closed abnormally or when |
|
clients close without sending a DISCONNECT frame. |
|
STOMP Broker Relay:: |
|
TCP Connections::: indicates how many TCP connections on behalf of client |
|
WebSocket sessions are established to the broker. This should be equal to the |
|
number of client WebSocket sessions + 1 additional shared "system" connection |
|
for sending messages from within the application. |
|
STOMP Frames::: the total number of CONNECT, CONNECTED, and DISCONNECT frames |
|
forwarded to or received from the broker on behalf of clients. Note that a |
|
DISCONNECT frame is sent to the broker regardless of how the client WebSocket |
|
session was closed. Therefore a lower DISCONNECT frame count is an indication |
|
that the broker is pro-actively closing connections, may be because of a |
|
heartbeat that didn't arrive in time, an invalid input frame, or other. |
|
Client Inbound Channel:: stats from thread pool backing the "clientInboundChannel" |
|
providing insight into the health of incoming message processing. Tasks queueing |
|
up here is an indication the application may be too slow to handle messages. |
|
If there I/O bound tasks (e.g. slow database query, HTTP request to 3rd party |
|
REST API, etc) consider increasing the thread pool size. |
|
Client Outbound Channel:: stats from the thread pool backing the "clientOutboundChannel" |
|
providing insight into the health of broadcasting messages to clients. Tasks |
|
queueing up here is an indication clients are too slow to consume messages. |
|
One way to address this is to increase the thread pool size to accommodate the |
|
number of concurrent slow clients expected. Another option is to reduce the |
|
send timeout and send buffer size limits (see the previous section). |
|
SockJS Task Scheduler:: stats from thread pool of the SockJS task scheduler which |
|
is used to send heartbeats. Note that when heartbeats are negotiated on the |
|
STOMP level the SockJS heartbeats are disabled. |
|
|
|
[[websocket-stomp-testing]] |
|
==== Testing Annotated Controller Methods |
|
|
|
There are two main approaches to testing applications using Spring's STOMP over |
|
WebSocket support. The first is to write server-side tests verifying the functionality |
|
of controllers and their annotated message handling methods. The second is to write |
|
full end-to-end tests that involve running a client and a server. |
|
|
|
The two approaches are not mutually exclusive. On the contrary each has a place |
|
in an overall test strategy. Server-side tests are more focused and easier to write |
|
and maintain. End-to-end integration tests on the other hand are more complete and |
|
test much more, but they're also more involved to write and maintain. |
|
|
|
The simplest form of server-side tests is to write controller unit tests. However |
|
this is not useful enough since much of what a controller does depends on its |
|
annotations. Pure unit tests simply can't test that. |
|
|
|
Ideally controllers under test should be invoked as they are at runtime, much like |
|
the approach to testing controllers handling HTTP requests using the Spring MVC Test |
|
framework. i.e. without running a Servlet container but relying on the Spring Framework |
|
to invoke the annotated controllers. Just like with Spring MVC Test here there are two |
|
two possible alternatives, either using a "context-based" or "standalone" setup: |
|
|
|
1. Load the actual Spring configuration with the help of the |
|
Spring TestContext framework, inject "clientInboundChannel" as a test field, and |
|
use it to send messages to be handled by controller methods. |
|
|
|
2. Manually set up the minimum Spring framework infrastructure required to invoke |
|
controllers (namely the `SimpAnnotationMethodMessageHandler`) and pass messages for |
|
controllers directly to it. |
|
|
|
Both of these setup scenarios are demonstrated in the |
|
https://github.com/rstoyanchev/spring-websocket-portfolio/tree/master/src/test/java/org/springframework/samples/portfolio/web[tests for the stock portfolio] |
|
sample application. |
|
|
|
The second approach is to create end-to-end integration tests. For that you will need |
|
to run a WebSocket server in embedded mode and connect to it as a WebSocket client |
|
sending WebSocket messages containing STOMP frames. |
|
The https://github.com/rstoyanchev/spring-websocket-portfolio/tree/master/src/test/java/org/springframework/samples/portfolio/web[tests for the stock portfolio] |
|
sample application also demonstrates this approach using Tomcat as the embedded |
|
WebSocket server and a simple STOMP client for test purposes. |
|
|
|
|
|
[[spring-integration]] |
|
= Integration |
|
[partintro] |
|
-- |
|
This part of the reference documentation covers the Spring Framework's integration with |
|
a number of Java EE (and related) technologies. |
|
|
|
* <<remoting>> |
|
* <<ejb>> |
|
* <<jms>> |
|
* <<jmx>> |
|
* <<cci>> |
|
* <<mail>> |
|
* <<scheduling>> |
|
* <<dynamic-language>> |
|
* <<cache>> |
|
-- |
|
|
|
|
|
|
|
|
|
|
|
[[remoting]] |
|
== Remoting and web services using Spring |
|
|
|
|
|
|
|
|
|
[[remoting-introduction]] |
|
=== Introduction |
|
Spring features integration classes for remoting support using various technologies. The |
|
remoting support eases the development of remote-enabled services, implemented by your |
|
usual (Spring) POJOs. Currently, Spring supports the following remoting technologies: |
|
|
|
* __Remote Method Invocation (RMI)__. Through the use of the `RmiProxyFactoryBean` and |
|
the `RmiServiceExporter` Spring supports both traditional RMI (with `java.rmi.Remote` |
|
interfaces and `java.rmi.RemoteException`) and transparent remoting via RMI invokers |
|
(with any Java interface). |
|
* __Spring's HTTP invoker__. Spring provides a special remoting strategy which allows |
|
for Java serialization via HTTP, supporting any Java interface (just like the RMI |
|
invoker). The corresponding support classes are `HttpInvokerProxyFactoryBean` and |
|
`HttpInvokerServiceExporter`. |
|
* __Hessian__. By using Spring's `HessianProxyFactoryBean` and the |
|
`HessianServiceExporter` you can transparently expose your services using the |
|
lightweight binary HTTP-based protocol provided by Caucho. |
|
* __Burlap__. Burlap is Caucho's XML-based alternative to Hessian. Spring provides |
|
support classes such as `BurlapProxyFactoryBean` and `BurlapServiceExporter`. |
|
* __JAX-WS__. Spring provides remoting support for web services via JAX-WS (the |
|
successor of JAX-RPC, as introduced in Java EE 5 and Java 6). |
|
* __JMS__. Remoting using JMS as the underlying protocol is supported via the |
|
`JmsInvokerServiceExporter` and `JmsInvokerProxyFactoryBean` classes. |
|
* __AMQP__. Remoting using AMQP as the underlying protocol is supported by the Spring |
|
AMQP project. |
|
|
|
While discussing the remoting capabilities of Spring, we'll use the following domain |
|
model and corresponding services: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class Account implements Serializable{ |
|
|
|
private String name; |
|
|
|
public String getName(){ |
|
return name; |
|
} |
|
|
|
public void setName(String name) { |
|
this.name = name; |
|
} |
|
|
|
} |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface AccountService { |
|
|
|
public void insertAccount(Account account); |
|
|
|
public List<Account> getAccounts(String name); |
|
|
|
} |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface RemoteAccountService extends Remote { |
|
|
|
public void insertAccount(Account account) throws RemoteException; |
|
|
|
public List<Account> getAccounts(String name) throws RemoteException; |
|
|
|
} |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// the implementation doing nothing at the moment |
|
public class AccountServiceImpl implements AccountService { |
|
|
|
public void insertAccount(Account acc) { |
|
// do something... |
|
} |
|
|
|
public List<Account> getAccounts(String name) { |
|
// do something... |
|
} |
|
|
|
} |
|
---- |
|
|
|
We will start exposing the service to a remote client by using RMI and talk a bit about |
|
the drawbacks of using RMI. We'll then continue to show an example using Hessian as the |
|
protocol. |
|
|
|
|
|
|
|
|
|
[[remoting-rmi]] |
|
=== Exposing services using RMI |
|
Using Spring's support for RMI, you can transparently expose your services through the |
|
RMI infrastructure. After having this set up, you basically have a configuration similar |
|
to remote EJBs, except for the fact that there is no standard support for security |
|
context propagation or remote transaction propagation. Spring does provide hooks for |
|
such additional invocation context when using the RMI invoker, so you can for example |
|
plug in security frameworks or custom security credentials here. |
|
|
|
|
|
|
|
[[remoting-rmi-server]] |
|
==== Exporting the service using the RmiServiceExporter |
|
|
|
Using the `RmiServiceExporter`, we can expose the interface of our AccountService object |
|
as RMI object. The interface can be accessed by using `RmiProxyFactoryBean`, or via |
|
plain RMI in case of a traditional RMI service. The `RmiServiceExporter` explicitly |
|
supports the exposing of any non-RMI services via RMI invokers. |
|
|
|
Of course, we first have to set up our service in the Spring container: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="accountService" class="example.AccountServiceImpl"> |
|
<!-- any additional properties, maybe a DAO? --> |
|
</bean> |
|
---- |
|
|
|
Next we'll have to expose our service using the `RmiServiceExporter`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="org.springframework.remoting.rmi.RmiServiceExporter"> |
|
<!-- does not necessarily have to be the same name as the bean to be exported --> |
|
<property name="serviceName" value="AccountService"/> |
|
<property name="service" ref="accountService"/> |
|
<property name="serviceInterface" value="example.AccountService"/> |
|
<!-- defaults to 1099 --> |
|
<property name="registryPort" value="1199"/> |
|
</bean> |
|
---- |
|
|
|
As you can see, we're overriding the port for the RMI registry. Often, your application |
|
server also maintains an RMI registry and it is wise to not interfere with that one. |
|
Furthermore, the service name is used to bind the service under. So right now, the |
|
service will be bound at `'rmi://HOST:1199/AccountService'`. We'll use the URL later on |
|
to link in the service at the client side. |
|
|
|
[NOTE] |
|
==== |
|
The `servicePort` property has been omitted (it defaults to 0). This means that an |
|
anonymous port will be used to communicate with the service. |
|
==== |
|
|
|
|
|
|
|
[[remoting-rmi-client]] |
|
==== Linking in the service at the client |
|
Our client is a simple object using the `AccountService` to manage accounts: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class SimpleObject { |
|
|
|
private AccountService accountService; |
|
|
|
public void setAccountService(AccountService accountService) { |
|
this.accountService = accountService; |
|
} |
|
|
|
// additional methods using the accountService |
|
|
|
} |
|
---- |
|
|
|
To link in the service on the client, we'll create a separate Spring container, |
|
containing the simple object and the service linking configuration bits: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="example.SimpleObject"> |
|
<property name="accountService" ref="accountService"/> |
|
</bean> |
|
|
|
<bean id="accountService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean"> |
|
<property name="serviceUrl" value="rmi://HOST:1199/AccountService"/> |
|
<property name="serviceInterface" value="example.AccountService"/> |
|
</bean> |
|
---- |
|
|
|
That's all we need to do to support the remote account service on the client. Spring |
|
will transparently create an invoker and remotely enable the account service through the |
|
`RmiServiceExporter`. At the client we're linking it in using the `RmiProxyFactoryBean`. |
|
|
|
|
|
|
|
|
|
[[remoting-caucho-protocols]] |
|
=== Using Hessian or Burlap to remotely call services via HTTP |
|
Hessian offers a binary HTTP-based remoting protocol. It is developed by Caucho and more |
|
information about Hessian itself can be found at http://www.caucho.com[]. |
|
|
|
|
|
|
|
[[remoting-caucho-protocols-hessian]] |
|
==== Wiring up the DispatcherServlet for Hessian and co. |
|
|
|
Hessian communicates via HTTP and does so using a custom servlet. Using Spring's |
|
`DispatcherServlet` principles, as known from Spring Web MVC usage, you can easily wire |
|
up such a servlet exposing your services. First we'll have to create a new servlet in |
|
your application (this is an excerpt from `'web.xml'`): |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<servlet> |
|
<servlet-name>remoting</servlet-name> |
|
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> |
|
<load-on-startup>1</load-on-startup> |
|
</servlet> |
|
|
|
<servlet-mapping> |
|
<servlet-name>remoting</servlet-name> |
|
<url-pattern>/remoting/*</url-pattern> |
|
</servlet-mapping> |
|
---- |
|
|
|
You're probably familiar with Spring's `DispatcherServlet` principles and if so, you |
|
know that now you'll have to create a Spring container configuration resource named |
|
`'remoting-servlet.xml'` (after the name of your servlet) in the `'WEB-INF'` directory. |
|
The application context will be used in the next section. |
|
|
|
Alternatively, consider the use of Spring's simpler `HttpRequestHandlerServlet`. This |
|
allows you to embed the remote exporter definitions in your root application context (by |
|
default in `'WEB-INF/applicationContext.xml'`), with individual servlet definitions |
|
pointing to specific exporter beans. Each servlet name needs to match the bean name of |
|
its target exporter in this case. |
|
|
|
|
|
|
|
[[remoting-caucho-protocols-hessian-server]] |
|
==== Exposing your beans by using the HessianServiceExporter |
|
|
|
In the newly created application context called `remoting-servlet.xml`, we'll create a |
|
`HessianServiceExporter` exporting your services: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="accountService" class="example.AccountServiceImpl"> |
|
<!-- any additional properties, maybe a DAO? --> |
|
</bean> |
|
|
|
<bean name="/AccountService" class="org.springframework.remoting.caucho.HessianServiceExporter"> |
|
<property name="service" ref="accountService"/> |
|
<property name="serviceInterface" value="example.AccountService"/> |
|
</bean> |
|
---- |
|
|
|
Now we're ready to link in the service at the client. No explicit handler mapping is |
|
specified, mapping request URLs onto services, so `BeanNameUrlHandlerMapping` will be |
|
used: Hence, the service will be exported at the URL indicated through its bean name |
|
within the containing `DispatcherServlet`'s mapping (as defined above): |
|
`'http://HOST:8080/remoting/AccountService'`. |
|
|
|
Alternatively, create a `HessianServiceExporter` in your root application context (e.g. |
|
in `'WEB-INF/applicationContext.xml'`): |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean name="accountExporter" class="org.springframework.remoting.caucho.HessianServiceExporter"> |
|
<property name="service" ref="accountService"/> |
|
<property name="serviceInterface" value="example.AccountService"/> |
|
</bean> |
|
---- |
|
|
|
In the latter case, define a corresponding servlet for this exporter in `'web.xml'`, |
|
with the same end result: The exporter getting mapped to the request path |
|
`/remoting/AccountService`. Note that the servlet name needs to match the bean name of |
|
the target exporter. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<servlet> |
|
<servlet-name>accountExporter</servlet-name> |
|
<servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class> |
|
</servlet> |
|
|
|
<servlet-mapping> |
|
<servlet-name>accountExporter</servlet-name> |
|
<url-pattern>/remoting/AccountService</url-pattern> |
|
</servlet-mapping> |
|
---- |
|
|
|
|
|
|
|
[[remoting-caucho-protocols-hessian-client]] |
|
==== Linking in the service on the client |
|
Using the `HessianProxyFactoryBean` we can link in the service at the client. The same |
|
principles apply as with the RMI example. We'll create a separate bean factory or |
|
application context and mention the following beans where the `SimpleObject` is using |
|
the `AccountService` to manage accounts: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="example.SimpleObject"> |
|
<property name="accountService" ref="accountService"/> |
|
</bean> |
|
|
|
<bean id="accountService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean"> |
|
<property name="serviceUrl" value="http://remotehost:8080/remoting/AccountService"/> |
|
<property name="serviceInterface" value="example.AccountService"/> |
|
</bean> |
|
---- |
|
|
|
|
|
|
|
[[remoting-caucho-protocols-burlap]] |
|
==== Using Burlap |
|
We won't discuss Burlap, the XML-based equivalent of Hessian, in detail here, since it |
|
is configured and set up in exactly the same way as the Hessian variant explained above. |
|
Just replace the word `Hessian` with `Burlap` and you're all set to go. |
|
|
|
|
|
|
|
[[remoting-caucho-protocols-security]] |
|
==== Applying HTTP basic authentication to a service exposed through Hessian or Burlap |
|
One of the advantages of Hessian and Burlap is that we can easily apply HTTP basic |
|
authentication, because both protocols are HTTP-based. Your normal HTTP server security |
|
mechanism can easily be applied through using the `web.xml` security features, for |
|
example. Usually, you don't use per-user security credentials here, but rather shared |
|
credentials defined at the `Hessian/BurlapProxyFactoryBean` level (similar to a JDBC |
|
`DataSource`). |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"> |
|
<property name="interceptors" ref="authorizationInterceptor"/> |
|
</bean> |
|
|
|
<bean id="authorizationInterceptor" |
|
class="org.springframework.web.servlet.handler.UserRoleAuthorizationInterceptor"> |
|
<property name="authorizedRoles" value="administrator,operator"/> |
|
</bean> |
|
---- |
|
|
|
This is an example where we explicitly mention the `BeanNameUrlHandlerMapping` and set |
|
an interceptor allowing only administrators and operators to call the beans mentioned in |
|
this application context. |
|
|
|
[NOTE] |
|
==== |
|
Of course, this example doesn't show a flexible kind of security infrastructure. For |
|
more options as far as security is concerned, have a look at the Spring Security project |
|
at http://projects.spring.io/spring-security/[]. |
|
==== |
|
|
|
|
|
|
|
|
|
[[remoting-httpinvoker]] |
|
=== Exposing services using HTTP invokers |
|
As opposed to Burlap and Hessian, which are both lightweight protocols using their own |
|
slim serialization mechanisms, Spring HTTP invokers use the standard Java serialization |
|
mechanism to expose services through HTTP. This has a huge advantage if your arguments |
|
and return types are complex types that cannot be serialized using the serialization |
|
mechanisms Hessian and Burlap use (refer to the next section for more considerations |
|
when choosing a remoting technology). |
|
|
|
Under the hood, Spring uses either the standard facilities provided by the JDK or |
|
Apache `HttpComponents` to perform HTTP calls. Use the latter if you need more |
|
advanced and easier-to-use functionality. Refer to |
|
http://hc.apache.org/httpcomponents-client-ga/[hc.apache.org/httpcomponents-client-ga/] |
|
for more information. |
|
|
|
|
|
|
|
[[remoting-httpinvoker-server]] |
|
==== Exposing the service object |
|
Setting up the HTTP invoker infrastructure for a service object resembles closely the |
|
way you would do the same using Hessian or Burlap. Just as Hessian support provides the |
|
`HessianServiceExporter`, Spring's HttpInvoker support provides the |
|
`org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter`. |
|
|
|
To expose the `AccountService` (mentioned above) within a Spring Web MVC |
|
`DispatcherServlet`, the following configuration needs to be in place in the |
|
dispatcher's application context: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean name="/AccountService" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter"> |
|
<property name="service" ref="accountService"/> |
|
<property name="serviceInterface" value="example.AccountService"/> |
|
</bean> |
|
---- |
|
|
|
Such an exporter definition will be exposed through the `DispatcherServlet`'s standard |
|
mapping facilities, as explained in the section on Hessian. |
|
|
|
Alternatively, create an `HttpInvokerServiceExporter` in your root application context |
|
(e.g. in `'WEB-INF/applicationContext.xml'`): |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean name="accountExporter" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter"> |
|
<property name="service" ref="accountService"/> |
|
<property name="serviceInterface" value="example.AccountService"/> |
|
</bean> |
|
---- |
|
|
|
In addition, define a corresponding servlet for this exporter in `'web.xml'`, with the |
|
servlet name matching the bean name of the target exporter: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<servlet> |
|
<servlet-name>accountExporter</servlet-name> |
|
<servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class> |
|
</servlet> |
|
|
|
<servlet-mapping> |
|
<servlet-name>accountExporter</servlet-name> |
|
<url-pattern>/remoting/AccountService</url-pattern> |
|
</servlet-mapping> |
|
---- |
|
|
|
If you are running outside of a servlet container and are using Oracle's Java 6, then you |
|
can use the built-in HTTP server implementation. You can configure the |
|
`SimpleHttpServerFactoryBean` together with a `SimpleHttpInvokerServiceExporter` as is |
|
shown in this example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean name="accountExporter" |
|
class="org.springframework.remoting.httpinvoker.SimpleHttpInvokerServiceExporter"> |
|
<property name="service" ref="accountService"/> |
|
<property name="serviceInterface" value="example.AccountService"/> |
|
</bean> |
|
|
|
<bean id="httpServer" |
|
class="org.springframework.remoting.support.SimpleHttpServerFactoryBean"> |
|
<property name="contexts"> |
|
<util:map> |
|
<entry key="/remoting/AccountService" value-ref="accountExporter"/> |
|
</util:map> |
|
</property> |
|
<property name="port" value="8080" /> |
|
</bean> |
|
---- |
|
|
|
|
|
|
|
[[remoting-httpinvoker-client]] |
|
==== Linking in the service at the client |
|
Again, linking in the service from the client much resembles the way you would do it |
|
when using Hessian or Burlap. Using a proxy, Spring will be able to translate your calls |
|
to HTTP POST requests to the URL pointing to the exported service. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="httpInvokerProxy" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean"> |
|
<property name="serviceUrl" value="http://remotehost:8080/remoting/AccountService"/> |
|
<property name="serviceInterface" value="example.AccountService"/> |
|
</bean> |
|
---- |
|
|
|
As mentioned before, you can choose what HTTP client you want to use. By default, the |
|
`HttpInvokerProxy` uses the JDK's HTTP functionality, but you can also use the Apache |
|
`HttpComponents` client by setting the `httpInvokerRequestExecutor` property: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<property name="httpInvokerRequestExecutor"> |
|
<bean class="org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor"/> |
|
</property> |
|
---- |
|
|
|
|
|
|
|
|
|
[[remoting-web-services]] |
|
=== Web services |
|
Spring provides full support for standard Java web services APIs: |
|
|
|
* Exposing web services using JAX-WS |
|
* Accessing web services using JAX-WS |
|
|
|
In addition to stock support for JAX-WS in Spring Core, the Spring portfolio also |
|
features http://www.springframework.org/spring-ws[Spring Web Services], a solution for |
|
contract-first, document-driven web services - highly recommended for building modern, |
|
future-proof web services. |
|
|
|
|
|
|
|
[[remoting-web-services-jaxws-export-servlet]] |
|
==== Exposing servlet-based web services using JAX-WS |
|
Spring provides a convenient base class for JAX-WS servlet endpoint implementations - |
|
`SpringBeanAutowiringSupport`. To expose our `AccountService` we extend Spring's |
|
`SpringBeanAutowiringSupport` class and implement our business logic here, usually |
|
delegating the call to the business layer. We'll simply use Spring's `@Autowired` |
|
annotation for expressing such dependencies on Spring-managed beans. |
|
|
|
[source,java,indent=0] |
|
---- |
|
/** |
|
* JAX-WS compliant AccountService implementation that simply delegates |
|
* to the AccountService implementation in the root web application context. |
|
* |
|
* This wrapper class is necessary because JAX-WS requires working with dedicated |
|
* endpoint classes. If an existing service needs to be exported, a wrapper that |
|
* extends SpringBeanAutowiringSupport for simple Spring bean autowiring (through |
|
* the @Autowired annotation) is the simplest JAX-WS compliant way. |
|
* |
|
* This is the class registered with the server-side JAX-WS implementation. |
|
* In the case of a Java EE 5 server, this would simply be defined as a servlet |
|
* in web.xml, with the server detecting that this is a JAX-WS endpoint and reacting |
|
* accordingly. The servlet name usually needs to match the specified WS service name. |
|
* |
|
* The web service engine manages the lifecycle of instances of this class. |
|
* Spring bean references will just be wired in here. |
|
*/ |
|
import org.springframework.web.context.support.SpringBeanAutowiringSupport; |
|
|
|
@WebService(serviceName="AccountService") |
|
public class AccountServiceEndpoint extends SpringBeanAutowiringSupport { |
|
|
|
@Autowired |
|
private AccountService biz; |
|
|
|
@WebMethod |
|
public void insertAccount(Account acc) { |
|
biz.insertAccount(acc); |
|
} |
|
|
|
@WebMethod |
|
public Account[] getAccounts(String name) { |
|
return biz.getAccounts(name); |
|
} |
|
|
|
} |
|
---- |
|
|
|
Our `AccountServletEndpoint` needs to run in the same web application as the Spring |
|
context to allow for access to Spring's facilities. This is the case by default in Java |
|
EE 5 environments, using the standard contract for JAX-WS servlet endpoint deployment. |
|
See Java EE 5 web service tutorials for details. |
|
|
|
|
|
|
|
[[remoting-web-services-jaxws-export-standalone]] |
|
==== Exporting standalone web services using JAX-WS |
|
The built-in JAX-WS provider that comes with Oracle's JDK 1.6 supports exposure of web |
|
services using the built-in HTTP server that's included in JDK 1.6 as well. Spring's |
|
`SimpleJaxWsServiceExporter` detects all `@WebService` annotated beans in the Spring |
|
application context, exporting them through the default JAX-WS server (the JDK 1.6 HTTP |
|
server). |
|
|
|
In this scenario, the endpoint instances are defined and managed as Spring beans |
|
themselves; they will be registered with the JAX-WS engine but their lifecycle will be |
|
up to the Spring application context. This means that Spring functionality like explicit |
|
dependency injection may be applied to the endpoint instances. Of course, |
|
annotation-driven injection through `@Autowired` will work as well. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="org.springframework.remoting.jaxws.SimpleJaxWsServiceExporter"> |
|
<property name="baseAddress" value="http://localhost:8080/"/> |
|
</bean> |
|
|
|
<bean id="accountServiceEndpoint" class="example.AccountServiceEndpoint"> |
|
... |
|
</bean> |
|
|
|
... |
|
---- |
|
|
|
The `AccountServiceEndpoint` may derive from Spring's `SpringBeanAutowiringSupport` but |
|
doesn't have to since the endpoint is a fully Spring-managed bean here. This means that |
|
the endpoint implementation may look like as follows, without any superclass declared - |
|
and Spring's `@Autowired` configuration annotation still being honored: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@WebService(serviceName="AccountService") |
|
public class AccountServiceEndpoint { |
|
|
|
@Autowired |
|
private AccountService biz; |
|
|
|
@WebMethod |
|
public void insertAccount(Account acc) { |
|
biz.insertAccount(acc); |
|
} |
|
|
|
@WebMethod |
|
public List<Account> getAccounts(String name) { |
|
return biz.getAccounts(name); |
|
} |
|
|
|
} |
|
---- |
|
|
|
|
|
|
|
[[remoting-web-services-jaxws-export-ri]] |
|
==== Exporting web services using the JAX-WS RI's Spring support |
|
Oracle's JAX-WS RI, developed as part of the GlassFish project, ships Spring support as |
|
part of its JAX-WS Commons project. This allows for defining JAX-WS endpoints as |
|
Spring-managed beans, similar to the standalone mode discussed in the previous section - |
|
but this time in a Servlet environment. __Note that this is not portable in a Java EE 5 |
|
environment; it is mainly intended for non-EE environments such as Tomcat, embedding the |
|
JAX-WS RI as part of the web application.__ |
|
|
|
The difference to the standard style of exporting servlet-based endpoints is that the |
|
lifecycle of the endpoint instances themselves will be managed by Spring here, and that |
|
there will be only one JAX-WS servlet defined in `web.xml`. With the standard Java EE 5 |
|
style (as illustrated above), you'll have one servlet definition per service endpoint, |
|
with each endpoint typically delegating to Spring beans (through the use of |
|
`@Autowired`, as shown above). |
|
|
|
Check out https://jax-ws-commons.java.net/spring/[https://jax-ws-commons.java.net/spring/] |
|
for details on setup and usage style. |
|
|
|
|
|
|
|
[[remoting-web-services-jaxws-access]] |
|
==== Accessing web services using JAX-WS |
|
Spring provides two factory beans to create JAX-WS web service proxies, namely |
|
`LocalJaxWsServiceFactoryBean` and `JaxWsPortProxyFactoryBean`. The former can only |
|
return a JAX-WS service class for us to work with. The latter is the full-fledged |
|
version that can return a proxy that implements our business service interface. In this |
|
example we use the latter to create a proxy for the `AccountService` endpoint (again): |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="accountWebService" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean"> |
|
<property name="serviceInterface" value="example.AccountService"/> |
|
<property name="wsdlDocumentUrl" value="http://localhost:8888/AccountServiceEndpoint?WSDL"/> |
|
<property name="namespaceUri" value="http://example/"/> |
|
<property name="serviceName" value="AccountService"/> |
|
<property name="portName" value="AccountServiceEndpointPort"/> |
|
</bean> |
|
---- |
|
|
|
Where `serviceInterface` is our business interface the clients will use. |
|
`wsdlDocumentUrl` is the URL for the WSDL file. Spring needs this a startup time to |
|
create the JAX-WS Service. `namespaceUri` corresponds to the targetNamespace in the |
|
.wsdl file. `serviceName` corresponds to the service name in the .wsdl file. `portName` |
|
corresponds to the port name in the .wsdl file. |
|
|
|
Accessing the web service is now very easy as we have a bean factory for it that will |
|
expose it as `AccountService` interface. We can wire this up in Spring: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="client" class="example.AccountClientImpl"> |
|
... |
|
<property name="service" ref="accountWebService"/> |
|
</bean> |
|
---- |
|
|
|
From the client code we can access the web service just as if it was a normal class: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class AccountClientImpl { |
|
|
|
private AccountService service; |
|
|
|
public void setService(AccountService service) { |
|
this.service = service; |
|
} |
|
|
|
public void foo() { |
|
service.insertAccount(...); |
|
} |
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
The above is slightly simplified in that JAX-WS requires endpoint interfaces |
|
and implementation classes to be annotated with `@WebService`, `@SOAPBinding` etc |
|
annotations. This means that you cannot (easily) use plain Java interfaces and |
|
implementation classes as JAX-WS endpoint artifacts; you need to annotate them |
|
accordingly first. Check the JAX-WS documentation for details on those requirements. |
|
==== |
|
|
|
|
|
|
|
|
|
[[remoting-jms]] |
|
=== JMS |
|
It is also possible to expose services transparently using JMS as the underlying |
|
communication protocol. The JMS remoting support in the Spring Framework is pretty basic |
|
- it sends and receives on the `same thread` and in the __same non-transactional__ |
|
`Session`, and as such throughput will be very implementation dependent. Note that these |
|
single-threaded and non-transactional constraints apply only to Spring's JMS |
|
__remoting__ support. See <<jms>> for information on Spring's rich support for JMS-based |
|
__messaging__. |
|
|
|
The following interface is used on both the server and the client side. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package com.foo; |
|
|
|
public interface CheckingAccountService { |
|
|
|
public void cancelAccount(Long accountId); |
|
|
|
} |
|
---- |
|
|
|
The following simple implementation of the above interface is used on the server-side. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package com.foo; |
|
|
|
public class SimpleCheckingAccountService implements CheckingAccountService { |
|
|
|
public void cancelAccount(Long accountId) { |
|
System.out.println("Cancelling account [" + accountId + "]"); |
|
} |
|
|
|
} |
|
---- |
|
|
|
This configuration file contains the JMS-infrastructure beans that are shared on both |
|
the client and server. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xsi:schemaLocation="http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd"> |
|
|
|
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"> |
|
<property name="brokerURL" value="tcp://ep-t43:61616"/> |
|
</bean> |
|
|
|
<bean id="queue" class="org.apache.activemq.command.ActiveMQQueue"> |
|
<constructor-arg value="mmm"/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
|
|
|
|
[[remoting-jms-server]] |
|
==== Server-side configuration |
|
On the server, you just need to expose the service object using the |
|
`JmsInvokerServiceExporter`. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xsi:schemaLocation="http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd"> |
|
|
|
<bean id="checkingAccountService" |
|
class="org.springframework.jms.remoting.JmsInvokerServiceExporter"> |
|
<property name="serviceInterface" value="com.foo.CheckingAccountService"/> |
|
<property name="service"> |
|
<bean class="com.foo.SimpleCheckingAccountService"/> |
|
</property> |
|
</bean> |
|
|
|
<bean class="org.springframework.jms.listener.SimpleMessageListenerContainer"> |
|
<property name="connectionFactory" ref="connectionFactory"/> |
|
<property name="destination" ref="queue"/> |
|
<property name="concurrentConsumers" value="3"/> |
|
<property name="messageListener" ref="checkingAccountService"/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package com.foo; |
|
|
|
import org.springframework.context.support.ClassPathXmlApplicationContext; |
|
|
|
public class Server { |
|
|
|
public static void main(String[] args) throws Exception { |
|
new ClassPathXmlApplicationContext(new String[]{"com/foo/server.xml", "com/foo/jms.xml"}); |
|
} |
|
|
|
} |
|
---- |
|
|
|
|
|
|
|
[[remoting-jms-client]] |
|
==== Client-side configuration |
|
The client merely needs to create a client-side proxy that will implement the agreed |
|
upon interface ( `CheckingAccountService`). The resulting object created off the back of |
|
the following bean definition can be injected into other client side objects, and the |
|
proxy will take care of forwarding the call to the server-side object via JMS. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xsi:schemaLocation="http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd"> |
|
|
|
<bean id="checkingAccountService" |
|
class="org.springframework.jms.remoting.JmsInvokerProxyFactoryBean"> |
|
<property name="serviceInterface" value="com.foo.CheckingAccountService"/> |
|
<property name="connectionFactory" ref="connectionFactory"/> |
|
<property name="queue" ref="queue"/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package com.foo; |
|
|
|
import org.springframework.context.ApplicationContext; |
|
import org.springframework.context.support.ClassPathXmlApplicationContext; |
|
|
|
public class Client { |
|
|
|
public static void main(String[] args) throws Exception { |
|
ApplicationContext ctx = new ClassPathXmlApplicationContext( |
|
new String[] {"com/foo/client.xml", "com/foo/jms.xml"}); |
|
CheckingAccountService service = (CheckingAccountService) ctx.getBean("checkingAccountService"); |
|
service.cancelAccount(new Long(10)); |
|
} |
|
|
|
} |
|
---- |
|
|
|
You may also wish to investigate the support provided by the |
|
http://lingo.codehaus.org/[Lingo] project, which (to quote the homepage blurb) "__... is |
|
a lightweight POJO based remoting and messaging library based on the Spring Framework's |
|
remoting libraries which extends it to support JMS.__" |
|
|
|
|
|
|
|
|
|
[[remoting-amqp]] |
|
=== AMQP |
|
Refer to the http://docs.spring.io/spring-amqp/reference/html/amqp.html#remoting[Spring |
|
AMQP Reference Document 'Spring Remoting with AMQP' section] for more information. |
|
|
|
|
|
|
|
|
|
[[remoting-autodection-remote-interfaces]] |
|
=== Auto-detection is not implemented for remote interfaces |
|
The main reason why auto-detection of implemented interfaces does not occur for remote |
|
interfaces is to avoid opening too many doors to remote callers. The target object might |
|
implement internal callback interfaces like `InitializingBean` or `DisposableBean` which |
|
one would not want to expose to callers. |
|
|
|
Offering a proxy with all interfaces implemented by the target usually does not matter |
|
in the local case. But when exporting a remote service, you should expose a specific |
|
service interface, with specific operations intended for remote usage. Besides internal |
|
callback interfaces, the target might implement multiple business interfaces, with just |
|
one of them intended for remote exposure. For these reasons, we __require__ such a |
|
service interface to be specified. |
|
|
|
This is a trade-off between configuration convenience and the risk of accidental |
|
exposure of internal methods. Always specifying a service interface is not too much |
|
effort, and puts you on the safe side regarding controlled exposure of specific methods. |
|
|
|
|
|
|
|
|
|
[[remoting-considerations]] |
|
=== Considerations when choosing a technology |
|
Each and every technology presented here has its drawbacks. You should carefully |
|
consider your needs, the services you are exposing and the objects you'll be sending |
|
over the wire when choosing a technology. |
|
|
|
When using RMI, it's not possible to access the objects through the HTTP protocol, |
|
unless you're tunneling the RMI traffic. RMI is a fairly heavy-weight protocol in that |
|
it supports full-object serialization which is important when using a complex data model |
|
that needs serialization over the wire. However, RMI-JRMP is tied to Java clients: It is |
|
a Java-to-Java remoting solution. |
|
|
|
Spring's HTTP invoker is a good choice if you need HTTP-based remoting but also rely on |
|
Java serialization. It shares the basic infrastructure with RMI invokers, just using |
|
HTTP as transport. Note that HTTP invokers are not only limited to Java-to-Java remoting |
|
but also to Spring on both the client and server side. (The latter also applies to |
|
Spring's RMI invoker for non-RMI interfaces.) |
|
|
|
Hessian and/or Burlap might provide significant value when operating in a heterogeneous |
|
environment, because they explicitly allow for non-Java clients. However, non-Java |
|
support is still limited. Known issues include the serialization of Hibernate objects in |
|
combination with lazily-initialized collections. If you have such a data model, consider |
|
using RMI or HTTP invokers instead of Hessian. |
|
|
|
JMS can be useful for providing clusters of services and allowing the JMS broker to take |
|
care of load balancing, discovery and auto-failover. By default: Java serialization is |
|
used when using JMS remoting but the JMS provider could use a different mechanism for |
|
the wire formatting, such as XStream to allow servers to be implemented in other |
|
technologies. |
|
|
|
Last but not least, EJB has an advantage over RMI in that it supports standard |
|
role-based authentication and authorization and remote transaction propagation. It is |
|
possible to get RMI invokers or HTTP invokers to support security context propagation as |
|
well, although this is not provided by core Spring: There are just appropriate hooks for |
|
plugging in third-party or custom solutions here. |
|
|
|
|
|
|
|
|
|
[[rest-client-access]] |
|
=== Accessing RESTful services on the Client |
|
The `RestTemplate` is the core class for client-side access to RESTful services. It is |
|
conceptually similar to other template classes in Spring, such as `JdbcTemplate` and |
|
`JmsTemplate` and other template classes found in other Spring portfolio projects. |
|
`RestTemplate`'s behavior is customized by providing callback methods and configuring |
|
the `HttpMessageConverter` used to marshal objects into the HTTP request body and to |
|
unmarshal any response back into an object. As it is common to use XML as a message |
|
format, Spring provides a `MarshallingHttpMessageConverter` that uses the Object-to-XML |
|
framework that is part of the `org.springframework.oxm` package. This gives you a wide |
|
range of choices of XML to Object mapping technologies to choose from. |
|
|
|
This section describes how to use the `RestTemplate` and its associated |
|
`HttpMessageConverters`. |
|
|
|
|
|
|
|
[[rest-resttemplate]] |
|
==== RestTemplate |
|
Invoking RESTful services in Java is typically done using a helper class such as Apache |
|
HttpComponents `HttpClient`. For common REST operations this approach is too low level as |
|
shown below. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
String uri = "http://example.com/hotels/1/bookings"; |
|
|
|
PostMethod post = new PostMethod(uri); |
|
String request = // create booking request content |
|
post.setRequestEntity(new StringRequestEntity(request)); |
|
|
|
httpClient.executeMethod(post); |
|
|
|
if (HttpStatus.SC_CREATED == post.getStatusCode()) { |
|
Header location = post.getRequestHeader("Location"); |
|
if (location != null) { |
|
System.out.println("Created new booking at :" + location.getValue()); |
|
} |
|
} |
|
---- |
|
|
|
RestTemplate provides higher level methods that correspond to each of the six main HTTP |
|
methods that make invoking many RESTful services a one-liner and enforce REST best |
|
practices. |
|
|
|
|
|
[NOTE] |
|
==== |
|
RestTemplate has an asynchronous counter-part: see <<rest-async-resttemplate>>. |
|
==== |
|
|
|
[[rest-overview-of-resttemplate-methods-tbl]] |
|
.Overview of RestTemplate methods |
|
[cols="1,3"] |
|
|=== |
|
| HTTP Method | RestTemplate Method |
|
|
|
| DELETE |
|
| {javadoc-baseurl}/org/springframework/web/client/RestTemplate.html#delete(String,%20Object...)[delete] |
|
|
|
| GET |
|
| {javadoc-baseurl}/org/springframework/web/client/RestTemplate.html#getForObject(String,%20Class,%20Object...)[getForObject] |
|
{javadoc-baseurl}/org/springframework/web/client/RestTemplate.html#getForEntity(String,%20Class,%20Object...)[getForEntity] |
|
|
|
| HEAD |
|
| {javadoc-baseurl}/org/springframework/web/client/RestTemplate.html#headForHeaders(String,%20Object...)[headForHeaders(String |
|
url, String... urlVariables)] |
|
|
|
| OPTIONS |
|
| {javadoc-baseurl}/org/springframework/web/client/RestTemplate.html#optionsForAllow(String,%20Object...)[optionsForAllow(String |
|
url, String... urlVariables)] |
|
|
|
| POST |
|
| {javadoc-baseurl}/org/springframework/web/client/RestTemplate.html#postForLocation(String,%20Object,%20Object...)[postForLocation(String |
|
url, Object request, String... urlVariables)] |
|
{javadoc-baseurl}/org/springframework/web/client/RestTemplate.html#postForObject(java.lang.String,%20java.lang.Object,%20java.lang.Class,%20java.lang.String...)[postForObject(String |
|
url, Object request, Class<T> responseType, String... uriVariables)] |
|
|
|
| PUT |
|
| {javadoc-baseurl}/org/springframework/web/client/RestTemplate.html#put(String,%20Object,%20Object...)[put(String |
|
url, Object request, String...urlVariables)] |
|
|
|
| PATCH and others |
|
| {javadoc-baseurl}/org/springframework/web/client/RestTemplate.html#exchange(java.lang.String,%20org.springframework.http.HttpMethod,%20org.springframework.http.HttpEntity,%20java.lang.Class,%20java.lang.Object...)[exchange] |
|
{javadoc-baseurl}/org/springframework/web/client/RestTemplate.html#execute(java.lang.String,%20org.springframework.http.HttpMethod,%20org.springframework.web.client.RequestCallback,%20org.springframework.web.client.ResponseExtractor,%20java.lang.Object...)[execute] |
|
|=== |
|
|
|
The names of `RestTemplate` methods follow a naming convention, the first part indicates |
|
what HTTP method is being invoked and the second part indicates what is returned. For |
|
example, the method `getForObject()` will perform a GET, convert the HTTP response into |
|
an object type of your choice and return that object. The method `postForLocation()` |
|
will do a POST, converting the given object into a HTTP request and return the response |
|
HTTP Location header where the newly created object can be found. In case of an |
|
exception processing the HTTP request, an exception of the type `RestClientException` |
|
will be thrown; this behavior can be changed by plugging in another |
|
`ResponseErrorHandler` implementation into the `RestTemplate`. |
|
|
|
The `exchange` and `execute` methods are generalized versions of the more |
|
specific methods listed above them and can support additional combinations and methods, |
|
like HTTP PATCH. However, note that the underlying HTTP library must also support the |
|
desired combination. The JDK `HttpURLConnection` does not support the `PATCH` method, but |
|
Apache HttpComponents HttpClient version 4.2 or later does. They also enable |
|
`RestTemplate` to read an HTTP response to a generic type (e.g. `List<Account>`), using a |
|
`ParameterizedTypeReference`, a new class that enables capturing and passing generic |
|
type info. |
|
|
|
Objects passed to and returned from these methods are converted to and from HTTP |
|
messages by `HttpMessageConverter` instances. Converters for the main mime types are |
|
registered by default, but you can also write your own converter and register it via the |
|
`messageConverters()` bean property. The default converter instances registered with the |
|
template are `ByteArrayHttpMessageConverter`, `StringHttpMessageConverter`, |
|
`FormHttpMessageConverter` and `SourceHttpMessageConverter`. You can override these |
|
defaults using the `messageConverters()` bean property as would be required if using the |
|
`MarshallingHttpMessageConverter` or `MappingJackson2HttpMessageConverter`. |
|
|
|
Each method takes URI template arguments in two forms, either as a `String` variable |
|
length argument or a `Map<String,String>`. For example, |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
String result = restTemplate.getForObject( |
|
"http://example.com/hotels/{hotel}/bookings/{booking}", String.class,"42", "21"); |
|
---- |
|
|
|
using variable length arguments and |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
Map<String, String> vars = Collections.singletonMap("hotel", "42"); |
|
String result = restTemplate.getForObject( |
|
"http://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars); |
|
---- |
|
|
|
using a `Map<String,String>`. |
|
|
|
To create an instance of `RestTemplate` you can simply call the default no-arg |
|
constructor. This will use standard Java classes from the `java.net` package as the |
|
underlying implementation to create HTTP requests. This can be overridden by specifying |
|
an implementation of `ClientHttpRequestFactory`. Spring provides the implementation |
|
`HttpComponentsClientHttpRequestFactory` that uses the Apache HttpComponents |
|
`HttpClient` to create requests. `HttpComponentsClientHttpRequestFactory` is configured |
|
using an instance of `org.apache.http.client.HttpClient` which can in turn be configured |
|
with credentials information or connection pooling functionality. |
|
|
|
[TIP] |
|
==== |
|
Note that the `java.net` implementation for HTTP requests may raise an exception when |
|
accessing the status of a response that represents an error (e.g. 401). If this is an |
|
issue, switch to `HttpComponentsClientHttpRequestFactory` instead. |
|
==== |
|
The previous example using Apache HttpComponents `HttpClient` directly rewritten to use |
|
the `RestTemplate` is shown below |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
uri = "http://example.com/hotels/{id}/bookings"; |
|
|
|
RestTemplate template = new RestTemplate(); |
|
|
|
Booking booking = // create booking object |
|
|
|
URI location = template.postForLocation(uri, booking, "1"); |
|
---- |
|
|
|
To use Apache HttpComponents instead of the native `java.net` functionality, construct |
|
the `RestTemplate` as follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory()); |
|
---- |
|
|
|
[TIP] |
|
==== |
|
Apache HttpClient supports gzip encoding. To use it, |
|
construct a `HttpComponentsClientHttpRequestFactory` like so: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
HttpClient httpClient = HttpClientBuilder.create().build(); |
|
ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); |
|
RestTemplate restTemplate = new RestTemplate(requestFactory); |
|
---- |
|
==== |
|
The general callback interface is `RequestCallback` and is called when the execute |
|
method is invoked. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback, |
|
ResponseExtractor<T> responseExtractor, String... urlVariables) |
|
|
|
// also has an overload with urlVariables as a Map<String, String>. |
|
---- |
|
|
|
The `RequestCallback` interface is defined as |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface RequestCallback { |
|
void doWithRequest(ClientHttpRequest request) throws IOException; |
|
} |
|
---- |
|
|
|
and allows you to manipulate the request headers and write to the request body. When |
|
using the execute method you do not have to worry about any resource management, the |
|
template will always close the request and handle any errors. Refer to the API |
|
documentation for more information on using the execute method and the meaning of its |
|
other method arguments. |
|
|
|
|
|
[[rest-resttemplate-uri]] |
|
===== Working with the URI |
|
For each of the main HTTP methods, the `RestTemplate` provides variants that either take |
|
a String URI or `java.net.URI` as the first argument. |
|
|
|
The String URI variants accept template arguments as a String variable length argument |
|
or as a `Map<String,String>`. They also assume the URL String is not encoded and needs |
|
to be encoded. For example the following: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
restTemplate.getForObject("http://example.com/hotel list", String.class); |
|
---- |
|
|
|
will perform a GET on `http://example.com/hotel%20list`. That means if the input URL |
|
String is already encoded, it will be encoded twice -- i.e. |
|
`http://example.com/hotel%20list` will become `http://example.com/hotel%2520list`. If |
|
this is not the intended effect, use the `java.net.URI` method variant, which assumes |
|
the URL is already encoded is also generally useful if you want to reuse a single (fully |
|
expanded) `URI` multiple times. |
|
|
|
The `UriComponentsBuilder` class can be used to build and encode the `URI` including |
|
support for URI templates. For example you can start with a URL String: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
UriComponents uriComponents = UriComponentsBuilder.fromUriString( |
|
"http://example.com/hotels/{hotel}/bookings/{booking}").build() |
|
.expand("42", "21") |
|
.encode(); |
|
|
|
URI uri = uriComponents.toUri(); |
|
---- |
|
|
|
Or specify each URI component individually: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
UriComponents uriComponents = UriComponentsBuilder.newInstance() |
|
.scheme("http").host("example.com").path("/hotels/{hotel}/bookings/{booking}").build() |
|
.expand("42", "21") |
|
.encode(); |
|
|
|
URI uri = uriComponents.toUri(); |
|
---- |
|
|
|
|
|
[[rest-template-headers]] |
|
===== Dealing with request and response headers |
|
Besides the methods described above, the `RestTemplate` also has the `exchange()` |
|
method, which can be used for arbitrary HTTP method execution based on the `HttpEntity` |
|
class. |
|
|
|
Perhaps most importantly, the `exchange()` method can be used to add request headers and |
|
read response headers. For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
HttpHeaders requestHeaders = new HttpHeaders(); |
|
requestHeaders.set("MyRequestHeader", "MyValue"); |
|
HttpEntity<?> requestEntity = new HttpEntity(requestHeaders); |
|
|
|
HttpEntity<String> response = template.exchange( |
|
"http://example.com/hotels/{hotel}", |
|
HttpMethod.GET, requestEntity, String.class, "42"); |
|
|
|
String responseHeader = response.getHeaders().getFirst("MyResponseHeader"); |
|
String body = response.getBody(); |
|
---- |
|
|
|
In the above example, we first prepare a request entity that contains the |
|
`MyRequestHeader` header. We then retrieve the response, and read the `MyResponseHeader` |
|
and body. |
|
|
|
[[rest-template-jsonview]] |
|
===== Jackson JSON Views support |
|
|
|
It is possible to specify a http://wiki.fasterxml.com/JacksonJsonViews[Jackson JSON View] |
|
to serialize only a subset of the object properties. For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
MappingJacksonValue value = new MappingJacksonValue(new User("eric", "7!jd#h23")); |
|
value.setSerializationView(User.WithoutPasswordView.class); |
|
HttpEntity<MappingJacksonValue> entity = new HttpEntity<MappingJacksonValue>(value); |
|
String s = template.postForObject("http://example.com/user", entity, String.class); |
|
---- |
|
|
|
|
|
[[rest-message-conversion]] |
|
==== HTTP Message Conversion |
|
Objects passed to and returned from the methods `getForObject()`, `postForLocation()`, |
|
and `put()` are converted to HTTP requests and from HTTP responses by |
|
`HttpMessageConverters`. The `HttpMessageConverter` interface is shown below to give you |
|
a better feel for its functionality |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface HttpMessageConverter<T> { |
|
|
|
// Indicate whether the given class and media type can be read by this converter. |
|
boolean canRead(Class<?> clazz, MediaType mediaType); |
|
|
|
// Indicate whether the given class and media type can be written by this converter. |
|
boolean canWrite(Class<?> clazz, MediaType mediaType); |
|
|
|
// Return the list of MediaType objects supported by this converter. |
|
List<MediaType> getSupportedMediaTypes(); |
|
|
|
// Read an object of the given type from the given input message, and returns it. |
|
T read(Class<T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException; |
|
|
|
// Write an given object to the given output message. |
|
void write(T t, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException; |
|
|
|
} |
|
---- |
|
|
|
Concrete implementations for the main media (mime) types are provided in the framework |
|
and are registered by default with the `RestTemplate` on the client-side and with |
|
`AnnotationMethodHandlerAdapter` on the server-side. |
|
|
|
The implementations of ++HttpMessageConverter++s are described in the following sections. |
|
For all converters a default media type is used but can be overridden by setting the |
|
`supportedMediaTypes` bean property |
|
|
|
|
|
[[rest-string-converter]] |
|
===== StringHttpMessageConverter |
|
An `HttpMessageConverter` implementation that can read and write Strings from the HTTP |
|
request and response. By default, this converter supports all text media types ( |
|
`text/*`), and writes with a `Content-Type` of `text/plain`. |
|
|
|
|
|
[[rest-form-converter]] |
|
===== FormHttpMessageConverter |
|
An `HttpMessageConverter` implementation that can read and write form data from the HTTP |
|
request and response. By default, this converter reads and writes the media type |
|
`application/x-www-form-urlencoded`. Form data is read from and written into a |
|
`MultiValueMap<String, String>`. |
|
|
|
|
|
[[rest-byte-converter]] |
|
===== ByteArrayHttpMessageConverter |
|
An `HttpMessageConverter` implementation that can read and write byte arrays from the |
|
HTTP request and response. By default, this converter supports all media types ( `*/*`), |
|
and writes with a `Content-Type` of `application/octet-stream`. This can be overridden |
|
by setting the `supportedMediaTypes` property, and overriding `getContentType(byte[])`. |
|
|
|
|
|
[[rest-marhsalling-converter]] |
|
===== MarshallingHttpMessageConverter |
|
An `HttpMessageConverter` implementation that can read and write XML using Spring's |
|
`Marshaller` and `Unmarshaller` abstractions from the `org.springframework.oxm` package. |
|
This converter requires a `Marshaller` and `Unmarshaller` before it can be used. These |
|
can be injected via constructor or bean properties. By default this converter supports ( |
|
`text/xml`) and ( `application/xml`). |
|
|
|
|
|
[[rest-mapping-json-converter]] |
|
===== MappingJackson2HttpMessageConverter |
|
An `HttpMessageConverter` implementation that can read and write JSON using Jackson's |
|
`ObjectMapper`. JSON mapping can be customized as needed through the use of Jackson's |
|
provided annotations. When further control is needed, a custom `ObjectMapper` can be |
|
injected through the `ObjectMapper` property for cases where custom JSON |
|
serializers/deserializers need to be provided for specific types. By default this |
|
converter supports ( `application/json`). |
|
|
|
|
|
[[rest-mapping-xml-converter]] |
|
===== MappingJackson2XmlHttpMessageConverter |
|
An `HttpMessageConverter` implementation that can read and write XML using |
|
https://github.com/FasterXML/jackson-dataformat-xml[Jackson XML] extension's |
|
`XmlMapper`. XML mapping can be customized as needed through the use of JAXB |
|
or Jackson's provided annotations. When further control is needed, a custom `XmlMapper` |
|
can be injected through the `ObjectMapper` property for cases where custom XML |
|
serializers/deserializers need to be provided for specific types. By default this |
|
converter supports ( `application/xml`). |
|
|
|
|
|
[[rest-source-converter]] |
|
===== SourceHttpMessageConverter |
|
An `HttpMessageConverter` implementation that can read and write |
|
`javax.xml.transform.Source` from the HTTP request and response. Only `DOMSource`, |
|
`SAXSource`, and `StreamSource` are supported. By default, this converter supports ( |
|
`text/xml`) and ( `application/xml`). |
|
|
|
|
|
[[rest-buffered-image-converter]] |
|
===== BufferedImageHttpMessageConverter |
|
An `HttpMessageConverter` implementation that can read and write |
|
`java.awt.image.BufferedImage` from the HTTP request and response. This converter reads |
|
and writes the media type supported by the Java I/O API. |
|
|
|
[[rest-async-resttemplate]] |
|
==== Async RestTemplate |
|
|
|
Web applications often need to query external REST services those days. The very nature of |
|
HTTP and synchronous calls can lead up to challenges when scaling applications for those |
|
needs: multiple threads may be blocked, waiting for remote HTTP responses. |
|
|
|
`AsyncRestTemplate` and <<rest-resttemplate>>'s APIs are very similar; see |
|
<<rest-overview-of-resttemplate-methods-tbl>>. The main difference between those APIs is |
|
that `AsyncRestTemplate` returns |
|
{javadoc-baseurl}/org/springframework/util/concurrent/ListenableFuture.html[`ListenableFuture`] |
|
wrappers as opposed to concrete results. |
|
|
|
The previous `RestTemplate` example translates to: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// async call |
|
Future<ResponseEntity<String>> futureEntity = template.getForEntity( |
|
"http://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42", "21"); |
|
|
|
// get the concrete result - synchronous call |
|
ResponseEntity<String> entity = futureEntity.get(); |
|
---- |
|
|
|
{javadoc-baseurl}/org/springframework/util/concurrent/ListenableFuture.html[`ListenableFuture`] |
|
accepts completion callbacks: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ListenableFuture<ResponseEntity<String>> futureEntity = template.getForEntity( |
|
"http://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42", "21"); |
|
|
|
// register a callback |
|
futureEntity.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() { |
|
@Override |
|
public void onSuccess(ResponseEntity<String> entity) { |
|
//... |
|
} |
|
|
|
@Override |
|
public void onFailure(Throwable t) { |
|
//... |
|
} |
|
}); |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
The default `AsyncRestTemplate` constructor registers a |
|
{javadoc-baseurl}/org/springframework/core/task/SimpleAsyncTaskExecutor.html[`SimpleAsyncTaskExecutor` |
|
] for executing HTTP requests. |
|
When dealing with a large number of short-lived requests, a thread-pooling TaskExecutor |
|
implementation like |
|
{javadoc-baseurl}/org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.html[`ThreadPoolTaskExecutor`] |
|
may be a good choice. |
|
==== |
|
|
|
See the |
|
{javadoc-baseurl}/org/springframework/util/concurrent/ListenableFuture.html[`ListenableFuture` javadocs] |
|
and |
|
{javadoc-baseurl}/org/springframework/web/client/AsyncRestTemplate.html[`AsyncRestTemplate` javadocs] |
|
for more details. |
|
|
|
|
|
|
|
|
|
|
|
[[ejb]] |
|
== Enterprise JavaBeans (EJB) integration |
|
|
|
|
|
|
|
|
|
[[ejb-introduction]] |
|
=== Introduction |
|
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. |
|
|
|
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. |
|
|
|
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. |
|
|
|
|
|
|
|
|
|
[[ejb-access]] |
|
=== Accessing EJBs |
|
|
|
|
|
|
|
[[ejb-access-concepts]] |
|
==== Concepts |
|
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. |
|
|
|
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: |
|
|
|
* Typically code using EJBs depends on Service Locator or Business Delegate singletons, |
|
making it hard to test. |
|
* 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. |
|
* 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. |
|
|
|
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. |
|
|
|
|
|
|
|
[[ejb-access-local]] |
|
==== Accessing local SLSBs |
|
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 `MyComponent`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface MyComponent { |
|
... |
|
} |
|
---- |
|
|
|
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 `SessionBean` and the `MyComponent` 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 `MyComponent` |
|
on the controller. This will save the reference as an instance variable in the |
|
controller: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
private MyComponent myComponent; |
|
|
|
public void setMyComponent(MyComponent myComponent) { |
|
this.myComponent = myComponent; |
|
} |
|
---- |
|
|
|
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 `LocalStatelessSessionProxyFactoryBean` instance, |
|
which will be the EJB proxy object. The configuration of the proxy, and setting of the |
|
`myComponent` property of the controller is done with a configuration entry such as: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<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> |
|
---- |
|
|
|
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 |
|
`myComponent` 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 `classname` method on the |
|
local EJB and invokes the corresponding business method on the EJB. |
|
|
|
The `myController` bean definition sets the `myComponent` property of the controller |
|
class to the EJB proxy. |
|
|
|
Alternatively (and preferably in case of many such proxy definitions), consider using |
|
the `<jee:local-slsb>` configuration element in Spring's "jee" namespace: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<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> |
|
---- |
|
|
|
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 `myComponent` 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. |
|
|
|
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. |
|
|
|
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 |
|
`ApplicationContext` 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 `init()` 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 `lazy-init` attribute. |
|
|
|
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 `LocalSlsbInvokerInterceptor`. |
|
|
|
|
|
|
|
[[ejb-access-remote]] |
|
==== Accessing remote SLSBs |
|
Accessing remote EJBs is essentially identical to accessing local EJBs, except that the |
|
`SimpleRemoteStatelessSessionProxyFactoryBean` or `<jee:remote-slsb>` 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. |
|
|
|
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 `RemoteException`, 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 `RemoteException` in your Business Method |
|
Interface and implementing EJB code, have a remote interface which is identical except |
|
that it does throw `RemoteException`, 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 `RemoteException` class. Any actual `RemoteException` that is thrown during the |
|
EJB invocation will be re-thrown as the non-checked `RemoteAccessException` class, which |
|
is a subclass of `RuntimeException`. 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 `RemoteExceptions` in your business interface. |
|
|
|
|
|
|
|
[[ejb-access-ejb2-ejb3]] |
|
==== Accessing EJB 2.x SLSBs versus EJB 3 SLSBs |
|
Accessing EJB 2.x Session Beans and EJB 3 Session Beans via Spring is largely |
|
transparent. Spring's EJB accessors, including the `<jee:local-slsb>` and |
|
`<jee:remote-slsb>` 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). |
|
|
|
Note: For EJB 3 Session Beans, you could effectively use a `JndiObjectFactoryBean` / |
|
`<jee:jndi-lookup>` as well, since fully usable component references are exposed for |
|
plain JNDI lookups there. Defining explicit `<jee:local-slsb>` / `<jee:remote-slsb>` |
|
lookups simply provides consistent and more explicit EJB access configuration. |
|
|
|
|
|
|
|
|
|
[[ejb-implementation]] |
|
=== Using Spring's EJB implementation support classes |
|
|
|
|
|
|
|
[[ejb-implementation-ejb3]] |
|
==== EJB 3 injection interceptor |
|
For EJB 3 Session Beans and Message-Driven Beans, Spring provides a convenient |
|
interceptor that resolves Spring's `@Autowired` annotation in the EJB component |
|
class: `org.springframework.ejb.interceptor.SpringBeanAutowiringInterceptor`. This |
|
interceptor can be applied through an `@Interceptors` annotation in the EJB component |
|
class, or through an `interceptor-binding` XML element in the EJB deployment descriptor. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@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(...); |
|
} |
|
|
|
... |
|
|
|
} |
|
---- |
|
|
|
`SpringBeanAutowiringInterceptor` by default obtains target beans from a |
|
`ContextSingletonBeanFactoryLocator`, with the context defined in a bean definition file |
|
named `beanRefContext.xml`. 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 `beanRefContext.xml`) can be explicitly specified either |
|
through overriding the `getBeanFactoryLocatorKey` method in a custom |
|
`SpringBeanAutowiringInterceptor` subclass. |
|
|
|
Alternatively, consider overriding `SpringBeanAutowiringInterceptor`'s `getBeanFactory` |
|
method, e.g. obtaining a shared `ApplicationContext` from a custom holder class. |
|
|
|
|
|
|
|
|
|
|
|
[[jms]] |
|
== JMS (Java Message Service) |
|
|
|
|
|
|
|
|
|
[[jms-introduction]] |
|
=== Introduction |
|
Spring provides a JMS integration framework that simplifies the use of the JMS API much |
|
like Spring's integration does for the JDBC API. |
|
|
|
JMS can be roughly divided into two areas of functionality, namely the production and |
|
consumption of messages. The `JmsTemplate` class is used for message production and |
|
synchronous message reception. For asynchronous reception similar to Java EE's |
|
message-driven bean style, Spring provides a number of message listener containers that |
|
are used to create Message-Driven POJOs (MDPs). Spring also provides a declarative way |
|
of creating message listeners. |
|
|
|
The package `org.springframework.jms.core` provides the core functionality for using |
|
JMS. It contains JMS template classes that simplify the use of the JMS by handling the |
|
creation and release of resources, much like the `JdbcTemplate` does for JDBC. The |
|
design principle common to Spring template classes is to provide helper methods to |
|
perform common operations and for more sophisticated usage, delegate the essence of the |
|
processing task to user implemented callback interfaces. The JMS template follows the |
|
same design. The classes offer various convenience methods for the sending of messages, |
|
consuming a message synchronously, and exposing the JMS session and message producer to |
|
the user. |
|
|
|
The package `org.springframework.jms.support` provides `JMSException` translation |
|
functionality. The translation converts the checked `JMSException` hierarchy to a |
|
mirrored hierarchy of unchecked exceptions. If there are any provider specific |
|
subclasses of the checked `javax.jms.JMSException`, this exception is wrapped in the |
|
unchecked `UncategorizedJmsException`. |
|
|
|
The package `org.springframework.jms.support.converter` provides a `MessageConverter` |
|
abstraction to convert between Java objects and JMS messages. |
|
|
|
The package `org.springframework.jms.support.destination` provides various strategies |
|
for managing JMS destinations, such as providing a service locator for destinations |
|
stored in JNDI. |
|
|
|
The package `org.springframework.jms.annotation` provides the necessary infrastructure |
|
to support annotation-driven listener endpoints using `@JmsListener`. |
|
|
|
The package `org.springframework.jms.config` provides the parser implementation for the |
|
`jms` namespace as well the java config support to configure listener containers and |
|
create listener endpoints. |
|
|
|
Finally, the package `org.springframework.jms.connection` provides an implementation of |
|
the `ConnectionFactory` suitable for use in standalone applications. It also contains an |
|
implementation of Spring's `PlatformTransactionManager` for JMS (the cunningly named |
|
`JmsTransactionManager`). This allows for seamless integration of JMS as a transactional |
|
resource into Spring's transaction management mechanisms. |
|
|
|
|
|
|
|
|
|
[[jms-using]] |
|
=== Using Spring JMS |
|
|
|
|
|
|
|
[[jms-jmstemplate]] |
|
==== JmsTemplate |
|
|
|
The `JmsTemplate` class is the central class in the JMS core package. It simplifies the |
|
use of JMS since it handles the creation and release of resources when sending or |
|
synchronously receiving messages. |
|
|
|
Code that uses the `JmsTemplate` only needs to implement callback interfaces giving them |
|
a clearly defined high level contract. The `MessageCreator` callback interface creates a |
|
message given a `Session` provided by the calling code in `JmsTemplate`. In order to |
|
allow for more complex usage of the JMS API, the callback `SessionCallback` provides the |
|
user with the JMS session and the callback `ProducerCallback` exposes a `Session` and |
|
`MessageProducer` pair. |
|
|
|
The JMS API exposes two types of send methods, one that takes delivery mode, priority, |
|
and time-to-live as Quality of Service (QOS) parameters and one that takes no QOS |
|
parameters which uses default values. Since there are many send methods in |
|
`JmsTemplate`, the setting of the QOS parameters have been exposed as bean properties to |
|
avoid duplication in the number of send methods. Similarly, the timeout value for |
|
synchronous receive calls is set using the property `setReceiveTimeout`. |
|
|
|
Some JMS providers allow the setting of default QOS values administratively through the |
|
configuration of the `ConnectionFactory`. This has the effect that a call to |
|
`MessageProducer`'s send method `send(Destination destination, Message message)` will |
|
use different QOS default values than those specified in the JMS specification. In order |
|
to provide consistent management of QOS values, the `JmsTemplate` must therefore be |
|
specifically enabled to use its own QOS values by setting the boolean property |
|
`isExplicitQosEnabled` to `true`. |
|
|
|
For convenience, `JmsTemplate` also exposes a basic request-reply operation that allows |
|
to send a message and wait for a reply on a temporary queue that is created as part of |
|
the operation. |
|
|
|
[NOTE] |
|
==== |
|
Instances of the `JmsTemplate` class are __thread-safe once configured__. This is |
|
important because it means that you can configure a single instance of a `JmsTemplate` |
|
and then safely inject this __shared__ reference into multiple collaborators. To be |
|
clear, the `JmsTemplate` is stateful, in that it maintains a reference to a |
|
`ConnectionFactory`, but this state is __not__ conversational state. |
|
==== |
|
|
|
As of Spring Framework 4.1, `JmsMessagingTemplate` is built on top of `JmsTemplate` |
|
and provides an integration with the messaging abstraction, i.e. |
|
`org.springframework.messaging.Message`. This allows you to create the message to |
|
send in generic manner. |
|
|
|
|
|
[[jms-connections]] |
|
==== Connections |
|
The `JmsTemplate` requires a reference to a `ConnectionFactory`. The `ConnectionFactory` |
|
is part of the JMS specification and serves as the entry point for working with JMS. It |
|
is used by the client application as a factory to create connections with the JMS |
|
provider and encapsulates various configuration parameters, many of which are vendor |
|
specific such as SSL configuration options. |
|
|
|
When using JMS inside an EJB, the vendor provides implementations of the JMS interfaces |
|
so that they can participate in declarative transaction management and perform pooling |
|
of connections and sessions. In order to use this implementation, Java EE containers |
|
typically require that you declare a JMS connection factory as a `resource-ref` inside |
|
the EJB or servlet deployment descriptors. To ensure the use of these features with the |
|
`JmsTemplate` inside an EJB, the client application should ensure that it references the |
|
managed implementation of the `ConnectionFactory`. |
|
|
|
|
|
[[jms-caching-resources]] |
|
===== Caching Messaging Resources |
|
The standard API involves creating many intermediate objects. To send a message the |
|
following 'API' walk is performed |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ConnectionFactory->Connection->Session->MessageProducer->send |
|
---- |
|
|
|
Between the ConnectionFactory and the Send operation there are three intermediate |
|
objects that are created and destroyed. To optimise the resource usage and increase |
|
performance two implementations of `ConnectionFactory` are provided. |
|
|
|
|
|
[[jms-connection-factory]] |
|
===== SingleConnectionFactory |
|
Spring provides an implementation of the `ConnectionFactory` interface, |
|
`SingleConnectionFactory`, that will return the same `Connection` on all |
|
`createConnection()` calls and ignore calls to `close()`. This is useful for testing and |
|
standalone environments so that the same connection can be used for multiple |
|
`JmsTemplate` calls that may span any number of transactions. `SingleConnectionFactory` |
|
takes a reference to a standard `ConnectionFactory` that would typically come from JNDI. |
|
|
|
|
|
[[jdbc-connection-factory-caching]] |
|
===== CachingConnectionFactory |
|
The `CachingConnectionFactory` extends the functionality of `SingleConnectionFactory` |
|
and adds the caching of Sessions, MessageProducers, and MessageConsumers. The initial |
|
cache size is set to 1, use the property `SessionCacheSize` to increase the number of |
|
cached sessions. Note that the number of actual cached sessions will be more than that |
|
number as sessions are cached based on their acknowledgment mode, so there can be up to |
|
4 cached session instances when `SessionCacheSize` is set to one, one for each |
|
`AcknowledgementMode`. MessageProducers and MessageConsumers are cached within their |
|
owning session and also take into account the unique properties of the producers and |
|
consumers when caching. MessageProducers are cached based on their destination. |
|
MessageConsumers are cached based on a key composed of the destination, selector, |
|
noLocal delivery flag, and the durable subscription name (if creating durable consumers). |
|
|
|
|
|
|
|
[[jms-destinations]] |
|
==== Destination Management |
|
Destinations, like ConnectionFactories, are JMS administered objects that can be stored |
|
and retrieved in JNDI. When configuring a Spring application context you can use the |
|
JNDI factory class `JndiObjectFactoryBean` / `<jee:jndi-lookup>` to perform dependency |
|
injection on your object's references to JMS destinations. However, often this strategy |
|
is cumbersome if there are a large number of destinations in the application or if there |
|
are advanced destination management features unique to the JMS provider. Examples of |
|
such advanced destination management would be the creation of dynamic destinations or |
|
support for a hierarchical namespace of destinations. The `JmsTemplate` delegates the |
|
resolution of a destination name to a JMS destination object to an implementation of the |
|
interface `DestinationResolver`. `DynamicDestinationResolver` is the default |
|
implementation used by `JmsTemplate` and accommodates resolving dynamic destinations. A |
|
`JndiDestinationResolver` is also provided that acts as a service locator for |
|
destinations contained in JNDI and optionally falls back to the behavior contained in |
|
`DynamicDestinationResolver`. |
|
|
|
Quite often the destinations used in a JMS application are only known at runtime and |
|
therefore cannot be administratively created when the application is deployed. This is |
|
often because there is shared application logic between interacting system components |
|
that create destinations at runtime according to a well-known naming convention. Even |
|
though the creation of dynamic destinations is not part of the JMS specification, most |
|
vendors have provided this functionality. Dynamic destinations are created with a name |
|
defined by the user which differentiates them from temporary destinations and are often |
|
not registered in JNDI. The API used to create dynamic destinations varies from provider |
|
to provider since the properties associated with the destination are vendor specific. |
|
However, a simple implementation choice that is sometimes made by vendors is to |
|
disregard the warnings in the JMS specification and to use the `TopicSession` method |
|
`createTopic(String topicName)` or the `QueueSession` method `createQueue(String |
|
queueName)` to create a new destination with default destination properties. Depending |
|
on the vendor implementation, `DynamicDestinationResolver` may then also create a |
|
physical destination instead of only resolving one. |
|
|
|
The boolean property `pubSubDomain` is used to configure the `JmsTemplate` with |
|
knowledge of what JMS domain is being used. By default the value of this property is |
|
false, indicating that the point-to-point domain, Queues, will be used. This property |
|
used by `JmsTemplate` determines the behavior of dynamic destination resolution via |
|
implementations of the `DestinationResolver` interface. |
|
|
|
You can also configure the `JmsTemplate` with a default destination via the property |
|
`defaultDestination`. The default destination will be used with send and receive |
|
operations that do not refer to a specific destination. |
|
|
|
|
|
|
|
[[jms-mdp]] |
|
==== Message Listener Containers |
|
One of the most common uses of JMS messages in the EJB world is to drive message-driven |
|
beans (MDBs). Spring offers a solution to create message-driven POJOs (MDPs) in a way |
|
that does not tie a user to an EJB container. (See <<jms-asynchronousMessageReception>> |
|
for detailed coverage of Spring's MDP support.) As from Spring Framework 4.1, endpoint |
|
methods can be simply annotated using `@JmsListener` see <<jms-annotated>> for more |
|
details. |
|
|
|
A message listener container is used to receive messages from a JMS message queue and |
|
drive the `MessageListener` that is injected into it. The listener container is |
|
responsible for all threading of message reception and dispatches into the listener for |
|
processing. A message listener container is the intermediary between an MDP and a |
|
messaging provider, and takes care of registering to receive messages, participating in |
|
transactions, resource acquisition and release, exception conversion and suchlike. This |
|
allows you as an application developer to write the (possibly complex) business logic |
|
associated with receiving a message (and possibly responding to it), and delegates |
|
boilerplate JMS infrastructure concerns to the framework. |
|
|
|
There are two standard JMS message listener containers packaged with Spring, each with |
|
its specialised feature set. |
|
|
|
|
|
[[jms-mdp-simple]] |
|
===== SimpleMessageListenerContainer |
|
This message listener container is the simpler of the two standard flavors. It creates a |
|
fixed number of JMS sessions and consumers at startup, registers the listener using the |
|
standard JMS `MessageConsumer.setMessageListener()` method, and leaves it up the JMS |
|
provider to perform listener callbacks. This variant does not allow for dynamic adaption |
|
to runtime demands or for participation in externally managed transactions. |
|
Compatibility-wise, it stays very close to the spirit of the standalone JMS |
|
specification - but is generally not compatible with Java EE's JMS restrictions. |
|
|
|
|
|
[[jms-mdp-default]] |
|
===== DefaultMessageListenerContainer |
|
This message listener container is the one used in most cases. In contrast to |
|
`SimpleMessageListenerContainer`, this container variant does allow for dynamic adaption |
|
to runtime demands and is able to participate in externally managed transactions. Each |
|
received message is registered with an XA transaction when configured with a |
|
`JtaTransactionManager`; so processing may take advantage of XA transaction semantics. |
|
This listener container strikes a good balance between low requirements on the JMS |
|
provider, advanced functionality such as transaction participation, and compatibility |
|
with Java EE environments. |
|
|
|
The cache level of the container can be customized. Note that when no caching is enabled, |
|
a new connection and a new session is created for each message reception. Combining this |
|
with a non durable subscription with high loads may lead to message lost. Make sure to |
|
use a proper cache level in such case. |
|
|
|
This container also has recoverable capabilities when the broker goes down. By default, |
|
a simple `BackOff` implementation retries every 5 seconds. It is possible to specify |
|
a custom `BackOff` implementation for more fine-grained recovery options, see |
|
`ExponentialBackOff` for an example. |
|
|
|
|
|
[[jms-tx]] |
|
==== Transaction management |
|
Spring provides a `JmsTransactionManager` that manages transactions for a single JMS |
|
`ConnectionFactory`. This allows JMS applications to leverage the managed transaction |
|
features of Spring as described in <<transaction>>. The `JmsTransactionManager` performs |
|
local resource transactions, binding a JMS Connection/Session pair from the specified |
|
`ConnectionFactory` to the thread. `JmsTemplate` automatically detects such |
|
transactional resources and operates on them accordingly. |
|
|
|
In a Java EE environment, the `ConnectionFactory` will pool Connections and Sessions, so |
|
those resources are efficiently reused across transactions. In a standalone environment, |
|
using Spring's `SingleConnectionFactory` will result in a shared JMS `Connection`, with |
|
each transaction having its own independent `Session`. Alternatively, consider the use |
|
of a provider-specific pooling adapter such as ActiveMQ's `PooledConnectionFactory` |
|
class. |
|
|
|
`JmsTemplate` can also be used with the `JtaTransactionManager` and an XA-capable JMS |
|
`ConnectionFactory` for performing distributed transactions. Note that this requires the |
|
use of a JTA transaction manager as well as a properly XA-configured ConnectionFactory! |
|
(Check your Java EE server's / JMS provider's documentation.) |
|
|
|
Reusing code across a managed and unmanaged transactional environment can be confusing |
|
when using the JMS API to create a `Session` from a `Connection`. This is because the |
|
JMS API has only one factory method to create a `Session` and it requires values for the |
|
transaction and acknowledgement modes. In a managed environment, setting these values is |
|
the responsibility of the environment's transactional infrastructure, so these values |
|
are ignored by the vendor's wrapper to the JMS Connection. When using the `JmsTemplate` |
|
in an unmanaged environment you can specify these values through the use of the |
|
properties `sessionTransacted` and `sessionAcknowledgeMode`. When using a |
|
`PlatformTransactionManager` with `JmsTemplate`, the template will always be given a |
|
transactional JMS `Session`. |
|
|
|
|
|
|
|
|
|
[[jms-sending]] |
|
=== Sending a Message |
|
|
|
The `JmsTemplate` contains many convenience methods to send a message. There are send |
|
methods that specify the destination using a `javax.jms.Destination` object and those |
|
that specify the destination using a string for use in a JNDI lookup. The send method |
|
that takes no destination argument uses the default destination. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import javax.jms.ConnectionFactory; |
|
import javax.jms.JMSException; |
|
import javax.jms.Message; |
|
import javax.jms.Queue; |
|
import javax.jms.Session; |
|
|
|
import org.springframework.jms.core.MessageCreator; |
|
import org.springframework.jms.core.JmsTemplate; |
|
|
|
public class JmsQueueSender { |
|
|
|
private JmsTemplate jmsTemplate; |
|
private Queue queue; |
|
|
|
public void setConnectionFactory(ConnectionFactory cf) { |
|
this.jmsTemplate = new JmsTemplate(cf); |
|
} |
|
|
|
public void setQueue(Queue queue) { |
|
this.queue = queue; |
|
} |
|
|
|
public void simpleSend() { |
|
this.jmsTemplate.send(this.queue, new MessageCreator() { |
|
public Message createMessage(Session session) throws JMSException { |
|
return session.createTextMessage("hello queue world"); |
|
} |
|
}); |
|
} |
|
} |
|
---- |
|
|
|
This example uses the `MessageCreator` callback to create a text message from the |
|
supplied `Session` object. The `JmsTemplate` is constructed by passing a reference to a |
|
`ConnectionFactory`. As an alternative, a zero argument constructor and |
|
`connectionFactory` is provided and can be used for constructing the instance in |
|
JavaBean style (using a BeanFactory or plain Java code). Alternatively, consider |
|
deriving from Spring's `JmsGatewaySupport` convenience base class, which provides |
|
pre-built bean properties for JMS configuration. |
|
|
|
The method `send(String destinationName, MessageCreator creator)` lets you send a |
|
message using the string name of the destination. If these names are registered in JNDI, |
|
you should set the `destinationResolver` property of the template to an instance of |
|
`JndiDestinationResolver`. |
|
|
|
If you created the `JmsTemplate` and specified a default destination, the |
|
`send(MessageCreator c)` sends a message to that destination. |
|
|
|
|
|
|
|
[[jms-msg-conversion]] |
|
==== Using Message Converters |
|
In order to facilitate the sending of domain model objects, the `JmsTemplate` has |
|
various send methods that take a Java object as an argument for a message's data |
|
content. The overloaded methods `convertAndSend()` and `receiveAndConvert()` in |
|
`JmsTemplate` delegate the conversion process to an instance of the `MessageConverter` |
|
interface. This interface defines a simple contract to convert between Java objects and |
|
JMS messages. The default implementation `SimpleMessageConverter` supports conversion |
|
between `String` and `TextMessage`, `byte[]` and `BytesMesssage`, and `java.util.Map` |
|
and `MapMessage`. By using the converter, you and your application code can focus on the |
|
business object that is being sent or received via JMS and not be concerned with the |
|
details of how it is represented as a JMS message. |
|
|
|
The sandbox currently includes a `MapMessageConverter` which uses reflection to convert |
|
between a JavaBean and a `MapMessage`. Other popular implementation choices you might |
|
implement yourself are Converters that use an existing XML marshalling package, such as |
|
JAXB, Castor, XMLBeans, or XStream, to create a `TextMessage` representing the object. |
|
|
|
To accommodate the setting of a message's properties, headers, and body that can not be |
|
generically encapsulated inside a converter class, the `MessagePostProcessor` interface |
|
gives you access to the message after it has been converted, but before it is sent. The |
|
example below demonstrates how to modify a message header and a property after a |
|
`java.util.Map` is converted to a message. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public void sendWithConversion() { |
|
Map map = new HashMap(); |
|
map.put("Name", "Mark"); |
|
map.put("Age", new Integer(47)); |
|
jmsTemplate.convertAndSend("testQueue", map, new MessagePostProcessor() { |
|
public Message postProcessMessage(Message message) throws JMSException { |
|
message.setIntProperty("AccountID", 1234); |
|
message.setJMSCorrelationID("123-00001"); |
|
return message; |
|
} |
|
}); |
|
} |
|
---- |
|
|
|
This results in a message of the form: |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
MapMessage={ |
|
Header={ |
|
... standard headers ... |
|
CorrelationID={123-00001} |
|
} |
|
Properties={ |
|
AccountID={Integer:1234} |
|
} |
|
Fields={ |
|
Name={String:Mark} |
|
Age={Integer:47} |
|
} |
|
} |
|
---- |
|
|
|
|
|
|
|
[[jms-callbacks]] |
|
==== SessionCallback and ProducerCallback |
|
|
|
While the send operations cover many common usage scenarios, there are cases when you |
|
want to perform multiple operations on a JMS `Session` or `MessageProducer`. The |
|
`SessionCallback` and `ProducerCallback` expose the JMS `Session` and `Session` / |
|
`MessageProducer` pair respectively. The `execute()` methods on `JmsTemplate` execute |
|
these callback methods. |
|
|
|
|
|
|
|
|
|
[[jms-receiving]] |
|
=== Receiving a message |
|
|
|
|
|
|
|
[[jms-receiving-sync]] |
|
==== Synchronous Reception |
|
While JMS is typically associated with asynchronous processing, it is possible to |
|
consume messages synchronously. The overloaded `receive(..)` methods provide this |
|
functionality. During a synchronous receive, the calling thread blocks until a message |
|
becomes available. This can be a dangerous operation since the calling thread can |
|
potentially be blocked indefinitely. The property `receiveTimeout` specifies how long |
|
the receiver should wait before giving up waiting for a message. |
|
|
|
|
|
|
|
[[jms-asynchronousMessageReception]] |
|
==== Asynchronous Reception - Message-Driven POJOs |
|
|
|
[NOTE] |
|
==== |
|
Spring also supports annotated-listener endpoints through the use of the `@JmsListener` |
|
annotation and provides an open infrastructure to register endpoints programmatically. This |
|
is by far the most convenient way to setup an asynchronous receiver, see |
|
<<jms-annotated-support>> for more details. |
|
==== |
|
|
|
In a fashion similar to a Message-Driven Bean (MDB) in the EJB world, the Message-Driven |
|
POJO (MDP) acts as a receiver for JMS messages. The one restriction (but see also below |
|
for the discussion of the `MessageListenerAdapter` class) on an MDP is that it must |
|
implement the `javax.jms.MessageListener` interface. Please also be aware that in the |
|
case where your POJO will be receiving messages on multiple threads, it is important to |
|
ensure that your implementation is thread-safe. |
|
|
|
Below is a simple implementation of an MDP: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import javax.jms.JMSException; |
|
import javax.jms.Message; |
|
import javax.jms.MessageListener; |
|
import javax.jms.TextMessage; |
|
|
|
public class ExampleListener implements MessageListener { |
|
|
|
public void onMessage(Message message) { |
|
if (message instanceof TextMessage) { |
|
try { |
|
System.out.println(((TextMessage) message).getText()); |
|
} |
|
catch (JMSException ex) { |
|
throw new RuntimeException(ex); |
|
} |
|
} |
|
else { |
|
throw new IllegalArgumentException("Message must be of type TextMessage"); |
|
} |
|
} |
|
|
|
} |
|
---- |
|
|
|
Once you've implemented your `MessageListener`, it's time to create a message listener |
|
container. |
|
|
|
Find below an example of how to define and configure one of the message listener |
|
containers that ships with Spring (in this case the `DefaultMessageListenerContainer`). |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<!-- this is the Message Driven POJO (MDP) --> |
|
<bean id="messageListener" class="jmsexample.ExampleListener" /> |
|
|
|
<!-- and this is the message listener container --> |
|
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> |
|
<property name="connectionFactory" ref="connectionFactory"/> |
|
<property name="destination" ref="destination"/> |
|
**<property name="messageListener" ref="messageListener" />** |
|
</bean> |
|
---- |
|
|
|
Please refer to the Spring javadocs of the various message listener containers for a full |
|
description of the features supported by each implementation. |
|
|
|
|
|
|
|
[[jms-receiving-async-session-aware-message-listener]] |
|
==== the SessionAwareMessageListener interface |
|
|
|
The `SessionAwareMessageListener` interface is a Spring-specific interface that provides |
|
a similar contract to the JMS `MessageListener` interface, but also provides the message |
|
handling method with access to the JMS `Session` from which the `Message` was received. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.springframework.jms.listener; |
|
|
|
public interface SessionAwareMessageListener { |
|
|
|
void onMessage(Message message, Session session) throws JMSException; |
|
|
|
} |
|
---- |
|
|
|
You can choose to have your MDPs implement this interface (in preference to the standard |
|
JMS `MessageListener` interface) if you want your MDPs to be able to respond to any |
|
received messages (using the `Session` supplied in the `onMessage(Message, Session)` |
|
method). All of the message listener container implementations that ship with Spring |
|
have support for MDPs that implement either the `MessageListener` or |
|
`SessionAwareMessageListener` interface. Classes that implement the |
|
`SessionAwareMessageListener` come with the caveat that they are then tied to Spring |
|
through the interface. The choice of whether or not to use it is left entirely up to you |
|
as an application developer or architect. |
|
|
|
Please note that the `'onMessage(..)'` method of the `SessionAwareMessageListener` |
|
interface throws `JMSException`. In contrast to the standard JMS `MessageListener` |
|
interface, when using the `SessionAwareMessageListener` interface, it is the |
|
responsibility of the client code to handle any exceptions thrown. |
|
|
|
|
|
|
|
[[jms-receiving-async-message-listener-adapter]] |
|
==== the MessageListenerAdapter |
|
|
|
The `MessageListenerAdapter` class is the final component in Spring's asynchronous |
|
messaging support: in a nutshell, it allows you to expose almost __any__ class as a MDP |
|
(there are of course some constraints). |
|
|
|
Consider the following interface definition. Notice that although the interface extends |
|
neither the `MessageListener` nor `SessionAwareMessageListener` interfaces, it can still |
|
be used as a MDP via the use of the `MessageListenerAdapter` class. Notice also how the |
|
various message handling methods are strongly typed according to the __contents__ of the |
|
various `Message` types that they can receive and handle. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface MessageDelegate { |
|
|
|
void handleMessage(String message); |
|
|
|
void handleMessage(Map message); |
|
|
|
void handleMessage(byte[] message); |
|
|
|
void handleMessage(Serializable message); |
|
|
|
} |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class DefaultMessageDelegate implements MessageDelegate { |
|
// implementation elided for clarity... |
|
} |
|
---- |
|
|
|
In particular, note how the above implementation of the `MessageDelegate` interface (the |
|
above `DefaultMessageDelegate` class) has __no__ JMS dependencies at all. It truly is a |
|
POJO that we will make into an MDP via the following configuration. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<!-- this is the Message Driven POJO (MDP) --> |
|
**<bean id="messageListener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter"> |
|
<constructor-arg> |
|
<bean class="jmsexample.DefaultMessageDelegate"/> |
|
</constructor-arg> |
|
</bean>** |
|
|
|
<!-- and this is the message listener container... --> |
|
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> |
|
<property name="connectionFactory" ref="connectionFactory"/> |
|
<property name="destination" ref="destination"/> |
|
**<property name="messageListener" ref="messageListener" />** |
|
</bean> |
|
---- |
|
|
|
Below is an example of another MDP that can only handle the receiving of JMS |
|
`TextMessage` messages. Notice how the message handling method is actually called |
|
`'receive'` (the name of the message handling method in a `MessageListenerAdapter` |
|
defaults to `'handleMessage'`), but it is configurable (as you will see below). Notice |
|
also how the `'receive(..)'` method is strongly typed to receive and respond only to JMS |
|
`TextMessage` messages. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface TextMessageDelegate { |
|
|
|
void receive(TextMessage message); |
|
|
|
} |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class DefaultTextMessageDelegate implements TextMessageDelegate { |
|
// implementation elided for clarity... |
|
} |
|
---- |
|
|
|
The configuration of the attendant `MessageListenerAdapter` would look like this: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="messageListener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter"> |
|
<constructor-arg> |
|
<bean class="jmsexample.DefaultTextMessageDelegate"/> |
|
</constructor-arg> |
|
<property name="defaultListenerMethod" value="receive"/> |
|
<!-- we don't want automatic message context extraction --> |
|
<property name="messageConverter"> |
|
<null/> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
Please note that if the above `'messageListener'` receives a JMS `Message` of a type |
|
other than `TextMessage`, an `IllegalStateException` will be thrown (and subsequently |
|
swallowed). Another of the capabilities of the `MessageListenerAdapter` class is the |
|
ability to automatically send back a response `Message` if a handler method returns a |
|
non-void value. Consider the interface and class: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface ResponsiveTextMessageDelegate { |
|
|
|
// notice the return type... |
|
String receive(TextMessage message); |
|
|
|
} |
|
---- |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class DefaultResponsiveTextMessageDelegate implements ResponsiveTextMessageDelegate { |
|
// implementation elided for clarity... |
|
} |
|
---- |
|
|
|
If the above `DefaultResponsiveTextMessageDelegate` is used in conjunction with a |
|
`MessageListenerAdapter` then any non-null value that is returned from the execution of |
|
the `'receive(..)'` method will (in the default configuration) be converted into a |
|
`TextMessage`. The resulting `TextMessage` will then be sent to the `Destination` (if |
|
one exists) defined in the JMS Reply-To property of the original `Message`, or the |
|
default `Destination` set on the `MessageListenerAdapter` (if one has been configured); |
|
if no `Destination` is found then an `InvalidDestinationException` will be thrown (and |
|
please note that this exception __will not__ be swallowed and __will__ propagate up the |
|
call stack). |
|
|
|
|
|
|
|
[[jms-tx-participation]] |
|
==== Processing messages within transactions |
|
Invoking a message listener within a transaction only requires reconfiguration of the |
|
listener container. |
|
|
|
Local resource transactions can simply be activated through the `sessionTransacted` flag |
|
on the listener container definition. Each message listener invocation will then operate |
|
within an active JMS transaction, with message reception rolled back in case of listener |
|
execution failure. Sending a response message (via `SessionAwareMessageListener`) will |
|
be part of the same local transaction, but any other resource operations (such as |
|
database access) will operate independently. This usually requires duplicate message |
|
detection in the listener implementation, covering the case where database processing |
|
has committed but message processing failed to commit. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> |
|
<property name="connectionFactory" ref="connectionFactory"/> |
|
<property name="destination" ref="destination"/> |
|
<property name="messageListener" ref="messageListener"/> |
|
**<property name="sessionTransacted" value="true"/>** |
|
</bean> |
|
---- |
|
|
|
For participating in an externally managed transaction, you will need to configure a |
|
transaction manager and use a listener container which supports externally managed |
|
transactions: typically `DefaultMessageListenerContainer`. |
|
|
|
To configure a message listener container for XA transaction participation, you'll want |
|
to configure a `JtaTransactionManager` (which, by default, delegates to the Java EE |
|
server's transaction subsystem). Note that the underlying JMS ConnectionFactory needs to |
|
be XA-capable and properly registered with your JTA transaction coordinator! (Check your |
|
Java EE server's configuration of JNDI resources.) This allows message reception as well |
|
as e.g. database access to be part of the same transaction (with unified commit |
|
semantics, at the expense of XA transaction log overhead). |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/> |
|
---- |
|
|
|
Then you just need to add it to our earlier container configuration. The container will |
|
take care of the rest. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> |
|
<property name="connectionFactory" ref="connectionFactory"/> |
|
<property name="destination" ref="destination"/> |
|
<property name="messageListener" ref="messageListener"/> |
|
**<property name="transactionManager" ref="transactionManager"/>** |
|
</bean> |
|
---- |
|
|
|
|
|
|
|
|
|
[[jms-jca-message-endpoint-manager]] |
|
=== Support for JCA Message Endpoints |
|
Beginning with version 2.5, Spring also provides support for a JCA-based |
|
`MessageListener` container. The `JmsMessageEndpointManager` will attempt to |
|
automatically determine the `ActivationSpec` class name from the provider's |
|
`ResourceAdapter` class name. Therefore, it is typically possible to just provide |
|
Spring's generic `JmsActivationSpecConfig` as shown in the following example. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="org.springframework.jms.listener.endpoint.JmsMessageEndpointManager"> |
|
<property name="resourceAdapter" ref="resourceAdapter"/> |
|
<property name="activationSpecConfig"> |
|
<bean class="org.springframework.jms.listener.endpoint.JmsActivationSpecConfig"> |
|
<property name="destinationName" value="myQueue"/> |
|
</bean> |
|
</property> |
|
<property name="messageListener" ref="myMessageListener"/> |
|
</bean> |
|
---- |
|
|
|
Alternatively, you may set up a `JmsMessageEndpointManager` with a given |
|
`ActivationSpec` object. The `ActivationSpec` object may also come from a JNDI lookup |
|
(using `<jee:jndi-lookup>`). |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="org.springframework.jms.listener.endpoint.JmsMessageEndpointManager"> |
|
<property name="resourceAdapter" ref="resourceAdapter"/> |
|
<property name="activationSpec"> |
|
<bean class="org.apache.activemq.ra.ActiveMQActivationSpec"> |
|
<property name="destination" value="myQueue"/> |
|
<property name="destinationType" value="javax.jms.Queue"/> |
|
</bean> |
|
</property> |
|
<property name="messageListener" ref="myMessageListener"/> |
|
</bean> |
|
---- |
|
|
|
Using Spring's `ResourceAdapterFactoryBean`, the target `ResourceAdapter` may be |
|
configured locally as depicted in the following example. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="resourceAdapter" class="org.springframework.jca.support.ResourceAdapterFactoryBean"> |
|
<property name="resourceAdapter"> |
|
<bean class="org.apache.activemq.ra.ActiveMQResourceAdapter"> |
|
<property name="serverUrl" value="tcp://localhost:61616"/> |
|
</bean> |
|
</property> |
|
<property name="workManager"> |
|
<bean class="org.springframework.jca.work.SimpleTaskWorkManager"/> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
The specified `WorkManager` may also point to an environment-specific thread pool - |
|
typically through `SimpleTaskWorkManager's` "asyncTaskExecutor" property. Consider |
|
defining a shared thread pool for all your `ResourceAdapter` instances if you happen to |
|
use multiple adapters. |
|
|
|
In some environments (e.g. WebLogic 9 or above), the entire `ResourceAdapter` object may |
|
be obtained from JNDI instead (using `<jee:jndi-lookup>`). The Spring-based message |
|
listeners can then interact with the server-hosted `ResourceAdapter`, also using the |
|
server's built-in `WorkManager`. |
|
|
|
Please consult the JavaDoc for `JmsMessageEndpointManager`, `JmsActivationSpecConfig`, |
|
and `ResourceAdapterFactoryBean` for more details. |
|
|
|
Spring also provides a generic JCA message endpoint manager which is not tied to JMS: |
|
`org.springframework.jca.endpoint.GenericMessageEndpointManager`. This component allows |
|
for using any message listener type (e.g. a CCI MessageListener) and any |
|
provider-specific ActivationSpec object. Check out your JCA provider's documentation to |
|
find out about the actual capabilities of your connector, and consult |
|
`GenericMessageEndpointManager`'s JavaDoc for the Spring-specific configuration details. |
|
|
|
[NOTE] |
|
==== |
|
JCA-based message endpoint management is very analogous to EJB 2.1 Message-Driven Beans; |
|
it uses the same underlying resource provider contract. Like with EJB 2.1 MDBs, any |
|
message listener interface supported by your JCA provider can be used in the Spring |
|
context as well. Spring nevertheless provides explicit 'convenience' support for JMS, |
|
simply because JMS is the most common endpoint API used with the JCA endpoint management |
|
contract. |
|
==== |
|
|
|
|
|
|
|
[[jms-annotated]] |
|
=== Annotation-driven listener endpoints |
|
The easiest way to receive a message asynchronously is to use the annotated listener |
|
endpoint infrastructure. In a nutshell, it allows you to expose a method of a managed |
|
bean as a JMS listener endpoint. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Component |
|
public class MyService { |
|
|
|
@JmsListener(destination = "myDestination") |
|
public void processOrder(String data) { ... } |
|
} |
|
---- |
|
|
|
The idea of the example above is that whenever a message is available on the |
|
`javax.jms.Destination` "myDestination", the `processOrder` method is invoked |
|
accordingly (in this case, with the content of the JMS message similarly to |
|
what the <<jms-receiving-async-message-listener-adapter, `MessageListenerAdapter`>> |
|
provides). |
|
|
|
The annotated endpoint infrastructure creates a message listener container |
|
behind the scenes for each annotated method, using a `JmsListenerContainerFactory`. |
|
|
|
[[jms-annotated-support]] |
|
==== Enable listener endpoint annotations |
|
|
|
To enable support for `@JmsListener` annotations add `@EnableJms` to one of |
|
your `@Configuration` classes. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableJms |
|
public class AppConfig { |
|
|
|
@Bean |
|
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() { |
|
DefaultJmsListenerContainerFactory factory = |
|
new DefaultJmsListenerContainerFactory(); |
|
factory.setConnectionFactory(connectionFactory()); |
|
factory.setDestinationResolver(destinationResolver()); |
|
factory.setConcurrency("3-10"); |
|
return factory; |
|
} |
|
} |
|
---- |
|
|
|
By default, the infrastructure looks for a bean named `jmsListenerContainerFactory` |
|
as the source for the factory to use to create message listener containers. In this |
|
case, and ignoring the JMS infrastructure setup, the `processOrder` method can be |
|
invoked with a core poll size of 3 threads and a maximum pool size of 10 threads. |
|
|
|
It is possible to customize the listener container factory to use per annotation or |
|
an explicit default can be configured by implementing the `JmsListenerConfigurer` |
|
interface. The default is only required if at least one endpoint is registered |
|
without a specific container factory. See the javadoc for full details and examples. |
|
|
|
If you prefer <<jms-namespace,XML configuration>> use the `<jms:annotation-driven>` |
|
element. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<jms:annotation-driven/> |
|
|
|
<bean id="jmsListenerContainerFactory" |
|
class="org.springframework.jms.config.DefaultJmsListenerContainerFactory"> |
|
<property name="connectionFactory" ref="connectionFactory"/> |
|
<property name="destinationResolver" ref="destinationResolver"/> |
|
<property name="concurrency" value="3-10"/> |
|
</bean> |
|
---- |
|
|
|
[[jms-annotated-programmatic-registration]] |
|
==== Programmatic endpoints registration |
|
|
|
`JmsListenerEndpoint` provides a model of an JMS endpoint and is responsible for configuring |
|
the container for that model. The infrastructure allows you to configure endpoints |
|
programmatically in addition to the ones that are detected by the `JmsListener` annotation. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableJms |
|
public class AppConfig implements JmsListenerConfigurer { |
|
|
|
@Override |
|
public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) { |
|
SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint(); |
|
endpoint.setId("myJmsEndpoint"); |
|
endpoint.setDestination("anotherQueue"); |
|
endpoint.setMessageListener(message -> { |
|
// processing |
|
}); |
|
registrar.registerEndpoint(endpoint); |
|
} |
|
} |
|
---- |
|
|
|
In the example above, we used `SimpleJmsListenerEndpoint` which provides the actual |
|
`MessageListener` to invoke but you could just as well build your own endpoint variant |
|
describing a custom invocation mechanism. |
|
|
|
It should be noted that you could just as well skip the use of `@JmsListener` altogether |
|
and only register your endpoints programmatically through `JmsListenerConfigurer`. |
|
|
|
[[jms-annotated-method-signature]] |
|
==== Annotated endpoint method signature |
|
|
|
So far, we have been injecting a simple `String` in our endpoint but it can actually |
|
have a very flexible method signature. Let's rewrite it to inject the `Order` with |
|
a custom header: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Component |
|
public class MyService { |
|
|
|
@JmsListener(destination = "myDestination") |
|
public void processOrder(Order order, @Header("order_type") String orderType) { |
|
... |
|
} |
|
} |
|
---- |
|
|
|
These are the main elements you can inject in JMS listener endpoints: |
|
|
|
* The raw `javax.jms.Message` or any of its subclasses (provided of course that it |
|
matches the incoming message type). |
|
* The `javax.jms.Session` for optional access to the native JMS API e.g. for sending |
|
a custom reply. |
|
* The `org.springframework.messaging.Message` representing the incoming JMS message. |
|
Note that this message holds both the custom and the standard headers (as defined |
|
by `JmsHeaders`). |
|
* `@Header`-annotated method arguments to extract a specific header value, including |
|
standard JMS headers. |
|
* `@Headers`-annotated argument that must also be assignable to `java.util.Map` for |
|
getting access to all headers. |
|
* A non-annotated element that is not one of the supported types (i.e. `Message` and |
|
`Session`) is considered to be the payload. You can make that explicit by annotating |
|
the parameter with `@Payload`. You can also turn on validation by adding an extra |
|
`@Valid`. |
|
|
|
The ability to inject Spring's `Message` abstraction is particularly useful to benefit |
|
from all the information stored in the transport-specific message without relying on |
|
transport-specific API. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@JmsListener(destination = "myDestination") |
|
public void processOrder(Message<Order> order) { ... } |
|
---- |
|
|
|
Handling of method arguments is provided by `DefaultMessageHandlerMethodFactory` which can be |
|
further customized to support additional method arguments. The conversion and validation |
|
support can be customized there as well. |
|
|
|
For instance, if we want to make sure our `Order` is valid before processing it, we can |
|
annotate the payload with `@Valid` and configure the necessary validator as follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableJms |
|
public class AppConfig implements JmsListenerConfigurer { |
|
|
|
@Override |
|
public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) { |
|
registrar.setMessageHandlerMethodFactory(myJmsHandlerMethodFactory()); |
|
} |
|
|
|
@Bean |
|
public DefaultMessageHandlerMethodFactory myHandlerMethodFactory() { |
|
DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory(); |
|
factory.setValidator(myValidator()); |
|
return factory; |
|
} |
|
} |
|
---- |
|
|
|
[[jms-annotated-reply]] |
|
==== Reply management |
|
|
|
The existing support in <<jms-receiving-async-message-listener-adapter,MessageListenerAdapter>> |
|
already allows your method to have a non-`void` return type. When that's the case, the result of |
|
the invocation is encapsulated in a `javax.jms.Message` sent either in the destination specified |
|
in the `JMSReplyTo` header of the original message or in the default destination configured on |
|
the listener. That default destination can now be set using the `@SendTo` annotation of the |
|
messaging abstraction. |
|
|
|
Assuming our `processOrder` method should now return an `OrderStatus`, it is possible to write it |
|
as follow to automatically send a reply: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@JmsListener(destination = "myDestination") |
|
@SendTo("status") |
|
public OrderStatus processOrder(Order order) { |
|
// order processing |
|
return status; |
|
} |
|
---- |
|
|
|
If you need to set additional headers in a transport-independent manner, you could return a |
|
`Message` instead, something like: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@JmsListener(destination = "myDestination") |
|
@SendTo("status") |
|
public Message<OrderStatus> processOrder(Order order) { |
|
// order processing |
|
return MessageBuilder |
|
.withPayload(status) |
|
.setHeader("code", 1234) |
|
.build(); |
|
} |
|
---- |
|
|
|
[[jms-namespace]] |
|
=== JMS Namespace Support |
|
Spring provides an XML namespace for simplifying JMS configuration. To use the JMS |
|
namespace elements you will need to reference the JMS schema: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
**xmlns:jms="http://www.springframework.org/schema/jms"** |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd |
|
**http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms.xsd**"> |
|
|
|
<!-- bean definitions here --> |
|
|
|
</beans> |
|
---- |
|
|
|
The namespace consists of three top-level elements: `<annotation-driven/>`, `<listener-container/>` |
|
and `<jca-listener-container/>`. `<annotation-driven` enables the use of <<jms-annotated, |
|
annotation-driven listener endpoints>>. `<listener-container/>` and `<jca-listener-container/>` |
|
defines shared listener container configuration and may contain `<listener/>` child elements. Here |
|
is an example of a basic configuration for two listeners. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<jms:listener-container> |
|
|
|
<jms:listener destination="queue.orders" ref="orderService" method="placeOrder"/> |
|
|
|
<jms:listener destination="queue.confirmations" ref="confirmationLogger" method="log"/> |
|
|
|
</jms:listener-container> |
|
---- |
|
|
|
The example above is equivalent to creating two distinct listener container bean |
|
definitions and two distinct `MessageListenerAdapter` bean definitions as demonstrated |
|
in <<jms-receiving-async-message-listener-adapter>>. In addition to the attributes shown |
|
above, the `listener` element may contain several optional ones. The following table |
|
describes all available attributes: |
|
|
|
[[jms-namespace-listener-tbl]] |
|
.Attributes of the JMS <listener> element |
|
[cols="1,6"] |
|
|=== |
|
| Attribute| Description |
|
|
|
| id |
|
| A bean name for the hosting listener container. If not specified, a bean name will be |
|
automatically generated. |
|
|
|
| destination __(required)__ |
|
| The destination name for this listener, resolved through the `DestinationResolver` |
|
strategy. |
|
|
|
| ref __(required)__ |
|
| The bean name of the handler object. |
|
|
|
| method |
|
| The name of the handler method to invoke. If the `ref` points to a `MessageListener` |
|
or Spring `SessionAwareMessageListener`, this attribute may be omitted. |
|
|
|
| response-destination |
|
| The name of the default response destination to send response messages to. This will |
|
be applied in case of a request message that does not carry a "JMSReplyTo" field. The |
|
type of this destination will be determined by the listener-container's |
|
"destination-type" attribute. Note: This only applies to a listener method with a |
|
return value, for which each result object will be converted into a response message. |
|
|
|
| subscription |
|
| The name of the durable subscription, if any. |
|
|
|
| selector |
|
| An optional message selector for this listener. |
|
|
|
| concurrency |
|
| The number of concurrent sessions/consumers to start for this listener. Can either be |
|
a simple number indicating the maximum number (e.g. "5") or a range indicating the lower |
|
as well as the upper limit (e.g. "3-5"). Note that a specified minimum is just a hint |
|
and might be ignored at runtime. Default is the value provided by the container |
|
|=== |
|
|
|
The `<listener-container/>` element also accepts several optional attributes. This |
|
allows for customization of the various strategies (for example, `taskExecutor` and |
|
`destinationResolver`) as well as basic JMS settings and resource references. Using |
|
these attributes, it is possible to define highly-customized listener containers while |
|
still benefiting from the convenience of the namespace. |
|
|
|
Such settings can be automatically exposed as a `JmsListenerContainerFactory` by |
|
specifying the id of the bean to expose through the `factory-id` attribute. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<jms:listener-container connection-factory="myConnectionFactory" |
|
task-executor="myTaskExecutor" |
|
destination-resolver="myDestinationResolver" |
|
transaction-manager="myTransactionManager" |
|
concurrency="10"> |
|
|
|
<jms:listener destination="queue.orders" ref="orderService" method="placeOrder"/> |
|
|
|
<jms:listener destination="queue.confirmations" ref="confirmationLogger" method="log"/> |
|
|
|
</jms:listener-container> |
|
---- |
|
|
|
The following table describes all available attributes. Consult the class-level javadocs |
|
of the `AbstractMessageListenerContainer` and its concrete subclasses for more details |
|
on the individual properties. The javadocs also provide a discussion of transaction |
|
choices and message redelivery scenarios. |
|
|
|
[[jms-namespace-listener-container-tbl]] |
|
.Attributes of the JMS <listener-container> element |
|
[cols="1,6"] |
|
|=== |
|
| Attribute| Description |
|
|
|
| container-type |
|
| The type of this listener container. Available options are: `default`, `simple`, |
|
`default102`, or `simple102` (the default value is `'default'`). |
|
|
|
| container-class |
|
| A custom listener container implementation class as fully qualified class name. |
|
Default is Spring's standard `DefaultMessageListenerContainer` or |
|
`SimpleMessageListenerContainer`, according to the "container-type" attribute. |
|
|
|
| factory-id |
|
| Exposes the settings defined by this element as a `JmsListenerContainerFactory` |
|
with the specified id so that they can be reused with other endpoints. |
|
|
|
| connection-factory |
|
| A reference to the JMS `ConnectionFactory` bean (the default bean name is |
|
`'connectionFactory'`). |
|
|
|
| task-executor |
|
| A reference to the Spring `TaskExecutor` for the JMS listener invokers. |
|
|
|
| destination-resolver |
|
| A reference to the `DestinationResolver` strategy for resolving JMS `Destinations`. |
|
|
|
| message-converter |
|
| A reference to the `MessageConverter` strategy for converting JMS Messages to listener |
|
method arguments. Default is a `SimpleMessageConverter`. |
|
|
|
| error-handler |
|
| A reference to an `ErrorHandler` strategy for handling any uncaught Exceptions that |
|
may occur during the execution of the `MessageListener`. |
|
|
|
| destination-type |
|
| The JMS destination type for this listener: `queue`, `topic`, `durableTopic`, `sharedTopic` |
|
or `sharedDurableTopic`. This enables potentially the `pubSubDomain`, `subscriptionDurable` |
|
and `subscriptionShared` properties of the container. The default is `queue` (i.e. disabling |
|
those 3 properties). |
|
|
|
| client-id |
|
| The JMS client id for this listener container. Needs to be specified when using |
|
durable subscriptions. |
|
|
|
| cache |
|
| The cache level for JMS resources: `none`, `connection`, `session`, `consumer` or |
|
`auto`. By default ( `auto`), the cache level will effectively be "consumer", unless |
|
an external transaction manager has been specified - in which case the effective |
|
default will be `none` (assuming Java EE-style transaction management where the given |
|
ConnectionFactory is an XA-aware pool). |
|
|
|
| acknowledge |
|
| The native JMS acknowledge mode: `auto`, `client`, `dups-ok` or `transacted`. A value |
|
of `transacted` activates a locally transacted `Session`. As an alternative, specify |
|
the `transaction-manager` attribute described below. Default is `auto`. |
|
|
|
| transaction-manager |
|
| A reference to an external `PlatformTransactionManager` (typically an XA-based |
|
transaction coordinator, e.g. Spring's `JtaTransactionManager`). If not specified, |
|
native acknowledging will be used (see "acknowledge" attribute). |
|
|
|
| concurrency |
|
| The number of concurrent sessions/consumers to start for each listener. Can either be |
|
a simple number indicating the maximum number (e.g. "5") or a range indicating the |
|
lower as well as the upper limit (e.g. "3-5"). Note that a specified minimum is just a |
|
hint and might be ignored at runtime. Default is 1; keep concurrency limited to 1 in |
|
case of a topic listener or if queue ordering is important; consider raising it for |
|
general queues. |
|
|
|
| prefetch |
|
| The maximum number of messages to load into a single session. Note that raising this |
|
number might lead to starvation of concurrent consumers! |
|
|
|
| receive-timeout |
|
| The timeout to use for receive calls (in milliseconds). The default is `1000` ms (1 |
|
sec); `-1` indicates no timeout at all. |
|
|
|
| back-off |
|
| Specify the `BackOff` instance to use to compute the interval between recovery |
|
attempts. If the `BackOffExecution` implementation returns `BackOffExecution#STOP`, |
|
the listener container will not further attempt to recover. The `recovery-interval` |
|
value is ignored when this property is set. The default is a `FixedBackOff` with |
|
an interval of 5000 ms, that is 5 seconds. |
|
|
|
| recovery-interval |
|
| Specify the interval between recovery attempts, in milliseconds. Convenience |
|
way to create a `FixedBackOff` with the specified interval. For more recovery |
|
options, consider specifying a BackOff instance instead. The default is 5000 ms, |
|
that is 5 seconds. |
|
|
|
| phase |
|
| The lifecycle phase within which this container should start and stop. The lower the |
|
value the earlier this container will start and the later it will stop. The default is |
|
`Integer.MAX_VALUE` meaning the container will start as late as possible and stop as |
|
soon as possible. |
|
|=== |
|
|
|
Configuring a JCA-based listener container with the "jms" schema support is very similar. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<jms:jca-listener-container resource-adapter="myResourceAdapter" |
|
destination-resolver="myDestinationResolver" |
|
transaction-manager="myTransactionManager" |
|
concurrency="10"> |
|
|
|
<jms:listener destination="queue.orders" ref="myMessageListener"/> |
|
|
|
</jms:jca-listener-container> |
|
---- |
|
|
|
The available configuration options for the JCA variant are described in the following |
|
table: |
|
|
|
[[jms-namespace-jca-listener-container-tbl]] |
|
.Attributes of the JMS <jca-listener-container/> element |
|
[cols="1,6"] |
|
|=== |
|
| Attribute| Description |
|
|
|
| factory-id |
|
| Exposes the settings defined by this element as a `JmsListenerContainerFactory` |
|
with the specified id so that they can be reused with other endpoints. |
|
|
|
| resource-adapter |
|
| A reference to the JCA `ResourceAdapter` bean (the default bean name is |
|
`'resourceAdapter'`). |
|
|
|
| activation-spec-factory |
|
| A reference to the `JmsActivationSpecFactory`. The default is to autodetect the JMS |
|
provider and its `ActivationSpec` class (see `DefaultJmsActivationSpecFactory`) |
|
|
|
| destination-resolver |
|
| A reference to the `DestinationResolver` strategy for resolving JMS `Destinations`. |
|
|
|
| message-converter |
|
| A reference to the `MessageConverter` strategy for converting JMS Messages to listener |
|
method arguments. Default is a `SimpleMessageConverter`. |
|
|
|
| destination-type |
|
| The JMS destination type for this listener: `queue`, `topic`, `durableTopic`, `sharedTopic` |
|
or `sharedDurableTopic`. This enables potentially the `pubSubDomain`, `subscriptionDurable` |
|
and `subscriptionShared` properties of the container. The default is `queue` (i.e. disabling |
|
those 3 properties). |
|
|
|
| client-id |
|
| The JMS client id for this listener container. Needs to be specified when using |
|
durable subscriptions. |
|
|
|
| acknowledge |
|
| The native JMS acknowledge mode: `auto`, `client`, `dups-ok` or `transacted`. A value |
|
of `transacted` activates a locally transacted `Session`. As an alternative, specify |
|
the `transaction-manager` attribute described below. Default is `auto`. |
|
|
|
| transaction-manager |
|
| A reference to a Spring `JtaTransactionManager` or a |
|
`javax.transaction.TransactionManager` for kicking off an XA transaction for each |
|
incoming message. If not specified, native acknowledging will be used (see the |
|
"acknowledge" attribute). |
|
|
|
| concurrency |
|
| The number of concurrent sessions/consumers to start for each listener. Can either be |
|
a simple number indicating the maximum number (e.g. "5") or a range indicating the |
|
lower as well as the upper limit (e.g. "3-5"). Note that a specified minimum is just a |
|
hint and will typically be ignored at runtime when using a JCA listener container. |
|
Default is 1. |
|
|
|
| prefetch |
|
| The maximum number of messages to load into a single session. Note that raising this |
|
number might lead to starvation of concurrent consumers! |
|
|=== |
|
|
|
|
|
|
|
|
|
|
|
[[jmx]] |
|
== JMX |
|
|
|
|
|
|
|
|
|
[[jmx-introduction]] |
|
=== Introduction |
|
The JMX support in Spring provides you with the features to easily and transparently |
|
integrate your Spring application into a JMX infrastructure. |
|
|
|
.JMX? |
|
**** |
|
This chapter is not an introduction to JMX... it doesn't try to explain the motivations |
|
of why one might want to use JMX (or indeed what the letters JMX actually stand for). If |
|
you are new to JMX, check out <<jmx-resources>> at the end of this chapter. |
|
**** |
|
|
|
Specifically, Spring's JMX support provides four core features: |
|
|
|
* The automatic registration of __any__ Spring bean as a JMX MBean |
|
* A flexible mechanism for controlling the management interface of your beans |
|
* The declarative exposure of MBeans over remote, JSR-160 connectors |
|
* The simple proxying of both local and remote MBean resources |
|
|
|
These features are designed to work without coupling your application components to |
|
either Spring or JMX interfaces and classes. Indeed, for the most part your application |
|
classes need not be aware of either Spring or JMX in order to take advantage of the |
|
Spring JMX features. |
|
|
|
|
|
|
|
|
|
[[jmx-exporting]] |
|
=== Exporting your beans to JMX |
|
The core class in Spring's JMX framework is the `MBeanExporter`. This class is |
|
responsible for taking your Spring beans and registering them with a JMX `MBeanServer`. |
|
For example, consider the following class: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.springframework.jmx; |
|
|
|
public class JmxTestBean implements IJmxTestBean { |
|
|
|
private String name; |
|
private int age; |
|
private boolean isSuperman; |
|
|
|
public int getAge() { |
|
return age; |
|
} |
|
|
|
public void setAge(int age) { |
|
this.age = age; |
|
} |
|
|
|
public void setName(String name) { |
|
this.name = name; |
|
} |
|
|
|
public String getName() { |
|
return name; |
|
} |
|
|
|
public int add(int x, int y) { |
|
return x + y; |
|
} |
|
|
|
public void dontExposeMe() { |
|
throw new RuntimeException(); |
|
} |
|
} |
|
---- |
|
|
|
To expose the properties and methods of this bean as attributes and operations of an |
|
MBean you simply configure an instance of the `MBeanExporter` class in your |
|
configuration file and pass in the bean as shown below: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<!-- this bean must not be lazily initialized if the exporting is to happen --> |
|
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false"> |
|
<property name="beans"> |
|
<map> |
|
<entry key="bean:name=testBean1" value-ref="testBean"/> |
|
</map> |
|
</property> |
|
</bean> |
|
<bean id="testBean" class="org.springframework.jmx.JmxTestBean"> |
|
<property name="name" value="TEST"/> |
|
<property name="age" value="100"/> |
|
</bean> |
|
</beans> |
|
---- |
|
|
|
The pertinent bean definition from the above configuration snippet is the `exporter` |
|
bean. The `beans` property tells the `MBeanExporter` exactly which of your beans must be |
|
exported to the JMX `MBeanServer`. In the default configuration, the key of each entry |
|
in the `beans` `Map` is used as the `ObjectName` for the bean referenced by the |
|
corresponding entry value. This behavior can be changed as described in <<jmx-naming>>. |
|
|
|
With this configuration the `testBean` bean is exposed as an MBean under the |
|
`ObjectName` `bean:name=testBean1`. By default, all __public__ properties of the bean |
|
are exposed as attributes and all __public__ methods (bar those inherited from the |
|
`Object` class) are exposed as operations. |
|
|
|
[NOTE] |
|
==== |
|
`MBeanExporter` is a `Lifecycle` bean (see <<beans-factory-lifecycle-processor>>) |
|
and MBeans are exported as late as possible during the application lifecycle by default. It |
|
is possible to configure the `phase` at which the export happens or disable automatic |
|
registration by setting the `autoStartup` flag. |
|
==== |
|
|
|
|
|
[[jmx-exporting-mbeanserver]] |
|
==== Creating an MBeanServer |
|
|
|
The above configuration assumes that the application is running in an environment that |
|
has one (and only one) `MBeanServer` already running. In this case, Spring will attempt |
|
to locate the running `MBeanServer` and register your beans with that server (if any). |
|
This behavior is useful when your application is running inside a container such as |
|
Tomcat or IBM WebSphere that has its own `MBeanServer`. |
|
|
|
However, this approach is of no use in a standalone environment, or when running inside |
|
a container that does not provide an `MBeanServer`. To address this you can create an |
|
`MBeanServer` instance declaratively by adding an instance of the |
|
`org.springframework.jmx.support.MBeanServerFactoryBean` class to your configuration. |
|
You can also ensure that a specific `MBeanServer` is used by setting the value of the |
|
`MBeanExporter`'s `server` property to the `MBeanServer` value returned by an |
|
`MBeanServerFactoryBean`; for example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean"/> |
|
|
|
<!-- |
|
this bean needs to be eagerly pre-instantiated in order for the exporting to occur; |
|
this means that it must not be marked as lazily initialized |
|
--> |
|
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> |
|
<property name="beans"> |
|
<map> |
|
<entry key="bean:name=testBean1" value-ref="testBean"/> |
|
</map> |
|
</property> |
|
<property name="server" ref="mbeanServer"/> |
|
</bean> |
|
|
|
<bean id="testBean" class="org.springframework.jmx.JmxTestBean"> |
|
<property name="name" value="TEST"/> |
|
<property name="age" value="100"/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
Here an instance of `MBeanServer` is created by the `MBeanServerFactoryBean` and is |
|
supplied to the `MBeanExporter` via the server property. When you supply your own |
|
`MBeanServer` instance, the `MBeanExporter` will not attempt to locate a running |
|
`MBeanServer` and will use the supplied `MBeanServer` instance. For this to work |
|
correctly, you must (of course) have a JMX implementation on your classpath. |
|
|
|
|
|
|
|
[[jmx-mbean-server]] |
|
==== Reusing an existing MBeanServer |
|
|
|
If no server is specified, the `MBeanExporter` tries to automatically detect a running |
|
`MBeanServer`. This works in most environment where only one `MBeanServer` instance is |
|
used, however when multiple instances exist, the exporter might pick the wrong server. |
|
In such cases, one should use the `MBeanServer` `agentId` to indicate which instance to |
|
be used: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean"> |
|
<!-- indicate to first look for a server --> |
|
<property name="locateExistingServerIfPossible" value="true"/> |
|
<!-- search for the MBeanServer instance with the given agentId --> |
|
<property name="agentId" value="MBeanServer_instance_agentId>"/> |
|
</bean> |
|
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> |
|
<property name="server" ref="mbeanServer"/> |
|
... |
|
</bean> |
|
</beans> |
|
---- |
|
|
|
For platforms/cases where the existing `MBeanServer` has a dynamic (or unknown) |
|
`agentId` which is retrieved through lookup methods, one should use |
|
<<beans-factory-class-static-factory-method,factory-method>>: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> |
|
<property name="server"> |
|
<!-- Custom MBeanServerLocator --> |
|
<bean class="platform.package.MBeanServerLocator" factory-method="locateMBeanServer"/> |
|
</property> |
|
</bean> |
|
|
|
<!-- other beans here --> |
|
|
|
</beans> |
|
---- |
|
|
|
|
|
|
|
[[jmx-exporting-lazy]] |
|
==== Lazy-initialized MBeans |
|
If you configure a bean with the `MBeanExporter` that is also configured for lazy |
|
initialization, then the `MBeanExporter` will __not__ break this contract and will avoid |
|
instantiating the bean. Instead, it will register a proxy with the `MBeanServer` and |
|
will defer obtaining the bean from the container until the first invocation on the proxy |
|
occurs. |
|
|
|
|
|
|
|
[[jmx-exporting-auto]] |
|
==== Automatic registration of MBeans |
|
Any beans that are exported through the `MBeanExporter` and are already valid MBeans are |
|
registered as-is with the `MBeanServer` without further intervention from Spring. MBeans |
|
can be automatically detected by the `MBeanExporter` by setting the `autodetect` |
|
property to `true`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> |
|
<property name="autodetect" value="true"/> |
|
</bean> |
|
|
|
<bean name="spring:mbean=true" class="org.springframework.jmx.export.TestDynamicMBean"/> |
|
---- |
|
|
|
Here, the bean called `spring:mbean=true` is already a valid JMX MBean and will be |
|
automatically registered by Spring. By default, beans that are autodetected for JMX |
|
registration have their bean name used as the `ObjectName`. This behavior can be |
|
overridden as detailed in <<jmx-naming>>. |
|
|
|
|
|
|
|
[[jmx-exporting-registration-behavior]] |
|
==== Controlling the registration behavior |
|
Consider the scenario where a Spring `MBeanExporter` attempts to register an `MBean` |
|
with an `MBeanServer` using the `ObjectName` `'bean:name=testBean1'`. If an `MBean` |
|
instance has already been registered under that same `ObjectName`, the default behavior |
|
is to fail (and throw an `InstanceAlreadyExistsException`). |
|
|
|
It is possible to control the behavior of exactly what happens when an `MBean` is |
|
registered with an `MBeanServer`. Spring's JMX support allows for three different |
|
registration behaviors to control the registration behavior when the registration |
|
process finds that an `MBean` has already been registered under the same `ObjectName`; |
|
these registration behaviors are summarized on the following table: |
|
|
|
[[jmx-registration-behaviors]] |
|
.Registration Behaviors |
|
[cols="1,4"] |
|
|=== |
|
| Registration behavior| Explanation |
|
|
|
| `REGISTRATION_FAIL_ON_EXISTING` |
|
| This is the default registration behavior. If an `MBean` instance has already been |
|
registered under the same `ObjectName`, the `MBean` that is being registered will not |
|
be registered and an `InstanceAlreadyExistsException` will be thrown. The existing |
|
`MBean` is unaffected. |
|
|
|
| `REGISTRATION_IGNORE_EXISTING` |
|
| If an `MBean` instance has already been registered under the same `ObjectName`, the |
|
`MBean` that is being registered will __not__ be registered. The existing `MBean` is |
|
unaffected, and no `Exception` will be thrown. This is useful in settings where |
|
multiple applications want to share a common `MBean` in a shared `MBeanServer`. |
|
|
|
| `REGISTRATION_REPLACE_EXISTING` |
|
| If an `MBean` instance has already been registered under the same `ObjectName`, the |
|
existing `MBean` that was previously registered will be unregistered and the new |
|
`MBean` will be registered in its place (the new `MBean` effectively replaces the |
|
previous instance). |
|
|=== |
|
|
|
The above values are defined as constants on the `MBeanRegistrationSupport` class (the |
|
`MBeanExporter` class derives from this superclass). If you want to change the default |
|
registration behavior, you simply need to set the value of the |
|
`registrationBehaviorName` property on your `MBeanExporter` definition to one of those |
|
values. |
|
|
|
The following example illustrates how to effect a change from the default registration |
|
behavior to the `REGISTRATION_REPLACE_EXISTING` behavior: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> |
|
<property name="beans"> |
|
<map> |
|
<entry key="bean:name=testBean1" value-ref="testBean"/> |
|
</map> |
|
</property> |
|
<property name="registrationBehaviorName" value="REGISTRATION_REPLACE_EXISTING"/> |
|
</bean> |
|
|
|
<bean id="testBean" class="org.springframework.jmx.JmxTestBean"> |
|
<property name="name" value="TEST"/> |
|
<property name="age" value="100"/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
|
|
|
|
|
|
[[jmx-interface]] |
|
=== Controlling the management interface of your beans |
|
In the previous example, you had little control over the management interface of your |
|
bean; __all__ of the __public__ properties and methods of each exported bean was exposed |
|
as JMX attributes and operations respectively. To exercise finer-grained control over |
|
exactly which properties and methods of your exported beans are actually exposed as JMX |
|
attributes and operations, Spring JMX provides a comprehensive and extensible mechanism |
|
for controlling the management interfaces of your beans. |
|
|
|
|
|
|
|
[[jmx-interface-assembler]] |
|
==== the MBeanInfoAssembler Interface |
|
|
|
Behind the scenes, the `MBeanExporter` delegates to an implementation of the |
|
`org.springframework.jmx.export.assembler.MBeanInfoAssembler` interface which is |
|
responsible for defining the management interface of each bean that is being exposed. |
|
The default implementation, |
|
`org.springframework.jmx.export.assembler.SimpleReflectiveMBeanInfoAssembler`, simply |
|
defines a management interface that exposes all public properties and methods (as you |
|
saw in the previous examples). Spring provides two additional implementations of the |
|
`MBeanInfoAssembler` interface that allow you to control the generated management |
|
interface using either source-level metadata or any arbitrary interface. |
|
|
|
|
|
|
|
[[jmx-interface-metadata]] |
|
==== Using Source-Level Metadata (Java annotations) |
|
Using the `MetadataMBeanInfoAssembler` you can define the management interfaces for your |
|
beans using source level metadata. The reading of metadata is encapsulated by the |
|
`org.springframework.jmx.export.metadata.JmxAttributeSource` interface. Spring JMX |
|
provides a default implementation which uses Java annotations, namely |
|
`org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource`. The |
|
`MetadataMBeanInfoAssembler` __must__ be configured with an implementation instance of |
|
the `JmxAttributeSource` interface for it to function correctly (there is __no__ |
|
default). |
|
|
|
To mark a bean for export to JMX, you should annotate the bean class with the |
|
`ManagedResource` annotation. Each method you wish to expose as an operation must be |
|
marked with the `ManagedOperation` annotation and each property you wish to expose must |
|
be marked with the `ManagedAttribute` annotation. When marking properties you can omit |
|
either the annotation of the getter or the setter to create a write-only or read-only |
|
attribute respectively. |
|
|
|
The example below shows the annotated version of the `JmxTestBean` class that you saw |
|
earlier: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.springframework.jmx; |
|
|
|
import org.springframework.jmx.export.annotation.ManagedResource; |
|
import org.springframework.jmx.export.annotation.ManagedOperation; |
|
import org.springframework.jmx.export.annotation.ManagedAttribute; |
|
|
|
@ManagedResource( |
|
objectName="bean:name=testBean4", |
|
description="My Managed Bean", |
|
log=true, |
|
logFile="jmx.log", |
|
currencyTimeLimit=15, |
|
persistPolicy="OnUpdate", |
|
persistPeriod=200, |
|
persistLocation="foo", |
|
persistName="bar") |
|
public class AnnotationTestBean implements IJmxTestBean { |
|
|
|
private String name; |
|
private int age; |
|
|
|
@ManagedAttribute(description="The Age Attribute", currencyTimeLimit=15) |
|
public int getAge() { |
|
return age; |
|
} |
|
|
|
public void setAge(int age) { |
|
this.age = age; |
|
} |
|
|
|
@ManagedAttribute(description="The Name Attribute", |
|
currencyTimeLimit=20, |
|
defaultValue="bar", |
|
persistPolicy="OnUpdate") |
|
public void setName(String name) { |
|
this.name = name; |
|
} |
|
|
|
@ManagedAttribute(defaultValue="foo", persistPeriod=300) |
|
public String getName() { |
|
return name; |
|
} |
|
|
|
@ManagedOperation(description="Add two numbers") |
|
@ManagedOperationParameters({ |
|
@ManagedOperationParameter(name = "x", description = "The first number"), |
|
@ManagedOperationParameter(name = "y", description = "The second number")}) |
|
public int add(int x, int y) { |
|
return x + y; |
|
} |
|
|
|
public void dontExposeMe() { |
|
throw new RuntimeException(); |
|
} |
|
|
|
} |
|
---- |
|
|
|
Here you can see that the `JmxTestBean` class is marked with the `ManagedResource` |
|
annotation and that this `ManagedResource` annotation is configured with a set of |
|
properties. These properties can be used to configure various aspects of the MBean that |
|
is generated by the `MBeanExporter`, and are explained in greater detail later in |
|
section entitled <<jmx-interface-metadata-types>>. |
|
|
|
You will also notice that both the `age` and `name` properties are annotated with the |
|
`ManagedAttribute` annotation, but in the case of the `age` property, only the getter is |
|
marked. This will cause both of these properties to be included in the management |
|
interface as attributes, but the `age` attribute will be read-only. |
|
|
|
Finally, you will notice that the `add(int, int)` method is marked with the |
|
`ManagedOperation` attribute whereas the `dontExposeMe()` method is not. This will cause |
|
the management interface to contain only one operation, `add(int, int)`, when using the |
|
`MetadataMBeanInfoAssembler`. |
|
|
|
The configuration below shows how you configure the `MBeanExporter` to use the |
|
`MetadataMBeanInfoAssembler`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> |
|
<property name="assembler" ref="assembler"/> |
|
<property name="namingStrategy" ref="namingStrategy"/> |
|
<property name="autodetect" value="true"/> |
|
</bean> |
|
|
|
<bean id="jmxAttributeSource" |
|
class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/> |
|
|
|
<!-- will create management interface using annotation metadata --> |
|
<bean id="assembler" |
|
class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler"> |
|
<property name="attributeSource" ref="jmxAttributeSource"/> |
|
</bean> |
|
|
|
<!-- will pick up the ObjectName from the annotation --> |
|
<bean id="namingStrategy" |
|
class="org.springframework.jmx.export.naming.MetadataNamingStrategy"> |
|
<property name="attributeSource" ref="jmxAttributeSource"/> |
|
</bean> |
|
|
|
<bean id="testBean" class="org.springframework.jmx.AnnotationTestBean"> |
|
<property name="name" value="TEST"/> |
|
<property name="age" value="100"/> |
|
</bean> |
|
</beans> |
|
---- |
|
|
|
Here you can see that an `MetadataMBeanInfoAssembler` bean has been configured with an |
|
instance of the `AnnotationJmxAttributeSource` class and passed to the `MBeanExporter` |
|
through the assembler property. This is all that is required to take advantage of |
|
metadata-driven management interfaces for your Spring-exposed MBeans. |
|
|
|
|
|
|
|
[[jmx-interface-metadata-types]] |
|
==== Source-Level Metadata Types |
|
The following source level metadata types are available for use in Spring JMX: |
|
|
|
[[jmx-metadata-types]] |
|
.Source-Level Metadata Types |
|
|=== |
|
| Purpose| Annotation| Annotation Type |
|
|
|
| Mark all instances of a `Class` as JMX managed resources |
|
| `@ManagedResource` |
|
| Class |
|
|
|
| Mark a method as a JMX operation |
|
| `@ManagedOperation` |
|
| Method |
|
|
|
| Mark a getter or setter as one half of a JMX attribute |
|
| `@ManagedAttribute` |
|
| Method (only getters and setters) |
|
|
|
| Define descriptions for operation parameters |
|
| `@ManagedOperationParameter` and `@ManagedOperationParameters` |
|
| Method |
|
|=== |
|
|
|
The following configuration parameters are available for use on these source-level |
|
metadata types: |
|
|
|
[[jmx-metadata-parameters]] |
|
.Source-Level Metadata Parameters |
|
[cols="1,3,1"] |
|
|=== |
|
| Parameter| Description| Applies to |
|
|
|
| `ObjectName` |
|
| Used by `MetadataNamingStrategy` to determine the `ObjectName` of a managed resource |
|
| `ManagedResource` |
|
|
|
| `description` |
|
| Sets the friendly description of the resource, attribute or operation |
|
| `ManagedResource`, `ManagedAttribute`, `ManagedOperation`, `ManagedOperationParameter` |
|
|
|
| `currencyTimeLimit` |
|
| Sets the value of the `currencyTimeLimit` descriptor field |
|
| `ManagedResource`, `ManagedAttribute` |
|
|
|
| `defaultValue` |
|
| Sets the value of the `defaultValue` descriptor field |
|
| `ManagedAttribute` |
|
|
|
| `log` |
|
| Sets the value of the `log` descriptor field |
|
| `ManagedResource` |
|
|
|
| `logFile` |
|
| Sets the value of the `logFile` descriptor field |
|
| `ManagedResource` |
|
|
|
| `persistPolicy` |
|
| Sets the value of the `persistPolicy` descriptor field |
|
| `ManagedResource` |
|
|
|
| `persistPeriod` |
|
| Sets the value of the `persistPeriod` descriptor field |
|
| `ManagedResource` |
|
|
|
| `persistLocation` |
|
| Sets the value of the `persistLocation` descriptor field |
|
| `ManagedResource` |
|
|
|
| `persistName` |
|
| Sets the value of the `persistName` descriptor field |
|
| `ManagedResource` |
|
|
|
| `name` |
|
| Sets the display name of an operation parameter |
|
| `ManagedOperationParameter` |
|
|
|
| `index` |
|
| Sets the index of an operation parameter |
|
| `ManagedOperationParameter` |
|
|=== |
|
|
|
|
|
|
|
[[jmx-interface-autodetect]] |
|
==== the AutodetectCapableMBeanInfoAssembler interface |
|
|
|
To simplify configuration even further, Spring introduces the |
|
`AutodetectCapableMBeanInfoAssembler` interface which extends the `MBeanInfoAssembler` |
|
interface to add support for autodetection of MBean resources. If you configure the |
|
`MBeanExporter` with an instance of `AutodetectCapableMBeanInfoAssembler` then it is |
|
allowed to "vote" on the inclusion of beans for exposure to JMX. |
|
|
|
Out of the box, the only implementation of the `AutodetectCapableMBeanInfo` interface is |
|
the `MetadataMBeanInfoAssembler` which will vote to include any bean which is marked |
|
with the `ManagedResource` attribute. The default approach in this case is to use the |
|
bean name as the `ObjectName` which results in a configuration like this: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> |
|
<!-- notice how no 'beans' are explicitly configured here --> |
|
<property name="autodetect" value="true"/> |
|
<property name="assembler" ref="assembler"/> |
|
</bean> |
|
|
|
<bean id="testBean" class="org.springframework.jmx.JmxTestBean"> |
|
<property name="name" value="TEST"/> |
|
<property name="age" value="100"/> |
|
</bean> |
|
|
|
<bean id="assembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler"> |
|
<property name="attributeSource"> |
|
<bean class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/> |
|
</property> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
Notice that in this configuration no beans are passed to the `MBeanExporter`; however, |
|
the `JmxTestBean` will still be registered since it is marked with the `ManagedResource` |
|
attribute and the `MetadataMBeanInfoAssembler` detects this and votes to include it. The |
|
only problem with this approach is that the name of the `JmxTestBean` now has business |
|
meaning. You can address this issue by changing the default behavior for `ObjectName` |
|
creation as defined in <<jmx-naming>>. |
|
|
|
|
|
|
|
[[jmx-interface-java]] |
|
==== Defining management interfaces using Java interfaces |
|
In addition to the `MetadataMBeanInfoAssembler`, Spring also includes the |
|
`InterfaceBasedMBeanInfoAssembler` which allows you to constrain the methods and |
|
properties that are exposed based on the set of methods defined in a collection of |
|
interfaces. |
|
|
|
Although the standard mechanism for exposing MBeans is to use interfaces and a simple |
|
naming scheme, the `InterfaceBasedMBeanInfoAssembler` extends this functionality by |
|
removing the need for naming conventions, allowing you to use more than one interface |
|
and removing the need for your beans to implement the MBean interfaces. |
|
|
|
Consider this interface that is used to define a management interface for the |
|
`JmxTestBean` class that you saw earlier: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface IJmxTestBean { |
|
|
|
public int add(int x, int y); |
|
|
|
public long myOperation(); |
|
|
|
public int getAge(); |
|
|
|
public void setAge(int age); |
|
|
|
public void setName(String name); |
|
|
|
public String getName(); |
|
|
|
} |
|
---- |
|
|
|
This interface defines the methods and properties that will be exposed as operations and |
|
attributes on the JMX MBean. The code below shows how to configure Spring JMX to use |
|
this interface as the definition for the management interface: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> |
|
<property name="beans"> |
|
<map> |
|
<entry key="bean:name=testBean5" value-ref="testBean"/> |
|
</map> |
|
</property> |
|
<property name="assembler"> |
|
<bean class="org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler"> |
|
<property name="managedInterfaces"> |
|
<value>org.springframework.jmx.IJmxTestBean</value> |
|
</property> |
|
</bean> |
|
</property> |
|
</bean> |
|
|
|
<bean id="testBean" class="org.springframework.jmx.JmxTestBean"> |
|
<property name="name" value="TEST"/> |
|
<property name="age" value="100"/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
Here you can see that the `InterfaceBasedMBeanInfoAssembler` is configured to use the |
|
`IJmxTestBean` interface when constructing the management interface for any bean. It is |
|
important to understand that beans processed by the `InterfaceBasedMBeanInfoAssembler` |
|
are __not__ required to implement the interface used to generate the JMX management |
|
interface. |
|
|
|
In the case above, the `IJmxTestBean` interface is used to construct all management |
|
interfaces for all beans. In many cases this is not the desired behavior and you may |
|
want to use different interfaces for different beans. In this case, you can pass |
|
`InterfaceBasedMBeanInfoAssembler` a `Properties` instance via the `interfaceMappings` |
|
property, where the key of each entry is the bean name and the value of each entry is a |
|
comma-separated list of interface names to use for that bean. |
|
|
|
If no management interface is specified through either the `managedInterfaces` or |
|
`interfaceMappings` properties, then the `InterfaceBasedMBeanInfoAssembler` will reflect |
|
on the bean and use all of the interfaces implemented by that bean to create the |
|
management interface. |
|
|
|
|
|
|
|
[[jmx-interface-methodnames]] |
|
==== Using MethodNameBasedMBeanInfoAssembler |
|
|
|
The `MethodNameBasedMBeanInfoAssembler` allows you to specify a list of method names |
|
that will be exposed to JMX as attributes and operations. The code below shows a sample |
|
configuration for this: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> |
|
<property name="beans"> |
|
<map> |
|
<entry key="bean:name=testBean5" value-ref="testBean"/> |
|
</map> |
|
</property> |
|
<property name="assembler"> |
|
<bean class="org.springframework.jmx.export.assembler.MethodNameBasedMBeanInfoAssembler"> |
|
<property name="managedMethods"> |
|
<value>add,myOperation,getName,setName,getAge</value> |
|
</property> |
|
</bean> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
Here you can see that the methods `add` and `myOperation` will be exposed as JMX |
|
operations and `getName()`, `setName(String)` and `getAge()` will be exposed as the |
|
appropriate half of a JMX attribute. In the code above, the method mappings apply to |
|
beans that are exposed to JMX. To control method exposure on a bean-by-bean basis, use |
|
the `methodMappings` property of `MethodNameMBeanInfoAssembler` to map bean names to |
|
lists of method names. |
|
|
|
|
|
|
|
|
|
[[jmx-naming]] |
|
=== Controlling the ObjectNames for your beans |
|
|
|
Behind the scenes, the `MBeanExporter` delegates to an implementation of the |
|
`ObjectNamingStrategy` to obtain ++ObjectName++s for each of the beans it is registering. |
|
The default implementation, `KeyNamingStrategy`, will, by default, use the key of the |
|
`beans` `Map` as the `ObjectName`. In addition, the `KeyNamingStrategy` can map the key |
|
of the `beans` `Map` to an entry in a `Properties` file (or files) to resolve the |
|
`ObjectName`. In addition to the `KeyNamingStrategy`, Spring provides two additional |
|
`ObjectNamingStrategy` implementations: the `IdentityNamingStrategy` that builds an |
|
`ObjectName` based on the JVM identity of the bean and the `MetadataNamingStrategy` that |
|
uses source level metadata to obtain the `ObjectName`. |
|
|
|
|
|
|
|
[[jmx-naming-properties]] |
|
==== Reading ObjectNames from Properties |
|
|
|
You can configure your own `KeyNamingStrategy` instance and configure it to read |
|
++ObjectName++s from a `Properties` instance rather than use bean key. The |
|
`KeyNamingStrategy` will attempt to locate an entry in the `Properties` with a key |
|
corresponding to the bean key. If no entry is found or if the `Properties` instance is |
|
`null` then the bean key itself is used. |
|
|
|
The code below shows a sample configuration for the `KeyNamingStrategy`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> |
|
<property name="beans"> |
|
<map> |
|
<entry key="testBean" value-ref="testBean"/> |
|
</map> |
|
</property> |
|
<property name="namingStrategy" ref="namingStrategy"/> |
|
</bean> |
|
|
|
<bean id="testBean" class="org.springframework.jmx.JmxTestBean"> |
|
<property name="name" value="TEST"/> |
|
<property name="age" value="100"/> |
|
</bean> |
|
|
|
<bean id="namingStrategy" class="org.springframework.jmx.export.naming.KeyNamingStrategy"> |
|
<property name="mappings"> |
|
<props> |
|
<prop key="testBean">bean:name=testBean1</prop> |
|
</props> |
|
</property> |
|
<property name="mappingLocations"> |
|
<value>names1.properties,names2.properties</value> |
|
</property> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
Here an instance of `KeyNamingStrategy` is configured with a `Properties` instance that |
|
is merged from the `Properties` instance defined by the mapping property and the |
|
properties files located in the paths defined by the mappings property. In this |
|
configuration, the `testBean` bean will be given the `ObjectName` `bean:name=testBean1` |
|
since this is the entry in the `Properties` instance that has a key corresponding to the |
|
bean key. |
|
|
|
If no entry in the `Properties` instance can be found then the bean key name is used as |
|
the `ObjectName`. |
|
|
|
|
|
|
|
[[jmx-naming-metadata]] |
|
==== Using the MetadataNamingStrategy |
|
|
|
The `MetadataNamingStrategy` uses the `objectName` property of the `ManagedResource` |
|
attribute on each bean to create the `ObjectName`. The code below shows the |
|
configuration for the `MetadataNamingStrategy`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> |
|
<property name="beans"> |
|
<map> |
|
<entry key="testBean" value-ref="testBean"/> |
|
</map> |
|
</property> |
|
<property name="namingStrategy" ref="namingStrategy"/> |
|
</bean> |
|
|
|
<bean id="testBean" class="org.springframework.jmx.JmxTestBean"> |
|
<property name="name" value="TEST"/> |
|
<property name="age" value="100"/> |
|
</bean> |
|
|
|
<bean id="namingStrategy" class="org.springframework.jmx.export.naming.MetadataNamingStrategy"> |
|
<property name="attributeSource" ref="attributeSource"/> |
|
</bean> |
|
|
|
<bean id="attributeSource" |
|
class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/> |
|
|
|
</beans> |
|
---- |
|
|
|
If no `objectName` has been provided for the `ManagedResource` attribute, then an |
|
`ObjectName` will be created with the following |
|
format:__[fully-qualified-package-name]:type=[short-classname],name=[bean-name]__. For |
|
example, the generated `ObjectName` for the following bean would be: |
|
__com.foo:type=MyClass,name=myBean__. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="myBean" class="com.foo.MyClass"/> |
|
---- |
|
|
|
|
|
|
|
[[jmx-context-mbeanexport]] |
|
==== Configuring annotation based MBean export |
|
If you prefer using <<jmx-interface-metadata,the annotation based approach>> to define |
|
your management interfaces, then a convenience subclass of `MBeanExporter` is available: |
|
`AnnotationMBeanExporter`. When defining an instance of this subclass, the |
|
`namingStrategy`, `assembler`, and `attributeSource` configuration is no longer needed, |
|
since it will always use standard Java annotation-based metadata (autodetection is |
|
always enabled as well). In fact, rather than defining an `MBeanExporter` bean, an even |
|
simpler syntax is supported by the `@EnableMBeanExport` `@Configuration` annotation. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableMBeanExport |
|
public class AppConfig { |
|
|
|
} |
|
---- |
|
|
|
If you prefer XML based configuration the `'context:mbean-export'` element serves the |
|
same purpose. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<context:mbean-export/> |
|
---- |
|
|
|
You can provide a reference to a particular MBean `server` if necessary, and the |
|
`defaultDomain` attribute (a property of `AnnotationMBeanExporter`) accepts an alternate |
|
value for the generated MBean `ObjectNames`' domains. This would be used in place of the |
|
fully qualified package name as described in the previous section on |
|
<<jmx-naming-metadata, `MetadataNamingStrategy`>>. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@EnableMBeanExport(server="myMBeanServer", defaultDomain="myDomain") |
|
@Configuration |
|
ContextConfiguration { |
|
|
|
} |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<context:mbean-export server="myMBeanServer" default-domain="myDomain"/> |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
Do not use interface-based AOP proxies in combination with autodetection of JMX |
|
annotations in your bean classes. Interface-based proxies 'hide' the target class, which |
|
also hides the JMX managed resource annotations. Hence, use target-class proxies in that |
|
case: through setting the 'proxy-target-class' flag on `<aop:config/>`, |
|
`<tx:annotation-driven/>`, etc. Otherwise, your JMX beans might be silently ignored at |
|
startup... |
|
==== |
|
|
|
|
|
|
|
|
|
[[jmx-jsr160]] |
|
=== JSR-160 Connectors |
|
For remote access, Spring JMX module offers two `FactoryBean` implementations inside the |
|
`org.springframework.jmx.support` package for creating both server- and client-side |
|
connectors. |
|
|
|
|
|
|
|
[[jmx-jsr160-server]] |
|
==== Server-side Connectors |
|
To have Spring JMX create, start and expose a JSR-160 `JMXConnectorServer` use the |
|
following configuration: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean"/> |
|
---- |
|
|
|
By default `ConnectorServerFactoryBean` creates a `JMXConnectorServer` bound to |
|
`"service:jmx:jmxmp://localhost:9875"`. The `serverConnector` bean thus exposes the |
|
local `MBeanServer` to clients through the JMXMP protocol on localhost, port 9875. Note |
|
that the JMXMP protocol is marked as optional by the JSR 160 specification: currently, |
|
the main open-source JMX implementation, MX4J, and the one provided with the JDK |
|
do __not__ support JMXMP. |
|
|
|
To specify another URL and register the `JMXConnectorServer` itself with the |
|
`MBeanServer` use the `serviceUrl` and `ObjectName` properties respectively: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="serverConnector" |
|
class="org.springframework.jmx.support.ConnectorServerFactoryBean"> |
|
<property name="objectName" value="connector:name=rmi"/> |
|
<property name="serviceUrl" |
|
value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/myconnector"/> |
|
</bean> |
|
---- |
|
|
|
If the `ObjectName` property is set Spring will automatically register your connector |
|
with the `MBeanServer` under that `ObjectName`. The example below shows the full set of |
|
parameters which you can pass to the `ConnectorServerFactoryBean` when creating a |
|
JMXConnector: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="serverConnector" |
|
class="org.springframework.jmx.support.ConnectorServerFactoryBean"> |
|
<property name="objectName" value="connector:name=iiop"/> |
|
<property name="serviceUrl" |
|
value="service:jmx:iiop://localhost/jndi/iiop://localhost:900/myconnector"/> |
|
<property name="threaded" value="true"/> |
|
<property name="daemon" value="true"/> |
|
<property name="environment"> |
|
<map> |
|
<entry key="someKey" value="someValue"/> |
|
</map> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
Note that when using a RMI-based connector you need the lookup service (tnameserv or |
|
rmiregistry) to be started in order for the name registration to complete. If you are |
|
using Spring to export remote services for you via RMI, then Spring will already have |
|
constructed an RMI registry. If not, you can easily start a registry using the following |
|
snippet of configuration: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean"> |
|
<property name="port" value="1099"/> |
|
</bean> |
|
---- |
|
|
|
|
|
|
|
[[jmx-jsr160-client]] |
|
==== Client-side Connectors |
|
To create an `MBeanServerConnection` to a remote JSR-160 enabled `MBeanServer` use the |
|
`MBeanServerConnectionFactoryBean` as shown below: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="clientConnector" class="org.springframework.jmx.support.MBeanServerConnectionFactoryBean"> |
|
<property name="serviceUrl" value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxrmi"/> |
|
</bean> |
|
---- |
|
|
|
|
|
|
|
[[jmx-jsr160-protocols]] |
|
==== JMX over Burlap/Hessian/SOAP |
|
JSR-160 permits extensions to the way in which communication is done between the client |
|
and the server. The examples above are using the mandatory RMI-based implementation |
|
required by the JSR-160 specification (IIOP and JRMP) and the (optional) JMXMP. By using |
|
other providers or JMX implementations (such as http://mx4j.sourceforge.net[MX4J]) you |
|
can take advantage of protocols like SOAP, Hessian, Burlap over simple HTTP or SSL and |
|
others: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean"> |
|
<property name="objectName" value="connector:name=burlap"/> |
|
<property name="serviceUrl" value="service:jmx:burlap://localhost:9874"/> |
|
</bean> |
|
---- |
|
|
|
In the case of the above example, MX4J 3.0.0 was used; see the official MX4J |
|
documentation for more information. |
|
|
|
|
|
|
|
|
|
[[jmx-proxy]] |
|
=== Accessing MBeans via Proxies |
|
Spring JMX allows you to create proxies that re-route calls to MBeans registered in a |
|
local or remote `MBeanServer`. These proxies provide you with a standard Java interface |
|
through which you can interact with your MBeans. The code below shows how to configure a |
|
proxy for an MBean running in a local `MBeanServer`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="proxy" class="org.springframework.jmx.access.MBeanProxyFactoryBean"> |
|
<property name="objectName" value="bean:name=testBean"/> |
|
<property name="proxyInterface" value="org.springframework.jmx.IJmxTestBean"/> |
|
</bean> |
|
---- |
|
|
|
Here you can see that a proxy is created for the MBean registered under the |
|
`ObjectName`: `bean:name=testBean`. The set of interfaces that the proxy will implement |
|
is controlled by the `proxyInterfaces` property and the rules for mapping methods and |
|
properties on these interfaces to operations and attributes on the MBean are the same |
|
rules used by the `InterfaceBasedMBeanInfoAssembler`. |
|
|
|
The `MBeanProxyFactoryBean` can create a proxy to any MBean that is accessible via an |
|
`MBeanServerConnection`. By default, the local `MBeanServer` is located and used, but |
|
you can override this and provide an `MBeanServerConnection` pointing to a remote |
|
`MBeanServer` to cater for proxies pointing to remote MBeans: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="clientConnector" |
|
class="org.springframework.jmx.support.MBeanServerConnectionFactoryBean"> |
|
<property name="serviceUrl" value="service:jmx:rmi://remotehost:9875"/> |
|
</bean> |
|
|
|
<bean id="proxy" class="org.springframework.jmx.access.MBeanProxyFactoryBean"> |
|
<property name="objectName" value="bean:name=testBean"/> |
|
<property name="proxyInterface" value="org.springframework.jmx.IJmxTestBean"/> |
|
<property name="server" ref="clientConnector"/> |
|
</bean> |
|
---- |
|
|
|
Here you can see that we create an `MBeanServerConnection` pointing to a remote machine |
|
using the `MBeanServerConnectionFactoryBean`. This `MBeanServerConnection` is then |
|
passed to the `MBeanProxyFactoryBean` via the `server` property. The proxy that is |
|
created will forward all invocations to the `MBeanServer` via this |
|
`MBeanServerConnection`. |
|
|
|
|
|
|
|
|
|
[[jmx-notifications]] |
|
=== Notifications |
|
Spring's JMX offering includes comprehensive support for JMX notifications. |
|
|
|
|
|
|
|
[[jmx-notifications-listeners]] |
|
==== Registering Listeners for Notifications |
|
Spring's JMX support makes it very easy to register any number of |
|
`NotificationListeners` with any number of MBeans (this includes MBeans exported by |
|
Spring's `MBeanExporter` and MBeans registered via some other mechanism). By way of an |
|
example, consider the scenario where one would like to be informed (via a |
|
`Notification`) each and every time an attribute of a target MBean changes. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package com.example; |
|
|
|
import javax.management.AttributeChangeNotification; |
|
import javax.management.Notification; |
|
import javax.management.NotificationFilter; |
|
import javax.management.NotificationListener; |
|
|
|
public class ConsoleLoggingNotificationListener |
|
implements NotificationListener, NotificationFilter { |
|
|
|
public void handleNotification(Notification notification, Object handback) { |
|
System.out.println(notification); |
|
System.out.println(handback); |
|
} |
|
|
|
public boolean isNotificationEnabled(Notification notification) { |
|
return AttributeChangeNotification.class.isAssignableFrom(notification.getClass()); |
|
} |
|
|
|
} |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> |
|
<property name="beans"> |
|
<map> |
|
<entry key="bean:name=testBean1" value-ref="testBean"/> |
|
</map> |
|
</property> |
|
<property name="notificationListenerMappings"> |
|
<map> |
|
<entry key="bean:name=testBean1"> |
|
<bean class="com.example.ConsoleLoggingNotificationListener"/> |
|
</entry> |
|
</map> |
|
</property> |
|
</bean> |
|
|
|
<bean id="testBean" class="org.springframework.jmx.JmxTestBean"> |
|
<property name="name" value="TEST"/> |
|
<property name="age" value="100"/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
With the above configuration in place, every time a JMX `Notification` is broadcast from |
|
the target MBean ( `bean:name=testBean1`), the `ConsoleLoggingNotificationListener` bean |
|
that was registered as a listener via the `notificationListenerMappings` property will |
|
be notified. The `ConsoleLoggingNotificationListener` bean can then take whatever action |
|
it deems appropriate in response to the `Notification`. |
|
|
|
You can also use straight bean names as the link between exported beans and listeners: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> |
|
<property name="beans"> |
|
<map> |
|
<entry key="bean:name=testBean1" value-ref="testBean"/> |
|
</map> |
|
</property> |
|
<property name="notificationListenerMappings"> |
|
<map> |
|
<entry key="__testBean__"> |
|
<bean class="com.example.ConsoleLoggingNotificationListener"/> |
|
</entry> |
|
</map> |
|
</property> |
|
</bean> |
|
|
|
<bean id="__testBean__" class="org.springframework.jmx.JmxTestBean"> |
|
<property name="name" value="TEST"/> |
|
<property name="age" value="100"/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
If one wants to register a single `NotificationListener` instance for all of the beans |
|
that the enclosing `MBeanExporter` is exporting, one can use the special wildcard `'*'` |
|
(sans quotes) as the key for an entry in the `notificationListenerMappings` property |
|
map; for example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<property name="notificationListenerMappings"> |
|
<map> |
|
<entry key="*"> |
|
<bean class="com.example.ConsoleLoggingNotificationListener"/> |
|
</entry> |
|
</map> |
|
</property> |
|
---- |
|
|
|
If one needs to do the inverse (that is, register a number of distinct listeners against |
|
an MBean), then one has to use the `notificationListeners` list property instead (and in |
|
preference to the `notificationListenerMappings` property). This time, instead of |
|
configuring simply a `NotificationListener` for a single MBean, one configures |
|
`NotificationListenerBean` instances... a `NotificationListenerBean` encapsulates a |
|
`NotificationListener` and the `ObjectName` (or `ObjectNames`) that it is to be |
|
registered against in an `MBeanServer`. The `NotificationListenerBean` also encapsulates |
|
a number of other properties such as a `NotificationFilter` and an arbitrary handback |
|
object that can be used in advanced JMX notification scenarios. |
|
|
|
The configuration when using `NotificationListenerBean` instances is not wildly |
|
different to what was presented previously: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> |
|
<property name="beans"> |
|
<map> |
|
<entry key="bean:name=testBean1" value-ref="testBean"/> |
|
</map> |
|
</property> |
|
<property name="notificationListeners"> |
|
<list> |
|
<bean class="org.springframework.jmx.export.NotificationListenerBean"> |
|
<constructor-arg> |
|
<bean class="com.example.ConsoleLoggingNotificationListener"/> |
|
</constructor-arg> |
|
<property name="mappedObjectNames"> |
|
<list> |
|
<value>bean:name=testBean1</value> |
|
</list> |
|
</property> |
|
</bean> |
|
</list> |
|
</property> |
|
</bean> |
|
|
|
<bean id="testBean" class="org.springframework.jmx.JmxTestBean"> |
|
<property name="name" value="TEST"/> |
|
<property name="age" value="100"/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
The above example is equivalent to the first notification example. Lets assume then that |
|
we want to be given a handback object every time a `Notification` is raised, and that |
|
additionally we want to filter out extraneous `Notifications` by supplying a |
|
`NotificationFilter`. (For a full discussion of just what a handback object is, and |
|
indeed what a `NotificationFilter` is, please do consult that section of the JMX |
|
specification (1.2) entitled 'The JMX Notification Model'.) |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> |
|
<property name="beans"> |
|
<map> |
|
<entry key="bean:name=testBean1" value-ref="testBean1"/> |
|
<entry key="bean:name=testBean2" value-ref="testBean2"/> |
|
</map> |
|
</property> |
|
<property name="notificationListeners"> |
|
<list> |
|
<bean class="org.springframework.jmx.export.NotificationListenerBean"> |
|
<constructor-arg ref="customerNotificationListener"/> |
|
<property name="mappedObjectNames"> |
|
<list> |
|
<!-- handles notifications from two distinct MBeans --> |
|
<value>bean:name=testBean1</value> |
|
<value>bean:name=testBean2</value> |
|
</list> |
|
</property> |
|
<property name="handback"> |
|
<bean class="java.lang.String"> |
|
<constructor-arg value="This could be anything..."/> |
|
</bean> |
|
</property> |
|
<property name="notificationFilter" ref="customerNotificationListener"/> |
|
</bean> |
|
</list> |
|
</property> |
|
</bean> |
|
|
|
<!-- implements both the NotificationListener and NotificationFilter interfaces --> |
|
<bean id="customerNotificationListener" class="com.example.ConsoleLoggingNotificationListener"/> |
|
|
|
<bean id="testBean1" class="org.springframework.jmx.JmxTestBean"> |
|
<property name="name" value="TEST"/> |
|
<property name="age" value="100"/> |
|
</bean> |
|
|
|
<bean id="testBean2" class="org.springframework.jmx.JmxTestBean"> |
|
<property name="name" value="ANOTHER TEST"/> |
|
<property name="age" value="200"/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
|
|
|
|
[[jmx-notifications-publishing]] |
|
==== Publishing Notifications |
|
Spring provides support not just for registering to receive `Notifications`, but also |
|
for publishing `Notifications`. |
|
|
|
[NOTE] |
|
==== |
|
Please note that this section is really only relevant to Spring managed beans that have |
|
been exposed as MBeans via an `MBeanExporter`; any existing, user-defined MBeans should |
|
use the standard JMX APIs for notification publication. |
|
==== |
|
|
|
The key interface in Spring's JMX notification publication support is the |
|
`NotificationPublisher` interface (defined in the |
|
`org.springframework.jmx.export.notification` package). Any bean that is going to be |
|
exported as an MBean via an `MBeanExporter` instance can implement the related |
|
`NotificationPublisherAware` interface to gain access to a `NotificationPublisher` |
|
instance. The `NotificationPublisherAware` interface simply supplies an instance of a |
|
`NotificationPublisher` to the implementing bean via a simple setter method, which the |
|
bean can then use to publish `Notifications`. |
|
|
|
As stated in the javadocs of the `NotificationPublisher` class, managed beans that are |
|
publishing events via the `NotificationPublisher` mechanism are __not__ responsible for |
|
the state management of any notification listeners and the like ... Spring's JMX support |
|
will take care of handling all the JMX infrastructure issues. All one need do as an |
|
application developer is implement the `NotificationPublisherAware` interface and start |
|
publishing events using the supplied `NotificationPublisher` instance. Note that the |
|
`NotificationPublisher` will be set __after__ the managed bean has been registered with |
|
an `MBeanServer`. |
|
|
|
Using a `NotificationPublisher` instance is quite straightforward... one simply creates |
|
a JMX `Notification` instance (or an instance of an appropriate `Notification` |
|
subclass), populates the notification with the data pertinent to the event that is to be |
|
published, and one then invokes the `sendNotification(Notification)` on the |
|
`NotificationPublisher` instance, passing in the `Notification`. |
|
|
|
Find below a simple example... in this scenario, exported instances of the `JmxTestBean` |
|
are going to publish a `NotificationEvent` every time the `add(int, int)` operation is |
|
invoked. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.springframework.jmx; |
|
|
|
import org.springframework.jmx.export.notification.NotificationPublisherAware; |
|
import org.springframework.jmx.export.notification.NotificationPublisher; |
|
import javax.management.Notification; |
|
|
|
public class JmxTestBean implements IJmxTestBean, NotificationPublisherAware { |
|
|
|
private String name; |
|
private int age; |
|
private boolean isSuperman; |
|
private NotificationPublisher publisher; |
|
|
|
// other getters and setters omitted for clarity |
|
|
|
public int add(int x, int y) { |
|
int answer = x + y; |
|
this.publisher.sendNotification(new Notification("add", this, 0)); |
|
return answer; |
|
} |
|
|
|
public void dontExposeMe() { |
|
throw new RuntimeException(); |
|
} |
|
|
|
public void setNotificationPublisher(NotificationPublisher notificationPublisher) { |
|
this.publisher = notificationPublisher; |
|
} |
|
|
|
} |
|
---- |
|
|
|
The `NotificationPublisher` interface and the machinery to get it all working is one of |
|
the nicer features of Spring's JMX support. It does however come with the price tag of |
|
coupling your classes to both Spring and JMX; as always, the advice here is to be |
|
pragmatic... if you need the functionality offered by the `NotificationPublisher` and |
|
you can accept the coupling to both Spring and JMX, then do so. |
|
|
|
|
|
|
|
|
|
[[jmx-resources]] |
|
=== Further Resources |
|
This section contains links to further resources about JMX. |
|
|
|
* The http://www.oracle.com/technetwork/java/javase/tech/javamanagement-140525.html[JMX |
|
homepage] at Oracle |
|
* The http://jcp.org/aboutJava/communityprocess/final/jsr003/index3.html[JMX |
|
specification] (JSR-000003) |
|
* The http://jcp.org/aboutJava/communityprocess/final/jsr160/index.html[JMX Remote API |
|
specification] (JSR-000160) |
|
* The http://mx4j.sourceforge.net/[MX4J homepage] (an Open Source implementation of |
|
various JMX specs) |
|
|
|
|
|
|
|
|
|
|
|
[[cci]] |
|
== JCA CCI |
|
|
|
|
|
|
|
|
|
[[cci-introduction]] |
|
=== Introduction |
|
Java EE provides a specification to standardize access to enterprise information systems |
|
(EIS): the JCA (Java EE Connector Architecture). This specification is divided into |
|
several different parts: |
|
|
|
* SPI (Service provider interfaces) that the connector provider must implement. These |
|
interfaces constitute a resource adapter which can be deployed on a Java EE |
|
application server. In such a scenario, the server manages connection pooling, |
|
transaction and security (managed mode). The application server is also responsible |
|
for managing the configuration, which is held outside the client application. A |
|
connector can be used without an application server as well; in this case, the |
|
application must configure it directly (non-managed mode). |
|
* CCI (Common Client Interface) that an application can use to interact with the |
|
connector and thus communicate with an EIS. An API for local transaction demarcation |
|
is provided as well. |
|
|
|
The aim of the Spring CCI support is to provide classes to access a CCI connector in |
|
typical Spring style, leveraging the Spring Framework's general resource and transaction |
|
management facilities. |
|
|
|
[NOTE] |
|
==== |
|
The client side of connectors doesn't alway use CCI. Some connectors expose their own |
|
APIs, only providing JCA resource adapter to use the system contracts of a Java EE |
|
container (connection pooling, global transactions, security). Spring does not offer |
|
special support for such connector-specific APIs. |
|
==== |
|
|
|
|
|
|
|
|
|
[[cci-config]] |
|
=== Configuring CCI |
|
|
|
|
|
|
|
[[cci-config-connector]] |
|
==== Connector configuration |
|
The base resource to use JCA CCI is the `ConnectionFactory` interface. The connector |
|
used must provide an implementation of this interface. |
|
|
|
To use your connector, you can deploy it on your application server and fetch the |
|
`ConnectionFactory` from the server's JNDI environment (managed mode). The connector |
|
must be packaged as a RAR file (resource adapter archive) and contain a `ra.xml` file to |
|
describe its deployment characteristics. The actual name of the resource is specified |
|
when you deploy it. To access it within Spring, simply use Spring's |
|
`JndiObjectFactoryBean` / `<jee:jndi-lookup>` fetch the factory by its JNDI name. |
|
|
|
Another way to use a connector is to embed it in your application (non-managed mode), |
|
not using an application server to deploy and configure it. Spring offers the |
|
possibility to configure a connector as a bean, through a provided `FactoryBean` ( |
|
`LocalConnectionFactoryBean`). In this manner, you only need the connector library in |
|
the classpath (no RAR file and no `ra.xml` descriptor needed). The library must be |
|
extracted from the connector's RAR file, if necessary. |
|
|
|
Once you have got access to your `ConnectionFactory` instance, you can inject it into |
|
your components. These components can either be coded against the plain CCI API or |
|
leverage Spring's support classes for CCI access (e.g. `CciTemplate`). |
|
|
|
[NOTE] |
|
==== |
|
When you use a connector in non-managed mode, you can't use global transactions because |
|
the resource is never enlisted / delisted in the current global transaction of the |
|
current thread. The resource is simply not aware of any global Java EE transactions that |
|
might be running. |
|
==== |
|
|
|
|
|
|
|
[[cci-config-connectionfactory]] |
|
==== ConnectionFactory configuration in Spring |
|
|
|
In order to make connections to the EIS, you need to obtain a `ConnectionFactory` from |
|
the application server if you are in a managed mode, or directly from Spring if you are |
|
in a non-managed mode. |
|
|
|
In a managed mode, you access a `ConnectionFactory` from JNDI; its properties will be |
|
configured in the application server. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<jee:jndi-lookup id="eciConnectionFactory" jndi-name="eis/cicseci"/> |
|
---- |
|
|
|
In non-managed mode, you must configure the `ConnectionFactory` you want to use in the |
|
configuration of Spring as a JavaBean. The `LocalConnectionFactoryBean` class offers |
|
this setup style, passing in the `ManagedConnectionFactory` implementation of your |
|
connector, exposing the application-level CCI `ConnectionFactory`. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="eciManagedConnectionFactory" class="com.ibm.connector2.cics.ECIManagedConnectionFactory"> |
|
<property name="serverName" value="TXSERIES"/> |
|
<property name="connectionURL" value="tcp://localhost/"/> |
|
<property name="portNumber" value="2006"/> |
|
</bean> |
|
|
|
<bean id="eciConnectionFactory" class="org.springframework.jca.support.LocalConnectionFactoryBean"> |
|
<property name="managedConnectionFactory" ref="eciManagedConnectionFactory"/> |
|
</bean> |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
You can't directly instantiate a specific `ConnectionFactory`. You need to go through |
|
the corresponding implementation of the `ManagedConnectionFactory` interface for your |
|
connector. This interface is part of the JCA SPI specification. |
|
==== |
|
|
|
|
|
|
|
[[cci-config-cci-connections]] |
|
==== Configuring CCI connections |
|
JCA CCI allow the developer to configure the connections to the EIS using the |
|
`ConnectionSpec` implementation of your connector. In order to configure its properties, |
|
you need to wrap the target connection factory with a dedicated adapter, |
|
`ConnectionSpecConnectionFactoryAdapter`. So, the dedicated `ConnectionSpec` can be |
|
configured with the property `connectionSpec` (as an inner bean). |
|
|
|
This property is not mandatory because the CCI `ConnectionFactory` interface defines two |
|
different methods to obtain a CCI connection. Some of the `ConnectionSpec` properties |
|
can often be configured in the application server (in managed mode) or on the |
|
corresponding local `ManagedConnectionFactory` implementation. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface ConnectionFactory implements Serializable, Referenceable { |
|
... |
|
Connection getConnection() throws ResourceException; |
|
Connection getConnection(ConnectionSpec connectionSpec) throws ResourceException; |
|
... |
|
} |
|
---- |
|
|
|
Spring provides a `ConnectionSpecConnectionFactoryAdapter` that allows for specifying a |
|
`ConnectionSpec` instance to use for all operations on a given factory. If the adapter's |
|
`connectionSpec` property is specified, the adapter uses the `getConnection` variant |
|
with the `ConnectionSpec` argument, otherwise the variant without argument. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="managedConnectionFactory" |
|
class="com.sun.connector.cciblackbox.CciLocalTxManagedConnectionFactory"> |
|
<property name="connectionURL" value="jdbc:hsqldb:hsql://localhost:9001"/> |
|
<property name="driverName" value="org.hsqldb.jdbcDriver"/> |
|
</bean> |
|
|
|
<bean id="targetConnectionFactory" |
|
class="org.springframework.jca.support.LocalConnectionFactoryBean"> |
|
<property name="managedConnectionFactory" ref="managedConnectionFactory"/> |
|
</bean> |
|
|
|
<bean id="connectionFactory" |
|
class="org.springframework.jca.cci.connection.ConnectionSpecConnectionFactoryAdapter"> |
|
<property name="targetConnectionFactory" ref="targetConnectionFactory"/> |
|
<property name="connectionSpec"> |
|
<bean class="com.sun.connector.cciblackbox.CciConnectionSpec"> |
|
<property name="user" value="sa"/> |
|
<property name="password" value=""/> |
|
</bean> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
|
|
|
|
[[cci-config-single-connection]] |
|
==== Using a single CCI connection |
|
If you want to use a single CCI connection, Spring provides a further |
|
`ConnectionFactory` adapter to manage this. The `SingleConnectionFactory` adapter class |
|
will open a single connection lazily and close it when this bean is destroyed at |
|
application shutdown. This class will expose special `Connection` proxies that behave |
|
accordingly, all sharing the same underlying physical connection. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="eciManagedConnectionFactory" |
|
class="com.ibm.connector2.cics.ECIManagedConnectionFactory"> |
|
<property name="serverName" value="TEST"/> |
|
<property name="connectionURL" value="tcp://localhost/"/> |
|
<property name="portNumber" value="2006"/> |
|
</bean> |
|
|
|
<bean id="targetEciConnectionFactory" |
|
class="org.springframework.jca.support.LocalConnectionFactoryBean"> |
|
<property name="managedConnectionFactory" ref="eciManagedConnectionFactory"/> |
|
</bean> |
|
|
|
<bean id="eciConnectionFactory" |
|
class="org.springframework.jca.cci.connection.SingleConnectionFactory"> |
|
<property name="targetConnectionFactory" ref="targetEciConnectionFactory"/> |
|
</bean> |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
This `ConnectionFactory` adapter cannot directly be configured with a `ConnectionSpec`. |
|
Use an intermediary `ConnectionSpecConnectionFactoryAdapter` that the |
|
`SingleConnectionFactory` talks to if you require a single connection for a specific |
|
`ConnectionSpec`. |
|
==== |
|
|
|
|
|
|
|
|
|
[[cci-using]] |
|
=== Using Spring's CCI access support |
|
|
|
|
|
|
|
[[cci-record-creator]] |
|
==== Record conversion |
|
One of the aims of the JCA CCI support is to provide convenient facilities for |
|
manipulating CCI records. The developer can specify the strategy to create records and |
|
extract datas from records, for use with Spring's `CciTemplate`. The following |
|
interfaces will configure the strategy to use input and output records if you don't want |
|
to work with records directly in your application. |
|
|
|
In order to create an input `Record`, the developer can use a dedicated implementation |
|
of the `RecordCreator` interface. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface RecordCreator { |
|
|
|
Record createRecord(RecordFactory recordFactory) throws ResourceException, DataAccessException; |
|
|
|
} |
|
---- |
|
|
|
As you can see, the `createRecord(..)` method receives a `RecordFactory` instance as |
|
parameter, which corresponds to the `RecordFactory` of the `ConnectionFactory` used. |
|
This reference can be used to create `IndexedRecord` or `MappedRecord` instances. The |
|
following sample shows how to use the `RecordCreator` interface and indexed/mapped |
|
records. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MyRecordCreator implements RecordCreator { |
|
|
|
public Record createRecord(RecordFactory recordFactory) throws ResourceException { |
|
IndexedRecord input = recordFactory.createIndexedRecord("input"); |
|
input.add(new Integer(id)); |
|
return input; |
|
} |
|
|
|
} |
|
---- |
|
|
|
An output `Record` can be used to receive data back from the EIS. Hence, a specific |
|
implementation of the `RecordExtractor` interface can be passed to Spring's |
|
`CciTemplate` for extracting data from the output `Record`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface RecordExtractor { |
|
|
|
Object extractData(Record record) throws ResourceException, SQLException, DataAccessException; |
|
|
|
} |
|
---- |
|
|
|
The following sample shows how to use the `RecordExtractor` interface. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MyRecordExtractor implements RecordExtractor { |
|
|
|
public Object extractData(Record record) throws ResourceException { |
|
CommAreaRecord commAreaRecord = (CommAreaRecord) record; |
|
String str = new String(commAreaRecord.toByteArray()); |
|
String field1 = string.substring(0,6); |
|
String field2 = string.substring(6,1); |
|
return new OutputObject(Long.parseLong(field1), field2); |
|
} |
|
|
|
} |
|
---- |
|
|
|
|
|
|
|
[[cci-using-template]] |
|
==== the CciTemplate |
|
|
|
The `CciTemplate` is the central class of the core CCI support package ( |
|
`org.springframework.jca.cci.core`). It simplifies the use of CCI since it handles the |
|
creation and release of resources. This helps to avoid common errors like forgetting to |
|
always close the connection. It cares for the lifecycle of connection and interaction |
|
objects, letting application code focus on generating input records from application |
|
data and extracting application data from output records. |
|
|
|
The JCA CCI specification defines two distinct methods to call operations on an EIS. The |
|
CCI `Interaction` interface provides two execute method signatures: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface javax.resource.cci.Interaction { |
|
|
|
... |
|
|
|
boolean execute(InteractionSpec spec, Record input, Record output) throws ResourceException; |
|
|
|
Record execute(InteractionSpec spec, Record input) throws ResourceException; |
|
|
|
... |
|
|
|
} |
|
---- |
|
|
|
Depending on the template method called, `CciTemplate` will know which `execute` method |
|
to call on the interaction. In any case, a correctly initialized `InteractionSpec` |
|
instance is mandatory. |
|
|
|
`CciTemplate.execute(..)` can be used in two ways: |
|
|
|
* With direct `Record` arguments. In this case, you simply need to pass the CCI input |
|
record in, and the returned object be the corresponding CCI output record. |
|
* With application objects, using record mapping. In this case, you need to provide |
|
corresponding `RecordCreator` and `RecordExtractor` instances. |
|
|
|
With the first approach, the following methods of the template will be used. These |
|
methods directly correspond to those on the `Interaction` interface. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class CciTemplate implements CciOperations { |
|
|
|
public Record execute(InteractionSpec spec, Record inputRecord) |
|
throws DataAccessException { ... } |
|
|
|
public void execute(InteractionSpec spec, Record inputRecord, Record outputRecord) |
|
throws DataAccessException { ... } |
|
|
|
} |
|
---- |
|
|
|
With the second approach, we need to specify the record creation and record extraction |
|
strategies as arguments. The interfaces used are those describe in the previous section |
|
on record conversion. The corresponding `CciTemplate` methods are the following: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class CciTemplate implements CciOperations { |
|
|
|
public Record execute(InteractionSpec spec, |
|
RecordCreator inputCreator) throws DataAccessException { |
|
// ... |
|
} |
|
|
|
public Object execute(InteractionSpec spec, Record inputRecord, |
|
RecordExtractor outputExtractor) throws DataAccessException { |
|
// ... |
|
} |
|
|
|
public Object execute(InteractionSpec spec, RecordCreator creator, |
|
RecordExtractor extractor) throws DataAccessException { |
|
// ... |
|
} |
|
|
|
} |
|
---- |
|
|
|
Unless the `outputRecordCreator` property is set on the template (see the following |
|
section), every method will call the corresponding `execute` method of the CCI |
|
`Interaction` with two parameters: `InteractionSpec` and input `Record`, receiving an |
|
output `Record` as return value. |
|
|
|
`CciTemplate` also provides methods to create `IndexRecord` and `MappedRecord` outside a |
|
`RecordCreator` implementation, through its `createIndexRecord(..)` and |
|
`createMappedRecord(..)` methods. This can be used within DAO implementations to create |
|
`Record` instances to pass into corresponding `CciTemplate.execute(..)` methods. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class CciTemplate implements CciOperations { |
|
|
|
public IndexedRecord createIndexedRecord(String name) throws DataAccessException { ... } |
|
|
|
public MappedRecord createMappedRecord(String name) throws DataAccessException { ... } |
|
|
|
} |
|
---- |
|
|
|
|
|
|
|
[[cci-using-dao]] |
|
==== DAO support |
|
Spring's CCI support provides a abstract class for DAOs, supporting injection of a |
|
`ConnectionFactory` or a `CciTemplate` instances. The name of the class is |
|
`CciDaoSupport`: It provides simple `setConnectionFactory` and `setCciTemplate` methods. |
|
Internally, this class will create a `CciTemplate` instance for a passed-in |
|
`ConnectionFactory`, exposing it to concrete data access implementations in subclasses. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public abstract class CciDaoSupport { |
|
|
|
public void setConnectionFactory(ConnectionFactory connectionFactory) { |
|
// ... |
|
} |
|
|
|
public ConnectionFactory getConnectionFactory() { |
|
// ... |
|
} |
|
|
|
public void setCciTemplate(CciTemplate cciTemplate) { |
|
// ... |
|
} |
|
|
|
public CciTemplate getCciTemplate() { |
|
// ... |
|
} |
|
|
|
} |
|
---- |
|
|
|
|
|
|
|
[[automatic-output-generation]] |
|
==== Automatic output record generation |
|
If the connector used only supports the `Interaction.execute(..)` method with input and |
|
output records as parameters (that is, it requires the desired output record to be |
|
passed in instead of returning an appropriate output record), you can set the |
|
`outputRecordCreator` property of the `CciTemplate` to automatically generate an output |
|
record to be filled by the JCA connector when the response is received. This record will |
|
be then returned to the caller of the template. |
|
|
|
This property simply holds an implementation of the `RecordCreator` interface, used for |
|
that purpose. The `RecordCreator` interface has already been discussed in |
|
<<cci-record-creator>>. The `outputRecordCreator` property must be directly specified on |
|
the `CciTemplate`. This could be done in the application code like so: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
cciTemplate.setOutputRecordCreator(new EciOutputRecordCreator()); |
|
---- |
|
|
|
Or (recommended) in the Spring configuration, if the `CciTemplate` is configured as a |
|
dedicated bean instance: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="eciOutputRecordCreator" class="eci.EciOutputRecordCreator"/> |
|
|
|
<bean id="cciTemplate" class="org.springframework.jca.cci.core.CciTemplate"> |
|
<property name="connectionFactory" ref="eciConnectionFactory"/> |
|
<property name="outputRecordCreator" ref="eciOutputRecordCreator"/> |
|
</bean> |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
As the `CciTemplate` class is thread-safe, it will usually be configured as a shared |
|
instance. |
|
==== |
|
|
|
|
|
|
|
[[template-summary]] |
|
==== Summary |
|
The following table summarizes the mechanisms of the `CciTemplate` class and the |
|
corresponding methods called on the CCI `Interaction` interface: |
|
|
|
[[cci-interaction-execute-methods]] |
|
.Usage of Interaction execute methods |
|
[cols="3,1,3"] |
|
|=== |
|
| CciTemplate method signature| CciTemplate outputRecordCreator property| execute method called on the CCI Interaction |
|
|
|
| Record execute(InteractionSpec, Record) |
|
| not set |
|
| Record execute(InteractionSpec, Record) |
|
|
|
| Record execute(InteractionSpec, Record) |
|
| set |
|
| boolean execute(InteractionSpec, Record, Record) |
|
|
|
| void execute(InteractionSpec, Record, Record) |
|
| not set |
|
| void execute(InteractionSpec, Record, Record) |
|
|
|
| void execute(InteractionSpec, Record, Record) |
|
| set |
|
| void execute(InteractionSpec, Record, Record) |
|
|
|
| Record execute(InteractionSpec, RecordCreator) |
|
| not set |
|
| Record execute(InteractionSpec, Record) |
|
|
|
| Record execute(InteractionSpec, RecordCreator) |
|
| set |
|
| void execute(InteractionSpec, Record, Record) |
|
|
|
| Record execute(InteractionSpec, Record, RecordExtractor) |
|
| not set |
|
| Record execute(InteractionSpec, Record) |
|
|
|
| Record execute(InteractionSpec, Record, RecordExtractor) |
|
| set |
|
| void execute(InteractionSpec, Record, Record) |
|
|
|
| Record execute(InteractionSpec, RecordCreator, RecordExtractor) |
|
| not set |
|
| Record execute(InteractionSpec, Record) |
|
|
|
| Record execute(InteractionSpec, RecordCreator, RecordExtractor) |
|
| set |
|
| void execute(InteractionSpec, Record, Record) |
|
|=== |
|
|
|
|
|
|
|
[[cci-straight]] |
|
==== Using a CCI Connection and Interaction directly |
|
|
|
`CciTemplate` also offers the possibility to work directly with CCI connections and |
|
interactions, in the same manner as `JdbcTemplate` and `JmsTemplate`. This is useful |
|
when you want to perform multiple operations on a CCI connection or interaction, for |
|
example. |
|
|
|
The interface `ConnectionCallback` provides a CCI `Connection` as argument, in order to |
|
perform custom operations on it, plus the CCI `ConnectionFactory` which the `Connection` |
|
was created with. The latter can be useful for example to get an associated |
|
`RecordFactory` instance and create indexed/mapped records, for example. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface ConnectionCallback { |
|
|
|
Object doInConnection(Connection connection, ConnectionFactory connectionFactory) |
|
throws ResourceException, SQLException, DataAccessException; |
|
|
|
} |
|
---- |
|
|
|
The interface `InteractionCallback` provides the CCI `Interaction`, in order to perform |
|
custom operations on it, plus the corresponding CCI `ConnectionFactory`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface InteractionCallback { |
|
|
|
Object doInInteraction(Interaction interaction, ConnectionFactory connectionFactory) |
|
throws ResourceException, SQLException, DataAccessException; |
|
|
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
`InteractionSpec` objects can either be shared across multiple template calls or newly |
|
created inside every callback method. This is completely up to the DAO implementation. |
|
==== |
|
|
|
|
|
|
|
[[cci-template-example]] |
|
==== Example for CciTemplate usage |
|
|
|
In this section, the usage of the `CciTemplate` will be shown to acces to a CICS with |
|
ECI mode, with the IBM CICS ECI connector. |
|
|
|
Firstly, some initializations on the CCI `InteractionSpec` must be done to specify which |
|
CICS program to access and how to interact with it. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
ECIInteractionSpec interactionSpec = new ECIInteractionSpec(); |
|
interactionSpec.setFunctionName("MYPROG"); |
|
interactionSpec.setInteractionVerb(ECIInteractionSpec.SYNC_SEND_RECEIVE); |
|
---- |
|
|
|
Then the program can use CCI via Spring's template and specify mappings between custom |
|
objects and CCI `Records`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MyDaoImpl extends CciDaoSupport implements MyDao { |
|
|
|
public OutputObject getData(InputObject input) { |
|
ECIInteractionSpec interactionSpec = ...; |
|
|
|
OutputObject output = (ObjectOutput) getCciTemplate().execute(interactionSpec, |
|
new RecordCreator() { |
|
public Record createRecord(RecordFactory recordFactory) throws ResourceException { |
|
return new CommAreaRecord(input.toString().getBytes()); |
|
} |
|
}, |
|
new RecordExtractor() { |
|
public Object extractData(Record record) throws ResourceException { |
|
CommAreaRecord commAreaRecord = (CommAreaRecord)record; |
|
String str = new String(commAreaRecord.toByteArray()); |
|
String field1 = string.substring(0,6); |
|
String field2 = string.substring(6,1); |
|
return new OutputObject(Long.parseLong(field1), field2); |
|
} |
|
}); |
|
|
|
return output; |
|
} |
|
} |
|
---- |
|
|
|
As discussed previously, callbacks can be used to work directly on CCI connections or |
|
interactions. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MyDaoImpl extends CciDaoSupport implements MyDao { |
|
|
|
public OutputObject getData(InputObject input) { |
|
ObjectOutput output = (ObjectOutput) getCciTemplate().execute( |
|
new ConnectionCallback() { |
|
public Object doInConnection(Connection connection, |
|
ConnectionFactory factory) throws ResourceException { |
|
|
|
// do something... |
|
|
|
} |
|
}); |
|
} |
|
return output; |
|
} |
|
|
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
With a `ConnectionCallback`, the `Connection` used will be managed and closed by the |
|
`CciTemplate`, but any interactions created on the connection must be managed by the |
|
callback implementation. |
|
==== |
|
|
|
For a more specific callback, you can implement an `InteractionCallback`. The passed-in |
|
`Interaction` will be managed and closed by the `CciTemplate` in this case. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MyDaoImpl extends CciDaoSupport implements MyDao { |
|
|
|
public String getData(String input) { |
|
ECIInteractionSpec interactionSpec = ...; |
|
String output = (String) getCciTemplate().execute(interactionSpec, |
|
new InteractionCallback() { |
|
public Object doInInteraction(Interaction interaction, |
|
ConnectionFactory factory) throws ResourceException { |
|
Record input = new CommAreaRecord(inputString.getBytes()); |
|
Record output = new CommAreaRecord(); |
|
interaction.execute(holder.getInteractionSpec(), input, output); |
|
return new String(output.toByteArray()); |
|
} |
|
}); |
|
return output; |
|
} |
|
|
|
} |
|
---- |
|
|
|
For the examples above, the corresponding configuration of the involved Spring beans |
|
could look like this in non-managed mode: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="managedConnectionFactory" class="com.ibm.connector2.cics.ECIManagedConnectionFactory"> |
|
<property name="serverName" value="TXSERIES"/> |
|
<property name="connectionURL" value="local:"/> |
|
<property name="userName" value="CICSUSER"/> |
|
<property name="password" value="CICS"/> |
|
</bean> |
|
|
|
<bean id="connectionFactory" class="org.springframework.jca.support.LocalConnectionFactoryBean"> |
|
<property name="managedConnectionFactory" ref="managedConnectionFactory"/> |
|
</bean> |
|
|
|
<bean id="component" class="mypackage.MyDaoImpl"> |
|
<property name="connectionFactory" ref="connectionFactory"/> |
|
</bean> |
|
---- |
|
|
|
In managed mode (that is, in a Java EE environment), the configuration could look as |
|
follows: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<jee:jndi-lookup id="connectionFactory" jndi-name="eis/cicseci"/> |
|
|
|
<bean id="component" class="MyDaoImpl"> |
|
<property name="connectionFactory" ref="connectionFactory"/> |
|
</bean> |
|
---- |
|
|
|
|
|
|
|
|
|
[[cci-object]] |
|
=== Modeling CCI access as operation objects |
|
The `org.springframework.jca.cci.object` package contains support classes that allow you |
|
to access the EIS in a different style: through reusable operation objects, analogous to |
|
Spring's JDBC operation objects (see JDBC chapter). This will usually encapsulate the |
|
CCI API: an application-level input object will be passed to the operation object, so it |
|
can construct the input record and then convert the received record data to an |
|
application-level output object and return it. |
|
|
|
[NOTE] |
|
==== |
|
This approach is internally based on the `CciTemplate` class and the |
|
`RecordCreator` / `RecordExtractor` interfaces, reusing the machinery of Spring's core |
|
CCI support. |
|
==== |
|
|
|
|
|
|
|
[[cci-object-mapping-record]] |
|
==== MappingRecordOperation |
|
|
|
`MappingRecordOperation` essentially performs the same work as `CciTemplate`, but |
|
represents a specific, pre-configured operation as an object. It provides two template |
|
methods to specify how to convert an input object to a input record, and how to convert |
|
an output record to an output object (record mapping): |
|
|
|
* `createInputRecord(..)` to specify how to convert an input object to an input `Record` |
|
* `extractOutputData(..)` to specify how to extract an output object from an output |
|
`Record` |
|
|
|
Here are the signatures of these methods: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public abstract class MappingRecordOperation extends EisOperation { |
|
|
|
... |
|
|
|
protected abstract Record createInputRecord(RecordFactory recordFactory, |
|
Object inputObject) throws ResourceException, DataAccessException { |
|
// ... |
|
} |
|
|
|
protected abstract Object extractOutputData(Record outputRecord) |
|
throws ResourceException, SQLException, DataAccessException { |
|
// ... |
|
} |
|
|
|
... |
|
|
|
} |
|
---- |
|
|
|
Thereafter, in order to execute an EIS operation, you need to use a single execute |
|
method, passing in an application-level input object and receiving an application-level |
|
output object as result: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public abstract class MappingRecordOperation extends EisOperation { |
|
|
|
... |
|
|
|
public Object execute(Object inputObject) throws DataAccessException { |
|
} |
|
|
|
... |
|
} |
|
---- |
|
|
|
As you can see, contrary to the `CciTemplate` class, this `execute(..)` method does not |
|
have an `InteractionSpec` as argument. Instead, the `InteractionSpec` is global to the |
|
operation. The following constructor must be used to instantiate an operation object |
|
with a specific `InteractionSpec`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
InteractionSpec spec = ...; |
|
MyMappingRecordOperation eisOperation = new MyMappingRecordOperation(getConnectionFactory(), spec); |
|
... |
|
---- |
|
|
|
|
|
|
|
[[cci-object-mapping-comm-area]] |
|
==== MappingCommAreaOperation |
|
|
|
Some connectors use records based on a COMMAREA which represents an array of bytes |
|
containing parameters to send to the EIS and data returned by it. Spring provides a |
|
special operation class for working directly on COMMAREA rather than on records. The |
|
`MappingCommAreaOperation` class extends the `MappingRecordOperation` class to provide |
|
such special COMMAREA support. It implicitly uses the `CommAreaRecord` class as input |
|
and output record type, and provides two new methods to convert an input object into an |
|
input COMMAREA and the output COMMAREA into an output object. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public abstract class MappingCommAreaOperation extends MappingRecordOperation { |
|
|
|
... |
|
|
|
protected abstract byte[] objectToBytes(Object inObject) |
|
throws IOException, DataAccessException; |
|
|
|
protected abstract Object bytesToObject(byte[] bytes) |
|
throws IOException, DataAccessException; |
|
|
|
... |
|
|
|
} |
|
---- |
|
|
|
|
|
|
|
[[cci-automatic-record-gen]] |
|
==== Automatic output record generation |
|
As every `MappingRecordOperation` subclass is based on CciTemplate internally, the same |
|
way to automatically generate output records as with `CciTemplate` is available. Every |
|
operation object provides a corresponding `setOutputRecordCreator(..)` method. For |
|
further information, see <<automatic-output-generation>>. |
|
|
|
|
|
|
|
[[cci-object-summary]] |
|
==== Summary |
|
The operation object approach uses records in the same manner as the `CciTemplate` class. |
|
|
|
[[cci-interaction-methods]] |
|
.Usage of Interaction execute methods |
|
[cols="3,1,3"] |
|
|=== |
|
| MappingRecordOperation method signature| MappingRecordOperation outputRecordCreator property| execute method called on the CCI Interaction |
|
|
|
| Object execute(Object) |
|
| not set |
|
| Record execute(InteractionSpec, Record) |
|
|
|
| Object execute(Object) |
|
| set |
|
| boolean execute(InteractionSpec, Record, Record) |
|
|=== |
|
|
|
|
|
|
|
[[cci-objects-mappring-record-example]] |
|
==== Example for MappingRecordOperation usage |
|
|
|
In this section, the usage of the `MappingRecordOperation` will be shown to access a |
|
database with the Blackbox CCI connector. |
|
|
|
[NOTE] |
|
==== |
|
The original version of this connector is provided by the Java EE SDK (version 1.3), |
|
available from Oracle. |
|
==== |
|
|
|
Firstly, some initializations on the CCI `InteractionSpec` must be done to specify which |
|
SQL request to execute. In this sample, we directly define the way to convert the |
|
parameters of the request to a CCI record and the way to convert the CCI result record |
|
to an instance of the `Person` class. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class PersonMappingOperation extends MappingRecordOperation { |
|
|
|
public PersonMappingOperation(ConnectionFactory connectionFactory) { |
|
setConnectionFactory(connectionFactory); |
|
CciInteractionSpec interactionSpec = new CciConnectionSpec(); |
|
interactionSpec.setSql("select * from person where person_id=?"); |
|
setInteractionSpec(interactionSpec); |
|
} |
|
|
|
protected Record createInputRecord(RecordFactory recordFactory, |
|
Object inputObject) throws ResourceException { |
|
Integer id = (Integer) inputObject; |
|
IndexedRecord input = recordFactory.createIndexedRecord("input"); |
|
input.add(new Integer(id)); |
|
return input; |
|
} |
|
|
|
protected Object extractOutputData(Record outputRecord) |
|
throws ResourceException, SQLException { |
|
ResultSet rs = (ResultSet) outputRecord; |
|
Person person = null; |
|
if (rs.next()) { |
|
Person person = new Person(); |
|
person.setId(rs.getInt("person_id")); |
|
person.setLastName(rs.getString("person_last_name")); |
|
person.setFirstName(rs.getString("person_first_name")); |
|
} |
|
return person; |
|
} |
|
} |
|
---- |
|
|
|
Then the application can execute the operation object, with the person identifier as |
|
argument. Note that operation object could be set up as shared instance, as it is |
|
thread-safe. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MyDaoImpl extends CciDaoSupport implements MyDao { |
|
|
|
public Person getPerson(int id) { |
|
PersonMappingOperation query = new PersonMappingOperation(getConnectionFactory()); |
|
Person person = (Person) query.execute(new Integer(id)); |
|
return person; |
|
} |
|
} |
|
---- |
|
|
|
The corresponding configuration of Spring beans could look as follows in non-managed mode: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="managedConnectionFactory" |
|
class="com.sun.connector.cciblackbox.CciLocalTxManagedConnectionFactory"> |
|
<property name="connectionURL" value="jdbc:hsqldb:hsql://localhost:9001"/> |
|
<property name="driverName" value="org.hsqldb.jdbcDriver"/> |
|
</bean> |
|
|
|
<bean id="targetConnectionFactory" |
|
class="org.springframework.jca.support.LocalConnectionFactoryBean"> |
|
<property name="managedConnectionFactory" ref="managedConnectionFactory"/> |
|
</bean> |
|
|
|
<bean id="connectionFactory" |
|
class="org.springframework.jca.cci.connection.ConnectionSpecConnectionFactoryAdapter"> |
|
<property name="targetConnectionFactory" ref="targetConnectionFactory"/> |
|
<property name="connectionSpec"> |
|
<bean class="com.sun.connector.cciblackbox.CciConnectionSpec"> |
|
<property name="user" value="sa"/> |
|
<property name="password" value=""/> |
|
</bean> |
|
</property> |
|
</bean> |
|
|
|
<bean id="component" class="MyDaoImpl"> |
|
<property name="connectionFactory" ref="connectionFactory"/> |
|
</bean> |
|
---- |
|
|
|
In managed mode (that is, in a Java EE environment), the configuration could look as |
|
follows: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<jee:jndi-lookup id="targetConnectionFactory" jndi-name="eis/blackbox"/> |
|
|
|
<bean id="connectionFactory" |
|
class="org.springframework.jca.cci.connection.ConnectionSpecConnectionFactoryAdapter"> |
|
<property name="targetConnectionFactory" ref="targetConnectionFactory"/> |
|
<property name="connectionSpec"> |
|
<bean class="com.sun.connector.cciblackbox.CciConnectionSpec"> |
|
<property name="user" value="sa"/> |
|
<property name="password" value=""/> |
|
</bean> |
|
</property> |
|
</bean> |
|
|
|
<bean id="component" class="MyDaoImpl"> |
|
<property name="connectionFactory" ref="connectionFactory"/> |
|
</bean> |
|
---- |
|
|
|
|
|
|
|
[[cci-objects-mapping-comm-area-example]] |
|
==== Example for MappingCommAreaOperation usage |
|
|
|
In this section, the usage of the `MappingCommAreaOperation` will be shown: accessing a |
|
CICS with ECI mode with the IBM CICS ECI connector. |
|
|
|
Firstly, the CCI `InteractionSpec` needs to be initialized to specify which CICS program |
|
to access and how to interact with it. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public abstract class EciMappingOperation extends MappingCommAreaOperation { |
|
|
|
public EciMappingOperation(ConnectionFactory connectionFactory, String programName) { |
|
setConnectionFactory(connectionFactory); |
|
ECIInteractionSpec interactionSpec = new ECIInteractionSpec(), |
|
interactionSpec.setFunctionName(programName); |
|
interactionSpec.setInteractionVerb(ECIInteractionSpec.SYNC_SEND_RECEIVE); |
|
interactionSpec.setCommareaLength(30); |
|
setInteractionSpec(interactionSpec); |
|
setOutputRecordCreator(new EciOutputRecordCreator()); |
|
} |
|
|
|
private static class EciOutputRecordCreator implements RecordCreator { |
|
public Record createRecord(RecordFactory recordFactory) throws ResourceException { |
|
return new CommAreaRecord(); |
|
} |
|
} |
|
|
|
} |
|
---- |
|
|
|
The abstract `EciMappingOperation` class can then be subclassed to specify mappings |
|
between custom objects and `Records`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MyDaoImpl extends CciDaoSupport implements MyDao { |
|
|
|
public OutputObject getData(Integer id) { |
|
EciMappingOperation query = new EciMappingOperation(getConnectionFactory(), "MYPROG") { |
|
|
|
protected abstract byte[] objectToBytes(Object inObject) throws IOException { |
|
Integer id = (Integer) inObject; |
|
return String.valueOf(id); |
|
} |
|
|
|
protected abstract Object bytesToObject(byte[] bytes) throws IOException; |
|
String str = new String(bytes); |
|
String field1 = str.substring(0,6); |
|
String field2 = str.substring(6,1); |
|
String field3 = str.substring(7,1); |
|
return new OutputObject(field1, field2, field3); |
|
} |
|
}); |
|
|
|
return (OutputObject) query.execute(new Integer(id)); |
|
} |
|
|
|
} |
|
---- |
|
|
|
The corresponding configuration of Spring beans could look as follows in non-managed mode: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="managedConnectionFactory" class="com.ibm.connector2.cics.ECIManagedConnectionFactory"> |
|
<property name="serverName" value="TXSERIES"/> |
|
<property name="connectionURL" value="local:"/> |
|
<property name="userName" value="CICSUSER"/> |
|
<property name="password" value="CICS"/> |
|
</bean> |
|
|
|
<bean id="connectionFactory" class="org.springframework.jca.support.LocalConnectionFactoryBean"> |
|
<property name="managedConnectionFactory" ref="managedConnectionFactory"/> |
|
</bean> |
|
|
|
<bean id="component" class="MyDaoImpl"> |
|
<property name="connectionFactory" ref="connectionFactory"/> |
|
</bean> |
|
---- |
|
|
|
In managed mode (that is, in a Java EE environment), the configuration could look as |
|
follows: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<jee:jndi-lookup id="connectionFactory" jndi-name="eis/cicseci"/> |
|
|
|
<bean id="component" class="MyDaoImpl"> |
|
<property name="connectionFactory" ref="connectionFactory"/> |
|
</bean> |
|
---- |
|
|
|
|
|
|
|
|
|
[[cci-tx]] |
|
=== Transactions |
|
JCA specifies several levels of transaction support for resource adapters. The kind of |
|
transactions that your resource adapter supports is specified in its `ra.xml` file. |
|
There are essentially three options: none (for example with CICS EPI connector), local |
|
transactions (for example with a CICS ECI connector), global transactions (for example |
|
with an IMS connector). |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<connector> |
|
<resourceadapter> |
|
<!-- <transaction-support>NoTransaction</transaction-support> --> |
|
<!-- <transaction-support>LocalTransaction</transaction-support> --> |
|
<transaction-support>XATransaction</transaction-support> |
|
<resourceadapter> |
|
<connector> |
|
---- |
|
|
|
For global transactions, you can use Spring's generic transaction infrastructure to |
|
demarcate transactions, with `JtaTransactionManager` as backend (delegating to the Java |
|
EE server's distributed transaction coordinator underneath). |
|
|
|
For local transactions on a single CCI `ConnectionFactory`, Spring provides a specific |
|
transaction management strategy for CCI, analogous to the `DataSourceTransactionManager` |
|
for JDBC. The CCI API defines a local transaction object and corresponding local |
|
transaction demarcation methods. Spring's `CciLocalTransactionManager` executes such |
|
local CCI transactions, fully compliant with Spring's generic |
|
`PlatformTransactionManager` abstraction. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<jee:jndi-lookup id="eciConnectionFactory" jndi-name="eis/cicseci"/> |
|
|
|
<bean id="eciTransactionManager" |
|
class="org.springframework.jca.cci.connection.CciLocalTransactionManager"> |
|
<property name="connectionFactory" ref="eciConnectionFactory"/> |
|
</bean> |
|
---- |
|
|
|
Both transaction strategies can be used with any of Spring's transaction demarcation |
|
facilities, be it declarative or programmatic. This is a consequence of Spring's generic |
|
`PlatformTransactionManager` abstraction, which decouples transaction demarcation from |
|
the actual execution strategy. Simply switch between `JtaTransactionManager` and |
|
`CciLocalTransactionManager` as needed, keeping your transaction demarcation as-is. |
|
|
|
For more information on Spring's transaction facilities, see the chapter entitled |
|
<<transaction>>. |
|
|
|
|
|
|
|
|
|
|
|
[[mail]] |
|
== Email |
|
|
|
|
|
|
|
|
|
[[mail-introduction]] |
|
=== Introduction |
|
|
|
.Library dependencies |
|
**** |
|
The following additional jars to be on the classpath of your application in order to be |
|
able to use the Spring Framework's email library. |
|
|
|
* The https://java.net/projects/javamail/pages/Home[JavaMail] `mail.jar` library |
|
* The http://www.oracle.com/technetwork/java/jaf11-139815.html[JAF] |
|
`activation.jar` library |
|
|
|
All of these libraries are freely available on the web. |
|
**** |
|
|
|
The Spring Framework provides a helpful utility library for sending email that shields |
|
the user from the specifics of the underlying mailing system and is responsible for low |
|
level resource handling on behalf of the client. |
|
|
|
The `org.springframework.mail` package is the root level package for the Spring |
|
Framework's email support. The central interface for sending emails is the `MailSender` |
|
interface; a simple value object encapsulating the properties of a simple mail such as |
|
__from__ and __to__ (plus many others) is the `SimpleMailMessage` class. This package |
|
also contains a hierarchy of checked exceptions which provide a higher level of |
|
abstraction over the lower level mail system exceptions with the root exception being |
|
`MailException`. Please refer to the javadocs for more information on the rich mail |
|
exception hierarchy. |
|
|
|
The `org.springframework.mail.javamail.JavaMailSender` interface adds specialized |
|
__JavaMail__ features such as MIME message support to the `MailSender` interface (from |
|
which it inherits). `JavaMailSender` also provides a callback interface for preparation |
|
of JavaMail MIME messages, called |
|
`org.springframework.mail.javamail.MimeMessagePreparator` |
|
|
|
|
|
|
|
|
|
[[mail-usage]] |
|
=== Usage |
|
Let's assume there is a business interface called `OrderManager`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface OrderManager { |
|
|
|
void placeOrder(Order order); |
|
|
|
} |
|
---- |
|
|
|
Let us also assume that there is a requirement stating that an email message with an |
|
order number needs to be generated and sent to a customer placing the relevant order. |
|
|
|
|
|
|
|
[[mail-usage-simple]] |
|
==== Basic MailSender and SimpleMailMessage usage |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import org.springframework.mail.MailException; |
|
import org.springframework.mail.MailSender; |
|
import org.springframework.mail.SimpleMailMessage; |
|
|
|
public class SimpleOrderManager implements OrderManager { |
|
|
|
private MailSender mailSender; |
|
private SimpleMailMessage templateMessage; |
|
|
|
public void setMailSender(MailSender mailSender) { |
|
this.mailSender = mailSender; |
|
} |
|
|
|
public void setTemplateMessage(SimpleMailMessage templateMessage) { |
|
this.templateMessage = templateMessage; |
|
} |
|
|
|
public void placeOrder(Order order) { |
|
|
|
// Do the business calculations... |
|
|
|
// Call the collaborators to persist the order... |
|
|
|
// Create a thread safe "copy" of the template message and customize it |
|
SimpleMailMessage msg = new SimpleMailMessage(this.templateMessage); |
|
msg.setTo(order.getCustomer().getEmailAddress()); |
|
msg.setText( |
|
"Dear " + order.getCustomer().getFirstName() |
|
+ order.getCustomer().getLastName() |
|
+ ", thank you for placing order. Your order number is " |
|
+ order.getOrderNumber()); |
|
try{ |
|
this.mailSender.send(msg); |
|
} |
|
catch (MailException ex) { |
|
// simply log it and go on... |
|
System.err.println(ex.getMessage()); |
|
} |
|
} |
|
|
|
} |
|
---- |
|
|
|
Find below the bean definitions for the above code: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> |
|
<property name="host" value="mail.mycompany.com"/> |
|
</bean> |
|
|
|
<!-- this is a template message that we can pre-load with default state --> |
|
<bean id="templateMessage" class="org.springframework.mail.SimpleMailMessage"> |
|
<property name="from" value="customerservice@mycompany.com"/> |
|
<property name="subject" value="Your order"/> |
|
</bean> |
|
|
|
<bean id="orderManager" class="com.mycompany.businessapp.support.SimpleOrderManager"> |
|
<property name="mailSender" ref="mailSender"/> |
|
<property name="templateMessage" ref="templateMessage"/> |
|
</bean> |
|
---- |
|
|
|
|
|
|
|
[[mail-usage-mime]] |
|
==== Using the JavaMailSender and the MimeMessagePreparator |
|
|
|
Here is another implementation of `OrderManager` using the `MimeMessagePreparator` |
|
callback interface. Please note in this case that the `mailSender` property is of type |
|
`JavaMailSender` so that we are able to use the JavaMail `MimeMessage` class: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import javax.mail.Message; |
|
import javax.mail.MessagingException; |
|
import javax.mail.internet.InternetAddress; |
|
import javax.mail.internet.MimeMessage; |
|
|
|
import javax.mail.internet.MimeMessage; |
|
import org.springframework.mail.MailException; |
|
import org.springframework.mail.javamail.JavaMailSender; |
|
import org.springframework.mail.javamail.MimeMessagePreparator; |
|
|
|
public class SimpleOrderManager implements OrderManager { |
|
|
|
private JavaMailSender mailSender; |
|
|
|
public void setMailSender(JavaMailSender mailSender) { |
|
this.mailSender = mailSender; |
|
} |
|
|
|
public void placeOrder(final Order order) { |
|
|
|
// Do the business calculations... |
|
|
|
// Call the collaborators to persist the order... |
|
|
|
MimeMessagePreparator preparator = new MimeMessagePreparator() { |
|
|
|
public void prepare(MimeMessage mimeMessage) throws Exception { |
|
|
|
mimeMessage.setRecipient(Message.RecipientType.TO, |
|
new InternetAddress(order.getCustomer().getEmailAddress())); |
|
mimeMessage.setFrom(new InternetAddress("mail@mycompany.com")); |
|
mimeMessage.setText( |
|
"Dear " + order.getCustomer().getFirstName() + " " |
|
+ order.getCustomer().getLastName() |
|
+ ", thank you for placing order. Your order number is " |
|
+ order.getOrderNumber()); |
|
} |
|
}; |
|
|
|
try { |
|
this.mailSender.send(preparator); |
|
} |
|
catch (MailException ex) { |
|
// simply log it and go on... |
|
System.err.println(ex.getMessage()); |
|
} |
|
} |
|
|
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
The mail code is a crosscutting concern and could well be a candidate for refactoring |
|
into a <<aop,custom Spring AOP aspect>>, which then could be executed at appropriate |
|
joinpoints on the `OrderManager` target. |
|
==== |
|
|
|
The Spring Framework's mail support ships with the standard JavaMail implementation. |
|
Please refer to the relevant javadocs for more information. |
|
|
|
|
|
|
|
|
|
[[mail-javamail-mime]] |
|
=== Using the JavaMail MimeMessageHelper |
|
|
|
A class that comes in pretty handy when dealing with JavaMail messages is the |
|
`org.springframework.mail.javamail.MimeMessageHelper` class, which shields you from |
|
having to use the verbose JavaMail API. Using the `MimeMessageHelper` it is pretty easy |
|
to create a `MimeMessage`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// of course you would use DI in any real-world cases |
|
JavaMailSenderImpl sender = new JavaMailSenderImpl(); |
|
sender.setHost("mail.host.com"); |
|
|
|
MimeMessage message = sender.createMimeMessage(); |
|
MimeMessageHelper helper = new MimeMessageHelper(message); |
|
helper.setTo("test@host.com"); |
|
helper.setText("Thank you for ordering!"); |
|
|
|
sender.send(message); |
|
---- |
|
|
|
|
|
|
|
[[mail-javamail-mime-attachments]] |
|
==== Sending attachments and inline resources |
|
Multipart email messages allow for both attachments and inline resources. Examples of |
|
inline resources would be images or a stylesheet you want to use in your message, but |
|
that you don't want displayed as an attachment. |
|
|
|
|
|
[[mail-javamail-mime-attachments-attachment]] |
|
===== Attachments |
|
The following example shows you how to use the `MimeMessageHelper` to send an email |
|
along with a single JPEG image attachment. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
JavaMailSenderImpl sender = new JavaMailSenderImpl(); |
|
sender.setHost("mail.host.com"); |
|
|
|
MimeMessage message = sender.createMimeMessage(); |
|
|
|
// use the true flag to indicate you need a multipart message |
|
MimeMessageHelper helper = new MimeMessageHelper(message, true); |
|
helper.setTo("test@host.com"); |
|
|
|
helper.setText("Check out this image!"); |
|
|
|
// let's attach the infamous windows Sample file (this time copied to c:/) |
|
FileSystemResource file = new FileSystemResource(new File("c:/Sample.jpg")); |
|
helper.addAttachment("CoolImage.jpg", file); |
|
|
|
sender.send(message); |
|
---- |
|
|
|
|
|
[[mail-javamail-mime-attachments-inline]] |
|
===== Inline resources |
|
The following example shows you how to use the `MimeMessageHelper` to send an email |
|
along with an inline image. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
JavaMailSenderImpl sender = new JavaMailSenderImpl(); |
|
sender.setHost("mail.host.com"); |
|
|
|
MimeMessage message = sender.createMimeMessage(); |
|
|
|
// use the true flag to indicate you need a multipart message |
|
MimeMessageHelper helper = new MimeMessageHelper(message, true); |
|
helper.setTo("test@host.com"); |
|
|
|
// use the true flag to indicate the text included is HTML |
|
helper.setText("<html><body><img src=''cid:identifier1234''></body></html>", true); |
|
|
|
// let's include the infamous windows Sample file (this time copied to c:/) |
|
FileSystemResource res = new FileSystemResource(new File("c:/Sample.jpg")); |
|
helper.addInline("identifier1234", res); |
|
|
|
sender.send(message); |
|
---- |
|
|
|
[WARNING] |
|
==== |
|
|
|
Inline resources are added to the mime message using the specified `Content-ID` ( |
|
`identifier1234` in the above example). The order in which you are adding the text and |
|
the resource are __very__ important. Be sure to __first add the text__ and after that |
|
the resources. If you are doing it the other way around, it won't work! |
|
==== |
|
|
|
|
|
|
|
[[mail-templates]] |
|
==== Creating email content using a templating library |
|
The code in the previous examples explicitly created the content of the email message, |
|
using methods calls such as `message.setText(..)`. This is fine for simple cases, and it |
|
is okay in the context of the aforementioned examples, where the intent was to show you |
|
the very basics of the API. |
|
|
|
In your typical enterprise application though, you are not going to create the content |
|
of your emails using the above approach for a number of reasons. |
|
|
|
* Creating HTML-based email content in Java code is tedious and error prone |
|
* There is no clear separation between display logic and business logic |
|
* Changing the display structure of the email content requires writing Java code, |
|
recompiling, redeploying... |
|
|
|
Typically the approach taken to address these issues is to use a template library such |
|
as FreeMarker or Velocity to define the display structure of email content. This leaves |
|
your code tasked only with creating the data that is to be rendered in the email |
|
template and sending the email. It is definitely a best practice for when the content of |
|
your emails becomes even moderately complex, and with the Spring Framework's support |
|
classes for FreeMarker and Velocity becomes quite easy to do. Find below an example of |
|
using the Velocity template library to create email content. |
|
|
|
|
|
[[mail-templates-example]] |
|
===== A Velocity-based example |
|
To use http://velocity.apache.org[Velocity] to create your email template(s), you will |
|
need to have the Velocity libraries available on your classpath. You will also need to |
|
create one or more Velocity templates for the email content that your application needs. |
|
Find below the Velocity template that this example will be using. As you can see it is |
|
HTML-based, and since it is plain text it can be created using your favorite HTML or |
|
text editor. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
# in the com/foo/package |
|
<html> |
|
<body> |
|
<h3>Hi ${user.userName}, welcome to the Chipping Sodbury On-the-Hill message boards!</h3> |
|
|
|
<div> |
|
Your email address is <a href="mailto:${user.emailAddress}">${user.emailAddress}</a>. |
|
</div> |
|
</body> |
|
</html> |
|
---- |
|
|
|
Find below some simple code and Spring XML configuration that makes use of the above |
|
Velocity template to create email content and send email(s). |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package com.foo; |
|
|
|
import org.apache.velocity.app.VelocityEngine; |
|
import org.springframework.mail.javamail.JavaMailSender; |
|
import org.springframework.mail.javamail.MimeMessageHelper; |
|
import org.springframework.mail.javamail.MimeMessagePreparator; |
|
import org.springframework.ui.velocity.VelocityEngineUtils; |
|
|
|
import javax.mail.internet.MimeMessage; |
|
import java.util.HashMap; |
|
import java.util.Map; |
|
|
|
public class SimpleRegistrationService implements RegistrationService { |
|
|
|
private JavaMailSender mailSender; |
|
private VelocityEngine velocityEngine; |
|
|
|
public void setMailSender(JavaMailSender mailSender) { |
|
this.mailSender = mailSender; |
|
} |
|
|
|
public void setVelocityEngine(VelocityEngine velocityEngine) { |
|
this.velocityEngine = velocityEngine; |
|
} |
|
|
|
public void register(User user) { |
|
|
|
// Do the registration logic... |
|
|
|
sendConfirmationEmail(user); |
|
} |
|
|
|
private void sendConfirmationEmail(final User user) { |
|
MimeMessagePreparator preparator = new MimeMessagePreparator() { |
|
public void prepare(MimeMessage mimeMessage) throws Exception { |
|
MimeMessageHelper message = new MimeMessageHelper(mimeMessage); |
|
message.setTo(user.getEmailAddress()); |
|
message.setFrom("webmaster@csonth.gov.uk"); // could be parameterized... |
|
Map model = new HashMap(); |
|
model.put("user", user); |
|
String text = VelocityEngineUtils.mergeTemplateIntoString( |
|
velocityEngine, "com/dns/registration-confirmation.vm", model); |
|
message.setText(text, true); |
|
} |
|
}; |
|
this.mailSender.send(preparator); |
|
} |
|
|
|
} |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xsi:schemaLocation="http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd"> |
|
|
|
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> |
|
<property name="host" value="mail.csonth.gov.uk"/> |
|
</bean> |
|
|
|
<bean id="registrationService" class="com.foo.SimpleRegistrationService"> |
|
<property name="mailSender" ref="mailSender"/> |
|
<property name="velocityEngine" ref="velocityEngine"/> |
|
</bean> |
|
|
|
<bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean"> |
|
<property name="velocityProperties"> |
|
<value> |
|
resource.loader=class |
|
class.resource.loader.class=org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader |
|
</value> |
|
</property> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
|
|
|
|
|
|
|
|
[[scheduling]] |
|
== Task Execution and Scheduling |
|
|
|
|
|
|
|
|
|
[[scheduling-introduction]] |
|
=== Introduction |
|
The Spring Framework provides abstractions for asynchronous execution and scheduling of |
|
tasks with the `TaskExecutor` and `TaskScheduler` interfaces, respectively. Spring also |
|
features implementations of those interfaces that support thread pools or delegation to |
|
CommonJ within an application server environment. Ultimately the use of these |
|
implementations behind the common interfaces abstracts away the differences between Java |
|
SE 5, Java SE 6 and Java EE environments. |
|
|
|
Spring also features integration classes for supporting scheduling with the `Timer`, |
|
part of the JDK since 1.3, and the Quartz Scheduler ( http://quartz-scheduler.org[]). |
|
Both of those schedulers are set up using a `FactoryBean` with optional references to |
|
`Timer` or `Trigger` instances, respectively. Furthermore, a convenience class for both |
|
the Quartz Scheduler and the `Timer` is available that allows you to invoke a method of |
|
an existing target object (analogous to the normal `MethodInvokingFactoryBean` |
|
operation). |
|
|
|
|
|
|
|
|
|
[[scheduling-task-executor]] |
|
=== The Spring TaskExecutor abstraction |
|
|
|
Spring 2.0 introduces a new abstraction for dealing with executors. Executors are the |
|
Java 5 name for the concept of thread pools. The "executor" naming is due to the fact |
|
that there is no guarantee that the underlying implementation is actually a pool; an |
|
executor may be single-threaded or even synchronous. Spring's abstraction hides |
|
implementation details between Java SE 1.4, Java SE 5 and Java EE environments. |
|
|
|
Spring's `TaskExecutor` interface is identical to the `java.util.concurrent.Executor` |
|
interface. In fact, its primary reason for existence was to abstract away the need for |
|
Java 5 when using thread pools. The interface has a single method `execute(Runnable |
|
task)` that accepts a task for execution based on the semantics and configuration of the |
|
thread pool. |
|
|
|
The `TaskExecutor` was originally created to give other Spring components an abstraction |
|
for thread pooling where needed. Components such as the `ApplicationEventMulticaster`, |
|
JMS's `AbstractMessageListenerContainer`, and Quartz integration all use the |
|
`TaskExecutor` abstraction to pool threads. However, if your beans need thread pooling |
|
behavior, it is possible to use this abstraction for your own needs. |
|
|
|
|
|
|
|
[[scheduling-task-executor-types]] |
|
==== TaskExecutor types |
|
|
|
There are a number of pre-built implementations of `TaskExecutor` included with the |
|
Spring distribution. In all likelihood, you shouldn't ever need to implement your own. |
|
|
|
* `SimpleAsyncTaskExecutor` |
|
This implementation does not reuse any threads, rather it starts up a new thread |
|
for each invocation. However, it does support a concurrency limit which will block |
|
any invocations that are over the limit until a slot has been freed up. If you |
|
re looking for true pooling, keep scrolling further down the page. |
|
* `SyncTaskExecutor` |
|
This implementation doesn't execute invocations asynchronously. Instead, each |
|
invocation takes place in the calling thread. It is primarily used in situations |
|
where multi-threading isn't necessary such as simple test cases. |
|
* `ConcurrentTaskExecutor` |
|
This implementation is an adapter for a `java.util.concurrent.Executor` object. |
|
There is an alternative, `ThreadPoolTaskExecutor, that exposes the `Executor` |
|
configuration parameters as bean properties. It is rare to need to use the |
|
`ConcurrentTaskExecutor` but if the <<threadPoolTaskExecutor, `ThreadPoolTaskExecutor`>> |
|
isn't flexible enough for your needs, the `ConcurrentTaskExecutor` is an alternative. |
|
* `SimpleThreadPoolTaskExecutor` |
|
This implementation is actually a subclass of Quartz's `SimpleThreadPool` which |
|
listens to Spring's lifecycle callbacks. This is typically used when you have a |
|
thread pool that may need to be shared by both Quartz and non-Quartz components. |
|
* `ThreadPoolTaskExecutor` |
|
This implementation is the most commonly used one. It exposes bean properties for |
|
configuring a java.util.concurrent.ThreadPoolExecutor` and wraps it in a `TaskExecutor`. |
|
If you need to adapt to a different kind of `java.util.concurrent.Executor`, it is |
|
recommended that you use a <<concurrentTaskExecutor, `ConcurrentTaskExecutor`>> instead. |
|
* `WorkManagerTaskExecutor` |
|
|
|
+ |
|
|
|
**** |
|
CommonJ is a set of specifications jointly developed between BEA and IBM. These |
|
specifications are not Java EE standards, but are standard across BEA's and IBM's |
|
Application Server implementations. |
|
**** |
|
|
|
+ |
|
|
|
This implementation uses the CommonJ WorkManager as its backing implementation and is |
|
the central convenience class for setting up a CommonJ WorkManager reference in a Spring |
|
context. Similar to the <<simpleThreadPoolTaskExecutor, `SimpleThreadPoolTaskExecutor`>>, |
|
this class implements the WorkManager interface and therefore can be used directly as a |
|
WorkManager as well. |
|
|
|
|
|
|
|
[[scheduling-task-executor-usage]] |
|
==== Using a TaskExecutor |
|
|
|
Spring's `TaskExecutor` implementations are used as simple JavaBeans. In the example |
|
below, we define a bean that uses the `ThreadPoolTaskExecutor` to asynchronously print |
|
out a set of messages. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import org.springframework.core.task.TaskExecutor; |
|
|
|
public class TaskExecutorExample { |
|
|
|
private class MessagePrinterTask implements Runnable { |
|
|
|
private String message; |
|
|
|
public MessagePrinterTask(String message) { |
|
this.message = message; |
|
} |
|
|
|
public void run() { |
|
System.out.println(message); |
|
} |
|
|
|
} |
|
|
|
private TaskExecutor taskExecutor; |
|
|
|
public TaskExecutorExample(TaskExecutor taskExecutor) { |
|
this.taskExecutor = taskExecutor; |
|
} |
|
|
|
public void printMessages() { |
|
for(int i = 0; i < 25; i++) { |
|
taskExecutor.execute(new MessagePrinterTask("Message" + i)); |
|
} |
|
} |
|
|
|
} |
|
---- |
|
|
|
As you can see, rather than retrieving a thread from the pool and executing yourself, |
|
you add your `Runnable` to the queue and the `TaskExecutor` uses its internal rules to |
|
decide when the task gets executed. |
|
|
|
To configure the rules that the `TaskExecutor` will use, simple bean properties have |
|
been exposed. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> |
|
<property name="corePoolSize" value="5" /> |
|
<property name="maxPoolSize" value="10" /> |
|
<property name="queueCapacity" value="25" /> |
|
</bean> |
|
|
|
<bean id="taskExecutorExample" class="TaskExecutorExample"> |
|
<constructor-arg ref="taskExecutor" /> |
|
</bean> |
|
---- |
|
|
|
|
|
|
|
|
|
[[scheduling-task-scheduler]] |
|
=== The Spring TaskScheduler abstraction |
|
|
|
In addition to the `TaskExecutor` abstraction, Spring 3.0 introduces a `TaskScheduler` |
|
with a variety of methods for scheduling tasks to run at some point in the future. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface TaskScheduler { |
|
|
|
ScheduledFuture schedule(Runnable task, Trigger trigger); |
|
|
|
ScheduledFuture schedule(Runnable task, Date startTime); |
|
|
|
ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period); |
|
|
|
ScheduledFuture scheduleAtFixedRate(Runnable task, long period); |
|
|
|
ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay); |
|
|
|
ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay); |
|
|
|
} |
|
---- |
|
|
|
The simplest method is the one named 'schedule' that takes a `Runnable` and `Date` only. |
|
That will cause the task to run once after the specified time. All of the other methods |
|
are capable of scheduling tasks to run repeatedly. The fixed-rate and fixed-delay |
|
methods are for simple, periodic execution, but the method that accepts a Trigger is |
|
much more flexible. |
|
|
|
|
|
|
|
[[scheduling-trigger-interface]] |
|
==== the Trigger interface |
|
|
|
The `Trigger` interface is essentially inspired by JSR-236, which, as of Spring 3.0, has |
|
not yet been officially implemented. The basic idea of the `Trigger` is that execution |
|
times may be determined based on past execution outcomes or even arbitrary conditions. |
|
If these determinations do take into account the outcome of the preceding execution, |
|
that information is available within a `TriggerContext`. The `Trigger` interface itself |
|
is quite simple: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface Trigger { |
|
|
|
Date nextExecutionTime(TriggerContext triggerContext); |
|
|
|
} |
|
---- |
|
|
|
As you can see, the `TriggerContext` is the most important part. It encapsulates all of |
|
the relevant data, and is open for extension in the future if necessary. The |
|
`TriggerContext` is an interface (a `SimpleTriggerContext` implementation is used by |
|
default). Here you can see what methods are available for `Trigger` implementations. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface TriggerContext { |
|
|
|
Date lastScheduledExecutionTime(); |
|
|
|
Date lastActualExecutionTime(); |
|
|
|
Date lastCompletionTime(); |
|
|
|
} |
|
---- |
|
|
|
|
|
|
|
[[scheduling-trigger-implementations]] |
|
==== Trigger implementations |
|
|
|
Spring provides two implementations of the `Trigger` interface. The most interesting one |
|
is the `CronTrigger`. It enables the scheduling of tasks based on cron expressions. For |
|
example the following task is being scheduled to run 15 minutes past each hour but only |
|
during the 9-to-5 "business hours" on weekdays. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
scheduler.schedule(task, new CronTrigger("* 15 9-17 * * MON-FRI")); |
|
---- |
|
|
|
The other out-of-the-box implementation is a `PeriodicTrigger` that accepts a fixed |
|
period, an optional initial delay value, and a boolean to indicate whether the period |
|
should be interpreted as a fixed-rate or a fixed-delay. Since the `TaskScheduler` |
|
interface already defines methods for scheduling tasks at a fixed-rate or with a |
|
fixed-delay, those methods should be used directly whenever possible. The value of the |
|
`PeriodicTrigger` implementation is that it can be used within components that rely on |
|
the `Trigger` abstraction. For example, it may be convenient to allow periodic triggers, |
|
cron-based triggers, and even custom trigger implementations to be used interchangeably. |
|
Such a component could take advantage of dependency injection so that such `Triggers` |
|
could be configured externally. |
|
|
|
|
|
|
|
[[scheduling-task-scheduler-implementations]] |
|
==== TaskScheduler implementations |
|
|
|
As with Spring's `TaskExecutor` abstraction, the primary benefit of the `TaskScheduler` |
|
is that code relying on scheduling behavior need not be coupled to a particular |
|
scheduler implementation. The flexibility this provides is particularly relevant when |
|
running within Application Server environments where threads should not be created |
|
directly by the application itself. For such cases, Spring provides a |
|
`TimerManagerTaskScheduler` that delegates to a CommonJ TimerManager instance, typically |
|
configured with a JNDI-lookup. |
|
|
|
A simpler alternative, the `ThreadPoolTaskScheduler`, can be used whenever external |
|
thread management is not a requirement. Internally, it delegates to a |
|
`ScheduledExecutorService` instance. `ThreadPoolTaskScheduler` actually implements |
|
Spring's `TaskExecutor` interface as well, so that a single instance can be used for |
|
asynchronous execution __as soon as possible__ as well as scheduled, and potentially |
|
recurring, executions. |
|
|
|
|
|
|
|
|
|
[[scheduling-annotation-support]] |
|
=== Annotation Support for Scheduling and Asynchronous Execution |
|
Spring provides annotation support for both task scheduling and asynchronous method |
|
execution. |
|
|
|
|
|
|
|
[[scheduling-enable-annotation-support]] |
|
==== Enable scheduling annotations |
|
To enable support for `@Scheduled` and `@Async` annotations add `@EnableScheduling` and |
|
`@EnableAsync` to one of your `@Configuration` classes: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableAsync |
|
@EnableScheduling |
|
public class AppConfig { |
|
} |
|
---- |
|
|
|
You are free to pick and choose the relevant annotations for your application. For |
|
example, if you only need support for `@Scheduled`, simply omit `@EnableAsync`. For more |
|
fine-grained control you can additionally implement the `SchedulingConfigurer` and/or |
|
`AsyncConfigurer` interfaces. See the javadocs for full details. |
|
|
|
If you prefer XML configuration use the `<task:annotation-driven>` element. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<task:annotation-driven executor="myExecutor" scheduler="myScheduler"/> |
|
<task:executor id="myExecutor" pool-size="5"/> |
|
<task:scheduler id="myScheduler" pool-size="10"/> |
|
---- |
|
|
|
Notice with the above XML that an executor reference is provided for handling those |
|
tasks that correspond to methods with the `@Async` annotation, and the scheduler |
|
reference is provided for managing those methods annotated with `@Scheduled`. |
|
|
|
|
|
|
|
[[scheduling-annotation-support-scheduled]] |
|
==== The @Scheduled Annotation |
|
The @Scheduled annotation can be added to a method along with trigger metadata. For |
|
example, the following method would be invoked every 5 seconds with a fixed delay, |
|
meaning that the period will be measured from the completion time of each preceding |
|
invocation. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Scheduled(fixedDelay=5000) |
|
public void doSomething() { |
|
// something that should execute periodically |
|
} |
|
---- |
|
|
|
If a fixed rate execution is desired, simply change the property name specified within |
|
the annotation. The following would be executed every 5 seconds measured between the |
|
successive start times of each invocation. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Scheduled(fixedRate=5000) |
|
public void doSomething() { |
|
// something that should execute periodically |
|
} |
|
---- |
|
|
|
For fixed-delay and fixed-rate tasks, an initial delay may be specified indicating the |
|
number of milliseconds to wait before the first execution of the method. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Scheduled(initialDelay=1000, fixedRate=5000) |
|
public void doSomething() { |
|
// something that should execute periodically |
|
} |
|
---- |
|
|
|
If simple periodic scheduling is not expressive enough, then a cron expression may be |
|
provided. For example, the following will only execute on weekdays. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
@Scheduled(cron="*/5 * * * * MON-FRI") |
|
public void doSomething() { |
|
// something that should execute on weekdays only |
|
} |
|
---- |
|
|
|
[TIP] |
|
==== |
|
You can additionally use the `zone` attribute to specify the time zone in which the cron |
|
expression will be resolved. |
|
==== |
|
|
|
|
|
Notice that the methods to be scheduled must have void returns and must not expect any |
|
arguments. If the method needs to interact with other objects from the Application |
|
Context, then those would typically have been provided through dependency injection. |
|
|
|
[NOTE] |
|
==== |
|
Make sure that you are not initializing multiple instances of the same @Scheduled |
|
annotation class at runtime, unless you do want to schedule callbacks to each such |
|
instance. Related to this, make sure that you do not use @Configurable on bean classes |
|
which are annotated with @Scheduled and registered as regular Spring beans with the |
|
container: You would get double initialization otherwise, once through the container and |
|
once through the @Configurable aspect, with the consequence of each @Scheduled method |
|
being invoked twice. |
|
==== |
|
|
|
|
|
|
|
[[scheduling-annotation-support-async]] |
|
==== The @Async Annotation |
|
The `@Async` annotation can be provided on a method so that invocation of that method |
|
will occur asynchronously. In other words, the caller will return immediately upon |
|
invocation and the actual execution of the method will occur in a task that has been |
|
submitted to a Spring `TaskExecutor`. In the simplest case, the annotation may be |
|
applied to a `void`-returning method. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Async |
|
void doSomething() { |
|
// this will be executed asynchronously |
|
} |
|
---- |
|
|
|
Unlike the methods annotated with the `@Scheduled` annotation, these methods can expect |
|
arguments, because they will be invoked in the "normal" way by callers at runtime rather |
|
than from a scheduled task being managed by the container. For example, the following is |
|
a legitimate application of the `@Async` annotation. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Async |
|
void doSomething(String s) { |
|
// this will be executed asynchronously |
|
} |
|
---- |
|
|
|
Even methods that return a value can be invoked asynchronously. However, such methods |
|
are required to have a `Future` typed return value. This still provides the benefit of |
|
asynchronous execution so that the caller can perform other tasks prior to calling |
|
`get()` on that Future. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Async |
|
Future<String> returnSomething(int i) { |
|
// this will be executed asynchronously |
|
} |
|
---- |
|
|
|
`@Async` can not be used in conjunction with lifecycle callbacks such as |
|
`@PostConstruct`. To asynchronously initialize Spring beans you currently have to use a |
|
separate initializing Spring bean that invokes the `@Async` annotated method on the |
|
target then. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class SampleBeanImpl implements SampleBean { |
|
|
|
@Async |
|
void doSomething() { |
|
// ... |
|
} |
|
|
|
} |
|
|
|
public class SampleBeanInititalizer { |
|
|
|
private final SampleBean bean; |
|
|
|
public SampleBeanInitializer(SampleBean bean) { |
|
this.bean = bean; |
|
} |
|
|
|
@PostConstruct |
|
public void initialize() { |
|
bean.doSomething(); |
|
} |
|
|
|
} |
|
---- |
|
|
|
|
|
|
|
[[scheduling-annotation-support-qualification]] |
|
==== Executor qualification with @Async |
|
By default when specifying `@Async` on a method, the executor that will be used is the |
|
one supplied to the 'annotation-driven' element as described above. However, the `value` |
|
attribute of the `@Async` annotation can be used when needing to indicate that an |
|
executor other than the default should be used when executing a given method. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Async("otherExecutor") |
|
void doSomething(String s) { |
|
// this will be executed asynchronously by "otherExecutor" |
|
} |
|
---- |
|
|
|
In this case, "otherExecutor" may be the name of any `Executor` bean in the Spring |
|
container, or may be the name of a __qualifier__ associated with any `Executor`, e.g. as |
|
specified with the `<qualifier>` element or Spring's `@Qualifier` annotation. |
|
|
|
|
|
|
|
[[scheduling-annotation-support-exception]] |
|
==== Exception management with @Async |
|
When an `@Async` method has a `Future` typed return value, it is easy to manage |
|
an exception that was thrown during the method execution as this exception will |
|
be thrown when calling `get` on the `Future` result. With a void return type |
|
however, the exception is uncaught and cannot be transmitted. For those cases, an |
|
`AsyncUncaughtExceptionHandler` can be provided to handle such exceptions. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler { |
|
|
|
@Override |
|
public void handleUncaughtException(Throwable ex, Method method, Object... params) { |
|
// handle exception |
|
} |
|
} |
|
---- |
|
|
|
By default, the exception is simply logged. A custom `AsyncUncaughtExceptionHandler` can |
|
be defined _via_ `AsyncConfigurer` or the `task:annotation-driven` XML element. |
|
|
|
[[scheduling-task-namespace]] |
|
=== The Task Namespace |
|
Beginning with Spring 3.0, there is an XML namespace for configuring `TaskExecutor` and |
|
`TaskScheduler` instances. It also provides a convenient way to configure tasks to be |
|
scheduled with a trigger. |
|
|
|
|
|
|
|
[[scheduling-task-namespace-scheduler]] |
|
==== The 'scheduler' element |
|
The following element will create a `ThreadPoolTaskScheduler` instance with the |
|
specified thread pool size. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<task:scheduler id="scheduler" pool-size="10"/> |
|
---- |
|
|
|
The value provided for the 'id' attribute will be used as the prefix for thread names |
|
within the pool. The 'scheduler' element is relatively straightforward. If you do not |
|
provide a 'pool-size' attribute, the default thread pool will only have a single thread. |
|
There are no other configuration options for the scheduler. |
|
|
|
|
|
|
|
[[scheduling-task-namespace-executor]] |
|
==== The 'executor' element |
|
The following will create a `ThreadPoolTaskExecutor` instance: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<task:executor id="executor" pool-size="10"/> |
|
---- |
|
|
|
As with the scheduler above, the value provided for the 'id' attribute will be used as |
|
the prefix for thread names within the pool. As far as the pool size is concerned, the |
|
'executor' element supports more configuration options than the 'scheduler' element. For |
|
one thing, the thread pool for a `ThreadPoolTaskExecutor` is itself more configurable. |
|
Rather than just a single size, an executor's thread pool may have different values for |
|
the __core__ and the __max__ size. If a single value is provided then the executor will |
|
have a fixed-size thread pool (the core and max sizes are the same). However, the |
|
'executor' element's 'pool-size' attribute also accepts a range in the form of "min-max". |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<task:executor |
|
id="executorWithPoolSizeRange" |
|
pool-size="5-25" |
|
queue-capacity="100"/> |
|
---- |
|
|
|
As you can see from that configuration, a 'queue-capacity' value has also been provided. |
|
The configuration of the thread pool should also be considered in light of the |
|
executor's queue capacity. For the full description of the relationship between pool |
|
size and queue capacity, consult the documentation for |
|
http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html[ThreadPoolExecutor]. |
|
The main idea is that when a task is submitted, the executor will first try to use a |
|
free thread if the number of active threads is currently less than the core size. If the |
|
core size has been reached, then the task will be added to the queue as long as its |
|
capacity has not yet been reached. Only then, if the queue's capacity __has__ been |
|
reached, will the executor create a new thread beyond the core size. If the max size has |
|
also been reached, then the executor will reject the task. |
|
|
|
By default, the queue is __unbounded__, but this is rarely the desired configuration, |
|
because it can lead to `OutOfMemoryErrors` if enough tasks are added to that queue while |
|
all pool threads are busy. Furthermore, if the queue is unbounded, then the max size has |
|
no effect at all. Since the executor will always try the queue before creating a new |
|
thread beyond the core size, a queue must have a finite capacity for the thread pool to |
|
grow beyond the core size (this is why a __fixed size__ pool is the only sensible case |
|
when using an unbounded queue). |
|
|
|
In a moment, we will review the effects of the keep-alive setting which adds yet another |
|
factor to consider when providing a pool size configuration. First, let's consider the |
|
case, as mentioned above, when a task is rejected. By default, when a task is rejected, |
|
a thread pool executor will throw a `TaskRejectedException`. However, the rejection |
|
policy is actually configurable. The exception is thrown when using the default |
|
rejection policy which is the `AbortPolicy` implementation. For applications where some |
|
tasks can be skipped under heavy load, either the `DiscardPolicy` or |
|
`DiscardOldestPolicy` may be configured instead. Another option that works well for |
|
applications that need to throttle the submitted tasks under heavy load is the |
|
`CallerRunsPolicy`. Instead of throwing an exception or discarding tasks, that policy |
|
will simply force the thread that is calling the submit method to run the task itself. |
|
The idea is that such a caller will be busy while running that task and not able to |
|
submit other tasks immediately. Therefore it provides a simple way to throttle the |
|
incoming load while maintaining the limits of the thread pool and queue. Typically this |
|
allows the executor to "catch up" on the tasks it is handling and thereby frees up some |
|
capacity on the queue, in the pool, or both. Any of these options can be chosen from an |
|
enumeration of values available for the 'rejection-policy' attribute on the 'executor' |
|
element. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<task:executor |
|
id="executorWithCallerRunsPolicy" |
|
pool-size="5-25" |
|
queue-capacity="100" |
|
rejection-policy="CALLER_RUNS"/> |
|
---- |
|
|
|
Finally, the `keep-alive` setting determines the time limit (in seconds) for which threads |
|
may remain idle before being terminated. If there are more than the core number of threads |
|
currently in the pool, after waiting this amount of time without processing a task, excess |
|
threads will get terminated. A time value of zero will cause excess threads to terminate |
|
immediately after executing a task without remaining follow-up work in the task queue. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<task:executor |
|
id="executorWithKeepAlive" |
|
pool-size="5-25" |
|
keep-alive="120"/> |
|
---- |
|
|
|
|
|
|
|
[[scheduling-task-namespace-scheduled-tasks]] |
|
==== The 'scheduled-tasks' element |
|
The most powerful feature of Spring's task namespace is the support for configuring |
|
tasks to be scheduled within a Spring Application Context. This follows an approach |
|
similar to other "method-invokers" in Spring, such as that provided by the JMS namespace |
|
for configuring Message-driven POJOs. Basically a "ref" attribute can point to any |
|
Spring-managed object, and the "method" attribute provides the name of a method to be |
|
invoked on that object. Here is a simple example. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<task:scheduled-tasks scheduler="myScheduler"> |
|
<task:scheduled ref="beanA" method="methodA" fixed-delay="5000"/> |
|
</task:scheduled-tasks> |
|
|
|
<task:scheduler id="myScheduler" pool-size="10"/> |
|
---- |
|
|
|
As you can see, the scheduler is referenced by the outer element, and each individual |
|
task includes the configuration of its trigger metadata. In the preceding example, that |
|
metadata defines a periodic trigger with a fixed delay indicating the number of |
|
milliseconds to wait after each task execution has completed. Another option is |
|
'fixed-rate', indicating how often the method should be executed regardless of how long |
|
any previous execution takes. Additionally, for both fixed-delay and fixed-rate tasks an |
|
'initial-delay' parameter may be specified indicating the number of milliseconds to wait |
|
before the first execution of the method. For more control, a "cron" attribute may be |
|
provided instead. Here is an example demonstrating these other options. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<task:scheduled-tasks scheduler="myScheduler"> |
|
<task:scheduled ref="beanA" method="methodA" fixed-delay="5000" initial-delay="1000"/> |
|
<task:scheduled ref="beanB" method="methodB" fixed-rate="5000"/> |
|
<task:scheduled ref="beanC" method="methodC" cron="*/5 * * * * MON-FRI"/> |
|
</task:scheduled-tasks> |
|
|
|
<task:scheduler id="myScheduler" pool-size="10"/> |
|
---- |
|
|
|
|
|
|
|
|
|
[[scheduling-quartz]] |
|
=== Using the Quartz Scheduler |
|
Quartz uses `Trigger`, `Job` and `JobDetail` objects to realize scheduling of all kinds |
|
of jobs. For the basic concepts behind Quartz, have a look at |
|
http://quartz-scheduler.org[]. For convenience purposes, Spring offers a couple of |
|
classes that simplify the usage of Quartz within Spring-based applications. |
|
|
|
|
|
|
|
[[scheduling-quartz-jobdetail]] |
|
==== Using the JobDetailFactoryBean |
|
Quartz `JobDetail` objects contain all information needed to run a job. Spring provides a |
|
`JobDetailFactoryBean` which provides bean-style properties for XML configuration purposes. |
|
Let's have a look at an example: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean name="exampleJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean"> |
|
<property name="jobClass" value="example.ExampleJob"/> |
|
<property name="jobDataAsMap"> |
|
<map> |
|
<entry key="timeout" value="5"/> |
|
</map> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
The job detail configuration has all information it needs to run the job (`ExampleJob`). |
|
The timeout is specified in the job data map. The job data map is available through the |
|
`JobExecutionContext` (passed to you at execution time), but the `JobDetail` also gets |
|
its properties from the job data mapped to properties of the job instance. So in this |
|
case, if the `ExampleJob` contains a bean property named `timeout`, the `JobDetail` |
|
will have it applied automatically: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
package example; |
|
|
|
public class ExampleJob extends QuartzJobBean { |
|
|
|
private int timeout; |
|
|
|
/** |
|
* Setter called after the ExampleJob is instantiated |
|
* with the value from the JobDetailFactoryBean (5) |
|
*/ |
|
public void setTimeout(int timeout) { |
|
this.timeout = timeout; |
|
} |
|
|
|
protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException { |
|
// do the actual work |
|
} |
|
|
|
} |
|
---- |
|
|
|
All additional properties from the job data map are of course available to you as well. |
|
|
|
[NOTE] |
|
==== |
|
Using the `name` and `group` properties, you can modify the name and the group |
|
of the job, respectively. By default, the name of the job matches the bean name |
|
of the `JobDetailFactoryBean` (in the example above, this is `exampleJob`). |
|
==== |
|
|
|
|
|
|
|
[[scheduling-quartz-method-invoking-job]] |
|
==== Using the MethodInvokingJobDetailFactoryBean |
|
|
|
Often you just need to invoke a method on a specific object. Using the |
|
`MethodInvokingJobDetailFactoryBean` you can do exactly this: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> |
|
<property name="targetObject" ref="exampleBusinessObject"/> |
|
<property name="targetMethod" value="doIt"/> |
|
</bean> |
|
---- |
|
|
|
The above example will result in the `doIt` method being called on the |
|
`exampleBusinessObject` method (see below): |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class ExampleBusinessObject { |
|
|
|
// properties and collaborators |
|
|
|
public void doIt() { |
|
// do the actual work |
|
} |
|
} |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="exampleBusinessObject" class="examples.ExampleBusinessObject"/> |
|
---- |
|
|
|
Using the `MethodInvokingJobDetailFactoryBean`, you don't need to create one-line jobs |
|
that just invoke a method, and you only need to create the actual business object and |
|
wire up the detail object. |
|
|
|
By default, Quartz Jobs are stateless, resulting in the possibility of jobs interfering |
|
with each other. If you specify two triggers for the same `JobDetail`, it might be |
|
possible that before the first job has finished, the second one will start. If |
|
`JobDetail` classes implement the `Stateful` interface, this won't happen. The second |
|
job will not start before the first one has finished. To make jobs resulting from the |
|
`MethodInvokingJobDetailFactoryBean` non-concurrent, set the `concurrent` flag to |
|
`false`. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> |
|
<property name="targetObject" ref="exampleBusinessObject"/> |
|
<property name="targetMethod" value="doIt"/> |
|
<property name="concurrent" value="false"/> |
|
</bean> |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
By default, jobs will run in a concurrent fashion. |
|
==== |
|
|
|
|
|
|
|
[[scheduling-quartz-cron]] |
|
==== Wiring up jobs using triggers and the SchedulerFactoryBean |
|
|
|
We've created job details and jobs. We've also reviewed the convenience bean that allows |
|
you to invoke a method on a specific object. Of course, we still need to schedule the |
|
jobs themselves. This is done using triggers and a `SchedulerFactoryBean`. Several |
|
triggers are available within Quartz and Spring offers two Quartz `FactoryBean` |
|
implementations with convenient defaults: `CronTriggerFactoryBean` and |
|
`SimpleTriggerFactoryBean`. |
|
|
|
Triggers need to be scheduled. Spring offers a `SchedulerFactoryBean` that exposes |
|
triggers to be set as properties. `SchedulerFactoryBean` schedules the actual jobs with |
|
those triggers. |
|
|
|
Find below a couple of examples: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean"> |
|
<!-- see the example of method invoking job above --> |
|
<property name="jobDetail" ref="jobDetail"/> |
|
<!-- 10 seconds --> |
|
<property name="startDelay" value="10000"/> |
|
<!-- repeat every 50 seconds --> |
|
<property name="repeatInterval" value="50000"/> |
|
</bean> |
|
|
|
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> |
|
<property name="jobDetail" ref="exampleJob"/> |
|
<!-- run every morning at 6 AM --> |
|
<property name="cronExpression" value="0 0 6 * * ?"/> |
|
</bean> |
|
---- |
|
|
|
Now we've set up two triggers, one running every 50 seconds with a starting delay of 10 |
|
seconds and one every morning at 6 AM. To finalize everything, we need to set up the |
|
`SchedulerFactoryBean`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> |
|
<property name="triggers"> |
|
<list> |
|
<ref bean="cronTrigger"/> |
|
<ref bean="simpleTrigger"/> |
|
</list> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
More properties are available for the `SchedulerFactoryBean` for you to set, such as the |
|
calendars used by the job details, properties to customize Quartz with, etc. Have a look |
|
at the |
|
{javadoc-baseurl}/org/springframework/scheduling/quartz/SchedulerFactoryBean.html[`SchedulerFactoryBean` |
|
javadocs] for more information. |
|
|
|
|
|
|
|
|
|
|
|
[[dynamic-language]] |
|
== Dynamic language support |
|
|
|
|
|
|
|
|
|
[[dynamic-language-introduction]] |
|
=== Introduction |
|
Spring 2.0 introduces comprehensive support for using classes and objects that have been |
|
defined using a dynamic language (such as JRuby) with Spring. This support allows you to |
|
write any number of classes in a supported dynamic language, and have the Spring |
|
container transparently instantiate, configure and dependency inject the resulting |
|
objects. |
|
|
|
The dynamic languages currently supported are: |
|
|
|
* JRuby 1.5+ |
|
* Groovy 1.8+ |
|
* BeanShell 2.0 |
|
|
|
.Why only these languages? |
|
**** |
|
The supported languages were chosen because __a)__ the languages have a lot of traction in |
|
the Java enterprise community, __b)__ no requests were made for other languages at the time |
|
that this support was added, and __c)__ the Spring developers were most familiar with |
|
them. |
|
**** |
|
|
|
Fully working examples of where this dynamic language support can be immediately useful |
|
are described in <<dynamic-language-scenarios>>. |
|
|
|
|
|
|
|
|
|
[[dynamic-language-a-first-example]] |
|
=== A first example |
|
This bulk of this chapter is concerned with describing the dynamic language support in |
|
detail. Before diving into all of the ins and outs of the dynamic language support, |
|
let's look at a quick example of a bean defined in a dynamic language. The dynamic |
|
language for this first bean is Groovy (the basis of this example was taken from the |
|
Spring test suite, so if you want to see equivalent examples in any of the other |
|
supported languages, take a look at the source code). |
|
|
|
Find below the `Messenger` interface that the Groovy bean is going to be implementing, |
|
and note that this interface is defined in plain Java. Dependent objects that are |
|
injected with a reference to the `Messenger` won't know that the underlying |
|
implementation is a Groovy script. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.springframework.scripting; |
|
|
|
public interface Messenger { |
|
|
|
String getMessage(); |
|
|
|
} |
|
---- |
|
|
|
Here is the definition of a class that has a dependency on the `Messenger` interface. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.springframework.scripting; |
|
|
|
public class DefaultBookingService implements BookingService { |
|
|
|
private Messenger messenger; |
|
|
|
public void setMessenger(Messenger messenger) { |
|
this.messenger = messenger; |
|
} |
|
|
|
public void processBooking() { |
|
// use the injected Messenger object... |
|
} |
|
|
|
} |
|
---- |
|
|
|
Here is an implementation of the `Messenger` interface in Groovy. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// from the file 'Messenger.groovy' |
|
package org.springframework.scripting.groovy; |
|
|
|
// import the Messenger interface (written in Java) that is to be implemented |
|
import org.springframework.scripting.Messenger |
|
|
|
// define the implementation in Groovy |
|
class GroovyMessenger implements Messenger { |
|
|
|
String message |
|
|
|
} |
|
---- |
|
|
|
Finally, here are the bean definitions that will effect the injection of the |
|
Groovy-defined `Messenger` implementation into an instance of the |
|
`DefaultBookingService` class. |
|
|
|
[NOTE] |
|
==== |
|
To use the custom dynamic language tags to define dynamic-language-backed beans, you |
|
need to have the XML Schema preamble at the top of your Spring XML configuration file. |
|
You also need to be using a Spring `ApplicationContext` implementation as your IoC |
|
container. Using the dynamic-language-backed beans with a plain `BeanFactory` |
|
implementation is supported, but you have to manage the plumbing of the Spring internals |
|
to do so. |
|
|
|
For more information on schema-based configuration, see <<xsd-config>>. |
|
==== |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:lang="http://www.springframework.org/schema/lang" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd"> |
|
|
|
<!-- this is the bean definition for the Groovy-backed Messenger implementation --> |
|
<lang:groovy id="messenger" script-source="classpath:Messenger.groovy"> |
|
<lang:property name="message" value="I Can Do The Frug" /> |
|
</lang:groovy> |
|
|
|
<!-- an otherwise normal bean that will be injected by the Groovy-backed Messenger --> |
|
<bean id="bookingService" class="x.y.DefaultBookingService"> |
|
<property name="messenger" ref="messenger" /> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
The `bookingService` bean (a `DefaultBookingService`) can now use its private |
|
`messenger` member variable as normal because the `Messenger` instance that was injected |
|
into it __is__ a `Messenger` instance. There is nothing special going on here, just |
|
plain Java and plain Groovy. |
|
|
|
Hopefully the above XML snippet is self-explanatory, but don't worry unduly if it isn't. |
|
Keep reading for the in-depth detail on the whys and wherefores of the above |
|
configuration. |
|
|
|
|
|
|
|
|
|
[[dynamic-language-beans]] |
|
=== Defining beans that are backed by dynamic languages |
|
This section describes exactly how you define Spring managed beans in any of the |
|
supported dynamic languages. |
|
|
|
Please note that this chapter does not attempt to explain the syntax and idioms of the |
|
supported dynamic languages. For example, if you want to use Groovy to write certain of |
|
the classes in your application, then the assumption is that you already know Groovy. If |
|
you need further details about the dynamic languages themselves, please |
|
consult <<dynamic-language-resources>> at the end of this chapter. |
|
|
|
|
|
|
|
[[dynamic-language-beans-concepts]] |
|
==== Common concepts |
|
The steps involved in using dynamic-language-backed beans are as follows: |
|
|
|
* Write the test for the dynamic language source code (naturally) |
|
* __Then__ write the dynamic language source code itself :) |
|
* Define your dynamic-language-backed beans using the appropriate `<lang:language/>` |
|
element in the XML configuration (you can of course define such beans programmatically |
|
using the Spring API - although you will have to consult the source code for |
|
directions on how to do this as this type of advanced configuration is not covered in |
|
this chapter). Note this is an iterative step. You will need at least one bean |
|
definition per dynamic language source file (although the same dynamic language source |
|
file can of course be referenced by multiple bean definitions). |
|
|
|
The first two steps (testing and writing your dynamic language source files) are beyond |
|
the scope of this chapter. Refer to the language specification and / or reference manual |
|
for your chosen dynamic language and crack on with developing your dynamic language |
|
source files. You __will__ first want to read the rest of this chapter though, as |
|
Spring's dynamic language support does make some (small) assumptions about the contents |
|
of your dynamic language source files. |
|
|
|
|
|
[[dynamic-language-beans-concepts-xml-language-element]] |
|
===== The <lang:language/> element |
|
The final step involves defining dynamic-language-backed bean definitions, one for each |
|
bean that you want to configure (this is no different from normal JavaBean |
|
configuration). However, instead of specifying the fully qualified classname of the |
|
class that is to be instantiated and configured by the container, you use the |
|
`<lang:language/>` element to define the dynamic language-backed bean. |
|
|
|
Each of the supported languages has a corresponding `<lang:language/>` element: |
|
|
|
* `<lang:jruby/>` (JRuby) |
|
* `<lang:groovy/>` (Groovy) |
|
* `<lang:bsh/>` (BeanShell) |
|
|
|
The exact attributes and child elements that are available for configuration depends on |
|
exactly which language the bean has been defined in (the language-specific sections |
|
below provide the full lowdown on this). |
|
|
|
|
|
[[dynamic-language-refreshable-beans]] |
|
===== Refreshable beans |
|
One of the (if not __the__) most compelling value adds of the dynamic language support |
|
in Spring is the__'refreshable bean'__ feature. |
|
|
|
A refreshable bean is a dynamic-language-backed bean that with a small amount of |
|
configuration, a dynamic-language-backed bean can monitor changes in its underlying |
|
source file resource, and then reload itself when the dynamic language source file is |
|
changed (for example when a developer edits and saves changes to the file on the |
|
filesystem). |
|
|
|
This allows a developer to deploy any number of dynamic language source files as part of |
|
an application, configure the Spring container to create beans backed by dynamic |
|
language source files (using the mechanisms described in this chapter), and then later, |
|
as requirements change or some other external factor comes into play, simply edit a |
|
dynamic language source file and have any change they make reflected in the bean that is |
|
backed by the changed dynamic language source file. There is no need to shut down a |
|
running application (or redeploy in the case of a web application). The |
|
dynamic-language-backed bean so amended will pick up the new state and logic from the |
|
changed dynamic language source file. |
|
|
|
[NOTE] |
|
==== |
|
Please note that this feature is __off__ by default. |
|
==== |
|
|
|
Let's take a look at an example to see just how easy it is to start using refreshable |
|
beans. To __turn on__ the refreshable beans feature, you simply have to specify exactly |
|
__one__ additional attribute on the `<lang:language/>` element of your bean definition. |
|
So if we stick with <<dynamic-language-a-first-example,the example>> from earlier in this |
|
chapter, here's what we would change in the Spring XML configuration to effect |
|
refreshable beans: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<!-- this bean is now 'refreshable' due to the presence of the 'refresh-check-delay' attribute --> |
|
<lang:groovy id="messenger" |
|
refresh-check-delay="5000" <!-- switches refreshing on with 5 seconds between checks --> |
|
script-source="classpath:Messenger.groovy"> |
|
<lang:property name="message" value="I Can Do The Frug" /> |
|
</lang:groovy> |
|
|
|
<bean id="bookingService" class="x.y.DefaultBookingService"> |
|
<property name="messenger" ref="messenger" /> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
That really is all you have to do. The `'refresh-check-delay'` attribute defined on the |
|
`'messenger'` bean definition is the number of milliseconds after which the bean will be |
|
refreshed with any changes made to the underlying dynamic language source file. You can |
|
turn off the refresh behavior by assigning a negative value to the |
|
`'refresh-check-delay'` attribute. Remember that, by default, the refresh behavior is |
|
disabled. If you don't want the refresh behavior, then simply don't define the attribute. |
|
|
|
If we then run the following application we can exercise the refreshable feature; please |
|
do excuse the __'jumping-through-hoops-to-pause-the-execution'__ shenanigans in this |
|
next slice of code. The `System.in.read()` call is only there so that the execution of |
|
the program pauses while I (the author) go off and edit the underlying dynamic language |
|
source file so that the refresh will trigger on the dynamic-language-backed bean when |
|
the program resumes execution. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import org.springframework.context.ApplicationContext; |
|
import org.springframework.context.support.ClassPathXmlApplicationContext; |
|
import org.springframework.scripting.Messenger; |
|
|
|
public final class Boot { |
|
|
|
public static void main(final String[] args) throws Exception { |
|
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); |
|
Messenger messenger = (Messenger) ctx.getBean("messenger"); |
|
System.out.println(messenger.getMessage()); |
|
// pause execution while I go off and make changes to the source file... |
|
System.in.read(); |
|
System.out.println(messenger.getMessage()); |
|
} |
|
} |
|
---- |
|
|
|
Let's assume then, for the purposes of this example, that all calls to the |
|
`getMessage()` method of `Messenger` implementations have to be changed such that the |
|
message is surrounded by quotes. Below are the changes that I (the author) make to the |
|
`Messenger.groovy` source file when the execution of the program is paused. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.springframework.scripting |
|
|
|
class GroovyMessenger implements Messenger { |
|
|
|
private String message = "Bingo" |
|
|
|
public String getMessage() { |
|
// change the implementation to surround the message in quotes |
|
return "''" + this.message + "''" |
|
} |
|
|
|
public void setMessage(String message) { |
|
this.message = message |
|
} |
|
} |
|
---- |
|
|
|
When the program executes, the output before the input pause will be __I Can Do The |
|
Frug__. After the change to the source file is made and saved, and the program resumes |
|
execution, the result of calling the `getMessage()` method on the |
|
dynamic-language-backed `Messenger` implementation will be __'I Can Do The Frug'__ |
|
(notice the inclusion of the additional quotes). |
|
|
|
It is important to understand that changes to a script will __not__ trigger a refresh if |
|
the changes occur within the window of the `'refresh-check-delay'` value. It is equally |
|
important to understand that changes to the script are __not__ actually 'picked up' until |
|
a method is called on the dynamic-language-backed bean. It is only when a method is |
|
called on a dynamic-language-backed bean that it checks to see if its underlying script |
|
source has changed. Any exceptions relating to refreshing the script (such as |
|
encountering a compilation error, or finding that the script file has been deleted) will |
|
result in a __fatal__ exception being propagated to the calling code. |
|
|
|
The refreshable bean behavior described above does __not__ apply to dynamic language |
|
source files defined using the `<lang:inline-script/>` element notation (see |
|
<<dynamic-language-beans-inline>>). Additionally, it __only__ applies to beans where |
|
changes to the underlying source file can actually be detected; for example, by code |
|
that checks the last modified date of a dynamic language source file that exists on the |
|
filesystem. |
|
|
|
|
|
[[dynamic-language-beans-inline]] |
|
===== Inline dynamic language source files |
|
The dynamic language support can also cater for dynamic language source files that are |
|
embedded directly in Spring bean definitions. More specifically, the |
|
`<lang:inline-script/>` element allows you to define dynamic language source immediately |
|
inside a Spring configuration file. An example will perhaps make the inline script |
|
feature crystal clear: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<lang:groovy id="messenger"> |
|
<lang:inline-script> |
|
|
|
package org.springframework.scripting.groovy; |
|
|
|
import org.springframework.scripting.Messenger |
|
|
|
class GroovyMessenger implements Messenger { |
|
String message |
|
} |
|
|
|
</lang:inline-script> |
|
<lang:property name="message" value="I Can Do The Frug" /> |
|
</lang:groovy> |
|
---- |
|
|
|
If we put to one side the issues surrounding whether it is good practice to define |
|
dynamic language source inside a Spring configuration file, the `<lang:inline-script/>` |
|
element can be useful in some scenarios. For instance, we might want to quickly add a |
|
Spring `Validator` implementation to a Spring MVC `Controller`. This is but a moment's |
|
work using inline source. (See <<dynamic-language-scenarios-validators>> for such an |
|
example.) |
|
|
|
Find below an example of defining the source for a JRuby-based bean directly in a Spring |
|
XML configuration file using the `inline:` notation. (Notice the use of the < |
|
characters to denote a `'<'` character. In such a case surrounding the inline source in |
|
a `<![CDATA[]]>` region might be better.) |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<lang:jruby id="messenger" script-interfaces="org.springframework.scripting.Messenger"> |
|
<lang:inline-script> |
|
|
|
require 'java' |
|
|
|
include_class 'org.springframework.scripting.Messenger' |
|
|
|
class RubyMessenger < Messenger |
|
|
|
def setMessage(message) |
|
@@message = message |
|
end |
|
|
|
def getMessage |
|
@@message |
|
end |
|
|
|
end |
|
|
|
</lang:inline-script> |
|
<lang:property name="message" value="Hello World!" /> |
|
</lang:jruby> |
|
---- |
|
|
|
|
|
[[dynamic-language-beans-ctor-injection]] |
|
===== Understanding Constructor Injection in the context of dynamic-language-backed beans |
|
There is one __very__ important thing to be aware of with regard to Spring's dynamic |
|
language support. Namely, it is not (currently) possible to supply constructor arguments |
|
to dynamic-language-backed beans (and hence constructor-injection is not available for |
|
dynamic-language-backed beans). In the interests of making this special handling of |
|
constructors and properties 100% clear, the following mixture of code and configuration |
|
will __not__ work. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// from the file 'Messenger.groovy' |
|
package org.springframework.scripting.groovy; |
|
|
|
import org.springframework.scripting.Messenger |
|
|
|
class GroovyMessenger implements Messenger { |
|
|
|
GroovyMessenger() {} |
|
|
|
// this constructor is not available for Constructor Injection |
|
GroovyMessenger(String message) { |
|
this.message = message; |
|
} |
|
|
|
String message |
|
|
|
String anotherMessage |
|
|
|
} |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<lang:groovy id="badMessenger" |
|
script-source="classpath:Messenger.groovy"> |
|
<!-- this next constructor argument will *not* be injected into the GroovyMessenger --> |
|
<!-- in fact, this isn't even allowed according to the schema --> |
|
<constructor-arg value="This will *not* work" /> |
|
|
|
<!-- only property values are injected into the dynamic-language-backed object --> |
|
<lang:property name="anotherMessage" value="Passed straight through to the dynamic-language-backed object" /> |
|
|
|
</lang> |
|
---- |
|
|
|
In practice this limitation is not as significant as it first appears since setter |
|
injection is the injection style favored by the overwhelming majority of developers |
|
anyway (let's leave the discussion as to whether that is a good thing to another day). |
|
|
|
|
|
|
|
[[dynamic-language-beans-jruby]] |
|
==== JRuby beans |
|
|
|
.The JRuby library dependencies |
|
**** |
|
The JRuby scripting support in Spring requires the following libraries to be on the |
|
classpath of your application. |
|
|
|
* `jruby.jar` |
|
**** |
|
|
|
From the JRuby homepage... |
|
|
|
"__JRuby is an 100% pure-Java implementation of the Ruby programming language.__" |
|
|
|
In keeping with the Spring philosophy of offering choice, Spring's dynamic language |
|
support also supports beans defined in the JRuby language. The JRuby language is based |
|
on the quite intuitive Ruby language, and has support for inline regular expressions, |
|
blocks (closures), and a whole host of other features that do make solutions for some |
|
domain problems a whole lot easier to develop. |
|
|
|
The implementation of the JRuby dynamic language support in Spring is interesting in |
|
that what happens is this: Spring creates a JDK dynamic proxy implementing all of the |
|
interfaces that are specified in the `'script-interfaces'` attribute value of the |
|
`<lang:ruby>` element (this is why you __must__ supply at least one interface in the |
|
value of the attribute, and (accordingly) program to interfaces when using JRuby-backed |
|
beans). |
|
|
|
Let us look at a fully working example of using a JRuby-based bean. Here is the JRuby |
|
implementation of the `Messenger` interface that was defined earlier in this chapter |
|
(for your convenience it is repeated below). |
|
|
|
[source,ruby,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.springframework.scripting; |
|
|
|
public interface Messenger { |
|
|
|
String getMessage(); |
|
|
|
} |
|
---- |
|
|
|
[source,ruby,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
require 'java' |
|
|
|
class RubyMessenger |
|
include org.springframework.scripting.Messenger |
|
|
|
def setMessage(message) |
|
@@message = message |
|
end |
|
|
|
def getMessage |
|
@@message |
|
end |
|
end |
|
|
|
# this last line is not essential (but see below) |
|
RubyMessenger.new |
|
---- |
|
|
|
And here is the Spring XML that defines an instance of the `RubyMessenger` JRuby bean. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<lang:jruby id="messageService" |
|
script-interfaces="org.springframework.scripting.Messenger" |
|
script-source="classpath:RubyMessenger.rb"> |
|
|
|
<lang:property name="message" value="Hello World!" /> |
|
|
|
</lang:jruby> |
|
---- |
|
|
|
Take note of the last line of that JRuby source ( `'RubyMessenger.new'`). When using |
|
JRuby in the context of Spring's dynamic language support, you are encouraged to |
|
instantiate and return a new instance of the JRuby class that you want to use as a |
|
dynamic-language-backed bean as the result of the execution of your JRuby source. You |
|
can achieve this by simply instantiating a new instance of your JRuby class on the last |
|
line of the source file like so: |
|
|
|
[source,ruby,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
require 'java' |
|
|
|
include_class 'org.springframework.scripting.Messenger' |
|
|
|
# class definition same as above... |
|
|
|
# instantiate and return a new instance of the RubyMessenger class |
|
RubyMessenger.new |
|
---- |
|
|
|
If you forget to do this, it is not the end of the world; this will however result in |
|
Spring having to trawl (reflectively) through the type representation of your JRuby |
|
class looking for a class to instantiate. In the grand scheme of things this will be so |
|
fast that you'll never notice it, but it is something that can be avoided by simply |
|
having a line such as the one above as the last line of your JRuby script. If you don't |
|
supply such a line, or if Spring cannot find a JRuby class in your script to instantiate |
|
then an opaque `ScriptCompilationException` will be thrown immediately after the source |
|
is executed by the JRuby interpreter. The key text that identifies this as the root |
|
cause of an exception can be found immediately below (so if your Spring container throws |
|
the following exception when creating your dynamic-language-backed bean and the |
|
following text is there in the corresponding stacktrace, this will hopefully allow you |
|
to identify and then easily rectify the issue): |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
org.springframework.scripting.ScriptCompilationException: Compilation of JRuby script returned '' |
|
---- |
|
|
|
To rectify this, simply instantiate a new instance of whichever class you want to expose |
|
as a JRuby-dynamic-language-backed bean (as shown above). Please also note that you can |
|
actually define as many classes and objects as you want in your JRuby script; what is |
|
important is that the source file as a whole must return an object (for Spring to |
|
configure). |
|
|
|
See <<dynamic-language-scenarios>> for some scenarios where you might want to use |
|
JRuby-based beans. |
|
|
|
|
|
|
|
[[dynamic-language-beans-groovy]] |
|
==== Groovy beans |
|
|
|
.The Groovy library dependencies |
|
**** |
|
|
|
The Groovy scripting support in Spring requires the following libraries to be on the |
|
classpath of your application. |
|
|
|
* `groovy-1.8.jar` |
|
* `asm-3.2.jar` |
|
* `antlr-2.7.7.jar` |
|
**** |
|
|
|
From the Groovy homepage... |
|
|
|
"__Groovy is an agile dynamic language for the Java 2 Platform that has many of the |
|
features that people like so much in languages like Python, Ruby and Smalltalk, making |
|
them available to Java developers using a Java-like syntax. __" |
|
|
|
If you have read this chapter straight from the top, you will already have |
|
<<dynamic-language-a-first-example,seen an example>> of a Groovy-dynamic-language-backed |
|
bean. Let's look at another example (again using an example from the Spring test suite). |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.springframework.scripting; |
|
|
|
public interface Calculator { |
|
|
|
int add(int x, int y); |
|
|
|
} |
|
---- |
|
|
|
Here is an implementation of the `Calculator` interface in Groovy. |
|
|
|
[source,groovy,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// from the file 'calculator.groovy' |
|
package org.springframework.scripting.groovy |
|
|
|
class GroovyCalculator implements Calculator { |
|
|
|
int add(int x, int y) { |
|
x + y |
|
} |
|
|
|
} |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<-- from the file 'beans.xml' --> |
|
<beans> |
|
<lang:groovy id="calculator" script-source="classpath:calculator.groovy"/> |
|
</beans> |
|
---- |
|
|
|
Lastly, here is a small application to exercise the above configuration. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.springframework.scripting; |
|
|
|
import org.springframework.context.ApplicationContext; |
|
import org.springframework.context.support.ClassPathXmlApplicationContext; |
|
|
|
public class Main { |
|
|
|
public static void Main(String[] args) { |
|
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); |
|
Calculator calc = (Calculator) ctx.getBean("calculator"); |
|
System.out.println(calc.add(2, 8)); |
|
} |
|
} |
|
---- |
|
|
|
The resulting output from running the above program will be (unsurprisingly) __10__. |
|
(Exciting example, huh? Remember that the intent is to illustrate the concept. Please |
|
consult the dynamic language showcase project for a more complex example, or indeed |
|
<<dynamic-language-scenarios>> later in this chapter). |
|
|
|
It is important that you __do not__ define more than one class per Groovy source file. |
|
While this is perfectly legal in Groovy, it is (arguably) a bad practice: in the |
|
interests of a consistent approach, you should (in the opinion of this author) respect |
|
the standard Java conventions of one (public) class per source file. |
|
|
|
|
|
[[dynamic-language-beans-groovy-customizer]] |
|
===== Customizing Groovy objects via a callback |
|
The `GroovyObjectCustomizer` interface is a callback that allows you to hook additional |
|
creation logic into the process of creating a Groovy-backed bean. For example, |
|
implementations of this interface could invoke any required initialization method(s), or |
|
set some default property values, or specify a custom `MetaClass`. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public interface GroovyObjectCustomizer { |
|
|
|
void customize(GroovyObject goo); |
|
|
|
} |
|
---- |
|
|
|
The Spring Framework will instantiate an instance of your Groovy-backed bean, and will |
|
then pass the created `GroovyObject` to the specified `GroovyObjectCustomizer` if one |
|
has been defined. You can do whatever you like with the supplied `GroovyObject` |
|
reference: it is expected that the setting of a custom `MetaClass` is what most folks |
|
will want to do with this callback, and you can see an example of doing that below. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public final class SimpleMethodTracingCustomizer implements GroovyObjectCustomizer { |
|
|
|
public void customize(GroovyObject goo) { |
|
DelegatingMetaClass metaClass = new DelegatingMetaClass(goo.getMetaClass()) { |
|
|
|
public Object invokeMethod(Object object, String methodName, Object[] arguments) { |
|
System.out.println("Invoking ''" + methodName + "''."); |
|
return super.invokeMethod(object, methodName, arguments); |
|
} |
|
}; |
|
metaClass.initialize(); |
|
goo.setMetaClass(metaClass); |
|
} |
|
|
|
} |
|
---- |
|
|
|
A full discussion of meta-programming in Groovy is beyond the scope of the Spring |
|
reference manual. Consult the relevant section of the Groovy reference manual, or do a |
|
search online: there are plenty of articles concerning this topic. Actually making use |
|
of a `GroovyObjectCustomizer` is easy if you are using the Spring namespace support. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<!-- define the GroovyObjectCustomizer just like any other bean --> |
|
<bean id="tracingCustomizer" class="example.SimpleMethodTracingCustomizer" /> |
|
|
|
<!-- ... and plug it into the desired Groovy bean via the 'customizer-ref' attribute --> |
|
<lang:groovy id="calculator" |
|
script-source="classpath:org/springframework/scripting/groovy/Calculator.groovy" |
|
customizer-ref="tracingCustomizer" /> |
|
---- |
|
|
|
If you are not using the Spring namespace support, you can still use the |
|
`GroovyObjectCustomizer` functionality. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="calculator" class="org.springframework.scripting.groovy.GroovyScriptFactory"> |
|
<constructor-arg value="classpath:org/springframework/scripting/groovy/Calculator.groovy"/> |
|
<!-- define the GroovyObjectCustomizer (as an inner bean) --> |
|
<constructor-arg> |
|
<bean id="tracingCustomizer" class="example.SimpleMethodTracingCustomizer" /> |
|
</constructor-arg> |
|
</bean> |
|
|
|
<bean class="org.springframework.scripting.support.ScriptFactoryPostProcessor"/> |
|
---- |
|
|
|
|
|
|
|
[[dynamic-language-beans-bsh]] |
|
==== BeanShell beans |
|
|
|
.The BeanShell library dependencies |
|
**** |
|
|
|
The BeanShell scripting support in Spring requires the following libraries to be on the |
|
classpath of your application. |
|
|
|
* `bsh-2.0b4.jar` |
|
**** |
|
|
|
From the BeanShell homepage... |
|
|
|
"__BeanShell is a small, free, embeddable Java source interpreter with dynamic language |
|
features, written in Java. BeanShell dynamically executes standard Java syntax and |
|
extends it with common scripting conveniences such as loose types, commands, and method |
|
closures like those in Perl and JavaScript.__" |
|
|
|
In contrast to Groovy, BeanShell-backed bean definitions require some (small) additional |
|
configuration. The implementation of the BeanShell dynamic language support in Spring is |
|
interesting in that what happens is this: Spring creates a JDK dynamic proxy |
|
implementing all of the interfaces that are specified in the `'script-interfaces'` |
|
attribute value of the `<lang:bsh>` element (this is why you __must__ supply at least |
|
one interface in the value of the attribute, and (accordingly) program to interfaces |
|
when using BeanShell-backed beans). This means that every method call on a |
|
BeanShell-backed object is going through the JDK dynamic proxy invocation mechanism. |
|
|
|
Let's look at a fully working example of using a BeanShell-based bean that implements |
|
the `Messenger` interface that was defined earlier in this chapter (repeated below for |
|
your convenience). |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
package org.springframework.scripting; |
|
|
|
public interface Messenger { |
|
|
|
String getMessage(); |
|
|
|
} |
|
---- |
|
|
|
Here is the BeanShell 'implementation' (the term is used loosely here) of the |
|
`Messenger` interface. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
String message; |
|
|
|
String getMessage() { |
|
return message; |
|
} |
|
|
|
void setMessage(String aMessage) { |
|
message = aMessage; |
|
} |
|
---- |
|
|
|
And here is the Spring XML that defines an 'instance' of the above 'class' (again, the |
|
term is used very loosely here). |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<lang:bsh id="messageService" script-source="classpath:BshMessenger.bsh" |
|
script-interfaces="org.springframework.scripting.Messenger"> |
|
|
|
<lang:property name="message" value="Hello World!" /> |
|
</lang:bsh> |
|
---- |
|
|
|
See <<dynamic-language-scenarios>> for some scenarios where you might want to use |
|
BeanShell-based beans. |
|
|
|
|
|
|
|
|
|
[[dynamic-language-scenarios]] |
|
=== Scenarios |
|
The possible scenarios where defining Spring managed beans in a scripting language would |
|
be beneficial are, of course, many and varied. This section describes two possible use |
|
cases for the dynamic language support in Spring. |
|
|
|
|
|
|
|
[[dynamic-language-scenarios-controllers]] |
|
==== Scripted Spring MVC Controllers |
|
One group of classes that may benefit from using dynamic-language-backed beans is that |
|
of Spring MVC controllers. In pure Spring MVC applications, the navigational flow |
|
through a web application is to a large extent determined by code encapsulated within |
|
your Spring MVC controllers. As the navigational flow and other presentation layer logic |
|
of a web application needs to be updated to respond to support issues or changing |
|
business requirements, it may well be easier to effect any such required changes by |
|
editing one or more dynamic language source files and seeing those changes being |
|
immediately reflected in the state of a running application. |
|
|
|
Remember that in the lightweight architectural model espoused by projects such as |
|
Spring, you are typically aiming to have a really __thin__ presentation layer, with all |
|
the meaty business logic of an application being contained in the domain and service |
|
layer classes. Developing Spring MVC controllers as dynamic-language-backed beans allows |
|
you to change presentation layer logic by simply editing and saving text files; any |
|
changes to such dynamic language source files will (depending on the configuration) |
|
automatically be reflected in the beans that are backed by dynamic language source files. |
|
|
|
[NOTE] |
|
==== |
|
In order to effect this automatic 'pickup' of any changes to dynamic-language-backed |
|
beans, you will have had to enable the 'refreshable beans' functionality. See |
|
<<dynamic-language-refreshable-beans>> for a full treatment of this feature. |
|
==== |
|
|
|
Find below an example of an `org.springframework.web.servlet.mvc.Controller` implemented |
|
using the Groovy dynamic language. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// from the file '/WEB-INF/groovy/FortuneController.groovy' |
|
package org.springframework.showcase.fortune.web |
|
|
|
import org.springframework.showcase.fortune.service.FortuneService |
|
import org.springframework.showcase.fortune.domain.Fortune |
|
import org.springframework.web.servlet.ModelAndView |
|
import org.springframework.web.servlet.mvc.Controller |
|
|
|
import javax.servlet.http.HttpServletRequest |
|
import javax.servlet.http.HttpServletResponse |
|
|
|
class FortuneController implements Controller { |
|
|
|
@Property FortuneService fortuneService |
|
|
|
ModelAndView handleRequest(HttpServletRequest request, |
|
HttpServletResponse httpServletResponse) { |
|
return new ModelAndView("tell", "fortune", this.fortuneService.tellFortune()) |
|
} |
|
|
|
} |
|
---- |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<lang:groovy id="fortune" |
|
refresh-check-delay="3000" |
|
script-source="/WEB-INF/groovy/FortuneController.groovy"> |
|
<lang:property name="fortuneService" ref="fortuneService"/> |
|
</lang:groovy> |
|
---- |
|
|
|
|
|
|
|
[[dynamic-language-scenarios-validators]] |
|
==== Scripted Validators |
|
Another area of application development with Spring that may benefit from the |
|
flexibility afforded by dynamic-language-backed beans is that of validation. It __may__ |
|
be easier to express complex validation logic using a loosely typed dynamic language |
|
(that may also have support for inline regular expressions) as opposed to regular Java. |
|
|
|
Again, developing validators as dynamic-language-backed beans allows you to change |
|
validation logic by simply editing and saving a simple text file; any such changes will |
|
(depending on the configuration) automatically be reflected in the execution of a |
|
running application and would not require the restart of an application. |
|
|
|
[NOTE] |
|
==== |
|
Please note that in order to effect the automatic 'pickup' of any changes to |
|
dynamic-language-backed beans, you will have had to enable the 'refreshable beans' |
|
feature. See <<dynamic-language-refreshable-beans>> for a full and detailed treatment of |
|
this feature. |
|
==== |
|
|
|
Find below an example of a Spring `org.springframework.validation.Validator` implemented |
|
using the Groovy dynamic language. (See <<validator>> for a discussion of the |
|
`Validator` interface.) |
|
|
|
[source,groovy,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import org.springframework.validation.Validator |
|
import org.springframework.validation.Errors |
|
import org.springframework.beans.TestBean |
|
|
|
class TestBeanValidator implements Validator { |
|
|
|
boolean supports(Class clazz) { |
|
return TestBean.class.isAssignableFrom(clazz) |
|
} |
|
|
|
void validate(Object bean, Errors errors) { |
|
if(bean.name?.trim()?.size() > 0) { |
|
return |
|
} |
|
errors.reject("whitespace", "Cannot be composed wholly of whitespace.") |
|
} |
|
|
|
} |
|
---- |
|
|
|
|
|
|
|
|
|
[[dynamic-language-final-notes]] |
|
=== Bits and bobs |
|
This last section contains some bits and bobs related to the dynamic language support. |
|
|
|
|
|
|
|
[[dynamic-language-final-notes-aop]] |
|
==== AOP - advising scripted beans |
|
It is possible to use the Spring AOP framework to advise scripted beans. The Spring AOP |
|
framework actually is unaware that a bean that is being advised might be a scripted |
|
bean, so all of the AOP use cases and functionality that you may be using or aim to use |
|
will work with scripted beans. There is just one (small) thing that you need to be aware |
|
of when advising scripted beans... you cannot use class-based proxies, you must |
|
use <<aop-proxying,interface-based proxies>>. |
|
|
|
You are of course not just limited to advising scripted beans... you can also write |
|
aspects themselves in a supported dynamic language and use such beans to advise other |
|
Spring beans. This really would be an advanced use of the dynamic language support |
|
though. |
|
|
|
|
|
|
|
[[dynamic-language-final-notes-scopes]] |
|
==== Scoping |
|
In case it is not immediately obvious, scripted beans can of course be scoped just like |
|
any other bean. The `scope` attribute on the various `<lang:language/>` elements allows |
|
you to control the scope of the underlying scripted bean, just as it does with a regular |
|
bean. (The default scope is <<beans-factory-scopes-singleton,singleton>>, just as it is |
|
with 'regular' beans.) |
|
|
|
Find below an example of using the `scope` attribute to define a Groovy bean scoped as |
|
a <<beans-factory-scopes-prototype,prototype>>. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:lang="http://www.springframework.org/schema/lang" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd"> |
|
|
|
<lang:groovy id="messenger" script-source="classpath:Messenger.groovy" scope="prototype"> |
|
<lang:property name="message" value="I Can Do The RoboCop" /> |
|
</lang:groovy> |
|
|
|
<bean id="bookingService" class="x.y.DefaultBookingService"> |
|
<property name="messenger" ref="messenger" /> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
See <<beans-factory-scopes>> in <<beans>> for a fuller discussion of the scoping support |
|
in the Spring Framework. |
|
|
|
|
|
|
|
|
|
[[dynamic-language-resources]] |
|
=== Further Resources |
|
Find below links to further resources about the various dynamic languages described in |
|
this chapter. |
|
|
|
* The http://jruby.codehaus.org/[JRuby] homepage |
|
* The http://groovy.codehaus.org/[Groovy] homepage |
|
* The http://www.beanshell.org/[BeanShell] homepage |
|
|
|
|
|
|
|
|
|
|
|
[[cache]] |
|
== Cache Abstraction |
|
|
|
|
|
|
|
|
|
[[cache-introduction]] |
|
=== Introduction |
|
Since version 3.1, Spring Framework provides support for transparently adding caching |
|
into an existing Spring application. Similar to the <<transaction,transaction>> support, |
|
the caching abstraction allows consistent use of various caching solutions with minimal |
|
impact on the code. |
|
|
|
As from Spring 4.1, the cache abstraction has been significantly improved with the |
|
support of <<cache-jsr-107,JSR-107 annotations>> and more customization options. |
|
|
|
|
|
|
|
[[cache-strategies]] |
|
=== Understanding the cache abstraction |
|
.Cache vs Buffer |
|
**** |
|
|
|
The terms "buffer" and "cache" tend to be used interchangeably; note however they |
|
represent different things. A buffer is used traditionally as an intermediate temporary |
|
store for data between a fast and a slow entity. As one party would have to __wait__ for |
|
the other affecting performance, the buffer alleviates this by allowing entire blocks of |
|
data to move at once rather then in small chunks. The data is written and read only once |
|
from the buffer. Furthermore, the buffers are __visible__ to at least one party which is |
|
aware of it. |
|
|
|
A cache on the other hand by definition is hidden and neither party is aware that |
|
caching occurs.It as well improves performance but does that by allowing the same data |
|
to be read multiple times in a fast fashion. |
|
|
|
A further explanation of the differences between two can be found |
|
http://en.wikipedia.org/wiki/Cache_(computing)#The_difference_between_buffer_and_cache[here]. |
|
**** |
|
|
|
At its core, the abstraction applies caching to Java methods, reducing thus the number |
|
of executions based on the information available in the cache. That is, each time a |
|
__targeted__ method is invoked, the abstraction will apply a caching behavior checking |
|
whether the method has been already executed for the given arguments. If it has, then |
|
the cached result is returned without having to execute the actual method; if it has |
|
not, then method is executed, the result cached and returned to the user so that, the |
|
next time the method is invoked, the cached result is returned. This way, expensive |
|
methods (whether CPU or IO bound) can be executed only once for a given set of |
|
parameters and the result reused without having to actually execute the method again. |
|
The caching logic is applied transparently without any interference to the invoker. |
|
|
|
[IMPORTANT] |
|
==== |
|
|
|
Obviously this approach works only for methods that are guaranteed to return the same |
|
output (result) for a given input (or arguments) no matter how many times it is being |
|
executed. |
|
==== |
|
|
|
Other cache-related operations are provided by the abstraction such as the ability |
|
to update the content of the cache or remove one of all entries. These are useful if |
|
the cache deals with data that can change during the course of the application. |
|
|
|
Just like other services in the Spring Framework, the caching service is an |
|
abstraction (not a cache implementation) and requires the use of an actual storage to |
|
store the cache data - that is, the abstraction frees the developer from having to write |
|
the caching logic but does not provide the actual stores. This abstraction is |
|
materialized by the `org.springframework.cache.Cache` and |
|
`org.springframework.cache.CacheManager` interfaces. |
|
|
|
There are <<cache-store-configuration,a few implementations>> of that abstraction |
|
available out of the box: JDK `java.util.concurrent.ConcurrentMap` based caches, |
|
http://ehcache.org/[EhCache], Gemfire cache, |
|
https://code.google.com/p/guava-libraries/wiki/CachesExplained[Guava caches] and |
|
JSR-107 compliant caches. See <<cache-plug>> for more information on plugging in |
|
other cache stores/providers. |
|
|
|
[IMPORTANT] |
|
==== |
|
The caching abstraction has no special handling of multi-threaded and multi-process |
|
environments as such features are handled by the cache implementation. . |
|
==== |
|
|
|
If you have a multi-process environment (i.e. an application deployed on several nodes), |
|
you will need to configure your cache provider accordingly. Depending on your use cases, |
|
a copy of the same data on several nodes may be enough but if you change the data during |
|
the course of the application, you may need to enable other propagation mechanisms. |
|
|
|
Caching a particular item is a direct equivalent of the typical get-if-not-found-then- |
|
proceed-and-put-eventually code blocks found with programmatic cache interaction: no locks |
|
are applied and several threads may try to load the same item concurrently. The same applies |
|
to eviction: if several threads are trying to update or evict data concurrently, you may |
|
use stale data. Certain cache providers offer advanced features in that area, refer to |
|
the documentation of the cache provider that you are using for more details. |
|
|
|
To use the cache abstraction, the developer needs to take care of two aspects: |
|
|
|
* caching declaration - identify the methods that need to be cached and their policy |
|
* cache configuration - the backing cache where the data is stored and read from |
|
|
|
|
|
[[cache-annotations]] |
|
=== Declarative annotation-based caching |
|
For caching declaration, the abstraction provides a set of Java annotations: |
|
|
|
* `@Cacheable` triggers cache population |
|
* `@CacheEvict` triggers cache eviction |
|
* `@CachePut` updates the cache without interfering with the method execution |
|
* `@Caching` regroups multiple cache operations to be applied on a method |
|
* `@CacheConfig` shares some common cache-related settings at class-level |
|
|
|
Let us take a closer look at each annotation: |
|
|
|
[[cache-annotations-cacheable]] |
|
==== @Cacheable annotation |
|
|
|
As the name implies, `@Cacheable` is used to demarcate methods that are cacheable - that |
|
is, methods for whom the result is stored into the cache so on subsequent invocations |
|
(with the same arguments), the value in the cache is returned without having to actually |
|
execute the method. In its simplest form, the annotation declaration requires the name |
|
of the cache associated with the annotated method: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Cacheable("books") |
|
public Book findBook(ISBN isbn) {...} |
|
---- |
|
|
|
In the snippet above, the method `findBook` is associated with the cache named `books`. |
|
Each time the method is called, the cache is checked to see whether the invocation has |
|
been already executed and does not have to be repeated. While in most cases, only one |
|
cache is declared, the annotation allows multiple names to be specified so that more |
|
than one cache are being used. In this case, each of the caches will be checked before |
|
executing the method - if at least one cache is hit, then the associated value will be |
|
returned: |
|
|
|
[NOTE] |
|
==== |
|
All the other caches that do not contain the value will be updated as well even though |
|
the cached method was not actually executed. |
|
==== |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Cacheable({"books", "isbns"}) |
|
public Book findBook(ISBN isbn) {...} |
|
---- |
|
|
|
|
|
[[cache-annotations-cacheable-default-key]] |
|
===== Default Key Generation |
|
Since caches are essentially key-value stores, each invocation of a cached method needs |
|
to be translated into a suitable key for cache access. Out of the box, the caching |
|
abstraction uses a simple `KeyGenerator` based on the following algorithm: |
|
|
|
* If no params are given, return `SimpleKey.EMPTY`. |
|
* If only one param is given, return that instance. |
|
* If more the one param is given, return a `SimpleKey` containing all parameters. |
|
|
|
This approach works well for most use-cases; As long as parameters have __natural keys__ |
|
and implement valid `hashCode()` and `equals()` methods. If that is not the case then the |
|
strategy needs to be changed. |
|
|
|
To provide a different __default__ key generator, one needs to implement the |
|
`org.springframework.cache.interceptor.KeyGenerator` interface. |
|
|
|
|
|
[NOTE] |
|
==== |
|
The default key generation strategy changed with the release of Spring 4.0. Earlier |
|
versions of Spring used a key generation strategy that, for multiple key parameters, |
|
only considered the `hashCode()` of parameters and not `equals()`; this could cause |
|
unexpected key collisions (see https://jira.spring.io/browse/SPR-10237[SPR-10237] |
|
for background). The new 'SimpleKeyGenerator' uses a compound key for such scenarios. |
|
|
|
If you want to keep using the previous key strategy, you can configure the deprecated |
|
`org.springframework.cache.interceptor.DefaultKeyGenerator` class or create a custom |
|
hash-based 'KeyGenerator' implementation. |
|
==== |
|
|
|
|
|
[[cache-annotations-cacheable-key]] |
|
===== Custom Key Generation Declaration |
|
Since caching is generic, it is quite likely the target methods have various signatures |
|
that cannot be simply mapped on top of the cache structure. This tends to become obvious |
|
when the target method has multiple arguments out of which only some are suitable for |
|
caching (while the rest are used only by the method logic). For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Cacheable("books") |
|
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) |
|
---- |
|
|
|
At first glance, while the two `boolean` arguments influence the way the book is found, |
|
they are no use for the cache. Further more what if only one of the two is important |
|
while the other is not? |
|
|
|
For such cases, the `@Cacheable` annotation allows the user to specify how the key is |
|
generated through its `key` attribute. The developer can use <<expressions,SpEL>> to |
|
pick the arguments of interest (or their nested properties), perform operations or even |
|
invoke arbitrary methods without having to write any code or implement any interface. |
|
This is the recommended approach over the |
|
<<cache-annotations-cacheable-default-key,default generator>> since methods tend to be |
|
quite different in signatures as the code base grows; while the default strategy might |
|
work for some methods, it rarely does for all methods. |
|
|
|
Below are some examples of various SpEL declarations - if you are not familiar with it, |
|
do yourself a favor and read <<expressions>>: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Cacheable(value="books", **key="#isbn"**) |
|
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) |
|
|
|
@Cacheable(value="books", **key="#isbn.rawNumber"**) |
|
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) |
|
|
|
@Cacheable(value="books", **key="T(someType).hash(#isbn)"**) |
|
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) |
|
---- |
|
|
|
The snippets above show how easy it is to select a certain argument, one of its |
|
properties or even an arbitrary (static) method. |
|
|
|
If the algorithm responsible to generate the key is too specific or if it needs |
|
to be shared, you may define a custom `keyGenerator` on the operation. To do |
|
this, specify the name of the `KeyGenerator` bean implementation to use: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Cacheable(value="books", **keyGenerator="myKeyGenerator"**) |
|
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) |
|
---- |
|
|
|
|
|
[NOTE] |
|
==== |
|
The `key` and `keyGenerator` parameters are mutually exclusive and an operation |
|
specifying both will result in an exception. |
|
==== |
|
|
|
[[cache-annotations-cacheable-default-cache-resolver]] |
|
===== Default Cache Resolution |
|
|
|
Out of the box, the caching abstraction uses a simple `CacheResolver` that |
|
retrieves the cache(s) defined at the operation level using the configured |
|
`CacheManager`. |
|
|
|
To provide a different __default__ cache resolver, one needs to implement the |
|
`org.springframework.cache.interceptor.CacheResolver` interface. |
|
|
|
|
|
[[cache-annotations-cacheable-cache-resolver]] |
|
===== Custom cache resolution |
|
|
|
The default cache resolution fits well for applications working with a |
|
single `CacheManager` and with no complex cache resolution requirements. |
|
|
|
For applications working with several cache managers, it is possible |
|
to set the `cacheManager` to use per operation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Cacheable(value="books", **cacheManager="anotherCacheManager"**) |
|
public Book findBook(ISBN isbn) {...} |
|
---- |
|
|
|
It is also possible to replace the `CacheResolver` entirely in a similar |
|
fashion as for <<cache-annotations-cacheable-key,key generation>>. The |
|
resolution is requested for every cache operation, giving a chance to |
|
the implementation to actually resolve the cache(s) to use based on |
|
runtime arguments: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Cacheable(**cacheResolver="runtimeCacheResolver"**) |
|
public Book findBook(ISBN isbn) {...} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
Since Spring 4.1, the `value` attribute of the cache annotations are no longer |
|
mandatory since this particular information can be provided by the `CacheResolver` |
|
regardless of the content of the annotation. |
|
|
|
Similarly to `key` and `keyGenerator`, the `cacheManager` and `cacheResolver` |
|
parameters are mutually exclusive and an operation specifying both will |
|
result in an exception as a custom `CacheManager` will be ignored by the |
|
`CacheResolver` implementation. This is probably not what you expect. |
|
==== |
|
|
|
[[cache-annotations-cacheable-condition]] |
|
===== Conditional caching |
|
Sometimes, a method might not be suitable for caching all the time (for example, it |
|
might depend on the given arguments). The cache annotations support such functionality |
|
through the `condition` parameter which takes a `SpEL` expression that is evaluated to |
|
either `true` or `false`. If `true`, the method is cached - if not, it behaves as if the |
|
method is not cached, that is executed every since time no matter what values are in the |
|
cache or what arguments are used. A quick example - the following method will be cached |
|
only if the argument `name` has a length shorter than 32: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Cacheable(value="book", **condition="#name.length < 32"**) |
|
public Book findBook(String name) |
|
---- |
|
|
|
In addition the `condition` parameter, the `unless` parameter can be used to veto the |
|
adding of a value to the cache. Unlike `condition`, `unless` expressions are evaluated |
|
__after__ the method has been called. Expanding on the previous example - perhaps we |
|
only want to cache paperback books: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Cacheable(value="book", condition="#name.length < 32", **unless="#result.hardback"**) |
|
public Book findBook(String name) |
|
---- |
|
|
|
|
|
[[cache-spel-context]] |
|
===== Available caching SpEL evaluation context |
|
|
|
Each `SpEL` expression evaluates again a dedicated |
|
<<expressions-language-ref,`context`>>. In addition to the build in parameters, the |
|
framework provides dedicated caching related metadata such as the argument names. The |
|
next table lists the items made available to the context so one can use them for key and |
|
conditional computations: |
|
|
|
[[cache-spel-context-tbl]] |
|
.Cache SpEL available metadata |
|
|=== |
|
| Name| Location| Description| Example |
|
|
|
| methodName |
|
| root object |
|
| The name of the method being invoked |
|
| `#root.methodName` |
|
|
|
| method |
|
| root object |
|
| The method being invoked |
|
| `#root.method.name` |
|
|
|
| target |
|
| root object |
|
| The target object being invoked |
|
| `#root.target` |
|
|
|
| targetClass |
|
| root object |
|
| The class of the target being invoked |
|
| `#root.targetClass` |
|
|
|
| args |
|
| root object |
|
| The arguments (as array) used for invoking the target |
|
| `#root.args[0]` |
|
|
|
| caches |
|
| root object |
|
| Collection of caches against which the current method is executed |
|
| `#root.caches[0].name` |
|
|
|
| __argument name__ |
|
| evaluation context |
|
| Name of any of the method argument. If for some reason the names are not available |
|
(ex: no debug information), the argument names are also available under the `a<#arg>` |
|
where __#arg__ stands for the argument index (starting from 0). |
|
| `iban` or `a0` (one can also use `p0` or `p<#arg>` notation as an alias). |
|
|
|
| result |
|
| evaluation context |
|
| The result of the method call (the value to be cached). Only available in `unless` |
|
expressions, `cache put` expressions (to compute the `key`), or `cache evict` |
|
expressions (when `beforeInvocation` is `false`). |
|
| `#result` |
|
|=== |
|
|
|
|
|
[[cache-annotations-put]] |
|
==== @CachePut annotation |
|
|
|
For cases where the cache needs to be updated without interfering with the method |
|
execution, one can use the `@CachePut` annotation. That is, the method will always be |
|
executed and its result placed into the cache (according to the `@CachePut` options). It |
|
supports the same options as `@Cacheable` and should be used for cache population rather |
|
than method flow optimization: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@CachePut(value="book", key="#isbn") |
|
public Book updateBook(ISBN isbn, BookDescriptor descriptor) |
|
---- |
|
|
|
|
|
[IMPORTANT] |
|
==== |
|
Note that using `@CachePut` and `@Cacheable` annotations on the same method is generally |
|
strongly discouraged because they have different behaviors. While the latter causes the |
|
method execution to be skipped by using the cache, the former forces the execution in |
|
order to execute a cache update. This leads to unexpected behavior and with the exception of |
|
specific corner-cases (such as annotations having conditions that exclude them from each |
|
other), such declaration should be avoided. Note also that such condition should not rely |
|
on the result object (i.e. the `#result` variable) as these are validated upfront to confirm |
|
the exclusion. |
|
==== |
|
|
|
|
|
[[cache-annotations-evict]] |
|
==== @CacheEvict annotation |
|
|
|
The cache abstraction allows not just population of a cache store but also eviction. |
|
This process is useful for removing stale or unused data from the cache. Opposed to |
|
`@Cacheable`, annotation `@CacheEvict` demarcates methods that perform cache |
|
__eviction__, that is methods that act as triggers for removing data from the cache. |
|
Just like its sibling, `@CacheEvict` requires specifying one (or multiple) caches |
|
that are affected by the action, allows a custom cache and key resolution or a |
|
condition to be specified but in addition, features an extra parameter |
|
`allEntries` which indicates whether a cache-wide eviction needs to be performed |
|
rather then just an entry one (based on the key): |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@CacheEvict(value="books", **allEntries=true**) |
|
public void loadBooks(InputStream batch) |
|
---- |
|
|
|
This option comes in handy when an entire cache region needs to be cleared out - rather |
|
then evicting each entry (which would take a long time since it is inefficient), all the |
|
entries are removed in one operation as shown above. Note that the framework will ignore |
|
any key specified in this scenario as it does not apply (the entire cache is evicted not |
|
just one entry). |
|
|
|
One can also indicate whether the eviction should occur after (the default) or before |
|
the method executes through the `beforeInvocation` attribute. The former provides the |
|
same semantics as the rest of the annotations - once the method completes successfully, |
|
an action (in this case eviction) on the cache is executed. If the method does not |
|
execute (as it might be cached) or an exception is thrown, the eviction does not occur. |
|
The latter ( `beforeInvocation=true`) causes the eviction to occur always, before the |
|
method is invoked - this is useful in cases where the eviction does not need to be tied |
|
to the method outcome. |
|
|
|
It is important to note that void methods can be used with `@CacheEvict` - as the |
|
methods act as triggers, the return values are ignored (as they don't interact with the |
|
cache) - this is not the case with `@Cacheable` which adds/updates data into the cache |
|
and thus requires a result. |
|
|
|
|
|
|
|
[[cache-annotations-caching]] |
|
==== @Caching annotation |
|
|
|
There are cases when multiple annotations of the same type, such as `@CacheEvict` or |
|
`@CachePut` need to be specified, for example because the condition or the key |
|
expression is different between different caches. Unfortunately Java does not support |
|
such declarations however there is a workaround - using an __enclosing__ annotation, in |
|
this case, `@Caching`. `@Caching` allows multiple nested `@Cacheable`, `@CachePut` and |
|
`@CacheEvict` to be used on the same method: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Caching(evict = { @CacheEvict("primary"), @CacheEvict(value="secondary", key="#p0") }) |
|
public Book importBooks(String deposit, Date date) |
|
---- |
|
|
|
|
|
[[cache-annotations-config]] |
|
==== @CacheConfig annotation |
|
|
|
So far we have seen that caching operations offered many customization options and |
|
these can be set on an operation basis. However, some of the customization options |
|
can be tedious to configure if they apply to all operations of the class. For |
|
instance, specifying the name of the cache to use for every cache operation of the |
|
class could be replaced by a single class-level definition. This is where `@CacheConfig` |
|
comes into play. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
**@CacheConfig("books")** |
|
public class BookRepositoryImpl implements BookRepository { |
|
|
|
@Cacheable |
|
public Book findBook(ISBN isbn) {...} |
|
} |
|
---- |
|
|
|
`@CacheConfig` is a class-level annotation that allows to share the cache names, the custom |
|
`KeyGenerator`, the custom `CacheManager` and finally the custom `CacheResolver`. Placing |
|
this annotation on the class does not turn on any caching operation. |
|
|
|
An operation-level customization will always override a customization set on `@CacheConfig`. This |
|
gives therefore three levels of customizations per cache operation: |
|
|
|
* Globally configured, available for `CacheManager`, `KeyGenerator` |
|
* At class level, using `@CacheConfig` |
|
* At the operation level |
|
|
|
[[cache-annotation-enable]] |
|
==== Enable caching annotations |
|
It is important to note that even though declaring the cache annotations does not |
|
automatically trigger their actions - like many things in Spring, the feature has to be |
|
declaratively enabled (which means if you ever suspect caching is to blame, you can |
|
disable it by removing only one configuration line rather than all the annotations in |
|
your code). |
|
|
|
To enable caching annotations add the annotation `@EnableCaching` to one of your |
|
`@Configuration` classes: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableCaching |
|
public class AppConfig { |
|
} |
|
---- |
|
|
|
Alternatively for XML configuration use the `cache:annotation-driven` element: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:cache="http://www.springframework.org/schema/cache" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> |
|
|
|
<cache:annotation-driven /> |
|
|
|
</beans> |
|
---- |
|
|
|
Both the `cache:annotation-driven` element and `@EnableCaching` annotation allow various |
|
options to be specified that influence the way the caching behavior is added to the |
|
application through AOP. The configuration is intentionally similar with that of |
|
<<tx-annotation-driven-settings, `@Transactional`>>: |
|
|
|
[NOTE] |
|
==== |
|
Advanced customizations using Java config require to implement `CachingConfigurer`, refer |
|
to {javadoc-baseurl}/org/springframework/cache/annotation/CachingConfigurer.html[the |
|
javadoc for more details]. |
|
==== |
|
|
|
[[cache-annotation-driven-settings]] |
|
.Cache annotation settings |
|
[cols="1,1,1,3"] |
|
|=== |
|
| XML Attribute| Annotation Attribute| Default| Description |
|
|
|
| `cache-manager` |
|
| N/A (See `CachingConfigurer` javadocs) |
|
| cacheManager |
|
| Name of cache manager to use. A default `CacheResolver` will be initialized behind |
|
the scenes with this cache manager (or `cacheManager`if not set). For more |
|
fine-grained management of the cache resolution, consider setting the 'cache-resolver' |
|
attribute. |
|
|
|
| `cache-resolver` |
|
| N/A (See `CachingConfigurer` javadocs) |
|
| A `SimpleCacheResolver` using the configured `cacheManager`. |
|
| The bean name of the CacheResolver that is to be used to resolve the backing caches. |
|
This attribute is not required, and only needs to be specified as an alternative to |
|
the 'cache-manager' attribute. |
|
|
|
| `key-generator` |
|
| N/A (See `CachingConfigurer` javadocs) |
|
| `SimpleKeyGenerator` |
|
| Name of the custom key generator to use. |
|
|
|
| `error-handler` |
|
| N/A (See `CachingConfigurer` javadocs) |
|
| `SimpleCacheErrorHandler` |
|
| Name of the custom cache error handler to use. By default, any exception throw during |
|
a cache related operations are thrown back at the client. |
|
|
|
| `mode` |
|
| `mode` |
|
| proxy |
|
| The default mode "proxy" processes annotated beans to be proxied using Spring's AOP |
|
framework (following proxy semantics, as discussed above, applying to method calls |
|
coming in through the proxy only). The alternative mode "aspectj" instead weaves the |
|
affected classes with Spring's AspectJ caching aspect, modifying the target class byte |
|
code to apply to any kind of method call. AspectJ weaving requires spring-aspects.jar |
|
in the classpath as well as load-time weaving (or compile-time weaving) enabled. (See |
|
<<aop-aj-ltw-spring>> for details on how to set up load-time weaving.) |
|
|
|
| `proxy-target-class` |
|
| `proxyTargetClass` |
|
| false |
|
| Applies to proxy mode only. Controls what type of caching proxies are created for |
|
classes annotated with the `@Cacheable` or `@CacheEvict` annotations. If the |
|
`proxy-target-class` attribute is set to `true`, then class-based proxies are created. |
|
If `proxy-target-class` is `false` or if the attribute is omitted, then standard JDK |
|
interface-based proxies are created. (See <<aop-proxying>> for a detailed examination |
|
of the different proxy types.) |
|
|
|
| `order` |
|
| `order` |
|
| Ordered.LOWEST_PRECEDENCE |
|
| Defines the order of the cache advice that is applied to beans annotated with |
|
`@Cacheable` or `@CacheEvict`. (For more information about the rules related to |
|
ordering of AOP advice, see <<aop-ataspectj-advice-ordering>>.) No specified ordering |
|
means that the AOP subsystem determines the order of the advice. |
|
|=== |
|
|
|
[NOTE] |
|
==== |
|
`<cache:annotation-driven/>` only looks for `@Cacheable/@CachePut/@CacheEvict/@Caching` |
|
on beans in the same application context it is defined in. This means that, if you put |
|
`<cache:annotation-driven/>` in a `WebApplicationContext` for a `DispatcherServlet`, it |
|
only checks for beans in your controllers, and not your services. See <<mvc-servlet>> |
|
for more information. |
|
==== |
|
|
|
.Method visibility and cache annotations |
|
**** |
|
When using proxies, you should apply the cache annotations only to methods with |
|
__public__ visibility. If you do annotate protected, private or package-visible methods |
|
with these annotations, no error is raised, but the annotated method does not exhibit |
|
the configured caching settings. Consider the use of AspectJ (see below) if you need to |
|
annotate non-public methods as it changes the bytecode itself. |
|
**** |
|
|
|
[TIP] |
|
==== |
|
Spring recommends that you only annotate concrete classes (and methods of concrete |
|
classes) with the `@Cache*` annotation, as opposed to annotating interfaces. You |
|
certainly can place the `@Cache*` annotation on an interface (or an interface method), |
|
but this works only as you would expect it to if you are using interface-based proxies. |
|
The fact that Java annotations are __not inherited from interfaces__ means that if you |
|
are using class-based proxies ( `proxy-target-class="true"`) or the weaving-based aspect |
|
( `mode="aspectj"`), then the caching settings are not recognized by the proxying and |
|
weaving infrastructure, and the object will not be wrapped in a caching proxy, which |
|
would be decidedly __bad__. |
|
==== |
|
|
|
[NOTE] |
|
==== |
|
In proxy mode (which is the default), only external method calls coming in through the |
|
proxy are intercepted. This means that self-invocation, in effect, a method within the |
|
target object calling another method of the target object, will not lead to an actual |
|
caching at runtime even if the invoked method is marked with `@Cacheable` - considering |
|
using the aspectj mode in this case. Also, the proxy must be fully initialized to provide |
|
the expected behaviour so you should not rely on this feature in your initialization |
|
code, i.e. `@PostConstruct`. |
|
==== |
|
|
|
|
|
|
|
[[cache-annotation-stereotype]] |
|
==== Using custom annotations |
|
|
|
.Custom annotation and AspectJ |
|
**** |
|
This feature only works out-of-the-box with the proxy-based approach but can be enabled |
|
with a bit of extra effort using AspectJ. |
|
|
|
The `spring-aspects` module defines an aspect for the standard annotations only. If you |
|
have defined your own annotations, you also need to define an aspect for those. Check |
|
`AnnotationCacheAspect` for an example. |
|
**** |
|
|
|
The caching abstraction allows you to use your own annotations to identify what method |
|
triggers cache population or eviction. This is quite handy as a template mechanism as it |
|
eliminates the need to duplicate cache annotation declarations (especially useful if the |
|
key or condition are specified) or if the foreign imports (`org.springframework`) are |
|
not allowed in your code base. Similar to the rest of the |
|
<<beans-stereotype-annotations,stereotype>> annotations, `@Cacheable`, `@CachePut`, |
|
`@CacheEvict` and `@CacheConfig` can be used as <<beans-meta-annotations,meta-annotations>>, |
|
that is annotations that can annotate other annotations. To wit, let us replace a common |
|
`@Cacheable` declaration with our own, custom annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Retention(RetentionPolicy.RUNTIME) |
|
@Target({ElementType.METHOD}) |
|
@Cacheable(value="books", key="#isbn") |
|
public @interface SlowService { |
|
} |
|
---- |
|
|
|
Above, we have defined our own `SlowService` annotation which itself is annotated with |
|
`@Cacheable` - now we can replace the following code: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Cacheable(value="books", key="#isbn") |
|
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) |
|
---- |
|
|
|
with: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@SlowService |
|
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) |
|
---- |
|
|
|
Even though `@SlowService` is not a Spring annotation, the container automatically picks |
|
up its declaration at runtime and understands its meaning. Note that as mentioned |
|
<<cache-annotation-enable,above>>, the annotation-driven behavior needs to be enabled. |
|
|
|
|
|
[[cache-jsr-107]] |
|
=== JCache (JSR-107) annotations |
|
|
|
Since the Spring Framework 4.1, the caching abstraction fully supports the JCache |
|
standard annotations: these are `@CacheResult`, `@CacheEvict`, `@CacheRemove` and |
|
`@CacheRemoveAll` as well as the `@CacheDefaults`, `@CacheKey` and `@CacheValue` |
|
companions. These annotations can be used right the way without migrating your |
|
cache store to JSR-107: the internal implementation uses Spring's caching abstraction |
|
and provides default `CacheResolver` and `KeyGenerator` implementations that are |
|
compliant with the specification. In other words, if you are already using Spring's |
|
caching abstraction, you can switch to these standard annotations without changing |
|
your cache storage (or configuration, for that matter). |
|
|
|
[[cache-jsr-107-summary]] |
|
==== Features summary |
|
|
|
For those who are familiar with Spring's caching annotations, the following table |
|
describes the main differences between the Spring annotations and the JSR-107 |
|
counterpart: |
|
|
|
.Spring vs. JSR-107 caching annotations |
|
[cols="1,1,3"] |
|
|=== |
|
| Spring| JSR-107| Remark |
|
|
|
| `@Cacheable` |
|
| `@CacheResult` |
|
| Fairly similar. `@CacheResult` can cache specific exceptions and force the |
|
execution of the method regardless of the content of the cache. |
|
|
|
| `@CachePut` |
|
| `@CachePut` |
|
| While Spring updates the cache with the result of the method invocation, JCache |
|
requires to pass it as an argument that is annotated with `@CacheValue`. Due |
|
to this difference, JCache allows to update the cache before or after the |
|
actual method invocation. |
|
|
|
| `@CacheEvict` |
|
| `@CacheRemove` |
|
| Fairly similar. `@CacheRemove` supports a conditional evict in case the |
|
method invocation results in an exception. |
|
|
|
| `@CacheEvict(allEntries=true)` |
|
| `@CacheRemoveAll` |
|
| See `@CacheRemove`. |
|
|
|
| `@CacheConfig` |
|
| `@CacheDefaults` |
|
| Allows to configure the same concepts, in a similar fashion. |
|
|=== |
|
|
|
JCache has the notion of `javax.cache.annotation.CacheResolver` that is identical |
|
to the Spring's `CacheResolver` interface, except that JCache only supports a single |
|
cache. By default, a simple implementation retrieves the cache to use based on |
|
the name declared on the annotation. It should be noted that if no cache name |
|
is specified on the annotation, a default is automatically generated, check the |
|
javadoc of `@CacheResult#cacheName()` for more information. |
|
|
|
`CacheResolver` instances are retrieved by a `CacheResolverFactory`. It is |
|
possible to customize the factory per cache operation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@CacheResult(value="books", *cacheResolverFactory=MyCacheResolverFactory.class*) |
|
public Book findBook(ISBN isbn) |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
For all referenced _classes_, Spring tries to locate a bean with the given type. If |
|
more than one match exists, a new instance is created and can use the regular |
|
bean lifecycle callbacks such as dependency injection. |
|
==== |
|
|
|
Keys are generated by a `javax.cache.annotation.CacheKeyGenerator` that serves the |
|
same purpose as Spring's `KeyGenerator`. By default, all method arguments are taken |
|
into account unless at least one parameter is annotated with `@CacheKey`. This is |
|
similar to Spring's <<cache-annotations-cacheable-key,custom key generation |
|
declaration>>. For instance these are identical operations, one using Spring's |
|
abstraction and the other with JCache: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Cacheable(value="books", **key="#isbn"**) |
|
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) |
|
|
|
@CacheResult(cacheName="books") |
|
public Book findBook(**@CacheKey** ISBN isbn, boolean checkWarehouse, boolean includeUsed) |
|
---- |
|
|
|
The `CacheKeyResolver` to use can also be specified on the operation, in a similar |
|
fashion as the `CacheResolverFactory`. |
|
|
|
JCache can manage exceptions thrown by annotated methods: this can prevent an update of |
|
the cache but it can also cache the exception as an indicator of the failure instead of |
|
calling the method again. Let's assume that `InvalidIsbnNotFoundException` is thrown if |
|
the structure of the ISBN is invalid. This is a permanent failure, no book could ever be |
|
retrieved with such parameter. The following caches the exception so that further calls |
|
with the same, invalid ISBN, throws the cached exception directly instead of invoking |
|
the method again. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@CacheResult(cacheName="books", **exceptionCacheName="failures"** |
|
**cachedExceptions = InvalidIsbnNotFoundException.class**) |
|
public Book findBook(ISBN isbn) |
|
---- |
|
|
|
|
|
==== Enabling JSR-107 support |
|
|
|
Nothing specific needs to be done to enable the JSR-107 support alongside Spring's |
|
declarative annotation support. Both `@EnableCaching` and the |
|
`cache:annotation-driven` element will enable automatically the JCache support |
|
if both the JSR-107 API and the `spring-context-support` module are present in |
|
the classpath. |
|
|
|
[NOTE] |
|
==== |
|
Depending of your use case, the choice is basically yours. You can even mix |
|
and match services using the JSR-107 API and others using Spring's own |
|
annotations. Be aware however that if these services are impacting the same |
|
caches, a consistent and identical key generation implementation should be used. |
|
==== |
|
|
|
|
|
[[cache-declarative-xml]] |
|
=== Declarative XML-based caching |
|
If annotations are not an option (no access to the sources or no external code), one can |
|
use XML for declarative caching. So instead of annotating the methods for caching, one |
|
specifies the target method and the caching directives externally (similar to the |
|
declarative transaction management <<transaction-declarative-first-example,advice>>). |
|
The previous example can be translated into: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<!-- the service we want to make cacheable --> |
|
<bean id="bookService" class="x.y.service.DefaultBookService"/> |
|
|
|
<!-- cache definitions --> |
|
<cache:advice id="cacheAdvice" cache-manager="cacheManager"> |
|
<cache:caching cache="books"> |
|
<cache:cacheable method="findBook" key="#isbn"/> |
|
<cache:cache-evict method="loadBooks" all-entries="true"/> |
|
</cache:caching> |
|
</cache:advice> |
|
|
|
<!-- apply the cacheable behavior to all BookService interfaces --> |
|
<aop:config> |
|
<aop:advisor advice-ref="cacheAdvice" pointcut="execution(* x.y.BookService.*(..))"/> |
|
</aop:config> |
|
|
|
<!-- cache manager definition omitted --> |
|
---- |
|
|
|
In the configuration above, the `bookService` is made cacheable. The caching semantics |
|
to apply are encapsulated in the `cache:advice` definition which instructs method |
|
`findBooks` to be used for putting data into the cache while method `loadBooks` for |
|
evicting data. Both definitions are working against the `books` cache. |
|
|
|
The `aop:config` definition applies the cache advice to the appropriate points in the |
|
program by using the AspectJ pointcut expression (more information is available in |
|
<<aop>>). In the example above, all methods from the `BookService` are considered and |
|
the cache advice applied to them. |
|
|
|
The declarative XML caching supports all of the annotation-based model so moving between |
|
the two should be fairly easy - further more both can be used inside the same |
|
application. The XML based approach does not touch the target code however it is |
|
inherently more verbose; when dealing with classes with overloaded methods that are |
|
targeted for caching, identifying the proper methods does take an extra effort since the |
|
`method` argument is not a good discriminator - in these cases, the AspectJ pointcut can |
|
be used to cherry pick the target methods and apply the appropriate caching |
|
functionality. However through XML, it is easier to apply a package/group/interface-wide |
|
caching (again due to the AspectJ pointcut) and to create template-like definitions (as |
|
we did in the example above by defining the target cache through the `cache:definitions` |
|
`cache` attribute). |
|
|
|
|
|
|
|
|
|
[[cache-store-configuration]] |
|
=== Configuring the cache storage |
|
Out of the box, the cache abstraction provides several storages integration. To use |
|
them, one needs to simply declare an appropriate `CacheManager` - an entity that |
|
controls and manages ++Cache++s and can be used to retrieve these for storage. |
|
|
|
|
|
[[cache-store-configuration-jdk]] |
|
==== JDK ConcurrentMap-based Cache |
|
|
|
The JDK-based `Cache` implementation resides under |
|
`org.springframework.cache.concurrent` package. It allows one to use `ConcurrentHashMap` |
|
as a backing `Cache` store. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<!-- simple cache manager --> |
|
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager"> |
|
<property name="caches"> |
|
<set> |
|
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default"/> |
|
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="books"/> |
|
</set> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
The snippet above uses the `SimpleCacheManager` to create a `CacheManager` for the two |
|
nested `ConcurrentMapCache` instances named __default__ and __books__. Note that the |
|
names are configured directly for each cache. |
|
|
|
As the cache is created by the application, it is bound to its lifecycle, making it |
|
suitable for basic use cases, tests or simple applications. The cache scales well and is |
|
very fast but it does not provide any management or persistence capabilities nor |
|
eviction contracts. |
|
|
|
|
|
|
|
[[cache-store-configuration-ehcache]] |
|
==== EhCache-based Cache |
|
|
|
The EhCache implementation is located under `org.springframework.cache.ehcache` package. |
|
Again, to use it, one simply needs to declare the appropriate `CacheManager`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="cacheManager" |
|
class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cache-manager-ref="ehcache"/> |
|
|
|
<!-- EhCache library setup --> |
|
<bean id="ehcache" |
|
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:config-location="ehcache.xml"/> |
|
---- |
|
|
|
This setup bootstraps the ehcache library inside Spring IoC (through the `ehcache` bean) which |
|
is then wired into the dedicated `CacheManager` implementation. Note the entire |
|
ehcache-specific configuration is read from `ehcache.xml`. |
|
|
|
[[cache-store-configuration-guava]] |
|
==== Guava Cache |
|
|
|
The Guava implementation is located under `org.springframework.cache.guava` package and |
|
provides access to several features of Guava. |
|
|
|
Configuring a `CacheManager` that creates the cache on demand is straightforward: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="cacheManager" |
|
class="org.springframework.cache.guava.GuavaCacheManager"/> |
|
---- |
|
|
|
It is also possible to provide the caches to use explicitly. In that case, only those |
|
will be made available by the manager: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="cacheManager" class="org.springframework.cache.guava.GuavaCacheManager"> |
|
<property name="caches"> |
|
<set> |
|
<value>default</value> |
|
<value>books</value> |
|
</set> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
The Guava `CacheManager` also supports customs `CacheBuilder` and `CacheLoader`. See |
|
the https://code.google.com/p/guava-libraries/wiki/CachesExplained[Guava documentation] |
|
for more information about those. |
|
|
|
[[cache-store-configuration-gemfire]] |
|
==== GemFire-based Cache |
|
|
|
GemFire is a memory-oriented/disk-backed, elastically scalable, continuously available, |
|
active (with built-in pattern-based subscription notifications), globally replicated |
|
database and provides fully-featured edge caching. For further information on how to use |
|
GemFire as a CacheManager (and more), please refer to the |
|
http://docs.spring.io/spring-gemfire/docs/current/reference/htmlsingle/[Spring Data GemFire |
|
reference documentation]. |
|
|
|
[[cache-store-configuration-jsr107]] |
|
==== JSR-107 Cache |
|
|
|
JSR-107 compliant caches can also be used by Spring's caching abstraction. The JCache |
|
implementation is located under `org.springframework.cache.jcache` package. |
|
|
|
Again, to use it, one simply needs to declare the appropriate `CacheManager`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="cacheManager" |
|
class="org.springframework.cache.jcache.JCacheCacheManager" |
|
p:cache-manager-ref="jCacheManager"/> |
|
|
|
<!-- JSR-107 cache manager setup --> |
|
<bean id="jCacheManager" .../> |
|
---- |
|
|
|
|
|
[[cache-store-configuration-noop]] |
|
==== Dealing with caches without a backing store |
|
Sometimes when switching environments or doing testing, one might have cache |
|
declarations without an actual backing cache configured. As this is an invalid |
|
configuration, at runtime an exception will be thrown since the caching infrastructure |
|
is unable to find a suitable store. In situations like this, rather then removing the |
|
cache declarations (which can prove tedious), one can wire in a simple, dummy cache that |
|
performs no caching - that is, forces the cached methods to be executed every time: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="cacheManager" class="org.springframework.cache.support.CompositeCacheManager"> |
|
<property name="cacheManagers"> |
|
<list> |
|
<ref bean="jdkCache"/> |
|
<ref bean="gemfireCache"/> |
|
</list> |
|
</property> |
|
<property name="fallbackToNoOpCache" value="true"/> |
|
</bean> |
|
---- |
|
|
|
The `CompositeCacheManager` above chains multiple ++CacheManager++s and additionally, |
|
through the `fallbackToNoOpCache` flag, adds a __no op__ cache that for all the |
|
definitions not handled by the configured cache managers. That is, every cache |
|
definition not found in either `jdkCache` or `gemfireCache` (configured above) will be |
|
handled by the no op cache, which will not store any information causing the target |
|
method to be executed every time. |
|
|
|
|
|
|
|
|
|
[[cache-plug]] |
|
=== Plugging-in different back-end caches |
|
Clearly there are plenty of caching products out there that can be used as a backing |
|
store. To plug them in, one needs to provide a `CacheManager` and `Cache` implementation |
|
since unfortunately there is no available standard that we can use instead. This may |
|
sound harder than it is since in practice, the classes tend to be simple |
|
http://en.wikipedia.org/wiki/Adapter_pattern[adapter]s that map the caching abstraction |
|
framework on top of the storage API as the `ehcache` classes can show. Most |
|
`CacheManager` classes can use the classes in `org.springframework.cache.support` |
|
package, such as `AbstractCacheManager` which takes care of the boiler-plate code |
|
leaving only the actual __mapping__ to be completed. We hope that in time, the libraries |
|
that provide integration with Spring can fill in this small configuration gap. |
|
|
|
|
|
|
|
|
|
[[cache-specific-config]] |
|
=== How can I set the TTL/TTI/Eviction policy/XXX feature? |
|
Directly through your cache provider. The cache abstraction is... well, an abstraction |
|
not a cache implementation. The solution you are using might support various data |
|
policies and different topologies which other solutions do not (take for example the JDK |
|
`ConcurrentHashMap`) - exposing that in the cache abstraction would be useless simply |
|
because there would no backing support. Such functionality should be controlled directly |
|
through the backing cache, when configuring it or through its native API. |
|
|
|
|
|
|
|
|
|
|
|
|
|
include::appendix.adoc[]
|