From d6b4d92eed13d5908114cec37c056481f89a7d0f Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 11 Dec 2013 10:56:19 -0800 Subject: [PATCH] Clean reference docs and prepare for Spring 4 Polish and cleanup the asciidoc source and prepare the reference guide for the upcoming Spring 4.0 release. --- src/asciidoc/appendix.adoc | 2723 +-- src/asciidoc/images/spring-overview.png | Bin 66242 -> 58395 bytes src/asciidoc/index.adoc | 26409 +++++++++++----------- 3 files changed, 14624 insertions(+), 14508 deletions(-) diff --git a/src/asciidoc/appendix.adoc b/src/asciidoc/appendix.adoc index c57b66cc50..ec1ed61e82 100644 --- a/src/asciidoc/appendix.adoc +++ b/src/asciidoc/appendix.adoc @@ -40,33 +40,33 @@ property setter. The following snippets show a DAO definition in a Spring contai referencing the above defined `SessionFactory`, and an example for a DAO method implementation. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - + + + - + ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class ProductDaoImpl implements ProductDao { + public class ProductDaoImpl implements ProductDao { - private HibernateTemplate hibernateTemplate; + private HibernateTemplate hibernateTemplate; - public void setSessionFactory(SessionFactory sessionFactory) { - this.hibernateTemplate = new HibernateTemplate(sessionFactory); - } + public void setSessionFactory(SessionFactory sessionFactory) { + this.hibernateTemplate = new HibernateTemplate(sessionFactory); + } - public Collection loadProductsByCategory(String category) throws DataAccessException { - return this.hibernateTemplate.find("from test.Product product where product.category=?", category); - } -} + public Collection loadProductsByCategory(String category) throws DataAccessException { + return this.hibernateTemplate.find("from test.Product product where product.category=?", category); + } + } ---- The `HibernateTemplate` class provides many methods that mirror the methods exposed on @@ -75,29 +75,29 @@ as the one shown above. If you need access to the `Session` to invoke methods th not exposed on the `HibernateTemplate`, you can always drop down to a callback-based approach like so. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class ProductDaoImpl implements ProductDao { + public class ProductDaoImpl implements ProductDao { - private HibernateTemplate hibernateTemplate; + private HibernateTemplate hibernateTemplate; - public void setSessionFactory(SessionFactory sessionFactory) { - this.hibernateTemplate = new HibernateTemplate(sessionFactory); - } + public void setSessionFactory(SessionFactory sessionFactory) { + this.hibernateTemplate = new HibernateTemplate(sessionFactory); + } - public Collection loadProductsByCategory(final String category) throws DataAccessException { - return this.hibernateTemplate.execute(new HibernateCallback() { + public Collection loadProductsByCategory(final String category) throws DataAccessException { + return this.hibernateTemplate.execute(new HibernateCallback() { + public Object doInHibernate(Session session) { + Criteria criteria = session.createCriteria(Product.class); + criteria.add(Expression.eq("category", category)); + criteria.setMaxResults(6); + return criteria.list(); + } + }; + } - public Object doInHibernate(Session session) { - Criteria criteria = session.createCriteria(Product.class); - criteria.add(Expression.eq("category", category)); - criteria.setMaxResults(6); - return criteria.list(); - } - }; - } -} + } ---- A callback implementation effectively can be used for any Hibernate data access. @@ -112,16 +112,17 @@ receiving a `SessionFactory`, and `getSessionFactory()` and `getHibernateTemplat use by subclasses. In combination, this allows for very simple DAO implementations for typical requirements: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class ProductDaoImpl extends HibernateDaoSupport implements ProductDao { + public class ProductDaoImpl extends HibernateDaoSupport implements ProductDao { - public Collection loadProductsByCategory(String category) throws DataAccessException { - return this.getHibernateTemplate().find( - "from test.Product product where product.category=?", category); - } -} + public Collection loadProductsByCategory(String category) throws DataAccessException { + return this.getHibernateTemplate().find( + "from test.Product product where product.category=?", category); + } + + } ---- @@ -138,27 +139,27 @@ methods ' `allowCreate`' argument, to enforce running within a transaction (whic the need to close the returned `Session`, as its lifecycle is managed by the transaction). -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class HibernateProductDao extends HibernateDaoSupport implements ProductDao { + public class HibernateProductDao extends HibernateDaoSupport implements ProductDao { - public Collection loadProductsByCategory(String category) throws DataAccessException, MyException { - Session session = getSession(false); - try { - Query query = session.createQuery("from test.Product product where product.category=?"); - query.setString(0, category); - List result = query.list(); - if (result == null) { - throw new MyException("No search results."); - } - return result; - } - catch (HibernateException ex) { - throw convertHibernateAccessException(ex); - } - } -} + public Collection loadProductsByCategory(String category) throws DataAccessException, MyException { + Session session = getSession(false); + try { + Query query = session.createQuery("from test.Product product where product.category=?"); + query.setString(0, category); + List result = query.list(); + if (result == null) { + throw new MyException("No search results."); + } + return result; + } + catch (HibernateException ex) { + throw convertHibernateAccessException(ex); + } + } + } ---- The advantage of such direct Hibernate access code is that it allows __any__ checked @@ -177,48 +178,49 @@ For the currently recommended usage patterns for JDO see <> [[orm-jdo-template]] -===== JdoTemplate and `JdoDaoSupport` +===== JdoTemplate and `JdoDaoSupport` Each JDO-based DAO will then receive the `PersistenceManagerFactory` through dependency injection. Such a DAO could be coded against plain JDO API, working with the given `PersistenceManagerFactory`, but will usually rather be used with the Spring Framework's `JdoTemplate`: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - + + + - + ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class ProductDaoImpl implements ProductDao { + public class ProductDaoImpl implements ProductDao { - private JdoTemplate jdoTemplate; + private JdoTemplate jdoTemplate; - public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) { - this.jdoTemplate = new JdoTemplate(pmf); - } + public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) { + this.jdoTemplate = new JdoTemplate(pmf); + } - public Collection loadProductsByCategory(final String category) throws DataAccessException { - return (Collection) this.jdoTemplate.execute(new JdoCallback() { - public Object doInJdo(PersistenceManager pm) throws JDOException { - Query query = pm.newQuery(Product.class, "category = pCategory"); - query.declareParameters("String pCategory"); - List result = query.execute(category); - // do some further stuff with the result list - return result; - } - }); - } -} + public Collection loadProductsByCategory(final String category) throws DataAccessException { + return (Collection) this.jdoTemplate.execute(new JdoCallback() { + public Object doInJdo(PersistenceManager pm) throws JDOException { + Query query = pm.newQuery(Product.class, "category = pCategory"); + query.declareParameters("String pCategory"); + List result = query.execute(category); + // do some further stuff with the result list + return result; + } + }); + } + + } ---- A callback implementation can effectively be used for any JDO data access. `JdoTemplate` @@ -233,16 +235,17 @@ receiving a `PersistenceManagerFactory`, and `getPersistenceManagerFactory()` an `getJdoTemplate()` for use by subclasses. In combination, this allows for very simple DAO implementations for typical requirements: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class ProductDaoImpl extends JdoDaoSupport implements ProductDao { + public class ProductDaoImpl extends JdoDaoSupport implements ProductDao { - public Collection loadProductsByCategory(String category) throws DataAccessException { - return getJdoTemplate().find( - Product.class, "category = pCategory", "String category", new Object[] {category}); - } -} + public Collection loadProductsByCategory(String category) throws DataAccessException { + return getJdoTemplate().find(Product.class, + "category = pCategory", "String category", new Object[] {category}); + } + + } ---- As alternative to working with Spring's `JdoTemplate`, you can also code Spring-based @@ -260,47 +263,48 @@ For the currently recommended usage patterns for JPA see <> [[orm-jpa-template]] -===== JpaTemplate and `JpaDaoSupport` +===== JpaTemplate and `JpaDaoSupport` Each JPA-based DAO will then receive a `EntityManagerFactory` via dependency injection. Such a DAO can be coded against plain JPA and work with the given `EntityManagerFactory` or through Spring's `JpaTemplate`: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - + + + - + ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class JpaProductDao implements ProductDao { + public class JpaProductDao implements ProductDao { - private JpaTemplate jpaTemplate; + private JpaTemplate jpaTemplate; - public void setEntityManagerFactory(EntityManagerFactory emf) { - this.jpaTemplate = new JpaTemplate(emf); - } + public void setEntityManagerFactory(EntityManagerFactory emf) { + this.jpaTemplate = new JpaTemplate(emf); + } - public Collection loadProductsByCategory(final String category) throws DataAccessException { - return (Collection) this.jpaTemplate.execute(new JpaCallback() { - public Object doInJpa(EntityManager em) throws PersistenceException { - Query query = em.createQuery("from Product as p where p.category = :category"); - query.setParameter("category", category); - List result = query.getResultList(); - // do some further processing with the result list - return result; - } - }); - } -} + public Collection loadProductsByCategory(final String category) throws DataAccessException { + return (Collection) this.jpaTemplate.execute(new JpaCallback() { + public Object doInJpa(EntityManager em) throws PersistenceException { + Query query = em.createQuery("from Product as p where p.category = :category"); + query.setParameter("category", category); + List result = query.getResultList(); + // do some further processing with the result list + return result; + } + }); + } + + } ---- The `JpaCallback` implementation allows any type of JPA data access. The `JpaTemplate` @@ -315,17 +319,18 @@ one line callback implementations. Furthermore, Spring provides a convenient `JpaDaoSupport` base class that provides the `get/setEntityManagerFactory` and `getJpaTemplate()` to be used by subclasses: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class ProductDaoImpl extends JpaDaoSupport implements ProductDao { + public class ProductDaoImpl extends JpaDaoSupport implements ProductDao { - public Collection loadProductsByCategory(String category) throws DataAccessException { - Map params = new HashMap(); - params.put("category", category); - return getJpaTemplate().findByNamedParams("from Product as p where p.category = :category", params); - } -} + public Collection loadProductsByCategory(String category) throws DataAccessException { + Map params = new HashMap(); + params.put("category", category); + return getJpaTemplate().findByNamedParams("from Product as p where p.category = :category", params); + } + + } ---- Besides working with Spring's `JpaTemplate`, one can also code Spring-based DAOs against @@ -455,16 +460,16 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface Pointcut { + public interface Pointcut { - ClassFilter getClassFilter(); + ClassFilter getClassFilter(); - MethodMatcher getMethodMatcher(); + MethodMatcher getMethodMatcher(); -} + } ---- Splitting the `Pointcut` interface into two parts allows reuse of class and method @@ -475,29 +480,31 @@ The `ClassFilter` interface is used to restrict the pointcut to a given set of t classes. If the `matches()` method always returns true, all target classes will be matched: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface ClassFilter { + public interface ClassFilter { - boolean matches(Class clazz); -} + boolean matches(Class clazz); + + } ---- The `MethodMatcher` interface is normally more important. The complete interface is shown below: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface MethodMatcher { + public interface MethodMatcher { - boolean matches(Method m, Class targetClass); + boolean matches(Method m, Class targetClass); - boolean isRuntime(); + boolean isRuntime(); - boolean matches(Method m, Class targetClass, Object[] args); -} + boolean matches(Method m, Class targetClass, Object[] args); + + } ---- The `matches(Method, Class)` method is used to test whether this pointcut will ever @@ -576,18 +583,18 @@ effectively the union of these pointcuts.) The usage is shown below: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - .*set.* - .*absquatulate - - - + + + + .*set.* + .*absquatulate + + + ---- Spring provides a convenience class, `RegexpMethodPointcutAdvisor`, that allows us to @@ -596,21 +603,21 @@ throws advice etc.). Behind the scenes, Spring will use a `JdkRegexpMethodPointc Using `RegexpMethodPointcutAdvisor` simplifies wiring, as the one bean encapsulates both pointcut and advice, as shown below: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - .*set.* - .*absquatulate - - - + + + + + + + .*set.* + .*absquatulate + + + ---- __RegexpMethodPointcutAdvisor__ can be used with any Advice type. @@ -655,15 +662,16 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -class TestStaticPointcut extends StaticMethodMatcherPointcut { + class TestStaticPointcut extends StaticMethodMatcherPointcut { - public boolean matches(Method m, Class targetClass) { - // return true if custom criteria match - } -} + public boolean matches(Method m, Class targetClass) { + // return true if custom criteria match + } + + } ---- There are also superclasses for dynamic pointcuts. @@ -725,13 +733,14 @@ Spring is compliant with the AOP Alliance interface for around advice using meth interception. MethodInterceptors implementing around advice should implement the following interface: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface MethodInterceptor extends Interceptor { + public interface MethodInterceptor extends Interceptor { - Object invoke(MethodInvocation invocation) throws Throwable; -} + Object invoke(MethodInvocation invocation) throws Throwable; + + } ---- The `MethodInvocation` argument to the `invoke()` method exposes the method being @@ -741,18 +750,19 @@ point. A simple `MethodInterceptor` implementation looks as follows: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class DebugInterceptor implements MethodInterceptor { + 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; - } -} + 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 @@ -786,13 +796,14 @@ The `MethodBeforeAdvice` interface is shown below. (Spring's API design would al field before advice, although the usual objects apply to field interception and it's unlikely that Spring will ever implement it). -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface MethodBeforeAdvice extends BeforeAdvice { + public interface MethodBeforeAdvice extends BeforeAdvice { - void before(Method m, Object[] args, Object target) throws Throwable; -} + 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 @@ -804,21 +815,21 @@ wrapped in an unchecked exception by the AOP proxy. An example of a before advice in Spring, which counts all method invocations: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class CountingBeforeAdvice implements MethodBeforeAdvice { + public class CountingBeforeAdvice implements MethodBeforeAdvice { - private int count; + private int count; - public void before(Method m, Object[] args, Object target) throws Throwable { - ++count; - } + public void before(Method m, Object[] args, Object target) throws Throwable { + ++count; + } - public int getCount() { - return count; - } -} + public int getCount() { + return count; + } + } ---- [TIP] @@ -836,10 +847,10 @@ an exception. Spring offers typed throws advice. Note that this means that the tag interface identifying that the given object implements one or more typed throws advice methods. These should be in the form of: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -afterThrowing([Method, args, target], subclassOfThrowable) + afterThrowing([Method, args, target], subclassOfThrowable) ---- Only the last argument is required. The method signatures may have either one or four @@ -848,49 +859,51 @@ arguments. The following classes are examples of throws advice. The advice below is invoked if a `RemoteException` is thrown (including subclasses): -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class RemoteThrowsAdvice implements ThrowsAdvice { + public class RemoteThrowsAdvice implements ThrowsAdvice { - public void afterThrowing(RemoteException ex) throws Throwable { - // Do something with remote exception - } -} + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class ServletThrowsAdviceWithArguments implements ThrowsAdvice { + public class ServletThrowsAdviceWithArguments implements ThrowsAdvice { - public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) { - // Do something with all arguments - } -} + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public static class CombinedThrowsAdvice implements ThrowsAdvice { + public static class CombinedThrowsAdvice implements ThrowsAdvice { - public void afterThrowing(RemoteException ex) throws Throwable { - // Do something with remote exception - } + 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 - } -} + 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 @@ -913,14 +926,15 @@ Throws advice can be used with any pointcut. An after returning advice in Spring must implement the __org.springframework.aop.AfterReturningAdvice__ interface, shown below: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface AfterReturningAdvice extends Advice { + public interface AfterReturningAdvice extends Advice { - void afterReturning(Object returnValue, Method m, Object[] args, Object target) - throws Throwable; -} + 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), @@ -929,22 +943,23 @@ invoked method, methods arguments and target. The following after returning advice counts all successful method invocations that have not thrown exceptions: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class CountingAfterReturningAdvice implements AfterReturningAdvice { + public class CountingAfterReturningAdvice implements AfterReturningAdvice { - private int count; + private int count; - public void afterReturning(Object returnValue, Method m, Object[] args, Object target) - throws Throwable { - ++count; - } + public void afterReturning(Object returnValue, Method m, Object[] args, + Object target) throws Throwable { + ++count; + } - public int getCount() { - return count; - } -} + public int getCount() { + return count; + } + + } ---- This advice doesn't change the execution path. If it throws an exception, this will be @@ -964,13 +979,14 @@ Spring treats introduction advice as a special kind of interception advice. Introduction requires an `IntroductionAdvisor`, and an `IntroductionInterceptor`, implementing the following interface: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface IntroductionInterceptor extends MethodInterceptor { + public interface IntroductionInterceptor extends MethodInterceptor { - boolean implementsInterface(Class intf); -} + boolean implementsInterface(Class intf); + + } ---- The `invoke()` method inherited from the AOP Alliance `MethodInterceptor` interface must @@ -982,20 +998,22 @@ Introduction advice cannot be used with any pointcut, as it applies only at clas rather than method, level. You can only use introduction advice with the `IntroductionAdvisor`, which has the following methods: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface IntroductionAdvisor extends Advisor, IntroductionInfo { + public interface IntroductionAdvisor extends Advisor, IntroductionInfo { - ClassFilter getClassFilter(); + ClassFilter getClassFilter(); - void validateInterfaces() throws IllegalArgumentException; -} + void validateInterfaces() throws IllegalArgumentException; -public interface IntroductionInfo { + } - Class[] getInterfaces(); -} + public interface IntroductionInfo { + + Class[] getInterfaces(); + + } ---- There is no `MethodMatcher`, and hence no `Pointcut`, associated with introduction @@ -1009,14 +1027,18 @@ introduced interfaces can be implemented by the configured `IntroductionIntercep 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface Lockable { - void lock(); - void unlock(); - boolean locked(); -} + public interface Lockable { + + void lock(); + + void unlock(); + + boolean locked(); + + } ---- This illustrates a __mixin__. We want to be able to cast advised objects to Lockable, @@ -1052,33 +1074,33 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class LockMixin extends DelegatingIntroductionInterceptor - implements Lockable { + public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable { - private boolean locked; + private boolean locked; - public void lock() { - this.locked = true; - } + public void lock() { + this.locked = true; + } - public void unlock() { - this.locked = false; - } + public void unlock() { + this.locked = false; + } - public boolean locked() { - return this.locked; - } + 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); - } + 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 @@ -1093,15 +1115,16 @@ The introduction advisor required is simple. All it needs to do is hold a distin 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class LockMixinAdvisor extends DefaultIntroductionAdvisor { + public class LockMixinAdvisor extends DefaultIntroductionAdvisor { - public LockMixinAdvisor() { - super(new LockMixin(), Lockable.class); - } -} + public LockMixinAdvisor() { + super(new LockMixin(), Lockable.class); + } + + } ---- We can apply this advisor very simply: it requires no configuration. (However, it __is__ @@ -1293,33 +1316,31 @@ Let's look at a simple example of `ProxyFactoryBean` in action. This example inv * 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - Tony - 51 - + + Tony + 51 + - - Custom string property value - + + Custom string property value + - - + + - - com.mycompany.Person - - - - - myAdvisor - debugInterceptor - - - + + com.mycompany.Person + + + + myAdvisor + debugInterceptor + + + ---- Note that the `interceptorNames` property takes a list of String: the bean names of the @@ -1338,21 +1359,21 @@ an instance of the prototype from the factory; holding a reference isn't suffici The "person" bean definition above can be used in place of a Person implementation, as follows: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -Person person = (Person) factory.getBean("person"); + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- The `PersonUser` class in this example would expose a property of type Person. As far as @@ -1364,31 +1385,31 @@ It's possible to conceal the distinction between target and proxy using an anony __inner bean__, as follows. Only the `ProxyFactoryBean` definition is different; the advice is included only for completeness: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - Custom string property value - + + Custom string property value + - + - - com.mycompany.Person - - - - Tony - 51 - - - - - myAdvisor - debugInterceptor - - - + + com.mycompany.Person + + + + Tony + 51 + + + + + myAdvisor + debugInterceptor + + + ---- This has the advantage that there's only one object of type `Person`: useful if we want @@ -1441,20 +1462,20 @@ By appending an asterisk to an interceptor name, all advisors with bean names ma 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - global* - - - + + + + + global* + + + - - + + ---- @@ -1468,55 +1489,55 @@ definitions, can result in much cleaner and more concise proxy definitions. First a parent, __template__, bean definition is created for the proxy: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - PROPAGATION_REQUIRED - - - + + + + + PROPAGATION_REQUIRED + + + ---- 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - + + + + + + ---- It is of course possible to override properties from the parent template, such as in this case, the transaction propagation settings: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - PROPAGATION_REQUIRED,readOnly - PROPAGATION_REQUIRED,readOnly - PROPAGATION_REQUIRED,readOnly - PROPAGATION_REQUIRED - - - + + + + + + + + PROPAGATION_REQUIRED,readOnly + PROPAGATION_REQUIRED,readOnly + PROPAGATION_REQUIRED,readOnly + PROPAGATION_REQUIRED + + + ---- Note that in the example above, we have explicitly marked the parent bean definition as @@ -1541,13 +1562,13 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl); -factory.addInterceptor(myMethodInterceptor); -factory.addAdvisor(myAdvisor); -MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy(); + ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl); + factory.addInterceptor(myMethodInterceptor); + factory.addAdvisor(myAdvisor); + MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy(); ---- The first step is to construct an object of type @@ -1581,29 +1602,28 @@ However you create AOP proxies, you can manipulate them using the interface, whichever other interfaces it implements. This interface includes the following methods: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -Advisor[] getAdvisors(); + Advisor[] getAdvisors(); -void addAdvice(Advice advice) throws AopConfigException; + void addAdvice(Advice advice) throws AopConfigException; -void addAdvice(int pos, Advice advice) - throws AopConfigException; + void addAdvice(int pos, Advice advice) throws AopConfigException; -void addAdvisor(Advisor advisor) throws AopConfigException; + void addAdvisor(Advisor advisor) throws AopConfigException; -void addAdvisor(int pos, Advisor advisor) throws AopConfigException; + void addAdvisor(int pos, Advisor advisor) throws AopConfigException; -int indexOf(Advisor advisor); + int indexOf(Advisor advisor); -boolean removeAdvisor(Advisor advisor) throws AopConfigException; + boolean removeAdvisor(Advisor advisor) throws AopConfigException; -void removeAdvisor(int index) throws AopConfigException; + void removeAdvisor(int index) throws AopConfigException; -boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException; + boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException; -boolean isFrozen(); + boolean isFrozen(); ---- The `getAdvisors()` method will return an Advisor for every advisor, interceptor or @@ -1626,24 +1646,23 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -Advised advised = (Advised) myObject; -Advisor[] advisors = advised.getAdvisors(); -int oldAdvisorCount = advisors.length; -System.out.println(oldAdvisorCount + " advisors"); + 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 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)); + // Add selective advice using a pointcut + advised.addAdvisor(new DefaultPointcutAdvisor(mySpecialPointcut, myAdvice)); -assertEquals("Added two advisors", - oldAdvisorCount + 2, advised.getAdvisors().length); + assertEquals("Added two advisors", oldAdvisorCount + 2, advised.getAdvisors().length); ---- [NOTE] @@ -1700,17 +1719,17 @@ standard autoproxy creators. The `BeanNameAutoProxyCreator` class is a `BeanPostProcessor` that automatically creates AOP proxies for beans with names matching literal values or wildcards. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - jdk*,onlyJdk - - - myInterceptor - - - + + jdk*,onlyJdk + + + myInterceptor + + + ---- As with `ProxyFactoryBean`, there is an `interceptorNames` property rather than a list @@ -1759,22 +1778,22 @@ dependencies to obtain an un-advised object. Calling getBean("businessObject1") ApplicationContext will return an AOP proxy, not the target business object. (The "inner bean" idiom shown earlier also offers this benefit.) -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - + + + - + - - - + + + - + ---- The `DefaultAdvisorAutoProxyCreator` is very useful if you want to apply the same advice @@ -1822,26 +1841,26 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - + + + - - - - - - - - + + + + + + + + - + ---- The `DefaultAdvisorAutoProxyCreator` bean definition (the name is not significant, hence @@ -1861,22 +1880,22 @@ example for auto-proxying driven by JDK 1.5+ annotations. The following configur enables automatic detection of Spring's `Transactional` annotation, leading to implicit proxies for beans containing that annotation: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - + + + - - - - - - + + + + + + ---- The `TransactionInterceptor` defined here depends on a `PlatformTransactionManager` @@ -1884,11 +1903,11 @@ definition, which is not included in this generic file (although it could be) be will be specific to the application's transaction requirements (typically JTA, as in this example, or Hibernate, JDO or JDBC): -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- [TIP] @@ -1916,19 +1935,19 @@ suite, shown above, could be used in conjunction with an attribute-driven pointc target a mixin, as shown here. We use the generic `DefaultPointcutAdvisor`, configured using JavaBean properties: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - - + + + + - + - - - + + + - - - + + + ---- The above `swap()` call changes the target of the swappable bean. Clients who hold a @@ -2029,23 +2047,22 @@ pooling API. Sample configuration is shown below: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - ... properties omitted - + + ... properties omitted + - - - - + + + + - - - - + + + + ---- Note that the target object - "businessObjectTarget" in the example - __must__ be a @@ -2064,13 +2081,13 @@ It's possible to configure Spring so as to be able to cast any pooled object to about the configuration and current size of the pool through an introduction. You'll need to define an advisor like this: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + ---- This advisor is obtained by calling a convenience method on the @@ -2080,11 +2097,11 @@ the ProxyFactoryBean exposing the pooled object. The cast will look as follows: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject"); -System.out.println("Max pool size is " + conf.getMaxSize()); + PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject"); + System.out.println("Max pool size is " + conf.getMaxSize()); ---- [NOTE] @@ -2110,12 +2127,12 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- There's only one property: the name of the target bean. Inheritance is used in the @@ -2125,7 +2142,7 @@ source, the target bean must be a prototype bean definition. [[classic-aop-ts-threadlocal]] -==== ThreadLocal target sources +==== 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 @@ -2133,12 +2150,12 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- [NOTE] @@ -2158,7 +2175,7 @@ ThreadLocals without other proper handling code. [[classic-aop-extensibility]] -=== Defining new Advice types +=== 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 @@ -2186,260 +2203,10 @@ Please refer to the Spring sample applications for further examples of Spring AO declarative transaction management. - - - -[[migration-3.1]] -== Migrating to Spring Framework 3.1 -In this appendix we discuss what users will want to know when upgrading to Spring -Framework 3.1. For a general overview of features, please see<> - - - - -[[migration-3.1-component-scan]] -=== Component scanning against the "org" base package -Spring Framework 3.1 introduces a number of `@Configuration` classes such as -`org.springframework.cache.annotation.ProxyCachingConfiguration` and -`org.springframework.scheduling.annotation.ProxyAsyncConfiguration`. Because -`@Configuration` is ultimately meta-annotated with Spring's `@Component` annotation, -these classes will inadvertently be scanned and processed by the container for any -component-scanning directive against the unqualified "org" package, e.g.: - -[source,xml] -[subs="verbatim,quotes"] ----- - ----- - -Therefore, in order to avoid errors like the one reported in -https://jira.springsource.org/browse/SPR-9843[SPR-9843], any such directives should be -updated to at least one more level of qualification e.g.: - -[source,xml] -[subs="verbatim,quotes"] ----- - ----- - -Alternatively, an `exclude-filter` may be used. See <> documentation for details. - - - - - -[[migration-3.2]] -== Migrating to Spring Framework 3.2 -In this appendix we discuss what users will want to know when upgrading to Spring -Framework 3.2. For a general overview of features, please see<> - - - - -[[migration-3.2-new-optional-deps]] -=== Newly optional dependencies -Certain inter-module dependencies are now `optional` at the Maven POM level where they -were once required. For example, `spring-tx` and its dependence on `spring-context`. -This may result in `ClassNotFoundErrors` or other similar problems for users that have -been relying on transitive dependency management to pull in affected downstream -`spring-*`. To resolve this problem, simply add the appropriate missing jars to your -build configuration. - - - - -[[migration-3.2-ehcache-support]] -=== EHCache support moved to spring-context-support -Along with Spring's new JCache support, the EHCache support classes in the -`org.springframework.cache.ehcache` package moved from the `spring-context` module to -`spring-context-support`. - - - - -[[migration-3.2-inline-asm]] -=== Inlining of spring-asm jar -In versions 3.0 and 3.1, we published a discrete `spring-asm` containing repackaged -`org.objectweb.asm` 3.x sources. As of Spring Framework 3.2, we have upgraded to -`org.objectweb.asm` 4.0 and done away with the separate module jar, favoring inlining -these classes directly within `spring-core`. This should cause no migration issue for -most users; but on the off chance that you have `spring-asm` declared directly within -your project's build script, you'll want to remove it when upgrading to Spring Framework -3.2. - - - - -[[migration-3.2-inline-cglib]] -=== Explicit CGLIB dependency no longer required -In prior versions, users of Spring's subclass-based AOP proxies (e.g. via -`proxy-target-class="true"`) and `@Configuration` class support were required to declare -an explicit dependency on CGLIB 2.2. As of Spring Framework 3.2, we now repackage and -inline the newly-released CGLIB 3.0. - -This means greater convenience for users, as well as correct functionality for Java 7 -users who are creating subclass proxies of types that contain `invokedynamic` bytecode -instructions. Repackaging CGLIB internally ensures no classpath conflicts with other -third party frameworks that may depend on other versions of CGLIB. - - - - -[[migration-3.2-osgi-users]] -=== For OSGi users -OSGi metadata is no longer published within individual Spring Framework jar MANIFEST.MF -files. For more information about how users can get OSGi-ready versions of Spring -Framework 3.2 jars. - - - - -[[migration-3.2-compatibility-mvc-config]] -=== MVC Java Config and MVC Namespace -As explained in <>, both the MVC Java config and the MVC -namespace register extensions such as `.json` and `.xml` if the corresponding classpath -dependencies are present. That means controller methods may now return JSON or XML -formatted content if those extensions are present in the request URI, even if the -'Accept' header doesn't request those media types. - -The newly added support for matrix variables is explained in -<>. To preserve backward compatibility, by default, semicolon -content is removed from incoming request URIs and therefore `@MatrixVariable` cannot be -used without additional configuration. However, when using the MVC Java config and the -MVC namespace, semicolon content is left in the URI so that matrix variables are -automatically supported. The removal of semicolon content is controlled through the -`UrlPathHelper` property of `RequestMappingHandlerMapping`. - - - - -[[migration-3.2-compatibility-uri-variable-values]] -=== Decoding of URI Variable Values -URI variable values now get decoded when `UrlPathHelper.setUrlDecode` is set to `false`. -See https://jira.springsource.org/browse/SPR-9098[SPR-9098]. - - - - -[[migration-3.2-compatibility-http-patch]] -=== HTTP PATCH method -The `DispatcherServlet` now allows the HTTP PATCH method where previously it didn't. - - - - -[[migration-3.2-compatibility-tiles3]] -=== Tiles 3 -Besides the version number change, the set of Tiles dependencies has also changed. You -will need to have a subset or all of `tiles-request-api`, `tiles-api`, `tiles-core`, -`tiles-servlet`, `tiles-jsp`, `tiles-el`. - - - - -[[migration-3.2-compatibility-spring-mvc-test]] -=== Spring MVC Test standalone project -If migrating from the https://github.com/SpringSource/spring-test-mvc[spring-test-mvc] -standalone project to the `spring-test` module in Spring Framework 3.2, you will need to -adjust the root package to be `org.springframework.test.web.servlet`. - -You will no longer be able to use the `MockMvcBuilders` `annotationConfigSetup` and -`xmlConfigSetup` options. Instead you'll need to switch to using the -`@WebAppConfiguration` support of `spring-test` for loading Spring configuration, then -inject a `WebApplicationContext` into the test and use it to create a `MockMvc`. See -<> for details. - - - - -[[migration-3.2-compatibility-spring-test]] -=== Spring Test Dependencies -The `spring-test` module has been upgraded to depend on JUnit 4.11 ( `junit:junit`), -TestNG 6.5.2 ( `org.testng:testng`), and Hamcrest Core 1.3 ( -`org.hamcrest:hamcrest-core`). Each of these dependencies is declared as an __optional__ -dependency in the Maven POM. Furthermore, it is important to note that the JUnit team -has stopped inlining Hamcrest Core within the `junit:junit` Maven artifact as of JUnit -4.11. Hamcrest Core is now a __required__ transitive dependency of `junit`, and users -may therefore need to remove any exclusions on `hamcrest-core` that they had previously -configured for their build. - - - - -[[migration-3.2-changes]] -=== Public API changes - - - -[[migration-3.2-api-changes]] -==== JDiff reports -Select JDiff reports are now being published to provide users with a convenient means of -understanding what's changed between versions. Going forward these will be published -between each minor version, e.g. from 3.1.3.RELEASE to 3.1.4.RELEASE; from the latest -maintenance version to the latest GA release, e.g. -http://docs.spring.io/spring-framework/docs/3.1.3.RELEASE_to_3.2.0.RELEASE[3.1.3.RELEASE -to 3.2.0.RELEASE]; and in between each milestone and/or RC for users who are tracking -next-generation development, e.g. -http://docs.spring.io/spring-framework/docs/3.2.0.RC2_to_3.2.0.RELEASE[3.2.0.RC2 to -3.2.0.RELEASE]. - - - -[[migration-3.2-removals-and-deprecations]] -==== Deprecations -The following packages and types have been wholly or partially deprecated in Spring -Framework 3.2 and may be removed in a future version. Click through to the linked -Javadoc for each item for exact details. See also the -http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/deprecated-list.html[complete -list of deprecations] in the framework. - -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/orm/ibatis/package-summary.html[org.springframework.orm.ibatis] -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/scheduling/backportconcurrent/package-summary.html[org.springframework.scheduling.backportconcurrent] -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/ejb/support/package-summary.html[org.springframework.ejb.support] -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/http/converter/xml/XmlAwareFormHttpMessageConverter.html[org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter] -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/web/jsf/DelegatingVariableResolver.html[org.springframework.web.jsf.DelegatingVariableResolver] -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/web/jsf/SpringBeanVariableResolver.html[org.springframework.web.jsf.SpringBeanVariableResolver] -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/ui/velocity/CommonsLoggingLogSystem.html[org.springframework.ui.velocity.CommonsLoggingLogSystem] -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/ui/velocity/VelocityEngineUtils.html[org.springframework.ui.velocity.VelocityEngineUtils] -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/beans/factory/config/BeanReferenceFactoryBean.html[org.springframework.beans.factory.config.BeanReferenceFactoryBean] -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/beans/factory/config/CommonsLogFactoryBean.html[org.springframework.beans.factory.config.CommonsLogFactoryBean] -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/instrument/classloading/oc4j/OC4JLoadTimeWeaver.html[org.springframework.beans.instrument.classloading.oc4j.OC4JLoadTimeWeaver] -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/transaction/jta/OC4JJtaTransactionManager.html[org.springframework.transaction.jta.OC4JJtaTransactionManager] -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/web/util/ExpressionEvaluationUtils.html[org.springframework.web.util.ExpressionEvaluationUtils] -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.html[org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter] -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerExceptionResolver.html[org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver] -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/web/servlet/mvc/annotation/DefaultAnnotationHandlerMapping.html[org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping] -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/web/servlet/mvc/annotation/ServletAnnotationMappingUtils.html[org.springframework.web.servlet.mvc.annotation.ServletAnnotationMappingUtils] -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/jmx/support/MBeanRegistrationSupport.html[org.springframework.jmx.support.MBeanRegistrationSupport] -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/test/context/ContextConfigurationAttributes.html[org.springframework.test.context.ContextConfigurationAttributes] -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.html[org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests]: - use of the `simpleJdbcTemplate` instance variable has been deprecated in favor of the - new `jdbcTemplate` instance variable. -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/test/context/testng/AbstractTransactionalTestNGSpringContextTests.html[org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests]: - use of the `simpleJdbcTemplate` instance variable has been deprecated in favor of the - new `jdbcTemplate` instance variable. -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/test/jdbc/SimpleJdbcTestUtils.html[org.springframework.test.jdbc.SimpleJdbcTestUtils] - has been deprecated in favor of `JdbcTestUtils` which now contains all of the - functionality previously available in `SimpleJdbcTestUtils`. -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/web/servlet/view/ContentNegotiatingViewResolver.html[org.springframework.web.servlet.view.ContentNegotiatingViewResolver] -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/transaction/interceptor/TransactionAspectUtils.html[org.springframework.transaction.interceptor.TransactionAspectUtils] -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/http/HttpStatus.html[org.springframework.http.HttpStatus] -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/web/util/UriUtils.html[org.springframework.web.util.UriUtils] -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/orm/jpa/vendor/TopLinkJpaDialect.html[org.springframework.orm.jpa.vendor.TopLinkJpaDialect] -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/orm/jpa/vendor/TopLinkJpaVendorAdapter.html[org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter] -* http://docs.spring.io/spring/docs/3.2.0.RELEASE/javadoc-api/org/springframework/util/CachingMapDecorator.html[org.springframework.orm.util.CachingMapDecorator] - - - - - [[xsd-config]] == XML Schema-based configuration - - [[xsd-config-introduction]] === Introduction This appendix details the XML Schema-based configuration introduced in Spring 2.0 and @@ -2499,34 +2266,34 @@ involved in doing so is covered in the appendix entitled <>. To switch over from the DTD-style to the new XML Schema-style, you need to make the following change. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + - + - + ---- The equivalent file in the XML Schema-style would be... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + - + ---- [NOTE] @@ -2560,34 +2327,34 @@ To use the tags in the `util` schema, you need to have the following preamble at of your Spring XML configuration file; the text in the snippet below references the correct schema so that the tags in the `util` namespace are available to you. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + ---- [[xsd-config-body-schemas-util-constant]] -===== +===== Before... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - + + + + + ---- The above configuration uses a Spring `FactoryBean` implementation, the @@ -2599,14 +2366,14 @@ plumbing to the end user. The following XML Schema-based version is more concise and clearly expresses the developer's intent (__'inject this constant value'__), and it just reads better. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - + + + + + ---- [[xsd-config-body-schemas-util-frfb]] @@ -2620,23 +2387,23 @@ Find below an example which shows how a `static` field is exposed, by using the http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/config/FieldRetrievingFactoryBean.html#setStaticField(java.lang.String)[`staticField`] property: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- There is also a convenience usage form where the `static` field is specified as the bean name: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- This does mean that there is no longer any choice in what the bean id is (so any other @@ -2644,15 +2411,15 @@ bean that refers to it will also have to use this longer name), but this form is concise to define, and very convenient to use as an inner bean since the id doesn't have to be specified for the bean reference: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - + + + + + ---- It is also possible to access a non-static (instance) field of another bean, as @@ -2666,44 +2433,45 @@ anything about the Spring internals (or even about classes such as the `FieldRetrievingFactoryBean`). Let's look at an example to see how easy injecting an enum value is; consider this JDK 5 enum: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package javax.persistence; + package javax.persistence; -public enum PersistenceContextType { + public enum PersistenceContextType { - TRANSACTION, - EXTENDED + TRANSACTION, + EXTENDED -} + } ---- Now consider a setter of type `PersistenceContextType`: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package example; + package example; -public class Client { + public class Client { - private PersistenceContextType persistenceContextType; + private PersistenceContextType persistenceContextType; - public void setPersistenceContextType(PersistenceContextType type) { - this.persistenceContextType = type; - } -} + public void setPersistenceContextType(PersistenceContextType type) { + this.persistenceContextType = type; + } + + } ---- .. and the corresponding bean definition: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- This works for classic type-safe emulated enums (on JDK 1.4 and JDK 1.3) as well; Spring @@ -2712,25 +2480,25 @@ class. [[xsd-config-body-schemas-util-property-path]] -===== +===== Before... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - + + + + + + + + + - - + + ---- The above configuration uses a Spring `FactoryBean` implementation, the @@ -2739,28 +2507,28 @@ has a value equal to the `'age'` property of the `'testBean'` bean. After... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - + + + + + + + + + - - + + ---- The value of the `'path'` attribute of the `` tag follows the form `'beanName.beanProperty'`. [[xsd-config-body-schemas-util-property-path-dependency]] -====== Using to set a bean property or constructor-argument +====== Using to set a bean property or constructor-argument `PropertyPathFactoryBean` is a `FactoryBean` that evaluates a property path on a given target object. The target object can be specified directly or via a bean name. This @@ -2769,67 +2537,67 @@ argument. Here's an example where a path is used against another bean, by name: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -// target bean to be referenced by name - - - - - - - - + // target bean to be referenced by name + + + + + + + + -// will result in 11, which is the value of property 'spouse.age' of bean 'person' - - - - + // will result in 11, which is the value of property 'spouse.age' of bean 'person' + + + + ---- In this example, a path is evaluated against an inner bean: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - + + + + + + + + + ---- There is also a shortcut form, where the bean name is the property path. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + ---- This form does mean that there is no choice in the name of the bean. Any reference to it will also have to use the same id, which is the path. Of course, if used as an inner bean, there is no need to refer to it at all: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - + + + + + ---- The result type may be specifically set in the actual definition. This is not necessary @@ -2838,17 +2606,17 @@ this feature. [[xsd-config-body-schemas-util-properties]] -===== +===== Before... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + ---- The above configuration uses a Spring `FactoryBean` implementation, the @@ -2857,33 +2625,33 @@ loaded from the supplied <> location). After... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + ---- [[xsd-config-body-schemas-util-list]] -===== +===== Before... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - pechorin@hero.org - raskolnikov@slums.org - stavrogin@gov.org - porfiry@gov.org - - - + + + + + pechorin@hero.org + raskolnikov@slums.org + stavrogin@gov.org + porfiry@gov.org + + + ---- The above configuration uses a Spring `FactoryBean` implementation, the @@ -2892,16 +2660,16 @@ from the supplied `'sourceList'`. After... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - pechorin@hero.org - raskolnikov@slums.org - stavrogin@gov.org - porfiry@gov.org - + + + pechorin@hero.org + raskolnikov@slums.org + stavrogin@gov.org + porfiry@gov.org + ---- You can also explicitly control the exact type of `List` that will be instantiated and @@ -2909,15 +2677,15 @@ populated via the use of the `'list-class'` attribute on the `` elem example, if we really need a `java.util.LinkedList` to be instantiated, we could use the following configuration: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - jackshaftoe@vagabond.org - eliza@thinkingmanscrumpet.org - vanhoek@pirate.org - d'Arcachon@nemesis.org - + + jackshaftoe@vagabond.org + eliza@thinkingmanscrumpet.org + vanhoek@pirate.org + d'Arcachon@nemesis.org + ---- If no `'list-class'` attribute is supplied, a `List` implementation will be chosen by @@ -2925,24 +2693,24 @@ the container. [[xsd-config-body-schemas-util-map]] -===== +===== Before... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - - - + + + + + + + + + + + ---- The above configuration uses a Spring `FactoryBean` implementation, the @@ -2951,16 +2719,16 @@ taken from the supplied `'sourceMap'`. After... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - + + + + + + + ---- You can also explicitly control the exact type of `Map` that will be instantiated and @@ -2968,15 +2736,15 @@ populated via the use of the `'map-class'` attribute on the `` elemen example, if we really need a `java.util.TreeMap` to be instantiated, we could use the following configuration: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - + + + + + + ---- If no `'map-class'` attribute is supplied, a `Map` implementation will be chosen by the @@ -2984,24 +2752,24 @@ container. [[xsd-config-body-schemas-util-set]] -===== +===== Before... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - pechorin@hero.org - raskolnikov@slums.org - stavrogin@gov.org - porfiry@gov.org - - - + + + + + pechorin@hero.org + raskolnikov@slums.org + stavrogin@gov.org + porfiry@gov.org + + + ---- The above configuration uses a Spring `FactoryBean` implementation, the @@ -3010,16 +2778,16 @@ from the supplied `'sourceSet'`. After... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - pechorin@hero.org - raskolnikov@slums.org - stavrogin@gov.org - porfiry@gov.org - + + + pechorin@hero.org + raskolnikov@slums.org + stavrogin@gov.org + porfiry@gov.org + ---- You can also explicitly control the exact type of `Set` that will be instantiated and @@ -3027,15 +2795,15 @@ populated via the use of the `'set-class'` attribute on the `` elemen example, if we really need a `java.util.TreeSet` to be instantiated, we could use the following configuration: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - pechorin@hero.org - raskolnikov@slums.org - stavrogin@gov.org - porfiry@gov.org - + + pechorin@hero.org + raskolnikov@slums.org + stavrogin@gov.org + porfiry@gov.org + ---- If no `'set-class'` attribute is supplied, a `Set` implementation will be chosen by the @@ -3053,240 +2821,240 @@ To use the tags in the `jee` schema, you need to have the following preamble at of your Spring XML configuration file; the text in the following snippet references the correct schema so that the tags in the `jee` namespace are available to you. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + ---- [[xsd-config-body-schemas-jee-jndi-lookup]] -===== (simple) +===== (simple) Before... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - + + + + + + + ---- After... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - - + + + + ---- [[xsd-config-body-schemas-jee-jndi-lookup-environment-single]] -===== (with single JNDI environment setting) +===== (with single JNDI environment setting) Before... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - bar - - - + + + + + bar + + + ---- After... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - foo=bar - + + foo=bar + ---- [[xsd-config-body-schemas-jee-jndi-lookup-evironment-multiple]] -===== (with multiple JNDI environment settings) +===== (with multiple JNDI environment settings) Before... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - bar - pong - - - + + + + + bar + pong + + + ---- After... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - foo=bar - ping=pong - - + + + + foo=bar + ping=pong + + ---- [[xsd-config-body-schemas-jee-jndi-lookup-complex]] -===== (complex) +===== (complex) Before... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - + + + + + + + + ---- After... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- [[xsd-config-body-schemas-jee-local-slsb]] -===== (simple) +===== (simple) The `` tag configures a reference to an EJB Stateless SessionBean. Before... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + ---- After... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- [[xsd-config-body-schemas-jee-local-slsb-complex]] -===== (complex) +===== (complex) -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - + + + + + + + ---- After... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- [[xsd-config-body-schemas-jee-remote-slsb]] -===== +===== The `` tag configures a reference to a `remote` EJB Stateless SessionBean. Before... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - + + + + + + + + + ---- After... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- @@ -3306,17 +3074,17 @@ the following preamble at the top of your Spring XML configuration file; the tex following snippet references the correct schema so that the tags in the `lang` namespace are available to you. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + ---- @@ -3334,17 +3102,17 @@ the following preamble at the top of your Spring XML configuration file; the tex following snippet references the correct schema so that the tags in the `jms` namespace are available to you. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + ---- @@ -3371,19 +3139,19 @@ the following preamble at the top of your Spring XML configuration file; the tex following snippet references the correct schema so that the tags in the `tx` namespace are available to you. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + ---- [NOTE] @@ -3408,17 +3176,17 @@ the following preamble at the top of your Spring XML configuration file; the tex following snippet references the correct schema so that the tags in the `aop` namespace are available to you. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + ---- @@ -3432,17 +3200,17 @@ a lot of grunt work in Spring, such as `BeanfactoryPostProcessors`. The followin snippet references the correct schema so that the tags in the `context` namespace are available to you. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + ---- [NOTE] @@ -3452,7 +3220,7 @@ The `context` schema was only introduced in Spring 2.5. [[xsd-config-body-schemas-context-pphc]] -===== +===== This element activates the replacement of `${...}` placeholders, resolved against the specified properties file (as a <>). This element is @@ -3462,7 +3230,7 @@ a convenience mechanism that sets up a< +===== Activates the Spring infrastructure for various annotations to be detected in bean classes: Spring's <> and @@ -3480,25 +3248,25 @@ This element does __not__ activate processing of Spring's [[xsd-config-body-schemas-context-component-scan]] -===== +===== This element is detailed in <>. [[xsd-config-body-schemas-context-ltw]] -===== +===== This element is detailed in <>. [[xsd-config-body-schemas-context-sc]] -===== +===== This element is detailed in <>. [[xsd-config-body-schemas-context-mbe]] -===== +===== This element is detailed in <>. @@ -3532,17 +3300,17 @@ To use the tags in the `jdbc` schema, you need to have the following preamble at of your Spring XML configuration file; the text in the following snippet references the correct schema so that the tags in the `jdbc` namespace are available to you. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + ---- @@ -3558,17 +3326,17 @@ To use the tags in the `cache` schema, you need to have the following preamble a top of your Spring XML configuration file; the text in the following snippet references the correct schema so that the tags in the `cache` namespace are available to you. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + ---- @@ -3592,17 +3360,21 @@ Find below an example of the `` tag in the context of a surrounding ` - + + - - ____ + + ____ + + + + ---- In the case of the above example, you would assume that there is some logic that will @@ -3647,12 +3419,12 @@ XML extension (a custom XML element) that allows us to configure objects of the `SimpleDateFormat` (from the `java.text` package) in an easy manner. When we are done, we will be able to define bean definitions of type `SimpleDateFormat` like this: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- __(Don't worry about the fact that this example is very simple; much more detailed @@ -3668,32 +3440,32 @@ Creating an XML configuration extension for use with Spring's IoC container star authoring an XML Schema to describe the extension. What follows is the schema we'll use to configure `SimpleDateFormat` objects. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - + + - + - - - - - - - - - - - + + + + + + + + + + + ---- (The emphasized line contains an extension base for all tags that will be identifiable @@ -3704,12 +3476,12 @@ container). We are able to use this attribute because we imported the Spring-pro The above schema will be used to configure `SimpleDateFormat` objects, directly in an XML application context file using the `` element. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- Note that after we've created the infrastructure classes, the above snippet of XML will @@ -3717,13 +3489,13 @@ essentially be exactly the same as the following XML snippet. In other words, we creating a bean in the container, identified by the name `'dateFormat'` of type `SimpleDateFormat`, with a couple of properties set. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + ---- [NOTE] @@ -3738,7 +3510,7 @@ defined in the enumeration. [[extensible-xml-namespacehandler]] -=== Coding a NamespaceHandler +=== Coding a NamespaceHandler In addition to the schema, we need a `NamespaceHandler` that will parse all elements of this specific namespace Spring encounters while parsing configuration files. The @@ -3767,19 +3539,20 @@ element results in a single `SimpleDateFormat` bean definition). Spring features number of convenience classes that support this scenario. In this example, we'll make use the `NamespaceHandlerSupport` class: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.springframework.samples.xml; + package org.springframework.samples.xml; -import org.springframework.beans.factory.xml.NamespaceHandlerSupport; + import org.springframework.beans.factory.xml.NamespaceHandlerSupport; -public class MyNamespaceHandler extends NamespaceHandlerSupport { + public class MyNamespaceHandler extends NamespaceHandlerSupport { - public void init() { - **registerBeanDefinitionParser("dateformat", new SimpleDateFormatBeanDefinitionParser());** - } -} + public void init() { + **registerBeanDefinitionParser("dateformat", new SimpleDateFormatBeanDefinitionParser());** + } + + } ---- The observant reader will notice that there isn't actually a whole lot of parsing logic @@ -3805,30 +3578,36 @@ responsible for parsing __one__ distinct top-level XML element defined in the sc the parser, we'll have access to the XML element (and thus its subelements too) so that we can parse our custom XML content, as can be seen in the following example: -[source,java] +[source,java,indent=0] ---- -package org.springframework.samples.xml; + package org.springframework.samples.xml; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; -import org.springframework.util.StringUtils; -import org.w3c.dom.Element; + import org.springframework.beans.factory.support.BeanDefinitionBuilder; + import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; + import org.springframework.util.StringUtils; + import org.w3c.dom.Element; -import java.text.SimpleDateFormat; + import java.text.SimpleDateFormat; -public class SimpleDateFormatBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { // <1> -protected Class getBeanClass(Element element) { return SimpleDateFormat.class; // <2> -} protected void doParse(Element element, BeanDefinitionBuilder bean) { // this will never be null since the schema explicitly requires that a value be supplied - String pattern = element.getAttribute("pattern"); - bean.addConstructorArg(pattern); + public class SimpleDateFormatBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { // <1> - // this however is an optional property - String lenient = element.getAttribute("lenient"); - if (StringUtils.hasText(lenient)) { - bean.addPropertyValue("lenient", Boolean.valueOf(lenient)); - } - } -} + protected Class getBeanClass(Element element) { + return SimpleDateFormat.class; // <2> + } + + protected void doParse(Element element, BeanDefinitionBuilder bean) { + // this will never be null since the schema explicitly requires that a value be supplied + String pattern = element.getAttribute("pattern"); + bean.addConstructorArg(pattern); + + // this however is an optional property + String lenient = element.getAttribute("lenient"); + if (StringUtils.hasText(lenient)) { + bean.addPropertyValue("lenient", Boolean.valueOf(lenient)); + } + } + + } ---- <1> We use the Spring-provided `AbstractSingleBeanDefinitionParser` to handle a lot of @@ -3857,12 +3636,12 @@ these special properties files, the formats of which are detailed below. [[extensible-xml-registration-spring-handlers]] -==== 'META-INF/spring.handlers' +==== 'META-INF/spring.handlers' The properties file called `'spring.handlers'` contains a mapping of XML Schema URIs to namespace handler classes. So for our example, we need to write the following: -[source] +[literal] [subs="verbatim,quotes"] ---- http\://www.mycompany.com/schema/myns=org.springframework.samples.xml.MyNamespaceHandler @@ -3878,7 +3657,7 @@ attribute as specified in your custom XSD schema. [[extensible-xml-registration-spring-schemas]] -==== 'META-INF/spring.schemas' +==== 'META-INF/spring.schemas' The properties file called `'spring.schemas'` contains a mapping of XML Schema locations (referred to along with the schema declaration in XML files that use the schema as part @@ -3888,7 +3667,7 @@ Internet access to retrieve the schema file. If you specify the mapping in this properties file, Spring will search for the schema on the classpath (in this case `'myns.xsd'` in the `'org.springframework.samples.xml'` package): -[source] +[literal] [subs="verbatim,quotes"] ---- http\://www.mycompany.com/schema/myns/myns.xsd=org/springframework/samples/xml/myns.xsd @@ -3907,28 +3686,28 @@ one of the 'custom' extensions that Spring provides straight out of the box. Fin an example of using the custom `` element developed in the previous steps in a Spring XML configuration file. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - + + - - - - - - + + + + + + - + ---- @@ -3945,26 +3724,26 @@ Find below some much meatier examples of custom XML extensions. This example illustrates how you might go about writing the various artifacts required to satisfy a target of the following configuration: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - - - - - - + + + + + + + - + ---- The above configuration actually nests custom extensions within each other. The class @@ -3973,80 +3752,82 @@ class (shown directly below). Notice how the `Component` class does __not__ expo setter method for the `'components'` property; this makes it hard (or rather impossible) to configure a bean definition for the `Component` class using setter injection. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.foo; + package com.foo; -import java.util.ArrayList; -import java.util.List; + import java.util.ArrayList; + import java.util.List; -public class Component { + public class Component { - private String name; - private List components = new ArrayList (); + private String name; + private List components = new ArrayList (); - // mmm, there is no setter method for the 'components' - public void addComponent(Component component) { - this.components.add(component); - } + // mmm, there is no setter method for the 'components' + public void addComponent(Component component) { + this.components.add(component); + } - public List getComponents() { - return components; - } + public List getComponents() { + return components; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public void setName(String name) { - this.name = name; - } -} + public void setName(String name) { + this.name = name; + } + + } ---- The typical solution to this issue is to create a custom `FactoryBean` that exposes a setter property for the `'components'` property. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.foo; + package com.foo; -import org.springframework.beans.factory.FactoryBean; + import org.springframework.beans.factory.FactoryBean; -import java.util.List; + import java.util.List; -public class ComponentFactoryBean implements FactoryBean { + public class ComponentFactoryBean implements FactoryBean { - private Component parent; - private List children; + private Component parent; + private List children; - public void setParent(Component parent) { - this.parent = parent; - } + public void setParent(Component parent) { + this.parent = parent; + } - public void setChildren(List children) { - this.children = children; - } + public void setChildren(List children) { + this.children = children; + } - public Component getObject() throws Exception { - if (this.children != null && this.children.size() > 0) { - for (Component child : children) { - this.parent.addComponent(child); - } - } - return this.parent; - } + public Component getObject() throws Exception { + if (this.children != null && this.children.size() > 0) { + for (Component child : children) { + this.parent.addComponent(child); + } + } + return this.parent; + } - public Class getObjectType() { - return Component.class; - } + public Class getObjectType() { + return Component.class; + } - public boolean isSingleton() { - return true; - } -} + public boolean isSingleton() { + return true; + } + + } ---- This is all very well, and does work nicely, but exposes a lot of Spring plumbing to the @@ -4055,112 +3836,112 @@ this Spring plumbing. If we stick to <>, we'll start off by creating the XSD schema to define the structure of our custom tag. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - - - - - - - - - + + + + + + + + + - + ---- We'll then create a custom `NamespaceHandler`. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.foo; + package com.foo; -import org.springframework.beans.factory.xml.NamespaceHandlerSupport; + import org.springframework.beans.factory.xml.NamespaceHandlerSupport; -public class ComponentNamespaceHandler extends NamespaceHandlerSupport { + public class ComponentNamespaceHandler extends NamespaceHandlerSupport { - public void init() { - registerBeanDefinitionParser("component", new ComponentBeanDefinitionParser()); - } -} + public void init() { + registerBeanDefinitionParser("component", new ComponentBeanDefinitionParser()); + } + + } ---- Next up is the custom `BeanDefinitionParser`. Remember that what we are creating is a `BeanDefinition` describing a `ComponentFactoryBean`. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.foo; + package com.foo; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.support.AbstractBeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.ManagedList; -import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; -import org.springframework.beans.factory.xml.ParserContext; -import org.springframework.util.xml.DomUtils; -import org.w3c.dom.Element; + import org.springframework.beans.factory.config.BeanDefinition; + import org.springframework.beans.factory.support.AbstractBeanDefinition; + import org.springframework.beans.factory.support.BeanDefinitionBuilder; + import org.springframework.beans.factory.support.ManagedList; + import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; + import org.springframework.beans.factory.xml.ParserContext; + import org.springframework.util.xml.DomUtils; + import org.w3c.dom.Element; -import java.util.List; + import java.util.List; -public class ComponentBeanDefinitionParser extends AbstractBeanDefinitionParser { + public class ComponentBeanDefinitionParser extends AbstractBeanDefinitionParser { - protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { - return parseComponentElement(element); - } + protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { + return parseComponentElement(element); + } - private static AbstractBeanDefinition parseComponentElement(Element element) { - BeanDefinitionBuilder factory = BeanDefinitionBuilder.rootBeanDefinition(ComponentFactoryBean.class); - factory.addPropertyValue("parent", parseComponent(element)); + private static AbstractBeanDefinition parseComponentElement(Element element) { + BeanDefinitionBuilder factory = BeanDefinitionBuilder.rootBeanDefinition(ComponentFactoryBean.class); + factory.addPropertyValue("parent", parseComponent(element)); - List childElements = DomUtils.getChildElementsByTagName(element, "component"); - if (childElements != null && childElements.size() > 0) { - parseChildComponents(childElements, factory); - } + List childElements = DomUtils.getChildElementsByTagName(element, "component"); + if (childElements != null && childElements.size() > 0) { + parseChildComponents(childElements, factory); + } - return factory.getBeanDefinition(); - } + return factory.getBeanDefinition(); + } - private static BeanDefinition parseComponent(Element element) { - BeanDefinitionBuilder component = BeanDefinitionBuilder.rootBeanDefinition(Component.class); - component.addPropertyValue("name", element.getAttribute("name")); - return component.getBeanDefinition(); - } + private static BeanDefinition parseComponent(Element element) { + BeanDefinitionBuilder component = BeanDefinitionBuilder.rootBeanDefinition(Component.class); + component.addPropertyValue("name", element.getAttribute("name")); + return component.getBeanDefinition(); + } - private static void parseChildComponents(List childElements, BeanDefinitionBuilder factory) { - ManagedList children = new ManagedList(childElements.size()); + private static void parseChildComponents(List childElements, BeanDefinitionBuilder factory) { + ManagedList children = new ManagedList(childElements.size()); + for (Element element : childElements) { + children.add(parseComponentElement(element)); + } + factory.addPropertyValue("children", children); + } - for (Element element : childElements) { - children.add(parseComponentElement(element)); - } - - factory.addPropertyValue("children", children); - } -} + } ---- Lastly, the various artifacts need to be registered with the Spring XML infrastructure. -[source] +[literal] [subs="verbatim,quotes"] ---- # in 'META-INF/spring.handlers' http\://www.foo.com/schema/component=com.foo.ComponentNamespaceHandler ---- -[source] +[literal] [subs="verbatim,quotes"] ---- # in 'META-INF/spring.schemas' @@ -4182,13 +3963,13 @@ definition for a service object that will (unknown to it) be accessing a cluster http://jcp.org/en/jsr/detail?id=107[JCache], and you want to ensure that the named JCache instance is eagerly started within the surrounding cluster: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- What we are going to do here is create another `BeanDefinition` when the @@ -4197,128 +3978,132 @@ the named JCache for us. We will also modify the existing `BeanDefinition` for t `'checkingAccountService'` so that it will have a dependency on this new JCache-initializing `BeanDefinition`. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.foo; + package com.foo; -public class JCacheInitializer { + public class JCacheInitializer { - private String name; + private String name; - public JCacheInitializer(String name) { - this.name = name; - } + public JCacheInitializer(String name) { + this.name = name; + } - public void initialize() { - // lots of JCache API calls to initialize the named cache... - } -} + public void initialize() { + // lots of JCache API calls to initialize the named cache... + } + + } ---- Now onto the custom extension. Firstly, the authoring of the XSD schema describing the custom attribute (quite easy in this case). -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - + - + ---- Next, the associated `NamespaceHandler`. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.foo; + package com.foo; -import org.springframework.beans.factory.xml.NamespaceHandlerSupport; + import org.springframework.beans.factory.xml.NamespaceHandlerSupport; -public class JCacheNamespaceHandler extends NamespaceHandlerSupport { + public class JCacheNamespaceHandler extends NamespaceHandlerSupport { - public void init() { - super.registerBeanDefinitionDecoratorForAttribute("cache-name", - new JCacheInitializingBeanDefinitionDecorator()); - } -} + public void init() { + super.registerBeanDefinitionDecoratorForAttribute("cache-name", + new JCacheInitializingBeanDefinitionDecorator()); + } + + } ---- Next, the parser. Note that in this case, because we are going to be parsing an XML attribute, we write a `BeanDefinitionDecorator` rather than a `BeanDefinitionParser`. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.foo; + package com.foo; -import org.springframework.beans.factory.config.BeanDefinitionHolder; -import org.springframework.beans.factory.support.AbstractBeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.xml.BeanDefinitionDecorator; -import org.springframework.beans.factory.xml.ParserContext; -import org.w3c.dom.Attr; -import org.w3c.dom.Node; + import org.springframework.beans.factory.config.BeanDefinitionHolder; + import org.springframework.beans.factory.support.AbstractBeanDefinition; + import org.springframework.beans.factory.support.BeanDefinitionBuilder; + import org.springframework.beans.factory.xml.BeanDefinitionDecorator; + import org.springframework.beans.factory.xml.ParserContext; + import org.w3c.dom.Attr; + import org.w3c.dom.Node; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; + import java.util.ArrayList; + import java.util.Arrays; + import java.util.List; -public class JCacheInitializingBeanDefinitionDecorator implements BeanDefinitionDecorator { + public class JCacheInitializingBeanDefinitionDecorator implements BeanDefinitionDecorator { - private static final String[] EMPTY_STRING_ARRAY = new String[0]; + private static final String[] EMPTY_STRING_ARRAY = new String[0]; - public BeanDefinitionHolder decorate( - Node source, BeanDefinitionHolder holder, ParserContext ctx) { - String initializerBeanName = registerJCacheInitializer(source, ctx); - createDependencyOnJCacheInitializer(holder, initializerBeanName); - return holder; - } + public BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder holder, + ParserContext ctx) { + String initializerBeanName = registerJCacheInitializer(source, ctx); + createDependencyOnJCacheInitializer(holder, initializerBeanName); + return holder; + } - private void createDependencyOnJCacheInitializer(BeanDefinitionHolder holder, String initializerBeanName) { - AbstractBeanDefinition definition = ((AbstractBeanDefinition) holder.getBeanDefinition()); - String[] dependsOn = definition.getDependsOn(); - if (dependsOn == null) { - dependsOn = new String[]{initializerBeanName}; - } else { - List dependencies = new ArrayList(Arrays.asList(dependsOn)); - dependencies.add(initializerBeanName); - dependsOn = (String[]) dependencies.toArray(EMPTY_STRING_ARRAY); - } - definition.setDependsOn(dependsOn); - } + private void createDependencyOnJCacheInitializer(BeanDefinitionHolder holder, + String initializerBeanName) { + AbstractBeanDefinition definition = ((AbstractBeanDefinition) holder.getBeanDefinition()); + String[] dependsOn = definition.getDependsOn(); + if (dependsOn == null) { + dependsOn = new String[]{initializerBeanName}; + } else { + List dependencies = new ArrayList(Arrays.asList(dependsOn)); + dependencies.add(initializerBeanName); + dependsOn = (String[]) dependencies.toArray(EMPTY_STRING_ARRAY); + } + definition.setDependsOn(dependsOn); + } - private String registerJCacheInitializer(Node source, ParserContext ctx) { - String cacheName = ((Attr) source).getValue(); - String beanName = cacheName + "-initializer"; - if (!ctx.getRegistry().containsBeanDefinition(beanName)) { - BeanDefinitionBuilder initializer = BeanDefinitionBuilder.rootBeanDefinition(JCacheInitializer.class); - initializer.addConstructorArg(cacheName); - ctx.getRegistry().registerBeanDefinition(beanName, initializer.getBeanDefinition()); - } - return beanName; - } -} + private String registerJCacheInitializer(Node source, ParserContext ctx) { + String cacheName = ((Attr) source).getValue(); + String beanName = cacheName + "-initializer"; + if (!ctx.getRegistry().containsBeanDefinition(beanName)) { + BeanDefinitionBuilder initializer = BeanDefinitionBuilder.rootBeanDefinition(JCacheInitializer.class); + initializer.addConstructorArg(cacheName); + ctx.getRegistry().registerBeanDefinition(beanName, initializer.getBeanDefinition()); + } + return beanName; + } + + } ---- Lastly, the various artifacts need to be registered with the Spring XML infrastructure. -[source] +[literal] [subs="verbatim,quotes"] ---- # in 'META-INF/spring.handlers' http\://www.foo.com/schema/jcache=com.foo.JCacheNamespaceHandler ---- -[source] +[literal] [subs="verbatim,quotes"] ---- # in 'META-INF/spring.schemas' @@ -4715,13 +4500,13 @@ escaping. Modeled after the JSTL c:url tag with backwards compatibility in mind. | true | true | The URL to build. This value can include template {placeholders} that are replaced - with the URL encoded value of the named parameter. Parameters must be defined using + with the URL encoded value of the named parameter. Parameters must be defined using the param tag inside the body of this tag. | context | false | true -| Specifies a remote application context path. The default is the current application +| Specifies a remote application context path. The default is the current application context path. | var @@ -4733,8 +4518,8 @@ escaping. Modeled after the JSTL c:url tag with backwards compatibility in mind. | scope | false | true -| The scope for the var. 'application', 'session', 'request' and 'page' scopes are - supported. Defaults to page scope. This attribute has no effect unless the var +| The scope for the var. 'application', 'session', 'request' and 'page' scopes are + supported. Defaults to page scope. This attribute has no effect unless the var attribute is also defined. | htmlEscape @@ -4778,8 +4563,8 @@ variable. | scope | false | true -| The scope for the var. 'application', 'session', 'request' and 'page' scopes are - supported. Defaults to page scope. This attribute has no effect unless the var +| The scope for the var. 'application', 'session', 'request' and 'page' scopes are + supported. Defaults to page scope. This attribute has no effect unless the var attribute is also defined. | htmlEscape @@ -5038,7 +4823,7 @@ Renders multiple HTML 'input' tags with type 'checkbox'. | false | true | Specifies the HTML element that is used to enclose each 'input' tag with type - 'checkbox'. Defaults to 'span'. + 'checkbox'. Defaults to 'span'. | htmlEscape | false @@ -6390,7 +6175,7 @@ Renders multiple HTML 'input' tags with type 'radio'. | false | true | Specifies the HTML element that is used to enclose each 'input' tag with type 'radio'. - Defaults to 'span'. + Defaults to 'span'. | htmlEscape | false diff --git a/src/asciidoc/images/spring-overview.png b/src/asciidoc/images/spring-overview.png index c971a29e9496cb338df5b7c456c5da1742b41387..952c9db6c86f7f9887ef7e07f3f03ec09ea15ff0 100644 GIT binary patch literal 58395 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 literal 66242 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/S5@_ift3qq{))9&#m4{)o9jv2^qtS%JZ#Ih)!xR@geQ?z^HBSv{H|8 zWt!DuDjMNn;0A&G2Wt6)#OAIei&>eeR7E+esC{&S$ra#V34yKRtj(=?loD-N6u9Ny zzQjghVZSoB1E(=K-eK$=90ZFbl7QI~>W#zD4XSW?D;Ba*cXXIC(>4^eVTGxo+C1h7G*LrrgJtiRaTDGOB{(Q$=bXs zs|b#b7%5*#&ef*^sc6d>LUJ%c5dC&EW*G)L2W^6vDuJM*(?cK8*m~ZDVIu<%t17o_qYRza*2O(1QZa+ zXjIwYiDjg33V*Atfg+K3W}7tNr~cB}`$M5+qkB}O)!Boev%#x}j9>GawnNxKf+;R!hu26R}8X~D|h7nGA+o%7W z)m%yc{d|vqVsL#OpxrQ`y5mbhi+$&Wbd;0gM2CZ1TK2W~Nn8>UwsHZLnf^q}8 z-wzdIT0yaVun;V@<}b>8ysPO-sX|%3R;e7}&m!k#qHdLS;KL)oAbYyjgp4nK&vYnr zDX~>XQ!Ym4bjv!_1Jmj7^v)8+yug3r_J>S|c;_&XD@$-YQqrY5tX`u75E9;F#q@i1 zKhH?s4(sdtD&loS5=V=|thV;u;vo>C{>WnlrMnwzL*Xxn2+;`bOcspv3kF?C8=Ll^ zt*KY}OXj)S^CZ_j8aQ!455okb1eB z%H-j)tYG)^-zB1KQeYS+6oa*1G>LkC&$eU(Y<-~-*gZ8OeW|el0f|ZVB`xNWYll3+ z?*S0>eyO#1p~_%y`{Q_S$X9EL#7NZf<6{n6hbdXL`ad~Ut6BKvJUqCsCGnon%m{nE zc5^&}J&E6zF6J_>)ZK|beDpA(cc!O8a>Svr8giLCE@fWAj9y9u5@_t90ZDQ$>Oecs zT4y6^*=!&-Cy_LJnGq$S+1B~HQZW9iTGt#gSC!H;VM6pj9K?A=I1Jn$ zPcgg?Q7kpy_mjs;E?~*>Mm_(8TdMXBI6At%9j0raF}vW5!p@JBj0D?ngrYwg0gAfP zRy89(2q0p0$|Av*S~t5Oop{S~8G-u(>xLo87u#*&AggsTV&n#*)O*0ke7`QRqiw?| z7t0>VzPwUmvrc?lmZSfaH}Ip%G@0xbFkMr1>1S60hZWc?yPs(l#aO6o7|?)Q>yJ$! z1X#ZQBz$-Az1}CU{VFLe+{-5&BCwq9nF05`dV{k}kr5L+r)0m| zHblpapoOswW2u0a9On$jdh1* z?>wWyagq`a-vYi6CiD$|Xo;56`gZ!nXwR!g?%YGEa~HL(Wb@vbQ*x^%5vPnF-SfA{s@ zX1MX9nCf%|YjXOcuienArnHW|F1jG-Zqkg^fv7m#EOIb-#qQzDC`ts#D-(L!`llb= z{Qz-Ev(516k3Boz9BSv74*&BT(X81iu2ouD2vTyOJ+kl@LqnZO?{}WytAv~)QY^zno>|;P{2%wD#|(O{?jJ#= zZwB~feNwH>zVEg$l>PaT2(>mdq-hNZe|MXH+yECJD2wSWUTL|l-{^k3!L@%zsd!;> zXnv&MH3MUKG!I~!=9K31B)_dGy7KcNo@&6zZ5x6PyUd_*yYP)2XFBh5ST*FV?J;byfP;HyEU9*@>IVo>@NT_Bc4oUb?^)@T) z5A6*AI#=ig;(QV`Oa%Wk!7nMeBP0Sw-Ozns4seb1^7&=`hWX!K;!*vP_a%Prrge^a zO*3>Vp6l(Z`jVKnX37T2N`h3gNatbj(ayg9I@YVC^~I@;i--j+9)$WXy7c{Txp6{z zCJK~+Bq}Dqs1g)VtwX3oWk`0G;^Ulb4iG-NYTNA){l-8QwX&Bm z+RQZ*lRzv~lq`@`Ez&~;V}9v+Cr<)*5{YegJ;BVhd-Anrxd-_?37rbNcN$2Bp-Y`v zoR!LVl7c?tfW+R%&Q5fcZ@u%Mj#=EO|4%i0ST0uS_y%8Ip5xvVrh zha=8u9lVR+13#<8xes@RKcMHBaTg!sMxW(t@Z`g7*^9?K4%p6bF!*Yt5&yj=>|3Ch zZkN|+FJ^y0#=AgwK=;l}DSU+(RFnb15#bR>?11Ck`*?&08HwMKFa>E|?WC^qO6OOg z?-&JU8F>ZB+y>qmsX$}LJqQtS!o3h$LuQFkfF@A9mU7Qc2kM=9)jM4#|jQ(&=#f+Kdf`!^Z+P*xrXQRz`y*FBZ3= zznZD?@#ajZqke^P&kC8hq5_U5E%Uxa=3bKlE_bhL9COmt1fZ39@tmS21g2sbS>m5~ zC*O+aGjhTb@3;;s6EY$2pQ1>B8|{)&JH7C`6*?OugStJK83v>olY-@{|bE=ENP|HqlxWZ%~g<8&XtSs(hKT-fTb0Dk(4*sL2*k?5Cq!6|hbq zRj4)OT_(VAsH1OoSFL+Quh6jx-H*e}qOF92PEm;Xfb>dh0v2W--UvHNX)}&WiJP)I z+OGwiH3g0XbNFL0(ZdIzz(c{}#&E4IB8e$TepLq*C?kDJ86HAxll{OrV+LE_F9xuL z;je(+Pf_Ez@q_~e^chR^kO$|Uq3;Zu9If2j^W~{rA=P(4>w;3{Y8fy*my0#9D7ia1 zVFWqj{raHrx)(rM*e)?lB*{Ar1%XjYQX_V*;J^n!-G&Y>_(y24-)jp&%%pR<#oB=e zSM065S;_J-2!mb+SvF=l~mW1)hb?NL{==2yo znWjQ?P{&cFWY6MOh~Dj>}Mf&*8mq z2#h!5+|~3MC@7ZTdfS~36&WzD8L&$U6x`$5*X^xryYmqP&LAnLkrpYb|M85fC5r>; z>_!Sql?i7iRW6+Bdr+#EfT{gfZW{3|&e+fq5mueqUQN3jkuBASahk1+LTK!N&jqq! z@2)pYR}YUMTz$s-*k!@$H`X9cCPy;}2>ZLjB0WC>u3netCN&E9cb0MRW%J+{Z{c4( zPAoGHD8uPjJ@T|ZLN9S3=Bl*B%0a@YkIYI93`k{SFi#|a#^7*$4D^*Le!P3dR1ImY z%LNO)!^PE=yCXIwg81URL1vTt+i^U&!M7pSlDGVzHqJ))bHb3|5ly2#OCb&Btmc*0 zMF7tW%=uQ!{TWa^i)mD6Xbc;ByA0OI%vsZ3b{x^XANQb7!W$rKyJx=FE8n*V2t4x! z$Z+9+pdjFZ;ogr%c z^)tYSsu`p52w~Fl!78r~a_^NcVJjWE*Boh2W2cGZ;v4bt;!s6#1T^P9WV@$zMB)~D zu|uT!PnV}T+Vr!=#8WvZu`3mkvn@h|u-+O_U0wv1zzvedT?RS#o`1BxIf@##;mOFz zl**ML4#!i(R_l%Bi={9;e9Z&{yTHmDN3BQW8DkmzEF+L-I!^5Yc-o&-#1CWt63i4d z@Yrm2q-Lmf9gvX@I0`;sh$mC&BH;0Md`HE1j6D1Eb2M`6Me=}&3n}3X03ALE^W_E* z3s;_796HOzjZ7RP5UK=%QZCWMpNac!);X1HQV0$Zv;1-L#Wq>84ej@Q-oKfiWUycJ ztX$O#jYp^!4)Ym~z@Ou1kjIUH2Fw}kjhr}XKo?h2qc2xwnjj1%I}Gv@Xi5hsyZEmV z<43gQ(aEY;AP{ADltzg0;?nu%qiNGY|LYtpGkDt2_f9P*6euoVR?&dp+{z14R4o2R z_2ikWUHv(8x)hMMu5PweDGy(18WIMjZa4ykGA@m!Ei5|XNB(w`MUa~Z)o?LI@IDoy zK;9N3CDAz)N!(ELhLthMd*4`rdQ$l3#?Dmsf!i6?l|taC%2<@wCTy z=H1F6JrTt{9U3y`T0=*gkp8izBRxM1{j<(~>;a=+_gg&PtY>2mS37;tWElJQ4-{Y- z9-!Uh3&DoR19mvEW_dn_PvbS5rQ&IbOJXwSX{09H$1mkzLtQ0Fp&A#Vh01)A#(2zA zX@_27v160+vjX>X(ivn21^5GZ5IQsaB+5y{vUKa&pv%Vb{}VBKApyU<-&*$5LJX|s zT$-mS>FvgWOEG)IB*E@Es%Wcw5@YvcM)1n$x1{L3KrUAf_@PSf+${qxAgPqi6EypB z_1J(|9}wVuE!>fok9-#)`V^$l$i2w{4%w^`CslYWHq9j`zZiX!?z zAc@M36O&1%*3yCRS3=`MxF#hq^=FI0%?huST+zrpUV?I334l8vZT7^i(HITBX8rHF zcQFEPcs(Tngho)7OROuI4hUai;0)~mKqlO6%MvaSYCEGr}nTuj7e!>eoPZOJrV-xsIQm6`wBa>CLkwB_~78= z7v&46X!xP{ys+m|5y&SrWly6K?msEmkFe6eCg|5PniolKlDWy{0v~;4bt}tR;uep& z>dIH4JZJcKHCXQN<27?-Ckzg_?kC1LUb0>*GmMG6NuBH05FozfKHnJ0LU%{|%M(cS z2G+IL@pc@?tR^qq6rkqQn7whuL(@L-3X z&mB{=4@Crtk0=Wqm-)A@WQ8S26h*CHK)rtS!G}dgBKe~~(0PbM#HH%_WizSl$95S7 zJaj@hTnchZM-%1H#K(WxzQNP1<1@fgV5lL?FS++07NZdpW#B)2xqCE=<_H+2bDc=o z<93k(;t>+v9uJ(l9k{@3g24=Zz+~iU7JxcAIY&N93Z!v}=O(}s_VwA_--%>y*P;2+ zy~F%a4p5OY4+W1ff(z>{E8OOo*;^gaK0qEl_Va{FY%^lYn`2(Ij%l$gE9@PxzLLj; zZTlxVzI!_ni|F0@IG7rrsgk4lX_LR@39^?N7Y9;K+`sWTqf35k$mU>Y``Luf;}*qb zo0$%I#p(z>@B@}BtJtm4SpT{YIgROyV7WOa=Wait?$Mjil_@s-#oRatS2^*5H;f^K z$t;M2q_TiD?JYuVjrtA9baxw%qac4=!t3Od$cFvh9^FFGiQqPkV%CH#9W7*!jC{OX zL2&c+Xeni79-S=|s=qkpO)jGOb)U-(m|jRxu5U%{1satDzO3l9q?VGMTIY=95jB1h zTjDk8p5pa>hf>^J7t(+7#a?gAB9VmnT|40^J=hS0Y96sN+$uvOmC6lB7Lz>)pE32~ z{7vhG&Rx6WAAj4@xdQV|1cNDNvD6gm|@3!9I zZ5$d*=U`$6h)uCr>Kj%lMWF2^aIMro$3xP62PVrWNj_WbXhxcOubfp+&bWp$R7H_ zL^5tD?=^YWbx?AkVbIxXkwVVzrqzYXp}8kCEm8_6;gk-FBhq?YHe!Z9O>cipBuP{_ z!elPNma;^myl^1S=*?DA6*}rnFD2$+0VdCvryG+3yby4wb%9fBX6}51=#AE#WWw&y zG?dCzV5VZd?cF9lxSgD*kJp$~Q_|BT4!uDQmO8>S;f+I%BNZ%`7<)d4Q)+E@6<#)4 zA4QzKZG$-|D0eU_Y)pun;Xjj~NC6cH18z%wGF!0ANb2uP|bHZz$5}%*ze5!$B zw0XntAvQ|LfpQ(-l+PrO$6XmzY})^bT5P>v3}&v*VdSBhI4>MR=216}&6KU5Vam}} zg?cni3`zkxr)9*wWa0~~ro;4VxcSU0i(AUc;!%z=Vp2uiTJ{p$94z4ky@DC6m9u~> zi!)UJRah)fZL?tcO8>O<=VIq`a2y6>45ZayEkGyIu@kCM(T0muofv3Vca+bR@z^OW z-RV*koHTZlmd_c?rV(IThdnW}mMx}5ZOo-uw`sFx!sj=B)k0E2OQeGOV6eJzu!T@+DXA1W2C5N_r} zI$Vl5jhn?LBfY00$lY{B&?7GyLJCT*NPuuT-WEf$Rt zrXG-Btmcg*p|UvTG&3SJ0Sc6t)div$tvB_dGXt*1W)I4nH{n-iA&a&ac+NLlw&ijo z=bfd5zqZ5$;heDy{pu~GAd;&$8U?g2VOQlH%i}}L&+qw4C8zaI$PdUqg=AfMT1Ko( zGObn+{E41QSaY*;t>uCrP|9bJm?l=8z_>yWo6~YI)*8{HP<&r^mR<6Znj*4krrLD+ z)YNB9^KB6?H^7I92pTVRCG%*$Qe*r;IlrD@V+&+dOv}00nHwy4PXctKyZ~w}ZGn z>S62niX(6NX?TE=Qwiy8LThH1=tUxSmb2;M>CB6z<>r|N8owvdA6ldD#gCY)iC`%| zI<rUs1GZ0e1}S%+$jI~jSNDbuRsU*AcZUvjn1 z*t_l=j(|a~S9PqzxM{&Gl`zNou`(o4^3zrjC>jGDX$39+l|f~ooKna!Ujzpy0_I;B z1e`d#M9T?8Sx8j2UW=f^iv%+TvM2&!4XaW-q53UPQ@V@1=8?f#O=o4NVaAm@jkAB$ z;02nf`p+T=SqJzMGFCN)QPYT0RlSM+?R!J(i2za>faqJ9a4AX=DguW>P*Z(;W-S^pE*Z~Q&>AC|?-^B&$>4L`8- zSS$y1dnClyJ*S3E;_-1p8yYeh)Gzgl`fma|4e&q12WUnlk& z`t!cX6c!#MIKGtC{hW96^`8%qKcJ9bLcUdQ7QH)2=f!(}eAq0_wB@Qe%JxA!oK3#x zH{NWAGTB60%V`Cme3HuFrt29n-|ElDistuDR#ZH+Nae_4JwopiWu+INs_k>`JD-2e zg2ev%4xRwmADvezK&>i$b3dYIrZ)#~ulAlP%mcAds!_gxO*DFkuQPsz>%8uL-H>`NkWHsV`3XG@8(Sa{jH}8%_Cb8RPK{+sT=(e=~zoYxAu5 z#nxk~1nG~yZP|K_22!dlbU!7X`@2Y?z{~#p&Cx3e(?Zjg-s5zA4p_L} z;lzN=DwY%*??)@;wbVFsv#1gI<*^~$NL8GHvpXhLrIzr@P~d|tmgtj z5g+1EZk6Y2t^Pd@!?)_AYZhsAy;p{LmGr{4L;H+;Tq>zZ? zKD_%)xcJU1({=^;JS zEDiVZv`)ZyU+z6gd(&Jml-ohkrUsN<-Gf8OAE;5?iQrZ5Yf=UN?MZphik zUz;h?<2-M>!srbYN8+dkeH9aTZ&DB){-OCwvjJB%$Y+^#1Aub7-&lms_8}&dI5#sX zDGcaKr<=zg3lqtZ*AfnItE7u{CG%7`_&2;n;8r+7f{5Y*pqX|(0%V;o9*(?@m`BZ_2Tt1e%wIzGYj9+cLharXh1(M?Tmy&9(8IP_R}GT;`3vu>3k8&C zJcofU(t4}jQs}vpEO~mJ1a{&O5tR;XX!0sjV^vdAMBQf+<%idF2o8htf&-_Y-zuHG z51M|S(cK!}3*mQXz7R_{Sq8GRzYR9(AobFM0Aj*g=>egYiM?^rrOs|=hw6boH4!Kz zMV;!WGK@=rrlND>teeKL@^ZnNG#;&vq#sv)`o-;P=dU_JbCJ10$P+i5D9n%Z7IM)_ zsGFI+1d_m4DJ+{2LSLl`R>BKXVrfLkt*=`}7e{P<(-yvY;dsIFffplWbR&)J(k7l0 zr~rVJC`jyL+YRx;G9XSUa;P9^@03`Oo0M+MmvJz1EX8{;GgPo?p>|9i&$dIzN*5!N zW1fjT+W-95zU%G6F>e3xzj8=^TzL9gD>{jshXKc$cfFy8tNr6#MJ~q(9>LseByPS@ z;fdL{GG5}YlER=7&~qt@v0vf;u9yLmW-1ofKG+Xs4H;GAK&l`u8R?DpS4 zY2B$*o#>)fs}YiHY&|vyzZiNC3A5?H?)L~-p(yzBa+11I$y_JPq9ad&cQPVH%rgeT zWu`X4epg%IP67Z_Q;Y#%XAk~otZ)Yf;9P*$+kptTL;2&Zfv2Sy2yV;uP?q&Q^2&vkJ0;liBE_>xKyW+ zKypN3Dd^(8i`a8RQfs})vioaqZ?1{z$JD*48qydoj%74k{5|B`^Jrp$S)-VFeu3LI zhskm3o%j0AYx^|wxWn=aSSf18DIJ7+8FU|>s}(g5a}iojpn-zxIq3MpIDk1F);zNQ zf_m>$vzO%=;rXj`r{jv9Y{@#=uh1c+l8P(>j;6VQi*I(e@|+!U8=t9iRzPlvW$o zBLf$64kzc+aQA}PzUys6spM_L`7+sr%JH8v>wGnajT2%A5D~wq`~bq=r|J@{)*^DHNSx`V`PT7A{|H5CQ%GSbt)}m zFhs5*gZAS{^A!zgbXez0G_Ksitqo`~S-?#Ko)yuIkbnDMzdv8MI`1R?=rw}D2-x7i z_TuIyt@e^hO4Ta>sHv-FSAF2Au^iX{9fbYB4}$#!`c*JMhQ*Eepyxm?Ez1 zDPTm1c9py)+pK$~$gvF+Up-kNg>)@lzTo)a*Ig{>(^-xVIMAYWCzCsOZJn7}YfBM&!l*VZ%gG$q{BG?^OAZ%{5=WC$WmrcN1axx!tpE+(8WZq{*>h$l` z?&jPwzekbXDdrd<9|m0O*x@f#exZy~*q`v3un*Co10jurlK;3w(lSJ<+BrBd;%;@t zQXK=;9>m&{m*_$$medG;f~pKk%Fl&>kHq`=5ILRpLgyO#S))Pqvg79hM*ZzwdRAi^ zV1JJS%V>iz;pql{oW0x~)$oIbh$79sPLE7w>BH7tJ-h2@@S~Ar+QdJrLnTXg%0sEX zSC4IKo9;afM4|86W@Jqcn-rAHIb4Lu_&_yC_7UT8a_}G}uH@8d8rI60flRfu%Pu9E`k7_}qvq`0sOAs}9eyUWydxIp`aQY3Y zSSO0wDlq#3r?V>c>MkE2bKE5s+uXbkSvShjPgNI^W(+2sCacmzjI$@DjB2XubKqpk%MDp0N$^2xr+4U=z__PQ%VYx0xQIztftyupLX4?lz+J=SM5y4O`@!~=VrLiZgeg)GSpanV`5Ht&`}`OBGDE=q2mpJlF@;lOQ=$u-o6Y_fSZsFR z_nGrV=2GCn7Z7>@tbdH}BEbJG;NZK!&tyGX4mI~iqyo{w>8Si~+!BNRfzcgi` z39UN6=yrq80|ysZTQI$i(&m*m7Y16~&2S%{cERqb>^V$fY@h0)k-i1sgdtd=7j0dy z*9X9=baRMio;b%xIhUVv1Kyud%rA;CoDvGM!!M}N4B82k06{Qi72#(OlApWA3hkim z2AHfupyu+iLD^lDy4>*nMax>BPc5#TNv74tWb=yA>zyb|Jz2oDw`WifkHbU22mEOl z9O92W@vUO(z!UBnXKqOlo%WGv?s`x5&EqZ_(YSOoexE8$2Ke-3h5lyZ(8R%z<7{S` z*;XsB%cWXU26-^jzY{z${TGG^;-c?+GoMwvbK*_W_y%~R<4M@g&iLPO6!F&UL86%- z_YH#2l>oehLc&^~K`poMbAPrPU2zgw!GDtjFec^~jT5fw_CWzH;M|xiI=BSQ4WdlechSN?#w2$&+$+G2>XDyh>COX{2CG zm)A4mE+ok~c`@=&D0zXdsL82f9|kZkk2^qM`QOUrL;{_I=@D|uJLOU@Na9*lifXPf ztu1h+Y9#>O{DM2wGW6f)s+GSk86WVI#F9d`Is?;9iW$cvDkQbNdD4nxAxz<0z1fhP zCMFrVBVI8uLhQc7#o;M6+k>HgKw`ED;W1BA?|@=u2w+T{XdOD z3N$*~#NXdwp09()sNp11$!b4yI)4nAbj|Jl$QLpzOGh9A5bDjZBW$(1z=LBTf`dD zxdz6POENXfRMD<_4@rlfVUDt*LZ13ErnFS)Ilo^BZ(=gPZptXSdN%^7Olpt^s+FmS z1Bis5Ww;i*549-u2aHBr8%TPyk-?|0b1!>jv7G~M0H?G0P%UvzPUq26tkVxhEJ&da z;9TccR>#H=ai}>Z*3xD{?bnhuo?5j8`*Hy}(rt}Ia9PaF&-M$93AFc5sDHr`a4pWq zj<>T2fiZEj&t_dgTLc-^|3bfH553o$N_>4S2-i!vh-F_Z#``(~(yQOYmPsA)wPn(40noGOZA7$S{C&C3IZc98)#* zY@6Eh_QG+h!qY}z+^@mDP!Uao<~7@>-JT=)$6&udgU#-A9_cZIW$+pd#Mas&&wwiz zFMe-N&uRg&OZ7{H>3qmxw1wRi@%E0z!xb;^3Fu94>dJaheUaL^pxr)M&FBy#^!v*| z6!w_9xc_*_O9^d(9|_w>4#OKW$|ywsS2MPoAL%|;uHZ3*+HNa!^_JiJ!7jLwKH%{S zpVXC3G+)X~2PSe1!UTDQhg0Zi0u?&oC_9HGb8)a;v`eU6i;fU+7i`d{9-ICh>5Zok zU=>JbLQd_tK66l0CM}-3GZ`g&*nvpV8tgI#DZ85^-`mmw7moIFrV5^; zSgc=o5eerrmLDn(mq0>QOHp+m!TLm`z?r$ZxwziYNM|m-R;L}TSc|RDbr}+a*RIxI zOH)1*Bq9OfabPtmZnJ+G!y( zsoNipn-}~9vrc@1yqvYH4DS~ zcBjq1l-5yn${M?>7kZ=(wV2*^8I=onJ5Yz?P#Y{JDnHYgjuDE5c?V3!)F?L~Vlh~D zi!5j5gn+&PT#n%)fKSCqM0XdRI6;JJcI#x=f(c^|NP9(bee)c2tK_k#focN2J}b}! zX3HR_G?;>jiIYkXke5wYpOPtpFlQsQ#%S$#&TT4&QYO~@I`e~B&!VnSDz+EUwU5<; z>I>NbBc6tPI1#aK$&f~HTQnXK`#}cNp~?(!Yj00XLMDiqK#BAz1sp(nJl%Ag49JQR z{nl@u0Wv@UQ<-oB0;gRF?tN-f2(){Zk%j-$0+cI*pt2IBT|005b%$zEk4*f{<)}nQ zM5L<{B4j^#g2!iG}AQ z+Pva}c&~o^Wv+BKF~a~GFn0UPs7i=GGGu7s?S`lf@UiY>86@((j#zec=)BR5yJJYy zVO6c5%0BvMC>Dlo!~9>I2Y^~+vLQ{;IAfUUeGVrUFF{2aV;J@)Q9lsiXD|mcKM=>A zP_-<)InoN4Lr3qt=mI7T{s=d;;#_N&l-Z|J^VZU)FgVA5KPV6|4fcx6-VE#c0mPE# zEWGo15BT^L&z@qY!ZHL_tCjAY({O4dpHZ_Q47U{!-Th6x|M-ONBFwac=kOw+iL$^P zYHH_if3h_Akh00LMq_pC8lv_csrNVkUSPtF#!*0?Hj_qNfb3`VlM0lbWIm_kRFYu$ zuYahC9~F3nJRW2bd(LXFo?oxkj)wDoT`gaxANNG1o-^d{%@7>+vSFe&Nphc7NBvM@p8`ErD3-ehwaozY-Y3&QL}j%jvbBg$2gJpjL}} z>tF4j*NoAUnGRrJYfvh}CC$#JP1FgA_QZvnUQN-vU16j@?{5+Atph1%w3U|(O@46@ z#i%=>#sUP)w8C9|6w{K_=+>Bar68#6|@BtkN`CSb-{86Vx2YV0` zT;B~Bz6s$``qOAAXUgR;4xJdw3j~i}-QKLb+?ad+?!tXNrf!Tl{YAZx-7QmN-oMSb zO~g64xDjNfequLOgEzL+#P3vvmOGMU*r% zGCPg{R2HtN&28YaQ#DWcUq7f*&^Qr3)eWx;Xd>Pq-v8|g9>R!O8rgt=+>$WY40wU6 zspm?TKn($bgx*fcd91fqN))e9CQ!hN^E3CsV14po;5qFyg&yxO=Jsmb)dCEI+n2>d zPS-Cy-eJ=;lQ!zCk))n;(`ozD9(`aOF8^l)gqN2qpw>zHQDa$tMNKG_3KfCRB3~PS7N({z_>ol@!=om)dC( z(OX#3f>O6-I`d~m-~~Oko(!pSv-93RI0uk9{9aPfUoL6kxkx25({)T==}Z_+7D^(@ z{SuJ3eso@ft10*~Rj4?(M>sOt&^=#8<8{N<7@5nJ0e_{(FNj9ziJ$IGtSwU;II(u)wrwq0`NLKELA*HmQ$)wULk3si-_=>y0 zJiVWzB?8Z%4v(*|sEEd+o~OP8P&Pjbm#?ww#hel=x=~$H-n%)E*7bu?ANUEIH=Mwl zbLSaI$85eO#fou~bWygF#muAP34JDy>nW!l1jkwfix*2t-Q)c1Bp<+afyMv4)qKTg z1*#yUrFU*WG^u_uYzW!-mdXA7%o(-oAPEVex?Lc+YC|dpoSYM&I=ECd_$-xv1>wr3 z+6W$nKN#j}-;Ye7q|WQbEOWK4{g*-_ZFuX6*+Z2U5)&ww{$k~z5&Z8^8nH5Y?~t9Z zGX9C{RlKfuxI=PHY@i>ASh~pC4ErPOtNt%~+c~^PE0z0gV4`9ZN1h4(6PdPfoYedG z(;LXqwaQ>n9dAUsx;R^45Da{yH&3R8g82(DswR6Pya)3148viuJ>R z3d(@#_b2M!q*`MYNyYqxpKH}hv*2{N_m-pLz_Z4%z_=iM{Lz?09OB4Q@Rx2`zA@Nb z#V+|X??Y_ZupH6T<_l)oj! zqWtWu+EV#WRjsmFR3}7b`C)5o9Q&gkdkEdOiRhkL?a-M#!|#m&AI{8--TaD* zP}gfBUrrihN&G~N_aqog@{F~#ht5`OG9P0@y@dq{)4P($0HPi4N<2h9k=-v8ABOn= zNe)K|w3ds32?oX7a>~c6wKSyM3N46MXh+e7Nbn$??zjpo`+xUgJ}H)FlgL6Sxz=B| z--JQ54Mgw`T<9&;z`fN%*B8Q&3*8bZL_uORtu-Bjojzp9O%@Hxq)98Atvi9VE>%gX zeqKs=9F2HWmk){NCT!kcLdQ#&#_3=rQ>a-u%!sDV4JX8;(A_ zip$6(gBMBu2K<4GsT;1yqS0wUG&HXhZ?)$8VV`2MlBmJejs!Q;S3Z-2qq*Xo6%9N2r1>}+GS8LYb6Vlf7 ziOxkNWF*D}0y^0K3kymQw#KodFzW_AxuXt&b7kYdE_m&r85rsu#v4jaoj6WNvx7VR zq%A}9tH)%M_y~9b+Bnzvl0ZeKviOmh3_HBg)7y7s#feEt1T`bi?#SRJP@3GmB|E^~ z@sq6V;Ry@KGc+<;-szNPYQl>_oo{chKCKE9m)~zc!6wWu!p9EJ^|k_h48!JRs0fhJ;LxGGW+VSbufX>6Tb2$B}veO$t5NTrWtE!@zF4ZU1{n z0f0#Qk1`=g0MM|eOButZH-l{Bis>su%3gV2<_+R8{Bcini)3DME|ii9?xaCiDuu32 zDwfW`eK~9m-57#59!$gPZggH&URg^V1Y)hQIA0@~$+L-*D!tnB4U0?g4Rj~coX~6~ zQAF4N4-bX)#Qq?Sx42O_-t_dfll5(NyAODA++b0DO-(c()o#7hJ^RwdR-hs1%Rt^u zt^(1YiXC#@>X|WRH4H~2AYM`Lkncq-cAqQ6sdt^B)LW>Tk{m~Ul<+%}fAc`505n2I zDLc#WlR%uAnF(00=W#n{L%#N4|4WJC1HKaQ16AD%MeqI3Wd7algE2ou z&0w{}Z-Rf=^FK6&&-XLOF*ue8q5u8hXJlM}%*CZE$`lmB{{Sky`1=bsnoST;5fdx< z;S$|KjSdtICVGCZZF5fl&wGsIksB-6g0utcgY)Vg4$__wcuQb+Uz+5I>7!Yw(sJ9q zbypT?Yp~y_(vm?weanqr#I^tHT^9d#5@iKWGo1zYef!2^Sq|LKQj4g&e= zsv7<5D0DNBT3sCbi5eq}K-Jma?vnMPD(hQ$KS9T%;Cx`0zn|vjs{rqyA)_$JLBi+v zrm4gi5dP!Otxt{*NQprs9MhjwyRrx&WF2IlqZ1(#s{1AUgAm=_VchHxp z@|KjwN7#m;6?hwVhQT8hyHH*o58{RNPSRim14~(!s`)}PLSYT-3d-<9jz9k1eD`J5 zw}sz;3Ca?lk4N&YCl{b$!EB2U*P}Y>m9>0TefK7 zj%~AJ+qTuQoenyAW83biW4mM9wr#Ux8~5#f?%C)0e%$YGJ*#R})v7gX))-^XTE3;E zzO{QJUbpLX}O)&YEjL*do`ykMY zLHoEV_Df8+HGwQPXejrsGdcQJ!_YFh(PF(C?2u6(O2@xgkT6KFFV{C|_n20ovTlv! zsa;y6d5Z%6bs+eBjoJzhE@k==c7( zJa2ZHz{<`SBqmpD#3MLVCWEB_)k3AxswHEaXE#)Bz@)=%5b5ffQ`{)FNvhqaW(57! zw9IK&wg>_Tyb7#BaI%Au#OV>I{Esq$5{mE@pHmzGRFJa7a@QN5O~fwvS#Q)Mnh|3w zrcY?LRy0qaOW`Xd6#fvKi>`qy`kYC}4h|8#u}@|8II~SGhSeTXGCzp3YK3+nL}>R~ z1^M7mYksn`iDotX%q0JNZw!iTCtTZR*$%A?nH{N(mz{X(lF!whz)19YFR}`mc#~h- z_9{reo_F40{x?@7#q-_bmO%Bu_QvIU9eR0s50aGSkN zt(rg58z!xM4Y&2QJvQCC0!hk`{hf6uo2&!A=L-nl6Y z)$kYm^9ttM`>k-I>5IYy2#fF3PI;Bg3$rBL>

S!APNgu-mz4uM!e1`ED~&?YM;d=KAcIYqMR39kj0jdpY6Tqr=zM z*<}QQxBg~xWaVEq@C0(Kq@iKAS$O$HlPRAHyXrdb{zqZDn0K<_`MyH$Vuo8W%$&;7 zxbbOH0a*}CAK16cem6k`vi>gQ7qJ~#WonDkYwFXaMWR3{DT*dWXHm~}1aHVsxS{LI z=i4iz3c(Ej{;02M93Nzx)@?C`)_1HY5~9Wy^?MTK1V>cGean(K5=t)5rB={ZS~>_P z0Y!B-FNUW-`o@`}8ZfyL&jLOe9GRM7aKu*Z-*9|!RT3aNzGVyK;rW}p^j$s)fE&PdKXyH*n&+wK zI3&6SN+}X99*p4r^s$Rx{Jr92>-{dAROc}Jb*Ki$?d#WI&58&++^w&ouN;1a9UmXV zbQ;-gLHZqjCdn7wIRjMXS{+cX*%CXztQXBGS8-r{0@4K}_;h4<&VX5_be??(Rasdv zEWO$t^MC}^6QlesoO0}AB6uS`_g8h3;qDM@m$MqFv`)BK;P$4~t^3%eWrxEIqY}z( zjv}KkmScgk+`Rp5zEl-Q5>ZxxjzQZ4J#*G2)LNF7t*KVT$_g%J30+52>IwNyZ$D5m zH|m9M+F-e~07lJzkDU7!Oue8hEuwfrYQN8S2$pe(PCGikZMhrJgkoW}ivB<_o=clm znf4@p$aXA~A%s6k{|9X7+FQ}L220Z%!;yxmSRahN4E8`dNChM~*^&lTlvbJLL{Uxr zJIDZ$w&LRqbbBw-KBmOfU13ksdwfT)X)}(j?rx)NTKJ?!q3Q5lD3KKqOR+*3O`vO9|yw`l1hX`tu#{ z*~#NTYzZrK|8rdTvW)y;5+Qhd(?svnJqX*YoDgYI42xZgKpfUpRv4_ac3#n~AqU(z z_#DI4X5ZSCyu?VkX{h65m)t;2axdH_?Yl}xDGok_#)pMq9-apxdoPvkOt+=cSnRPY z+HpW-SGhQsr-@`FQ>}D}z3lZaoGyYGJa)H43964lRD?E`t}Be&7(XPeyotBrn68<$!Di zE;x0)*;2!ilP-SeXs{`r?`mB7s+lrvikTGuJcEQs<6;_)auuW-V*zHxO zwN|qtJI?P09}~zJJDeFm7AJK6VAI^x7?X&8YFUsj?KTUm>&D6&}1DkvcjFw;PN)Cayu+E z{rN4B5zt@%47ZH~t;to{eX{wxoyZ%L${!UlIO=+h-sSxo0z4{kd=TNlz`%sX#7N4) zTU|u{|MT5b3^OQ!=|*b-Y8EytWV|Rhl%V3jy8rk^rd-w_rhlMx9*!zS{zg!{TDpIB zpR|`z;e7Ji)8V?o=Z=JSA!onlzY)eHzIl)tOxIA_<}pxR80)F_qJX1X>n$K3AZ3<^ zzy%Q5xMUhi+h<>epJpp9nf-}~(%V&*mz4l0<63bmhqkr?C}ZqSnJ~U@NQ>a5S58pl*?Z z!-UQIx1%=xrq|Q>r+z)WVR5o({BF$1dp%N^RE*gB^eUstAm?v;1w}xAJaq zKzV75ax{@{H(#vWp?NGKgA8I1YSs{iLWoIoA$|n(?GZwNTEC38h*tGMzY~CQnN@Od zuEp=B0(iWbfE%GhJNfLQ)V zJ!V498(}FGZAHZL_f?&#gc8b}q~(Bg?EgiAC4j(XOK#FP3ETs`-ojTZvV;XcGh?oG zqzv@$XUo1MmX>6@CdLzaEwMV5=yRmtM%TU0!m2dJFI8Q{pbbK6pTkE@;w`{l0ihpW z5L^}2aZAlY5uI|aKhYC)$wm_aE!Z^EQy?~Q%l81+GkrEfM!qA%<=z3V{mZB=i4&vc zk2X0Pn5M~!V7;lN>iy_A1-mt0khtCf$3CzolQ$8paz1-pUXEIVbO@Hfn#oUlo}Q_g zZzeDMNmNSN=ZCso%f@lq_MtBM^c4JZBma zpjKo-Zu4r}TND|;{%a?-&lQeYcroSCPEP|5!7_u)FYi!8?P=-bEU%c{RS=6KF2!ob z*x;HHLlxzz%*w0vXAnk02BAdGBBezmLyz@sNkf*8t1IZCxz{npom8w*9#||HG#LoS z`_R`8y=LQ14Mlx_PTF`YCzGG21>tjqWh*k>h<5nQ;1ZT7c2*Wi?vK}!lskMUWFnlK zJ-BOWf)#;E9)JD>{bvP4kb+oJ(?mDW%u0{%W1NqUL)4_^i!9-$FQeXMJtW$X#c+ll zmUl#7BIVn8fHUrTUSkT>&HdcSAlUYPU{_`|2#RM)#BRhf-5rAV$rsxTU+$cM3Qu1C z5IQF?Tzf;y+w}*^1u@AZFD{<0uHcN@Iblsg>*Sf@?YttVi7Y#@zg>#$>}c=YUt>s} zdZYVyaYOH0`|=0Q=TA>^i|cZcwp>X+A52?7hC*Wqc&Y>ttZ^N#)%n^hAltx~frmS*?r8Z3KjRm(&%{e0s4>}t+oTMe9}{v1D7n!u$P8Q!*gc~fW$=on@U2c?>2<@CSlh*Z0oa$l zPGaLgGcm+LQF6=Oe`V-G^Z7pGn$oHzXz72m*$ReSlS_k=ZyB5UqsWD@QCH1R5jEn z(UPguIChU=&Wmml7JSo!#=IMzB+dkG!?fYm&Bqy;Ty{%5-Y?4^gob)%qJzomkNPp| zJQLa`+;>IfcUZ{cas9*Lk*w~Cp+}{$b-4!|YAM9;=+si;=nBk1A@Vn@83L2ZhVhS{ zPvyEHGwSTM45)ZZ8HsTBdXem(8lrCNn5TnMPn*5~j^20<|$YqsPET>m&5){V9E2fCeYCgsw54 zNcQ!{4a-;<>foYimc>$6%xo+W$NW*s10}BJM7(P2-nFD%Zc;Z!=OZ{Gxs7L7`;)Go z|Ec;3*{xcgXz2$%v*maS8{bPUY2CdkTdce`J5>U$yN{)q26yEy>n#L^OEc5mIv`1;?_Eu_w7S|P+Lru#Cr(9Y^~g; znAN_42qBU>f0`@(;PEz3)WiQz!7!@DOc}2WK5{1rx)wcp+cu--(#itulHpugs2tpC zsk!SJU+cHu?o^(;F@0IPgB7RSnzTS5Q;OP>$7)O>%9@MHuF%r>W^fCsGA)2=`wSu8Mt0|*zv;uxgU;{=03E*FMau(4)aplHX`7w9 zU(R_-qZ750q+C!wGu9`Wivet)bXDS2R`1Xwe`n^_P8&a-Q%7sIp^!5>EtHSfG^v>v zfiF#CAEy<>?ynhZ|2|4r7@eZ!^2WW73`YD+mz~~z%z)eM1^;hB0+B5KAgtzwQ*kP8 zcdFlOu}rPe>&{5c==!%?DyQkkx}Zl&69Nw>dk^1vkw0Dv%WD;1oIDFp@O%*>42{RzfkG+y8WNZmmG@V?jB9Vzza z6oU2Y=#t2R%k9UcnIv_-Y~*Jc62M*zp<{G<>bEI5CxbNe*J}ZWxbPC8rVKG2gB67- zr5b>Soj3NeS;v^;Sy9syaxyrEbLLmqypObmnm*UTZPIWO@|}rvshx{G0Oy}tts_zt z>N(SBp6|ctJh0Yr6_zf)6CzO{(KmmVy`4o&oZIQewmCYs>aW**)$vIU)&a`=s+A$! zi>uh&fdJAMt5*fPTY(_uykrI(OJB<|movR)^ro1Xo}7&04xJDj8tK^#EyY?JocfCy z1h~(Q(10vy3k_vfgU7(P?V}4~eSLi@gHFD?`}^D>c)q{aFIifO=;;xZmX=bHbF?v} zgWx`ov_AE>*neWCrLZ#yz)F{dN2t4F*H)Hh+n=-^7!{<;!xkJ^Y|e?`Lrg7GsVkm5PrEHuWiJz%l@`gf%% zO??Pv*jid?qg0d!(i|r$Y&whpSQ%K>~F+6w0GQs78ZVR!ZH`u6A-E> zr;I*t>V@xBsSmtkwku!1h=S>S5heWse6)iB1BAK(U3y+*<0Wd_^PJ_y`42fzhzLC6 zqH#IxkfpI%Yt$!FbRm=4g(8=V!SPhtTc&Qz_&)F14Cy7T*6bxb$GQZaSlv28vy zFQ^XoAgRvSyr3jMP@h2sJk&TjK_(dw#D>XkOKT27(xw(&0eA8&X)Jvfdz%$2 zQgJ}6NDg!zJJ0w7Dj1uL2y2D@BUw#Ub)NTs z6~8w^C5D7ZWT`Asmjzo6);B6=^(cXp`e#`bq671?0=eLjBf3}PO0EJ_>Vq8;!6M9OuC1=AS3*$DaEQ8bdRkiC{GKH&`sZ6o zJrxV^IC?{Q#qdThk8_!A)t6(pvn#-)N&D$B2P#X@ z^NURy<6o``J+K=nv5d>e2s&Nzs`fZ=fg9_8XsvIQn^|Rf*2kP|qq@T*3*)iWD?t?-FdRdNE0x<8|t&+AL z#@^C>PvmPAPAw%!1Lxz#zq=_2~}@$QLf8He?^>m)?lVHNdhxR&!}!K>Ly2Oim# zqdh3-|E=h+3mvPJ07nF*I{xzvP~RO0G$W%*Qw8MzR*Z!M{?HB}Vm<$TCd|(P$RJ5^ zpQP54{aew2SV%&N!SBCnB;fK=L*fXk;i~9ZKgwqN z2Lb)iPRae#Z%+^R=^}}7u%h4zsX`^nEW50En?8`PwnQ3s;4-xy^9xHN&z7b$)g3UrC!$sD3O%OX$}$RaJ^bo1oKyVW zp;$^!PazAW8&hxz_C{ob?=DW@T?F2mjjY1?i1$jr18j`Y$qn2w&o*cb#A6v|(|Nd?Jbg9bi?U1~Z-`CF<5x=j7U7$L9 z8FHdk`_1XFWaI}|6a5zr#4QQlQ!`B#3rO7g%Ba- z+K*=_J<7&l=f@C5CHnh(J3{X_#Lezt0u3XZl`ma#Zx~4k^om6`)}=#y4yvlx8YorQ z``0Rftxh!BP=NSR4Tr@_osxeS(TK<=i4nSKRAnCw^!?UbEi&L$B#u%@ zs@PFW+?+34w&;fU43dxa9#Y8rIk?`3rX)k|%b2elxtS$S8GCb$r;s54Iwm1#OD9`| zAUzT$P0)D0M#6GKN)eL{+a!Qjb9GGiu=ebpubtGof0;}_hALg)J$1>9NGBMa zL#{JI^vh5*8AwmT)U#XttVa0UEt!JD*LUYI7?78j?xR~vw6mDU3{|fn4Wu$a_hnEv z4TR#Jqam5PHR^_xL6=#tJ8V^%G}`_7`?P`*cNWU0pLd*shS0 zmQk~yM_;9aZF__z>^d9TLx56hb5IM0lny2Z(yCUVxRxi-`g`GzoC4dWkLTsV4p(~f zCj5(xnttqcK4ZcW0sF#+Tchk2x_#&ZI#q|DL+T{l9ce4LGp5e>bsAS5XYw?;YMw<79+K1cp{S;o$U_K&$p~u2SsAkBfBJOA zB4dQ1qM09tr}=(9rorWWR#JUcYzX||Qr~ji`C03W&7-Y~0t%y@tUvk33D3b1oSjSd zVsij62@AA=)L@~+SK9<7tgv^TffpV4;DIHD(P9Au(SDVc3>4qe!*YBtsmpDEJJCkx z6LR0`DlHus4K~omxKlC}yG!!5-~;XUQ4dPBl?{CZAIxZ|ka_wdblbhm$#~Y5Ths+V zenApt&#r8acae*mkL1f%bh|WFxzgS41+5di6|pU0!rvel7P9H~ENXpz4@PH1+byv? z8)wpz18GG8@ROy|;3kwfW%GxZKfG+UwF%l;;E{~}9u)Ui9J()_e*bCgVn8xQt&05L z#uO{O#JwV3NSqO=sY|Bkr(=P&4n&2%=|I7Rhbx<7zEeYD(9W>HDudAu2JrB#O3BEn zYa(DQQtkfsa;Q`P>-;EsK_{360#sBAzgoPXuW=?&DdjzVp`u8$s~eh~?UbpI$1QWj zz`y`@v-$~j8&mfX(_Kxqs9GW}4>aH5|KmLY#T0e=(zL)`@IOY8=&xX8l#~gT@?Rqy zK?!oQPoH(KJ*-W>r+;M~R9JJct_DA|kjGK`k}Dpv)$Q{6Wj%5q(_clh0XK?yxRQSS zetvfBD~=D8#ujC6y(TC%U4sWZZg)YsSu1`fvVS&3>=mkc6+Z@9zUe$N2m9EE)31c? zZ^&T?eTAQd#Du2P^P9Mk#*cp4QfejnKAlG~@tg+2Tm6`bi-`$o;x40eQ*^v6``Nyr z**1N~{*g>R1iI^gV0ihj_kZm;eivB1yhD2gXCN4;&Fo$skYrv6_zaC|eni4=S_fX5 z-R_W9i3)c8szQ^w*@oCErRgj_i$L^J zApgth1XpzlV|`&5Ly7HE>elwz%a`A@lfRS_5Cl&I#vtL{kFtf1&smM*vWu9Z~P5T4L(8B$m+zz^@vQyGq~m(;(cYj`bCQ z@fxhz+m%4swts;YC@MKs&D#Inca=HiHSsTK(CB386`;<&?w}dYwXBQ4_E8ts) z82#~s(d2RBxUg}=8hs`{mIqL9xBk`j#6CRm=vIag!T0)iAMFx8z4b$}{^rgrXo2Cy zX9gVDtiSZ@P)XaqGn-7(jW7FvnorpZzY0WKESrO`D{P1>d(dRtty|&X!tOO?*Fl>1 z8PAIPe_xl0ISqT5!!(vertMoo;%a|qbc5TXr=a`%>b2=`+8(ywa+6a1M$vO_k+b}n zra3#X!$uA7JCy>mq!w!Try8o20{~{_BB$pJZz~=`a_VrKaSDI>>mX+$y0XJ;TL>A6 zq1PNq{X&3>$bPq*r+ft^tl3Vm8PU<~2Vr009$|o+&ZYRyTWJe6FMvUTA8k>87OmXg zWpEyc&~37PRqEL?#Ln zZ*ELItyCoNE80QxLR$w*K2$+(2?xs%C2Rr@l*;mUe8!7v!baGotuUVk?qt~lE&G?G z)onTjnRtd;(%6R!9$f>wKTSOkTiS*bvl!5wuJ9jm-qU@4=RY%1{UgtUlN}vH9!0Hd zCTbVT8V4deoeh1DsoS$d>q4ci^%ZaG5@1KAUuM`*o?t+G1bWr;Y#i;U&j?@W1tDYl zl6MvxFq<1k>!o~`AF`ky@(?3E2BCf4p!M65tM^FPp1pZSeSE+k(R6W#wi(o;4VOTk zF5i80@ztPs59|An(ek<_ulRciK6_s0L7b|7hxnZc(egzu2~3BPksU;s5K3$F8oO~A1|v`8x(Mp@)ZFx$wS$g0WpPLK z1;f>0i~Nhy0a%Txov9llYe-!(RZ8MSB=6)-cf8}VHm`i_5r?|K!({SKfavrHEsDRe z#M2zF*s(7x9_C<9l`Lh3^~BwQPeO)C?fTA915Ax;pHX6Pr@Vwg?@5FCbnD@lpMT)V zf(bqAEf*v{=i$+2fAaEJ#Qg)`Av*Kpmu;4%iFwuhHQk5^g;z;q-oIk z`9lgO{NziB?8d{__fg8Jjo0(!T5Z@_=xPM_>7@wkALaxNgua(BToZ(f-QI)1qOe+{ z!&QQj(=GQM`qAU#)v`nB2JHaz{4oWm@%0%NA@7jO?Tn&xqb&oi(^IVJt z*FBThMjPz@S~*wvF|We3>}8(GW^|o>|0Vv*;#@!)!xYcE3Z1R6TG0^ez-k$>+hc!Z z2jorV1U_TC40O+LVy~550SczQy`R%Wwf;wecIpq3%lrMop@!ESo#Dy}MJurjZoRD? zgjpYYDZ8>{X+77 zK@QO}4TUny>t8_Wk1Sv_T?}i5p;!hfX~dER67$3wH*@PonO5Hr^!t{FY`%7I_Mj`b zrrFxyQlt64E+|?X0-o@-tU8^O&>(LUMg>0L=pxZhza9Sq>bX8JFq>#soT?oH^_;mQ zlw4INabWt{$Iamczc*>E4rI&nEXLLHMEgt(%M5d{eW=GkDcU((p63QT%6iavYuyHx zO-NVs81(%lLbwC38htD$qLayJV^(oyTAnk_%%%Ffo^Z#tt%%y4{(kgIW*LhxhDE9U z88$yVLbyG*-cFkp1<$HxZkmreC&dhs5VGD`K1ewot#F$QFFfXl#S47BFwVV<#bOpM z>7}t?Cq@>8VfHJioO4#_J)zgw)O)vL4FiYadU9g^SNBMH-{ zl$#0G@IWu8kcDdM0=R3JC;1f=plzzJ)`dOp*SP%cl8|#B57R8>sVCa#3>L~Ttv?=? zyJJu!r;(7X9I|Y;Co}8eYie)x#TUNiJXqI7(_Q;09M+h??H&Rh#GU#nqk8m#5^at? zA|=MiwDD|Dg#q6PbZH`rll<-V_N_0=H4oF^9k6F0^td{`g7w@W7!$QjyL7~hdgIF5 z?LpBUT1@7g&tKh=9|K+;I;|#>ok1LidRZfv$6G*f4v+_F)D6~Su^*6^%DXq@Q|wXt zI#o}4`OnDYlC8VFkSg(L!6{RA?0ZcRCfyzMG@Vlo9vEorLQ}Q9g1*_>3yu|+>w}x3 zAXx6|{NS2VwOLM{9_Ylv87MEleBKI3orA6y2SpuILp>mgJ=AswkJgzbqaAX>kP>^z z^_|e)zD)Uz&2J8V;W+17===j_J0#PwvOeXecLwg`}e!Vyconiltg@$yETLJ;m<9t^k^Sx+I3^vDBW+R@eI6Zu>NeUyz+ z^xUQ?SZ|Aj;{8N#q~Pz(Z_@otoql%P^8){5^KyC4sJ@3PPU&p9hX!c2fk-Z2OExuD zTNopRE*=6}-EoXShmhZXa{2`{#t8ojF{9!?3R*U_UW_dqr7PS&veT&r>8y%py56#q zg1PDYgLD4OR{6Z+uNfyMPJ6K>ojY{3R=ek>F-eGowz;R4f}{@9R=vrG)-VrET^z0j z!tEE1T%J1!*#7jYWsYzNqr+6q%^D_h40(;)SRRTF7Uk_}m&bFevCY`*CC4NmQZYj! zVIWE145KsK8{rfz*Qxzt)0k(0dQPDAr%WG@KBz(`G-h)bf!-`5bRF>sjd}j?Org*b zbw5GDSGhR{x`40Gg+nOKQMBl+@8W?S{Ae8*uPbd)_ekO8nl)5MI9~mS8710ts2XZe zh3j(>ZIZp*@Oqa*&@3==+KW(^Zq(5nN8#=vVMo+lY`X}?3-KC65Nj5D^9V7YcgeP= zrHYX`--uFp?C7E{x4QBJo3Jegq|KEi#W|8)Em91d5k`+-(U{uJ`G_xIdwD^bsc~pc z(gJXQ3TCy3YiFEG2r#q>6sX$%J~D}PvzLOu5uNL~_uI}qBxTIR@XoIX`b;A+`1V?l zhZB=8<5%#Qs$2^H*N}@jC1Ni@Oklu)YgcdHigoOrCY2vgaRD`*F7x|nErNI;g7U+* zxrLqwasY}R&CVg-Vnlz24aA4fue5Qz49X8P))8Y&W8q|Yq1_)8j`ZKg-+$PKD@x*J zTp@1rFBdiAA5pt!GElkX=`~?U)SX%{2Mr<^7}v9Fs8_PX9XC#V)w33psOhFXMN*RJ z?8p$|Nn!oWh&zu}qXAe~aEGCz z75KQZ%WotJ1KzSoWnA%j<%IjG7_VC?q6cdxJ_!4ArUDesEUG^C`U|4=WvEY$GYs`U z^iY?J{@q4rH(%yJi{qTaCnm~BW|Y~EWz+96=3+vypiSQI?$m8P)hIY(8Q)4yj_CpJ z*Zh}IQI&N&+)1|K*Q+;_x*SYoPx-UP zk>ATJq1~V+RcIfrKrau%lOA5wrFy)b@u@UtcKS0Pra}SCG#G^Ujqj9pc^!9Oyj~v= zN&pCf`}^O?$jFEU1pXh1XH?Q!cX)Li19h({kY5i71jrn!R%oUdSb>h8=P89oIZVRw zkEpK7sbm?+3xTV1{bJSH8)TV?=b*>ybsetPj--k&-aCEd4ED!vcnFl)-`ZX(Dq_jdH1)l8p9BMDW-&g_&akg4qugTNXv{0gYGx3R7QYQ2S z;Rb5UIXdh+TnTJmA9vERgsSr^XA`xlgGB|Dw1H?g)&=eJChMFt(3m*r5_}`|TRCp=5Yp7;3BqTdU0V25b zW9q=`mbv${<&C9l#@CHPl3RJIu#)wSOL^eo$G+=5Dj%m~zM|L)_LHcIW0=yan}QA0 zjkNoc_nS&e33{hK&mViZf0!}r+~oS4;AVl!9;U_=i#_I|8FtKp`? zD7LEc?cvSkyfaHO_0nCzw1#*QUujJsK9Hllqx}cLeAU;zH0;A?v6T91zl1LLl@1upPG=*w81Nsg?ykI{}NJNB$tzqQ_2KEGfk*aj1nx@_Mm6nQw1`~+hJrmpA z9GcGm(yS$Jb1Ge``Uw@2O_E0Vdxe;W?bA<(Q>jiLqvVEBs}|fc9dD;s%b;Xmy4Vd3 zy43-J{Udt`U?9=>hJ4lPfk|O)DT~oi-%4}B zQRz>L#rQWR%?+vD`V^Lgd;x1jv+{z~kD$O;q>(DWg!o=muD*v`R!dDSqp`nu-0wa~xee{oU6rpq5Suyl5q)&h|D@S{Jk^LxUu7D)6AWQ`vjd!F3i3T{ZI% zmJ4OfpjD7NwV!H0%+&Cj6#3x|;^C4TI{hOGU7Z^^wOq#MAzFJkk%n^5<_*1|1pz?p zFP7IoX9!_rPUpW`fJbJpbCSEPIcirU=J3+NM8lOC@TiwZR5}`3OtGS1q3zpbMvLub zj19^)5k1*fkXv15HC5T%7%NHmKZ2k=u0eSCm?F>AW4)xvczAHPJnF|hoe0emNaD`0 z*62Sh%8cFy{WX1Wh&<@iA#56~|MF(&^q}Up5P1gx%H}s#)CD{{9rgVQ?#bgX>|koe z>>N;!iWC}L;3S(J!CIF#cGwGu69g-V*6vQfD^BuW6I?7-nh7eQHik_AsEniFn;qVv zhe;JPz9C@R_lcdsHB3%PHVURj334e#S^kJ0?fVhu455nV$_C??ys#Yi^h$Mk#HupX zUe|DO_@?sUu%UnHKa?>u%ID3l@Gga~y@9S%%-*L`^GxwR@DXk!E&i=&AA~>a(%+Pt zJSpXHnZPr3R&r8PA!L&1sJZ$N{Orn|hUW`l)w~vP;#1TIJ70!8Pv+Pk zK>|F{obUcHBcB7-jIXLeEKIDx*x4K8bm;ea!6J&0!JD@fw6$>qD)om_vv+7huRc*#o-N>EQ+zp71KRTgcHM(pH_PcO6>|HZ@UWC5 zvsV$ErK)gk`O@4U299=&B@jEiU1YnHsfv7^FQ`C)masBOQ#O7vAsna;aS`#$d2!AC zu;o%!k6F69_^p5$m5Oj{As1}x(}2q*>xFeqbYk2#m4!tDA+T5CfkNDke@vGQRxzV` zm8t8WRSnF5HkBKgg6w| z__xIG7b=i-l%~y^{~uk+f(8s_V?}BOGNb>uVnPCtJe1MCF5m!;>OY0CslcLC_i2oO zOL7YSa-p*klnwvmLdU^XSqx|<7n*FhP_D1&ZL&Q5LN&5UDQGZZm|-GRglJ&?tsYAc zr0--!^mGVX51bUc#OXDiE&VL-X#>}`^^`J2aK&q~cXwKj!3Emif(R$QuRb3p!5AX) z5%ywG3ZSV15X5T&P-`UFIOdCIpm7HRI*h_=$bd4Vu(W04s2a*NP2)3UPY}2VSPr^n zadGqljdwE_+ZZsA%k6G{x1*uWM(WGA&^Aw-(KZ`GQX?Z%7!^m>76-Dn|Ih-!dGL1v z&+8?<<{^VO|F|7`DdAOYHHNvnKMBekh?*Nn7%DjnnaU$IX71YhYIn8_>$|lQtsno= zGuX)jL*o9{6^rrT$(*O?=4E|y<@#he+WMn*%4ImK0k?eot$x*g-&9O1oMcmUf5h-U ziv_!b??t35%tyaAr}lrn&?V{anv<@A+Eh4b6375|)PKTM{h*IQ{SWmr?QWf_%jFWIs66m6VIm#KO(8;m z692o>974aiHlX2PCi145vubEPQOijCga$Q>r6dvwimd=7Wz@ew7l;Lu?(g9BQ<9T% z!%(TO;Hr}!{5cKe46R(z`SKB593dv84jz%rCi!zbgJnzv9m9B%ev8>i%pdp>;i1L& zdwW`FuTC@Kzpevz+2QsmxO1E7Hb-D@1sXa=!owoLe#)_DW@88QZ#52~W}9i^3P7>T z33kh40mm`T;{OOZXvqiwgiP&&2Lp&f9f*;=pP52Z+o&Ow;y3xH$I8BHFQL-&owH+9 z1|pFvQ9$~7c%tp_F2hV7DsW*J?3noH5$*|oMXUvv`}rL-VI25F5q*Oh{_|*>rJeyx z3hF?K>}vhB)$6*>km3^M#YWCCfrXwf4VcNi_2Zsb0`UWD*$KU8tn+1`AMd965d)Od zUW}pmN72r7UI~FuO9w^`QwX~^OT?%7oeVfEHi*Y5RyAbN$qBUozNDss2D;eRM;i`l ztGb-Io00127S?+z)e9V@{kaXgIqZDF)Of`RDFQ-4wI)6q6<%=u8g?SNjD~WtMj@xQJ4MV|mQt1%cKJotOmS z%37W}OLP@?*q6gCa5HkHwL;R?hbpAa8Z-|(P8WRGpyuu+JS$(iPyrRs{4{Rl$0rU2 z0Ds6FRArr$_)1@)KQ6=hw2zRZCUuZm@s{uzH*U1YMP)VS&05GrDC&%Zg$h~b)xZo` z(=`tZB<8|N?)wvAv5I)2B)ZKxmtUYj^3|CZ{LhJUrJ=-wt+GZ#JlnA8K)SAt84K>; z7{3rbO-{41luS+l@F&{QTfc>X{uexSEY&Yz(pVTVB7Xl^>rZ3KE=?=wR$rp9g5|?w zLZ2_0@%|*g<&eokceWn|x0l#=QI|&PPj^0Q#+rTUv5S_Tc9Mrqr>9GqltnJKyHRb| zLpjk@Bc^NF=@8KD&zlCGj53V}B9%f5r>ORScZ5HJR%fih zsd-{Z=-zXO?cO8qrE3pDw^XC?CKoVV@^|dhwK6NAySj5`bfqx=TwP6=!GfEVfb zaBoJ2!ekRM;`rk3{XX>+de4&h(g=5=`lZrV|7XXU*X6H+h*@;r!MkHU$0z(hbkJG- zz?(eUZ!FH#)+@(DII3#7ioDzHiQK4@zEw=!d=tLP{j5%InAl{uN_=K7nMB8f0B$1R zMg0!ZM}AWeBa;xJg!LX+=P~`^s==Tu7{fVh!DeCR6i0cuJv_k=CMsP(8q0n)T8P`S z!U8z$aeBmg4hisrNE<(*w%YmLrA%Ywg%EJJZL+U=i4x47^-aNdKT zWs_^9O;^t@=eWORShp;L#lD;Q0s7zUlwe zKHPb-t)>clxm^7Trs31~Mp{&TILXER}_6LtTEdTOO3>Am{u z10-pS8o>Qq%Jq!7ch|jDQbu_G`=l-)Tp(CYLt&>%@7=Ak`LY4wF&Ja0%5F>p6)?f{ zakaI4=Z3_?!Ym0iDj?6uL`r1YjYP6xeo{mN;7h?UU20dRkvAtwFi71LjKGX6>I#|I zA(rGAqNW~67J_PTB*~pVq72oZ= z@U(faTWCQh)E#c32kaW>@;!BPSi!y8jGt(vFKc~4kG6Fl-WT(7&H6LyH@298lpq~F3dMd9C7k&^8LK~~{4V9TU zJ4bz8>U%qKoSrO!mC;HSox8V>ShoNw)`S>Pa(3!;L1kA^{Q+ohXi-ZdyNpVes)K!M zZ!R)k=y{rqK_I8pu!7x(UuvwJ5AWU$d!3)al)i*n)%4oOAe`G>&v`3)XGI~CD7?^w z_y#a{Y>{#Fqmyoc>UYWWPaabxpLfvTRnLn`YUm`+=G$$&lJQmrup~k5-d`cFa}6S{ zAg3l4#NUl8?L|g#HJoA}mg|H-2>`g1e$uydSW-Q>Elwi5l)mpyW9%}Xl| z4SJg4wB2RnE!AE71(U|~G==7QcTuO``ngd~Ij;7rHinOakXbL}_*GcsRW*z4i!<3; zL0COdCm_TDF7@9(0yWIj-r%fZ7a`n~wt2BYtQidJM9CpbG9b{&K?T19yKuw40wv+{@+R@IR84O6dKtD*E|wIbp_pr#X*fzK@i%!N z#mK$sCgtwChm%N|UzT(L@2V8USm)jtL=4qcb5dtLadd9(;u%;AFJq&y%&3u2olwDR z?48I*M*I)A7?aPjdCJJlJ$t+d<`iL?#~aA%x{8ID&dOQ7;Sr^M$$*=CdLtGWUezXQ z(0mra3jfs55f5QWy^?OM;O{lWUL^VrdtN@2FyCm}Ui=zJs_c;y{w##QW?q0Fyl>-FN*Ga?k%DG#`CE3YgUW7!{TmS-=fX9Vj=*WW0% zWlza6wA(f1VhEoc6XT~}NyrpFe_1`$X5nyXL_n>j?J^~6I*|}&A&bXZ2P*H9Kj4;4 z2l9r}`(5XuRMV?yH@iB|>c0J$Mk_?U*m)v5jF* zkYCSu+i$ALLM7~={hzL`IlRuM>BqKh+iH@=)`^|QR%6?C8ruyTH)xzRXl&cIztcYN zgX=qg-q+dt-kqJ<9sFjd;#Ta^Go=*{$C4^*sZj@2{Zi-$iz9HMXbF7uXPQ%~R%`CG9Y2_xb6;5tRKIs{vHz z!?!giu80>wo!EDNSBMNw%OL&6w}FqNiqjvreX(q+M;FG<5#$b9()(Yr(}>%;`2hZ4 z<@1Lw;ae+!qY$)2GRnKJ4ikOFp$G8^5#8PQXAg#y*)*Vi(DrdvAG#8KNn;vJw58xHD#W2=)oWo%!UA%8O7n9K7Dx(Wv{cGrqa~b?pawO=#RtOL z@xfrqdg7H95Z4w}#6=CzCHZ6lN8c7r&10^reMzS_B`5WZMnQc(x@tIIc?nWOOZ@#p zpJ+LA4TO9zhs2g9P}!BZ`hl)TkbpDU%d%eH$!v6~OeTZgSp-N=JG8urx$ld1Ccixy ze;HPJreGA24G78KfT+j++2GDeAQ5D%Hr(Gb{;}{U*g(<5wV`Ad|Fzt{OZ$r@*frs6 z0YU5kGrKIl00%OH7oYt700nfFLa0EBdpV}+U!%W44mdD1K>1ItY*63<@OxfE+`sUN zS3SeY@sUX``Y>1c$px{cxiiJ?zjCp$N;G=T0@_Rk8eFZzKCAUZJ6qM8DV_^<*ZCoB zzvN82-!fp>>xTq+8JEW2LkK>fuw!s?hI;%&E1Pd3&2YBsUg~6eaIu9vbAKN=wC34A zjgt-gwRt--AMXB|x}>Y)&p8_A1;WK_8Ow$_+*Rkq-f*^xl)5`5sHoV>@G+d|?Ftde zo~LW+g+hcVctidG-|8HhW_J}*lw4FZwMpm}%C&608}+MaqpAMgbxg2Q;LIalhxZMb zpMq8d$d8eO*%8PnDz7~0)g=}~rgmCyJTO!MfX9auUxrpxbu1l1j#C?GB0aG~{_<&B^o zgUWR5@-_0Zwj$9<_+{sgyvoG(ZSRu4#b+57-S-(&{lu`QSXN6p)4Znjb5>;3O#D8x z#jX4Wx~@qBf7O z_cSPMvziaPBb*&)?BR+p{z^vMO9y!eRsK~GUE64L6kcvD+2t~1@xp40*%WpriW+nx zAVx_^38~LSMDw-=qVekeNriO3fNwCR7y$w-G!&{#buyy^`EibLP%&F{M4@9cH)2z8 z8hRIBX6bnSmodfo`N!Ucz23|Fsn&FE7ckfY{=FJI$CsFfll{YVHcuW;jVG-8>Jd%%fzJJiuI-&z4-U66+cl< zbej>*z&#U5hOzqFrTe@b6;5BVSM@qW_ce?Yl816WWyP>8InsioI_kkn9iwk1J7K+-oaATaZxZT#g9E)Ii{LtQ zOix4<6d>$211p<8euQiHg(LAwhq4g z@U-nGFCFd>0I0<$_q2{LUoYM^h`(bvhxvQG=N#-W;U1S%O${ALauaVXMSxUkv#1n0 zV?0iyozg4LI$c&9E!!h5Np;D;wRN!a7J6tem zH(LZ2h+Dd7S+O7eN(cx5{qFjP_adsa>fn2Bxhbl?`hcOR57GHn0m<~Fuewar`R#D3 zoZe#kO15!YNz4P);rJ@1Hd$YHb&vnMCubvruW$D)`8~)FTnYDhSS+* zR%E^v+v3^lgIiw+xcRIZ${iWP-0pA%VJ_Klls{yq8wxIJc@x0fR0SbZ(_ZAG_ly}; zbNDRX^2TE?O5>QP>pAR+ZDslIzsCtpd%8FcO!|YI&+ZB;aKN@VOEHJN*>TXuZCRy(7*xMgap<1P z8Pr*#dbe^zu2L#hhB7|u`W3WcDrr_b75t7NnUzrSA$O8!_(v_3Iox_74XHGqy*E1~ z6iQEUbW(Y!tgKLXc|CJ{aUR)Dyq;aLPg`LKXxoyKE{T|H{h5CC?+p_!l!H3anp40} zE(-bYg#{VuukdlNP^b|@E%mR9ZHD9mYd==MPxfFtC@hA8GG8i$&q7TFd%u`!`)bk$ z#v69VViqZ87^kc|u)snFcx{#R(Fp!^bq=Ko-GIA)M+!2a!s2tcmGoDLEWmm2di7`J z_HPYiN}w4o@^SL~A_h=^RrdUS$pKT|kU<4}l2tUfC%PfO(v?)}#>999U2%PXBC!;m zA>0hW9ruSSM%?ZFkgFw9K$FW!qZanpJjKAsOPRZBkBFyWkT1H6epCmvfw(^T@@LV5 z3d;J0tdH>YVY&5Gn8H^Jz5w#LL zX7L4y8b7<c|U z*r3f!QO)+42(l(d^`f0Np?kXv_(i1kNtcVKpSU(FuEDlC7_2ApH&0}IGpFSkG%b!; zm5+PZXyXb<;^o-X3*fO~r18ZLwaua%F(fv+tVyLAw^ z5}Sy)B{rG*Jj*DGMJ4>zb0>d*{>4K(vS9cElKC$CoexVc@kZAoO8k`-`jB!z8kDN8 zJ1re7KFIU9Uaa^>#4J((tu=x(_-1mp9S*LTT6Cr-58E!&)G$Hi-2Q{JMg(dkMxm{M z#sZQf7DQyxb5hz*;JWS=_)c{K8;o1PHk=TsC(uT#)r8?~t&qka8Uj?N9ol1kqdNTP zA?Ex;7B=glo4LHYii5}>btxuDDfVYg)a#NFv2CKpVXQSbMNh|aK%1AK@U9K(3! zOjn7fK5Z_{dR@MaQQ9OOIZ}Wewau&2a_15&D*^=VKUMq=N{Qv=p*i5OU}f7PlJKf3 zO3~yKu(4I`M0c6)Z?edcik%GhnCosgl${8$SPKyfYqrE>hPTo~u>#P8D7d_fXXLA$ z>P(!yxQeiXZlf?exSq|7j{4ec+?k?pr^37MPgD?WN_bl9kbo_ib7%$I=kDl7iI&iB ztt%jBE}*-5!J%NLC<1`OqUBs@bd|(tjB3 zyYT6noJMBN3rsFTeDXD>&(g%!&}FCZ;Y1H{h6tDDBXI_rBfquW#~`7&&ECbVg3%v) zqcRs?_#!{q!EN6F7Ab7YOCdwCKB>g@82Y~=qkGs{SJUf-=1NFJckDe z;bd;u8p2FxbU0YMp)x$V5UM*}k|WkIE``2wyA^iq=Uy;1f<_5wzyuE3=W;)A`RvPy zJ@Hf~4Jrp_oAoR6Pw(_^FL}qNA(<_)LJL?S zMS0txYe&}WN*aRb>&MU2&vZRlX#smB8zEdtrbiu06go(f45w2gxU{?pAeGT(yY1P# z=V|*YC4c@AT~uRs;LOlPx#n~^L=?Hx5WNl&<-|*9@wo7Wa*H)(s~5zS;Sog#gU^o))T8Q1X%tuWYJ;KM<*^yC5mQjLifz(c8x1d{kKO=mC6$8^-Nyy(`3H`C?!Q zhWwRzQz-<3;L(Y%^UkP4@omtNo$o6Rn=;(@QkeEvtXnH=V;8tSUq3sHi6UQ*yC+qi z66{;pcpBwgwNTci-ayBlEz!BOiYG2<5wgg9*FvZ=sW{7#c%8Sl^ys8Y{)rWpBLY|?~~$m#3fhDIElI8YX29_LvIyo!S~Rx z!|GEOJ$^Z##@tYD>L1-uZh{yCBGAb0Ztf7+T+w@{i-?)oU=z}~&kVXp%kbBp?f2Q3 zHAoI2;Lo~0ilfO15Ap$S8vgQGNV+rFjrRA!qB~eNs^J*t{v;StNW*UP4u4|-dX6wp z*p2%6bn*8CLPPW2^Q7#|wBVrbn?CVH{c!I`A5C8cbL39XKoj9imewfAabdSH>c3sX z3Jy@Xt@HASC#t$vzcE1+o9C0Gpe&6n;OLN!)Sb2v?{5t3BnKq-zWcYBzdbD3no+eb z7-j=2aBjfmh;&k29Q6&W3Y(k{dHFm#k8>rtyA{M* z|KXWU&HIaVO2*kP==iPIWmG*6x9InrB%g`9TtM$D!a%itp!jOV4h22w*QV={I^kRj zaDn7s6)TbA7YpW;12ju+t2@39)sJ}W3v^!(xzmSmeh}X8ONpw)9!!&h;Zq9tLCka* zXh#YBP{s(F=^3@YH%}@=pX{||0=u^bz+gyLeP}@WHFt76=gG#k8Yz4(ZZnJb+}!|3 zNLEZfQ|Vwx)UfY1ozJo9UqY4BkSl;%`n0@AT9&SjpyM_m3TR=x6xWvx?f{@*uf@%cj!iB4kVcp{17SsY~1o^<+`= zArJ0{6BzrzxUsDDmrq9B-7XC3?;NTZN|~dnj|%CH+45a4aO>)RKVOnYP4o^yan$Q+ z0u@jEu`{D;6N;TIjsm;Mp$$#Eyzny6X{?SZahIt};mArsiEyThV4A*y3f;W z3n|Nd55MPg?#63s@UhgS*-FQ#`zH4qpfh3%x>nEVQ)~SZ12o?b2G%R8h(aKlMhC8B z*j^jHMP4Vu{)b;o8lVa~vBPUjCN+@vy?w*n6z|jQPm)nXr+BjMh4-L&#|T7HDwlFcJdL4?z~fVzde#DVH%hIVuOkZ4$%OuFr5 zXEj@}ld*aWZ4J{KNk-W&Gyj8ok*Ag@zl0PO=AV|>ZfFC6t&HD&j>)R{Xv)+4!DapW ztJ6X)R?6_sY*r&X;>bP$reQzI6NZ@0ZJmc!We3vA7lPZOhx>vV__3;t+jfWv1AWEa zi`NiLF0iJ=c9cqy9m`wi4|&1c$d(K+D@{1D%fGe`vSTbB_I^RX0oDenwkHE}0kiTi zep?aXuN{lbdiJ`SP1yJ=Hq$$j)Qi;dFB>qr^HGP{3s*Mlu-^JG8+S3}PueaRzKe0Z z&S?<^P*tp=wZ3m)2)Y!nBKAQk9~B2`_qGyqC8^)KncM91WYDRTJ!_GpyzT1mT|5wo z*g*?*G`h*DKKreamE5ki;$27H^jL7G+MDH~!wj_lJif?!y`lp&W0$zpOAdV@e275I zt-TcBX{L>0#N8=<=)^n{X2wGcmoqU6=fuZfVwHpa4C7dIwRJGDV=__d3j_Z~*SWO)H9TjKDnNtW{1<^go5i2n>H@ZEbG)NGGx0btBA_e(Hy{t)3O& zr%}G2KzYc5_58y+5^uEdY^$k&wEjGEmY%$1fu9=Q6a?J~slDIctpgY2r9R54xM z7BXTBeKd1IZLGo4`i>wK}Z zE0vc|jC4(qtgK!TX6E)?n{!ON-smg=$Eaui`L+SoTHXpAO&;NU`sP}`$dk2u?gS?X z0kiz~sD4IWNwdUzgJM__WSB2jL`Up^?XE4T)MC?PE3y&p$sv!zri{eL?1%5R+&r!) zp?le3hD{qigeTp>F(JssBOktfKDs4HLyig@a(Wfs`$P}(EEyj9c#4QYU?Z*2M(|Li zw})}E$%P!UtB8U%9(wZ48Ls53jItmux|D>?0QZ!>X%dm5EjlyfmkC6*h31r;Vu7bR zB4Hvh<`v;!_sou_?!}bCiB3ntJ9mGIU6axJ=eNFELzTN?iYj2MEXDkEm(~=8iew1ck ztJnb`cLL5sd$^WBLua#5yF#yj?UU zJ06RMoe@j?QdBk-ZT7eF8??IPVVV=YikDBZ(cQ8{KP)Ww-ixS>dWZ9HI-)RD8(Z2? ze=bp8Up)1rZf4Ly^WJgVqQ!5j58K~UThQ0>R)rWhpI)qs%;~v#q>aoc1nQVp=y`D} zjQ}4CqkbwJ-n`1sA}cJVXbT5JUQRt-CBYXVV3F|&DmC{DFM8-r4a(75JA5d{);Wew zvJ1Ka&PYvYE%a(ciI`rOC)*0$!%8ajN|t`+%9uV-{$Jld8MvRli$-P0Zm3U#{WRoe zF;0f&`W$e)1gaxvGm-2=YiD8*`TmWltD6(Q{ynX^1?xezq1*<*H8TvM2qlcB7i7-# z$sqb9H)6NrQHkYxU7f&erU4gaWUW|Pjh-IuiFBhsu1r&6m>RcJwxjmO8^_9(C`><+ zyFuLYu2LK>bO4v{zOs+1Gm`YwOI;?_EO&Q5mXw0(!1$sNl`i`BMsCO$9g$}N2lVyY(3srbV?FOOcQ0avl z6fyc&`!2Ux3rI%oW24^el<9#cLpusC_w zEowak5Q0^cZI1NDy$Qx3#9$H<6UC={Rm{UtPW@I1Os$%BA7t-*ajl3Q^af(8~ z->db4im&~Gdy!9i)1Xs`3${Y!^dU<>9dC4}A|Piyaw9z&sQeOTEvycf%dF#g-(Z2G8Zl}lr(%ri?CH|PhRk*2HJ|2Z9IZu|&?R4bYgK9bd}nJJAfL{PyAm!l zcFO290B5BD!^-6VlA3f5Zl?CAchBS2uoApq4FxHuLrL*u=CP~f=81+@_@#U6bubR6 z>4xLTVc(Cz%gpMSi;04OQX-#3{OBWP5sq)8i!@gszm&Tk!KWO+yVU^{B;CXEU43Q>QdzH44~F;?gfW|rsT@68H~`O=}_KtRO@ zsdD#IHo_to9s=Pje!c0uFieLB*gezZmVOuS-Of>pgSC=OF^9m6(=)1c-1ufb%09$e z@JV;y+!mW%?~&f3m={;~^%%p9@~hD-`fFxio%1-D!p0&YSF77L&5R5;u4XX_-zZgp zwq1R2Y0~wqBtHb5=QDw>KswAVO7<3}y+5IO8m(u_FC$K;@~)tf7E>O!`~U}rTdF!F z5>q8PQf2*bt}ikth9+d5w@uJ2C|U2OSGrj>C!Y4*^rTS<*+k9bmD+6}hYdBIjC49k*wzANCyRfR%`0!((E^<>U~^$jMPr zQ3Fa9bB%^3x-~aGN^?rb#;q5(_bv?l*icHmsOKGdp=x+QlI5o^@JM3*7EQgJ`IGev z&#lpP@ItMQg)>}qWGUbER0Gi^D((l!X^z~LSc5V#H39{}Vs%!7Lmyfg$VM_13N5sW zq7Dy8lgYi4j;Dj4*4?zV3FVMZD`}H)Q4qnkJl*0d{fY4Es7#qH;Ir9G0JiC8B zDY1kxY1B9@s|Z#N?rxg+&C2&;>CAmsFDLV8GkFht1AL(0EKwjqFi3mwqnU4My1C<- z+wfZKV}NxuDfm-Qj6fU8$=cMH%FDbP>;n_@Sgq@3*tZ=5J;_Z!@@0DB&Fh2Fk2vg( zNi6M$XD4u8T%|Vy9S+VfljycFOx;d%q2i_*V%H95)Ek3 zneW!bF27(2%I+KE;SY!S;NKASQqk)3#%;7j;@poYbxUf`y(hO^|IU_qB z>{DiTV>a7W0@Ud&<59&a!636iylT`j)MHE`13h?~QeTuzF`1*h8?^wrN%s^e9kR>~!AWZA2 zvc5myOULH7@Wlb}>nO~F>U6X2>nzq}#;&0yVtu4{mHyAykMzJxelVBKMNw9S0R)`aWLKI>7 z!L7Mhkd|yp7K|4!Jk{0fBZ}N3}>=Tyg9sTm_ZfB z>ixxXZo`l3BGKtpud}pX8PUpmf(M7CdU;h@cc}><`FrI~^YF_d%1UWcAPN7XyHF<; zN=#{-0XhE(r#>zBp3d1KApm7DuD_n!tJZ1>Vl-vN+Pij(8(fHLchOj*?|5XMsEghe z$7PVX?MtCLZPEATa0xN~A5U23arI#kvZQZ@sc%O&(G>mlkbdXJu{w)r{v%$l^j?88 z*G`m}63?SBCPiP^qh#m*#)luUfbP`{)d94hPfIs1=|cQ!@u6#NXfp+zeW|>p>Twta z)sVswA?fS59fD(D*I%e7cW+Z2 ze<``tNK?84b^2P6E;aTti>q!GlVZbT$Ahk258cpvas$b7IT!xis?Dl$E;e})vzAj2*H^O+%mcU&& zu|NkL@uh|aMlV;6+1r6G#^aJ<)xMA^u5Oj-A@X>}laOjAdA)H#GA{1-|AD6;1N|+> z^lkbzYZ#8VnjDZja|j|^Ou&=rROC?j(ESwTzXkBBt;T!8wb|8Q!~E`;8!2iUwf2aGBCj1WI1gElcC$FR?brf3|5(^ zvBJgs!ahAevCH{zb{Eu7FBbYn27GkzteY_HBVsJ9?W=xOf(ZU@dW5Wq8squ}2z^Y3 z#rgiHi!=!sWR!m^rBhbsS2}!>y*oaHEJh^2*V8WknDc9OxfNG2y-cHF-P?2=x^n*K z3lbGS7n_`$a$0?3h)ln{FEYLRJZJCc_E4GEOZE;O z=;%&!&7)y`H7j&F>giiaW-RtDRD(&@T0?Wc*})x^O;f5=O4_38!M2i-iPXNNO?-h7 zlN{_Cl6qaQiwh#^{u;IwFmzRkq*VkJ0E7b=>MOqnlx?N2l#Hxa==&XQ&NpBk)pO%=(peLrlwf-GH`txknELgV}j!lI| z->h}>Or&DMjwH#$rg3s3Q(==KrLP|*tE^=k<*gp%(N7kflujQo z0QwNXE_NZ>V^)>6s;8;9DDI3f%%Z(B3ZkgH;v2cEUXw#wn`26d(??D3V9R$?Ub={? z-(s!?Q%@}xI~r|HRpwcMQB4;eZWK>MB0=8@J3`u=XqXFNfHe$QGfRWI@#|-zN5<5e zWif+9EA%AAzJ~{h(f3#62K?$b2Oyq^Nc-L@%41}1sFchkVhN%p{nc$=zv1(<%qd3< zA4j+0N56L1IHeDW;>pMXpXS)z_eLqb7h7AsqlZf<`KTi!Gcd*X&-GZ%Dk8xnY}9iS zcLf4Sw@Z5-+p4X`X}7RaE0mzVdN%jE<$Q!i=eb_9qGN4?(0xJ*`)Vw&TCOKw$e>Go zH}Kuhb;E2zFJ=m`qo|lSK6DAJ0f#7~m8QOoBU_mWY>X&C^((!`6)%#87z}(+UIIBz zkcHE{y4i&v7@6K`Z>vWh4XZIQ>U(%U>x;?wp(v}t5Hv|oT_AkS)Z%mhR$2~{Qo$Nv_)#I~yl&dz z{njgH`Yx9hV~|oiXOuCj|V#v%6PVAU&p z?bWz^+4)Z3&=3uyasEkzGztF}%T)O-ECK-(Ur}`dKWeSO#GeWrB=v+@uDF zLc%!^1qB7H9(N)(7i&R;%=g_Bny)G56`WQ}#gJ50xo~X~wso0%s1*eXtE|LqnaxIw zFEx-GKgtRhJ>&4d;22l0vyGt(L%HNa1H#mjuy?`C5ckKIH~S8+)9*&e z7G`WClBWhVKikR2uJNou4-j*|3(s;j0!mZhzaza8JtGmLL!iC)i)>D#ZaDQT@wR%977A*S1tTn@=$Y7>PAFTR{Lk7%C} zH{eCYv*BE8_YC5>yZQW7Fbby2w6=Bb=(M(H)47hF-Pv~~dmP;Aw?vb8 z&!Di*i{B0E=4f)NUxnL5$eh!GAvk{sUBd-q&WwEK^|Nm#y;Z8Nd!O;>(`+MRElnk+ z#_A$FO`7sy15ILja*aOdS(Kz~X#+C|ggbH{_gQwxR}j_aE{z0u=2Bn{|F2!WG$bGo zJ1=xUVi-vpi>-!3{C^?k{)8l16#fSUw}T7@Op?mQOpT#^EHo|0`WsbeqL4RXew7hN z0Z-WjnULda*eB6H)c!cc{}(s_7%mmU|DE?`sLA0~33UJXN_Y&3U^w=UGRR-L z8@nc#C#h%cx%b;Db14Zvpf5kUuCC8*#MAKeAaR!(#cv?+UkWnGJIclDK1#bT5H5Y} zkZkrR>+IS>uUgwuZvB?alg<-@}2)Ul0MoEf7HF z#zxA*4(@iHhu4~w9oRE-X8eK5-I-Kh)upP zb=wbh5!lJ_51^5R5cDlivxCf&#cW)zvYUwLc%}`>imRONfl>}zjjQwExc>>zt&r%0o)GZQBO(3YIt_MmIl@1y7rOfm zeeaAI68h&62{SNHXc&H2^?#}&kg_CRK%2s3UCe~!FI@%rC>|R)a2L{)?oWf4Kh%g9 z0Q&d8*^{yTuTu+fRYUN=fmeZSDgW~8P!15ZxyX%*EA=l6LjnhS2!9y{GUfj;p(ctz z^vg+RIKsarW3K}JZHQqqwRX>;`MX;MK;0cqarFfedfSrSzK9ne|2;BXhzRYW>cX6* zo<(?(juKEKFVDlX*u5~rg3LIi1>hr(6gdkq+VAOgf95AT?K}%3D&#AOln2LP(&aIS zp!p9{l@pAVIq-7hQk;P28`PStM&5C7jnX;2m-n3(>DQD59o)lUn(t?R*qMGyVMMD` z1;-5$t^I+2b`M&1;o9obE7=9^OK)b$I2Po0^xBXhjuF z4A!vLoNF_rdl7J+;+dEFCWrnjG<^_v9@JecWn_16zFX)zi7uDlu*BD7UngqTooA|q z18I)UirW6B*p93}a;E&3TEw>xOyq0~+D)Ou>KFo@xPkl4y_4I^Kfi@YhW;(QBw$CV zEZod7NT(}~Te+1-ln)SfCiIcj1g#N!V{LVdN8dOv(K)a&|1K>o-56xwW@Hga~`DzL8$rCphuOb0_r$vHLD6yXQ zXzsV)wUj|n-5q@)e6{@;qwvNDxQKPqO;E`zbrfUc!k)oCIzKwHujnk8VWyn@Ar=P} z5Q%JTlKeZtb`%Dq5jR|&(Y_%43#w%YoM?Q0~*8U1=(bRfzR!s*pAX) zz6kwgL@ok{(<~p*kOC#{5H|TMHyrv=ghs9^T<;gk%m4{v78|LL7yvO}q6hBO*EWdG z`RaUSM(zeD+#vhTvPvA~sKJiuAI4)?iV%Vjbc;NN4)!e+ARzDu(qh7@az{?W=Hj0qt0}46GK7_@s)-POTBp{K!gZ)|R_;$Hm=^+4Ntoya=)Touz9x${% zX$*r&$qrc`Dd5N&p7E!BJA-Fc%nKb0vAVXOY340IVj;RA{4XXKS1z6xT5KR-9#Zc7y=oyLG_F&quVJ#7bH~?raIt#rhzBYDtoZuN?PjnAh|!alhq5oqd`KF1 zaRUvvf>rkiBt-p)A^FLLot@5NUcY(Te-3z3PvOZ7hXZ+x0T#nh_FB(+bF|4?leZf3 ztid|CHpm*4h9V<|Q4kTpZ^Fn`J6W$Mh&<_L@qt3nyK?9oluWH%$xxBS=F4d3+XJI3 zSE$c&s(P*|Y_=V+a&{gpD4yVSFjhaXC$NwYn6i0nU9uQ5;tEn%Z;E1SCzOFS3g;8C zy3HGE|EE~4k5BnQh;ipZh}NCGKL%s#jm_n>-oRz*3KKcA0TJuI?ugH^tElRYmnv|; z<)eVwqU7a}*(w$NE-nAy?=OFRyD;9wk?$bgaMDI7|Gh&*l>=Xn-Uu$rXm6NCT*f=% zJZ->eV_B(>>RXru7FVEnMB=dYY=6Z~@0H#v>fjkBlZ{k|n9OJA--O|U8Pdkc7Pu!S zz;~IF5THc{lFLLaFB~APNn@^8$}NXX)nCPCU^m_u4=Aym-WT5o&S@w@2-sNqS(x15 zkaT&Iy)-B0@}(b~w~=!3k4OOd7I=R63we8UAeg>XN;DOedB-TXcy22pO{o0o;_&Ad z7gB&YTWgDE!>rmdQO45&9*!q?Y6CIHrKrTjNduqw{=K!Gle$v;$;*^~RN2I&0pUo% z3#-HR?!o$+W3h+4_b;8`H>GZ2#M*sAI-u1Hw8oeIK5==Xnt}*6-HgZ;vKL=rCzKNY z5cLmNG{M{uYQo0;Y6P?=e`)$Q<0%L{{0Ub4d708V+dhERtAZeJ?smA z%?G(i{B94qkrieJXi6aQEVQr+#q~umGA%UB!UK(s5n&fvTHtx~*Qeq&AoM%E z!wu5z2aby}A9Bat9!}~-Jk*?doyS=#x)tWOan9Khg&lJ%k})Yb zKjwbTP$Bzg0z$_S`YdjI=1eES^X+#yoSj5+6&>9dl+_c{=88V49`QBM#m*ZtYPDVQ zk9wCB2fR$goq?wKuA3eg^b`Zs#Zl6w|17?%l;lEdbG!>%CZ*gY;=8R?Uevq;24l&G zHFXyW4CxqaTMaDl|0r)jK17>{*krd=Prv%NDnz)D1OywVcnBP(5qd%2@=H3{KdS~N z`LG8dhy%>XXzGt2aZzSt{<(E0^uQ8brGJcB4q|XyVSJJ_7U0o9(0Q-ZV zx8E<%h1CD9Y)_L8p`56q6wZjVoy|AJ@}*%sS}L?aN`~T8Ed&q}xqG>>G(CD%_^)~d zl(roReE~$th{XPolpEba2YGVa#A<2#9q0lnG?5A~-%)lN-4+(XnnQn~^$&;F`M+<< zzqxTJROljQu(sSLNdiW?ARM3ekU%ZyBZ~NxJX0Xsk~VK+58`{<2`YZRvg*>mbv}M4 zk}4;zZ%z=uSOjqvo27aigQ7H(1_ca(SEzD;s@&dO67Y zhp;4IxyfBi5juc^7D$`QWw@n~V{GZ^GB`5nbEyF{n#c$FoOH=OFKLv+Ik8sp)!UP zw^*iB;Vta7lF=m=m%D8 z%P?7Ft*1H95Hy9!$d=Vrq>V)QAL@}%Ly+|rpguitGQGGFf(`~Lby&G@Wy}((tE&gn z(CN4HjE;?k7^|qL-~j9Nh(x$uT*MMts>*%a1PAolOBK-2(n_O9I6688(nQ9_N|=}w zk%YI&v|04mX?s`Li%M{q7$Q6tY8MG3iA2h*K(-Ninpyj{{}| z!8qkUa?Xy8jv|OhVW~xuF@+Z;u&A}&s4A+)dEBEZl8n)!5pc7a_O}(ZOl=8BE7zdK zP3H-Nd%r#I2ZnDM9j&&x^$cuZko2oD2F3pEF9+U^7p!2qNm9Z`s|hy|9F2oRLsBS$ z?UGVxLdxSPapbV1mJiXTio`&vz8V`cDWX_s^|Bu*mkdVNVOJg{ml3HS{8Irp!U8r46Q33g|MNWo%H>L9d$cz|T z%7(fV7YL6z_R2MJ6yd6Bc)39ow-}CwMRcJ>`h_3OU?{oKYUbNoQA?^ zs%H`fXfa)MQM;w$KSG8ADi7lToF_U{WQ*V?{yeqr%S8u8C`CYDjkIS7Jef3xsj6K` zffOCE8HawY6*KY=YFdu+wD20$ZTxe}L@`vwUP#o4g6N6qe~%y`JuqFb3z7)D&iz0s zG=Al{1d8|~A~N6X!31acamitNpT#f0d6JC>L+i63E1M;<0@nimkP}4;{2GuFAt~{= z9l&k+`zR2S|NXwMd+%+$kYn`Dv_9l@S$u5KM2EyF6RL3cPF$v+``kq&u(+1KDAd}08~3P>ChpA)OEN2fEtlw@$mhyN|6W)S zPMsW_z=1&(K_pFb+2LG;7gk!F&#n;guVBZkfK?QP?^8w)N)t$XF$Jskqx^Vd74ExM zk;S+Sujr9F0^RbGCXqG@KQDgfCyXHb@8CQ{FyKGX_!2?3wj`GxI1dGXlqftLWRcOL ztC2r&&N?v#QjiwW1r{0m#{K(Apm8T7gvQ5DEPZm(;jDM7$r;PHTd}H=EXb~w4?Nv~ z+b^Q*O`Fq@Rz02bpM&fOC7#bnbz5e>sDmQjonY2@mV@}iuO^ydn(dn&>Q@g5%LBas za2N+ANd#$*!mZ}Cx9YaF8b-O9+~7ar2`t(|33fEbKUIvE@by1WhoA!9!DDc85Xnj^ RVL^aDX>kRyYLQQY{|93%BbxvK diff --git a/src/asciidoc/index.adoc b/src/asciidoc/index.adoc index 3548644a0a..885664150d 100644 --- a/src/asciidoc/index.adoc +++ b/src/asciidoc/index.adoc @@ -1,9 +1,7 @@ = 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 - - - +:javadoc-baseurl: http://docs.spring.io/spring/docs/current/javadoc-api [[spring-introduction]] = Overview of Spring Framework @@ -35,6 +33,26 @@ list or on the support forums at http://forum.spring.io/[]. +[[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 with the lighter +https://spring.io/guides["Getting Started"] guides that are available from +http://spring.io. As well as being easier to digest, these guide are very +__task focused__. They also cover other projects from the Spring portfolio that you might + want to consider when solving a particular problem. + +https://spring.io/guides/gs/rest-service/[Getting Started Building a RESTful Web Service] +would be an excellent first choice to get your feet wet. + + + + + [[overview]] == Introduction to Spring Framework Spring Framework is a Java platform that provides comprehensive infrastructure support @@ -73,13 +91,13 @@ http://martinfowler.com/articles/injection.html[http://martinfowler.com/articles Java applications -- a loose term that runs the gamut from constrained applets to n-tier server-side enterprise applications -- typically consist of objects that collaborate to -form the application proper. Thus the objects in an application have__dependencies__ on +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. True, you can use design patterns such -as__Factory__, __Abstract Factory__, __Builder__, __Decorator__, and __Service Locator__ +as __Factory__, __Abstract Factory__, __Builder__, __Decorator__, and __Service Locator__ to compose the various classes and object instances that make up an application. However, 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. @@ -103,7 +121,7 @@ are grouped into Core Container, Data Access/Integration, Web, AOP (Aspect Orien Programming), Instrumentation, and Test, as shown in the following diagram. .Overview of the Spring Framework -image::images/spring-overview.png[] +image::images/spring-overview.png[width=400] @@ -147,11 +165,10 @@ The <> module provides a JDBC-abstraction layer that rem need to do tedious JDBC coding and parsing of database-vendor specific error codes. The <> module provides integration layers for popular -object-relational mapping APIs, including <>, <>, -<>, and <>. Using the ORM package 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. +object-relational mapping APIs, including <>, <>, and +<>. Using the ORM package 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 <> module provides an abstraction layer that supports Object/XML mapping implementations for JAXB, Castor, XMLBeans, JiBX and XStream. @@ -179,11 +196,6 @@ The __Web-Servlet__ module contains Spring's model-view-controller framework provides a clean separation between domain model code and web forms, and integrates with all the other features of the Spring Framework. -The __Web-Struts__ module contains the support classes for integrating a classic Struts -web tier within a Spring application. Note that this support is now deprecated as of -Spring 3.0. Consider migrating your application to Struts 2.0 and its Spring integration -or to a Spring MVC solution. - The __Web-Portlet__ module provides the MVC implementation to be used in a portlet environment and mirrors the functionality of Web-Servlet module. @@ -221,7 +233,7 @@ from applets to full-fledged enterprise applications that use Spring's transacti management functionality and web framework integration. .Typical full-fledged Spring web application -image::images/overview-full.png[] +image::images/overview-full.png[width=400] Spring's <> make the web application fully transactional, just as it would be if you used EJB @@ -229,32 +241,32 @@ container-managed transactions. All your custom business logic can be implemente 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, JDO and iBatis; for example, when using Hibernate, you can continue to use +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[] +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 WebWork, Struts, Tapestry, +__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[] +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[] +image::images/overview-ejb.png[width=400] The Spring Framework also provides an <> for Enterprise JavaBeans, enabling you to reuse your existing POJOs and wrap them in @@ -281,106 +293,30 @@ the pieces of Spring that you need. To make this easier Spring is packaged as a 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 may be in -this form (see below) or it may not, and normally it also has a version number in the -file name (e.g. __spring-core-{spring-version}.jar__). +`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__). -In general, Spring publishes its artifacts to four different places: +Each release of the Spring Framework will publish artifacts to the following places: -* On the community download site - http://www.springsource.org/download/community[http://www.springsource.org/download/community]. - Here you find all the Spring jars bundled together into a zip file for easy download. - The names of the jars here since version 3.0 are in the form - `org.springframework.*-.jar`. * 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-*-.jar` and the Maven groupId is `org.springframework`. -* The Enterprise Bundle Repository (EBR), which is run by SpringSource and also hosts - all the libraries that integrate with Spring. Both Maven and Ivy repositories are - available here for all Spring jars and their dependencies, plus a large number of - other common libraries that people use in applications with Spring. Both full releases - and also milestones and development snapshots are deployed here. The names of the jar - files are in the same form as the community download ( - `org.springframework.*-.jar`), and the dependencies are also in this "long" - form, with external libraries (not from SpringSource) having the prefix - `com.springsource`. See the http://ebr.springsource.com/repository/app/faq[FAQ] for - more information. -* In a public Maven repository hosted on Amazon S3 for development snapshots and - milestone releases (a copy of the final releases is also held here). The jar file - names are in the same form as Maven Central, so this is a useful place to get +* 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: most people -use an automated system like Maven or Ivy, but you can also do it manually by -downloading all the jars yourself. When obtaining Spring with Maven or Ivy you have then -to decide which place you'll get it from. In general, if you care about OSGi, use the -EBR, since it houses OSGi compatible artifacts for all of Spring's dependencies, such as -Hibernate and Freemarker. If OSGi does not matter to you, either place works, though -there are some pros and cons between them. In general, pick one place or the other for -your project; do not mix them. This is particularly important since EBR artifacts -necessarily use a different naming convention than Maven Central artifacts. - -[[dependency-comparison-of-maven-central-and-ebr-tbl]] -.Comparison of Maven Central and SpringSource EBR Repositories -|=== -| Feature| Maven Central| EBR - -| OSGi Compatible -| Not explicit -| Yes - -| Number of Artifacts -| Tens of thousands; all kinds -| Hundreds; those that Spring integrates with - -| Consistent Naming Conventions -| No -| Yes - -| Naming Convention: GroupId -| Varies. Newer artifacts often use domain name, e.g. org.slf4j. Older ones often just - use the artifact name, e.g. log4j. -| Domain name of origin or main package root, e.g. org.springframework - -| Naming Convention: ArtifactId -| Varies. Generally the project or module name, using a hyphen "-" separator, e.g. - spring-core, logj4. -| Bundle Symbolic Name, derived from the main package root, e.g. - org.springframework.beans. If the jar had to be patched to ensure OSGi compliance then - com.springsource is appended, e.g. com.springsource.org.apache.log4j - -| Naming Convention: Version -| Varies. Many new artifacts use m.m.m or m.m.m.X (with m=digit, X=text). Older ones use - m.m. Some neither. Ordering is defined but not often relied on, so not strictly - reliable. -| OSGi version number m.m.m.X, e.g. 3.0.0.RC3. The text qualifier imposes alphabetic - ordering on versions with the same numeric values. - -| Publishing -| Usually automatic via rsync or source control updates. Project authors can upload - individual jars to JIRA. -| Manual (JIRA processed by SpringSource) - -| Quality Assurance -| By policy. Accuracy is responsibility of authors. -| Extensive for OSGi manifest, Maven POM and Ivy metadata. QA performed by Spring team. - -| Hosting -| Contegix. Funded by Sonatype with several mirrors. -| S3 funded by SpringSource. - -| Search Utilities -| Various -| http://ebr.springsource.com/repository/app/[http://ebr.springsource.com/repository/app/] - -| Integration with SpringSource Tools -| Integration through STS with Maven dependency management -| Extensive integration through STS with Maven, Roo, CloudFoundry -|=== +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. We provide detailed instructions later +in this chapter. [[overview-spring-dependencies]] @@ -393,177 +329,214 @@ injection there is only one mandatory external dependency, and that is for loggi 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 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 Ivy to manage dependencies when it is building, and our samples -mostly use Maven. +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 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: +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] +[source,xml,indent=0] [subs="verbatim,quotes,attributes"] ---- - - - org.springframework - spring-context - {spring-version} - runtime - - + + + org.springframework + spring-context + {spring-version} + runtime + + ---- 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. -We used the Maven Central naming conventions in the example above, so that works with -Maven Central or the SpringSource Maven 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: +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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - io.spring.repo.maven.release - https://repo.spring.io/release/ - false - - + + + io.spring.repo.maven.release + http://repo.spring.io/release/ + false + + ---- For milestones: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - io.spring.repo.maven.milestone - https://repo.spring.io/milestone/ - false - - + + + io.spring.repo.maven.milestone + http://repo.spring.io/milestone/ + false + + ---- And for snapshots: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - io.spring.repo.maven.snapshot - https://repo.spring.io/snapshot/ - true - - + + + io.spring.repo.maven.snapshot + http://repo.spring.io/snapshot/ + true + + ---- -To use the SpringSource EBR you would need to use a different naming convention for the -dependencies. The names are usually easy to guess, e.g. in this case it is: -[source,xml] +[[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"] ---- - - - org.springframework - org.springframework.context - {spring-version} - runtime - - + + + + org.springframework + spring-framework-bom + {spring-version} + pom + import + + + ---- -You also need to declare the location of the repository explicitly (only the URL is -important): +An added benefit of using the BOM is that you no longer need to specify the `` +attribute when depending on Spring Framework artifacts: -[source,xml] +[source,xml,indent=0] +[subs="verbatim,quotes,attributes"] +---- + + + org.springframework + spring-context + + + org.springframework + spring-web + + +---- + + +[[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"] ---- - - - com.springsource.repository.bundles.release - http://repository.springsource.com/maven/bundles/release/ - - + repositories { + mavenCentral() + // and optionally... + maven { url "http://repo.spring.io/release" } + } ---- -If you are managing your dependencies by hand, the URL in the repository declaration -above is not browsable, but there is a user interface at -http://ebr.springsource.com/repository/app/[http://ebr.springsource.com/repository/app/] -that can be used to search for and download dependencies. It also has handy snippets of -Maven and Ivy configuration that you can copy and paste if you are using those tools. +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 names and configuration options. +are similar configuration options. -To configure Ivy to point to the SpringSource EBR add the following resolvers to your +To configure Ivy to point to the Spring repository add the following resolver to your `ivysettings.xml`: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - - - - - - - - - + + + ---- -The XML above is not valid because the lines are too long - if you copy-paste then -remove the extra line endings in the middle of the url patterns. +You can change the `root` URL from `/release/` to `/milestone/` or `/snapshot/` as +appropriate. -Once Ivy is configured to look in the EBR adding a dependency is easy. Simply pull up -the details page for the bundle in question in the repository browser and you'll find an -Ivy snippet ready for you to include in your dependencies section. For example (in -`ivy.xml`): +Once configured, you can add dependencies in the usual way. For example (in `ivy.xml`): -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes,attributes"] ---- - + ---- +[[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 +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 @@ -598,23 +571,23 @@ Switching off `commons-logging` is easy: just make sure it isn't on the classpat runtime. In Maven terms you exclude the dependency, and because of the way that the Spring dependencies are declared, you only have to do that once. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes,attributes"] ---- - - - org.springframework - spring-context - {spring-version} - runtime - - - commons-logging - commons-logging - - - - + + + org.springframework + spring-context + {spring-version} + runtime + + + commons-logging + commons-logging + + + + ---- Now this application is probably broken because there is no implementation of the JCL @@ -643,47 +616,47 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes,attributes"] ---- - - - org.springframework - spring-context - {spring-version} - runtime - - - commons-logging - commons-logging - - - - - org.slf4j - jcl-over-slf4j - 1.5.8 - runtime - - - org.slf4j - slf4j-api - 1.5.8 - runtime - - - org.slf4j - slf4j-log4j12 - 1.5.8 - runtime - - - log4j - log4j - 1.2.14 - runtime - - + + + org.springframework + spring-context + {spring-version} + runtime + + + commons-logging + commons-logging + + + + + org.slf4j + jcl-over-slf4j + 1.5.8 + runtime + + + org.slf4j + slf4j-api + 1.5.8 + runtime + + + org.slf4j + slf4j-log4j12 + 1.5.8 + runtime + + + log4j + log4j + 1.2.14 + runtime + + ---- That might seem like a lot of dependencies just to get some logging. Well it is, but it @@ -713,28 +686,28 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes,attributes"] ---- - - - org.springframework - spring-context - {spring-version} - runtime - - - log4j - log4j - 1.2.14 - runtime - - + + + org.springframework + spring-context + {spring-version} + runtime + + + log4j + log4j + 1.2.14 + runtime + + ---- And here's a sample log4j.properties for logging to the console: -[source] +[literal] [subs="verbatim,quotes"] ---- log4j.rootCategory=INFO, stdout @@ -771,7 +744,6 @@ the exact version and feature set of the container. - [[spring-core]] = Core Technologies [partintro] @@ -820,7 +792,7 @@ testing will hopefully convince you of this as well. [[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[<>] ] principle. IoC +(IoC) footnote:[See pass:specialcharacters,macros[<>] ] 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 @@ -832,10 +804,10 @@ 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 -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/BeanFactory.html[BeanFactory] +{javadoc-baseurl}/org/springframework/beans/factory/BeanFactory.html[BeanFactory] interface provides an advanced configuration mechanism capable of managing any type of object. -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/ApplicationContext.html[ApplicationContext] +{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` @@ -844,7 +816,7 @@ 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 +exclusively in this chapter in descriptions of Spring's IoC container. For more information on using the `BeanFactory` instead of the `ApplicationContext,` refer to <>. @@ -870,8 +842,8 @@ 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 -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/support/ClassPathXmlApplicationContext.html[`ClassPathXmlApplicationContext`] -or http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/support/FileSystemXmlApplicationContext.html[`FileSystemXmlApplicationContext`]. +{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 @@ -879,11 +851,11 @@ 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 J2EE web descriptor XML in the `web.xml` file +simple eight (or so) lines of boilerplate web descriptor XML in the `web.xml` file of the application will typically suffice (see <>). If you are using the http://spring.io/tools/sts[SpringSource Tool Suite] Eclipse-powered development -environment or https://github.com/spring-projects/spring-roo[Spring Roo] this -boilerplate configuration can be easily created with few mouse clicks or keystrokes. +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 @@ -891,7 +863,7 @@ created and initialized, you have a fully configured and executable system or application. .The Spring IoC container -image::images/container-magic.png[] +image::images/container-magic.png[width=250] @@ -909,8 +881,9 @@ 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. +Spring IoC container itself is __totally__ decoupled from the format in which this +configuration metadata is actually written. These days many developers choose +<> for their Spring applications. ==== For information about using other forms of metadata with the Spring container, see: @@ -920,12 +893,13 @@ For information about using other forms of metadata with the Spring container, s * <>: 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` + 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 `` elements inside a top-level `` element. +beans configured as `` elements inside a top-level `` 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 @@ -939,26 +913,26 @@ dependency-inject domain objects with Spring>>. The following example shows the basic structure of XML-based configuration metadata: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - - + + + - - - + + + - + - + ---- The `id` attribute is a string that you use to identify the individual bean definition. @@ -976,11 +950,11 @@ supplied to an `ApplicationContext` constructor are actually resource strings th 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -ApplicationContext context = - new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"}); + ApplicationContext context = + new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"}); ---- [NOTE] @@ -994,61 +968,60 @@ particular, `Resource` paths are used to construct applications contexts as desc The following example shows the service layer objects `(services.xml)` configuration file: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + - - - - - + + + + + - + - + ---- The following example shows the data access objects `daos.xml` file: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - - + + + - - - + + + - + - + ---- In the preceding example, the service layer consists of the class `PetStoreServiceImpl`, -and two data access objects of the type `SqlMapAccountDao` and SqlMapItemDao are based -on the http://ibatis.apache.org/[iBatis] Object/Relational mapping framework. 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 <>. +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-factory-xml-import]] @@ -1061,19 +1034,17 @@ XML fragments. This constructor takes multiple `Resource` locations, as was show previous section. Alternatively, use one or more occurrences of the `` element to load bean definitions from another file or files. For example: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + + + + - - - - - - - - + + + ---- In the preceding example, external bean definitions are loaded from three files, @@ -1084,7 +1055,7 @@ same directory or classpath location as the file doing the importing, while 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 `` element, must -be valid XML bean definitions according to the Spring Schema or DTD. +be valid XML bean definitions according to the Spring Schema. [NOTE] ==== @@ -1113,18 +1084,18 @@ name, Class requiredType)` you can retrieve instances of your beans. The `ApplicationContext` enables you to read bean definitions and access them as follows: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// create and configure beans -ApplicationContext context = - new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"}); + // create and configure beans + ApplicationContext context = + new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"}); -// retrieve configured instance -PetStoreServiceImpl service = context.getBean("petStore", PetStoreServiceImpl.class); + // retrieve configured instance + PetStoreService service = context.getBean("petStore", PetStoreService.class); -// use configured instance -List userList = service.getUsernameList(); + // use configured instance + List userList = service.getUsernameList(); ---- You use `getBean()` to retrieve instances of your beans. The `ApplicationContext` @@ -1151,7 +1122,7 @@ objects, which contain (among other information) the following metadata: * 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__. + 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. @@ -1164,31 +1135,31 @@ This metadata translates to a set of properties that make up each bean definitio | Property| Explained in... | class -| <> +| <> | name -| <> +| <> | scope -| <> +| <> | constructor arguments -| <> +| <> | properties -| <> +| <> | autowiring mode -| <> +| <> | lazy-initialization mode -| <> +| <> | initialization method -| <> +| <> | destruction method -| <> +| <> |=== In addition to bean definitions that contain information on how to create a specific @@ -1203,7 +1174,7 @@ defined through metadata bean definitions. [[beans-beanname]] -==== Naming beansBean naming conventions +==== 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. @@ -1211,11 +1182,11 @@ 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 -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 typed as an `xsd:ID`, which constrained possible characters. As of 3.1, -it is now `xsd:string`. Note that bean id uniqueness is still enforced by the +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 typed as an `xsd:ID`, which constrained possible characters. As of 3.1, +it is now `xsd:string`. 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 @@ -1225,6 +1196,7 @@ refer to that bean by name, through the use of the `ref` element or Motivations for not supplying a name are related to using <> and <>. +.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 @@ -1252,37 +1224,41 @@ elsewhere. This is commonly the case in large systems where configuration is spl amongst each subsystem, each subsystem having its own set of object definitions. In XML-based configuration metadata, you can use the `` element to accomplish this. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- 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 +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 +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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + ---- 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 <> for details. +**** [[beans-factory-class]] -==== Instantiating beansInner class names +==== 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. @@ -1304,6 +1280,7 @@ You use the `Class` property in one of two ways: 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. @@ -1336,12 +1313,12 @@ well. With XML-based configuration metadata you can specify your bean class as follows: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + ---- For details about the mechanism for supplying arguments to the constructor (if required) @@ -1363,25 +1340,25 @@ factory-method. The definition does not specify the type (class) of the returned only the class containing the factory method. In this example, the `createInstance()` method must be a __static__ method. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class ClientService { - private static ClientService clientService = new ClientService(); - private ClientService() {} + public class ClientService { + private static ClientService clientService = new ClientService(); + private ClientService() {} - public static ClientService createInstance() { - return clientService; - } -} + public static ClientService createInstance() { + return clientService; + } + } ---- For details about the mechanism for supplying (optional) arguments to the factory method @@ -1399,67 +1376,71 @@ specify the name of a bean in the current (or parent/ancestor) container that co 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + - - + + ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class DefaultServiceLocator { - private static ClientService clientService = new ClientServiceImpl(); - private DefaultServiceLocator() {} + public class DefaultServiceLocator { - public ClientService createClientServiceInstance() { - return clientService; - } -} + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + - + + + ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class DefaultServiceLocator { - private static ClientService clientService = new ClientServiceImpl(); - private static AccountService accountService = new AccountServiceImpl(); + public class DefaultServiceLocator { - private DefaultServiceLocator() {} + private static ClientService clientService = new ClientServiceImpl(); + private static AccountService accountService = new AccountServiceImpl(); - public ClientService createClientServiceInstance() { - return clientService; - } + private DefaultServiceLocator() {} - public AccountService createAccountServiceInstance() { - return accountService; - } -} + public ClientService createClientServiceInstance() { + return clientService; + } + + public AccountService createAccountServiceInstance() { + return accountService; + } + + } ---- This approach shows that the factory bean itself can be managed and configured through @@ -1496,7 +1477,7 @@ that is, the other objects they work with, only through constructor arguments, a 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 +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. @@ -1520,21 +1501,22 @@ following example shows a class that can only be dependency-injected with constr 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class SimpleMovieLister { + public class SimpleMovieLister { - // the SimpleMovieLister has a dependency on a MovieFinder - private MovieFinder movieFinder; + // 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; - } + // 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... -} + // business logic that actually 'uses' the injected MovieFinder is omitted... + + } ---- [[beans-factory-ctor-arguments-resolution]] @@ -1545,17 +1527,18 @@ order in which the constructor arguments are defined in a bean definition is the in which those arguments are supplied to the appropriate constructor when the bean is being instantiated. Consider the following class: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package x.y; + package x.y; -public class Foo { + public class Foo { - public Foo(Bar bar, Baz baz) { - // ... - } -} + public Foo(Bar bar, Baz baz) { + // ... + } + + } ---- No potential ambiguity exists, assuming that `Bar` and `Baz` classes are not related by @@ -1563,44 +1546,45 @@ inheritance. Thus the following configuration works fine, and you do not need to the constructor argument indexes and/or types explicitly in the `` element. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - + + + + + - - + - + + ---- 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 -`true`, Spring cannot determine the type of the value, and so cannot match +`true`, Spring cannot determine the type of the value, and so cannot match by type without help. Consider the following class: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package examples; + package examples; -public class ExampleBean { + public class ExampleBean { - // No. of years to the calculate the Ultimate Answer - private int years; + // No. of years to the calculate the Ultimate Answer + private int years; - // The Answer to Life, the Universe, and Everything - private String ultimateAnswer; + // The Answer to Life, the Universe, and Everything + private String ultimateAnswer; - public ExampleBean(int years, String ultimateAnswer) { - this.years = years; - this.ultimateAnswer = ultimateAnswer; - } -} + public ExampleBean(int years, String ultimateAnswer) { + this.years = years; + this.ultimateAnswer = ultimateAnswer; + } + + } ---- .[[beans-factory-ctor-arguments-type]]Constructor argument type matching @@ -1609,13 +1593,13 @@ In the preceding scenario, the container __can__ use type matching with simple t you explicitly specify the type of the constructor argument using the `type` attribute. For example: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + ---- -- @@ -1624,13 +1608,13 @@ For example: Use the `index` attribute to specify explicitly the index of constructor arguments. For example: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + ---- In addition to resolving the ambiguity of multiple simple values, specifying an index @@ -1640,15 +1624,15 @@ __index is 0 based__. .[[beans-factory-ctor-arguments-name]]Constructor argument name -- -As of Spring 3.0 you can also use the constructor parameter name for value disambiguation: +You can also use the constructor parameter name for value disambiguation: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + ---- Keep in mind that to make this work out of the box your code must be compiled with the @@ -1658,27 +1642,28 @@ http://download.oracle.com/javase/6/docs/api/java/beans/ConstructorProperties.ht JDK annotation to explicitly name your constructor arguments. The sample class would then have to look as follows: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package examples; + package examples; -public class ExampleBean { + public class ExampleBean { - // Fields omitted + // Fields omitted - @ConstructorProperties({"years", "ultimateAnswer"}) - public ExampleBean(int years, String ultimateAnswer) { - this.years = years; - this.ultimateAnswer = ultimateAnswer; - } -} + @ConstructorProperties({"years", "ultimateAnswer"}) + public ExampleBean(int years, String ultimateAnswer) { + this.years = years; + this.ultimateAnswer = ultimateAnswer; + } + + } ---- -- [[beans-setter-injection]] -===== Setter-based dependency injectionConstructor-based or setter-based DI? +===== 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. @@ -1687,21 +1672,22 @@ The following example shows a class that can only be dependency-injected using p 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class SimpleMovieLister { + public class SimpleMovieLister { - // the SimpleMovieLister has a dependency on the MovieFinder - private MovieFinder movieFinder; + // 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; - } + // 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... -} + // business logic that actually 'uses' the injected MovieFinder is omitted... + + } ---- The `ApplicationContext` supports constructor- and setter-based DI for the beans it @@ -1713,6 +1699,7 @@ directly (programmatically), but rather with an XML definition file that is then converted internally into instances of these classes, and used to load an entire Spring IoC container instance. +.Constructor-based or setter-based DI? **** Since you can mix both, Constructor- and Setter-based DI, it is a good rule of thumb to use constructor arguments for mandatory dependencies and setters for optional @@ -1737,7 +1724,7 @@ the only available DI. [[beans-dependency-resolution]] -===== Dependency resolution processCircular dependencies +===== Dependency resolution process The container performs bean dependency resolution as follows: * The `ApplicationContext` is created and initialized with configuration metadata that @@ -1763,6 +1750,7 @@ are created when the container is created. Scopes are defined in 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. +.Circular dependencies **** If you use predominantly constructor injection, it is possible to create an unresolvable circular dependency scenario. @@ -1812,85 +1800,85 @@ are invoked. 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + + + - - + + + + - - - - - - - + + ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class ExampleBean { + public class ExampleBean { - private AnotherBean beanOne; - private YetAnotherBean beanTwo; - private int i; + private AnotherBean beanOne; + private YetAnotherBean beanTwo; + private int i; - public void setBeanOne(AnotherBean beanOne) { - this.beanOne = beanOne; - } + public void setBeanOne(AnotherBean beanOne) { + this.beanOne = beanOne; + } - public void setBeanTwo(YetAnotherBean beanTwo) { - this.beanTwo = beanTwo; - } + public void setBeanTwo(YetAnotherBean beanTwo) { + this.beanTwo = beanTwo; + } - public void setIntegerProperty(int i) { - this.i = i; - } -} + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + + + + + - - - - + + - - + + - - - - - + + ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class ExampleBean { + public class ExampleBean { - private AnotherBean beanOne; - private YetAnotherBean beanTwo; - private int i; + 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; - } -} + 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 @@ -1899,41 +1887,41 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - + + + + + - - + + ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class ExampleBean { + public class ExampleBean { - // a private constructor - private 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) { + // 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; - } -} + ExampleBean eb = new ExampleBean (...); + // some other operations... + return eb; + } + + } ---- Arguments to the `static` factory method are supplied via `` elements, @@ -1958,43 +1946,42 @@ inline. Spring's XML-based configuration metadata supports sub-element types wit ===== Straight values (primitives, Strings, and so on) The `value` attribute of the `` element specifies a property or constructor -argument as a human-readable string representation. <>, JavaBeans `PropertyEditors` are used to convert these string +argument as a human-readable string representation. Spring's +<> is used to convert these values from a `String` to the actual type of the property or argument. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - + + + + + + + ---- The following example uses the <> for even more succinct XML configuration. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - + ---- The preceding XML is more succinct; however, typos are discovered at runtime rather than @@ -2005,20 +1992,20 @@ assistance is highly recommended. You can also configure a `java.util.Properties` instance as: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - - jdbc.driver.className=com.mysql.jdbc.Driver - jdbc.url=jdbc:mysql://localhost:3306/mydb - - - + + + + jdbc.driver.className=com.mysql.jdbc.Driver + jdbc.url=jdbc:mysql://localhost:3306/mydb + + + ---- The Spring container converts the text inside the `` element into a @@ -2033,29 +2020,29 @@ The `idref` element is simply an error-proof way to pass the __id__ (string valu a reference) of another bean in the container to a `` or `` element. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - - - + + + + + ---- The above bean definition snippet is __exactly__ equivalent (at runtime) to the following snippet: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - + + + ---- The first form is preferable to the second, because using the `idref` tag allows the @@ -2070,18 +2057,18 @@ Additionally, if the referenced bean is in the same XML unit, and the bean name bean __id__, you can use the `local` attribute, which allows the XML parser itself to validate the bean id earlier, at XML document parse time. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + ---- -A common place (at least in versions earlier than Spring 2.0) where the element +A common place (at least in versions earlier than Spring 2.0) where the `` element brings value is in the configuration of <> in a -`ProxyFactoryBean` bean definition. Using elements when you specify the +`ProxyFactoryBean` bean definition. Using `` elements when you specify the interceptor names prevents you from misspelling an interceptor id. @@ -2102,10 +2089,10 @@ parent container, regardless of whether it is in the same XML file. The value of `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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- Specifying the target bean through the `local` attribute leverages the ability of the @@ -2115,10 +2102,10 @@ issues an error if no matching element is found in the same file. As such, using local variant is the best choice (in order to know about errors as early as possible) if the target bean is in the same XML file. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- Specifying the target bean through the `parent` attribute creates a reference to a bean @@ -2129,26 +2116,26 @@ parent container of the current one. You use this bean reference variant mainly 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + ---- -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - class="org.springframework.aop.framework.ProxyFactoryBean"> - - - - - + + + class="org.springframework.aop.framework.ProxyFactoryBean"> + + + + + ---- @@ -2157,18 +2144,18 @@ container with a proxy that will have the same name as the parent bean. A `` element inside the `` or `` elements defines a so-called __inner bean__. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - + + + + + + + + + ---- An inner bean definition does not require a defined id or name; the container ignores @@ -2183,54 +2170,54 @@ In the ``, ``, ``, and `` elements, you set the prope and arguments of the Java `Collection` types `List`, `Set`, `Map`, and `Properties`, respectively. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - administrator@example.org - support@example.org - development@example.org - - - - - - a list element followed by a reference - - - - - - - - - - - - - - just some string - - - - + + + + + administrator@example.org + support@example.org + development@example.org + + + + + + a list element followed by a reference + + + + + + + + + + + + + + just some string + + + + ---- __The value of a map key or value, or a set value, can also again be any of the following elements:__ -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -bean | ref | idref | list | set | map | props | value | null + bean | ref | idref | list | set | map | props | value | null ---- [[beans-collection-elements-merging]] ====== Collection merging -As of Spring 2.0, the container supports the __merging__ of collections. An application +The Spring container also supports the __merging__ of collections. An application developer can define a parent-style ``, ``, `` or `` element, and have child-style ``, ``, `` or `` elements inherit and override values from the parent collection. That is, the child collection's values are @@ -2243,28 +2230,28 @@ with parent and child bean definitions may wish to read the The following example demonstrates collection merging: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - administrator@example.com - support@example.com - - - - - - - - sales@example.com - support@example.co.uk - - - - + + + + + administrator@example.com + support@example.com + + + + + + + + sales@example.com + support@example.co.uk + + + + ---- Notice the use of the `merge=true` attribute on the `` element of the @@ -2273,7 +2260,7 @@ 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. -[source] +[literal] [subs="verbatim,quotes"] ---- administrator=administrator@example.com @@ -2300,44 +2287,43 @@ You cannot merge different collection types (such as a `Map` and a `List`), and 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. -The merging feature is available only in Spring 2.0 and later. [[beans-collection-elements-strongly-typed]] -====== Strongly-typed collection (Java 5+ only) -In Java 5 and later, you can use strongly typed collections (using generic types). 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`. +====== 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class Foo { + public class Foo { - private Map accounts; + private Map accounts; - public void setAccounts(Map accounts) { - this.accounts = accounts; - } -} + public void setAccounts(Map accounts) { + this.accounts = accounts; + } + } ---- -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - - - + + + + + + + + + + + ---- When the `accounts` property of the `foo` bean is prepared for injection, the generics @@ -2353,23 +2339,25 @@ Spring treats empty arguments for properties and the like as empty `Strings`. Th following XML-based configuration metadata snippet sets the email property to the empty `String` value ("") -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- The preceding example is equivalent to the following Java code: `exampleBean.setEmail("")`. The `` element handles `null` values. For example: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + + + ---- The above configuration is equivalent to the following Java code: @@ -2381,30 +2369,30 @@ The above configuration is equivalent to the following Java code: The p-namespace enables you to use the `bean` element's attributes, instead of nested `` elements, to describe your property values and/or collaborating beans. -Spring 2.0 and later supports extensible configuration formats <>, 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. +Spring supports extensible configuration formats <>, 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - + + + - - + + ---- The example shows an attribute in the p-namespace called email in the bean definition. @@ -2415,29 +2403,29 @@ to the property name. This next example includes two more bean definitions that both have a reference to another bean: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - - + + + + - + - - - - + + + + ---- As you can see, this example includes not only a property value using the p-namespace, @@ -2466,29 +2454,29 @@ then nested `constructor-arg` elements. Let's review the examples from <> with the `c` namespace: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- - + - - + + - <-- 'traditional' declaration --> - - - - - + <-- 'traditional' declaration --> + + + + + - <-- 'c-namespace' declaration --> - + <-- 'c-namespace' declaration --> + - + ---- The `c:` namespace uses the same conventions as the `p:` one (trailing `-ref` for bean @@ -2500,16 +2488,16 @@ For the rare cases where the constructor argument names are not available (usual the bytecode was compiled without debugging information), one can use fallback to the argument indexes: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -<-- 'c-namespace' index declaration --> - + <-- 'c-namespace' index declaration --> + ---- [NOTE] ==== -Due to the XML grammar, the index notation requires the presence of the leading _____ as +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). ==== @@ -2525,12 +2513,12 @@ You can use compound or nested property names when you set bean properties, as l all components of the path except the final property name are not `null`. Consider the following bean definition. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- The `foo` bean has a `fred` property, which has a `bob` property, which has a `sammy` @@ -2544,7 +2532,7 @@ this to work, the `fred` property of `foo`, and the `bob` property of `fred` mus ==== 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<` +property of another. Typically you accomplish this with the <` 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 @@ -2552,32 +2540,32 @@ explicitly force one or more beans to be initialized before the bean using this is initialized. The following example uses the `depends-on` attribute to express a dependency on a single bean: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + ---- 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + - - + + ---- [NOTE] ==== The `depends-on` attribute in the bean definition can specify both an initialization -time dependency and, in the case of<> beans +time dependency and, in the case of <> 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. @@ -2599,11 +2587,11 @@ instance when it is first requested, rather than at startup. In XML, this behavior is controlled by the `lazy-init` attribute on the `` element; for example: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + ---- When the preceding configuration is consumed by an `ApplicationContext`, the bean named @@ -2618,12 +2606,12 @@ 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 `` element; for example: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- @@ -2700,7 +2688,7 @@ 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, + 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, @@ -2722,7 +2710,7 @@ In the latter scenario, you have several options: to `false` as described in the next section. * Designate a single bean definition as the __primary__ candidate by setting the `primary` attribute of its `` element to `true`. -* If you are using Java 5 or later, implement the more fine-grained control available +* Implement the more fine-grained control available with annotation-based configuration, as described in <>. @@ -2737,7 +2725,7 @@ makes that specific bean definition unavailable to the autowiring infrastructure You can also limit autowire candidates based on pattern-matching against bean names. The top-level `` 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 +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. @@ -2766,39 +2754,39 @@ and by <> ask typically new) bean B instance every time bean A needs it. The following is an example of this approach: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// a class that uses a stateful Command-style class to perform some processing -package fiona.apple; + // 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; + // Spring-API imports + import org.springframework.beans.BeansException; + import org.springframework.context.ApplicationContext; + import org.springframework.context.ApplicationContextAware; -public class CommandManager implements ApplicationContextAware { + public class CommandManager implements ApplicationContextAware { - private ApplicationContext applicationContext; + 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(); - } + 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); - } + 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; - } -} + 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 @@ -2838,53 +2826,53 @@ Spring container will dynamically override the implementation of the `createComm method. Your `CommandManager` class will not have any Spring dependencies, as can be seen in the reworked example: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package fiona.apple; + package fiona.apple; -// no more Spring imports! + // no more Spring imports! -public abstract class CommandManager { + 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(); - } + 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(); -} + // 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - [abstract] theMethodName(no-arguments); + [abstract] 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + - - - - + + + + ---- The bean identified as __commandManager__ calls its own method `createCommand()` @@ -2895,7 +2883,6 @@ 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, @@ -2915,55 +2902,55 @@ With XML-based configuration metadata, you can use the `replaced-method` element 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MyValueCalculator { + public class MyValueCalculator { -public String computeValue(String input) { - // some real code... -} + public String computeValue(String input) { + // some real code... + } -// some other methods... + // some other methods... -} + } ---- A class implementing the `org.springframework.beans.factory.support.MethodReplacer` interface provides the new method definition. -[source,java] +[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 { + /** + * 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 ...; - } -} + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + + + + String + + - - - String - - - - + ---- You can use one or more contained `` elements within the `` @@ -2973,12 +2960,12 @@ exist within the class. For convenience, the type string for an argument may be substring of the fully qualified type name. For example, the following all match `java.lang.String`: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -java.lang.String - String - Str + java.lang.String + String + Str ---- Because the number of arguments is often enough to distinguish between each possible @@ -3012,23 +2999,23 @@ The following scopes are supported out of the box. You can also create |=== | Scope| Description -| <> +| <> | (Default) Scopes a single bean definition to a single object instance per Spring IoC container. -| <> +| <> | Scopes a single bean definition to any number of object instances. -| <> +| <> | 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`. -| <> +| <> | Scopes a single bean definition to the lifecycle of an HTTP `Session`. Only valid in the context of a web-aware Spring `ApplicationContext`. -| <> +| <> | 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`. @@ -3038,7 +3025,7 @@ The following scopes are supported out of the box. You can also create ==== As of Spring 3.0, a __thread scope__ is available, but is not registered by default. For more information, see the documentation for -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/support/SimpleThreadScope.html[SimpleThreadScope]. +{javadoc-baseurl}/org/springframework/context/support/SimpleThreadScope.html[SimpleThreadScope]. For instructions on how to register this or any other custom scope, see <>. ==== @@ -3054,27 +3041,27 @@ 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 +singleton beans, and __all subsequent requests and references__ for that named bean return the cached object. -image::images/singleton.png[] +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 +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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - + + ---- @@ -3092,22 +3079,21 @@ The following diagram illustrates the Spring prototype scope. __A data access ob any conversational state; it was just easier for this author to reuse the core of the singleton diagram.__ -image::images/prototype.png[] +image::images/prototype.png[width=400] The following example defines a bean as a prototype in XML: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + ---- 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__ +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 @@ -3162,22 +3148,24 @@ setup is necessary: `DispatcherServlet` and `DispatcherPortlet` already expose a relevant state. If you use a Servlet 2.4+ web container, with requests processed outside of Spring's -DispatcherServlet (for example, when using JSF or Struts), you need to add the following -`javax.servlet.ServletRequestListener` to the declarations in your web applications -`web.xml` file: +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 applications `web.xml` file: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - -... - - - org.springframework.web.context.request.RequestContextListener - - -... - + + ... + + + org.springframework.web.context.request.RequestContextListener + + + ... + ---- If you use an older web container (Servlet 2.3), use the provided `javax.servlet.Filter` @@ -3187,21 +3175,21 @@ requests outside of Spring's DispatcherServlet on a Servlet 2.3 container. (The mapping depends on the surrounding web application configuration, so you must change it as appropriate.) -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - -.. - - requestContextFilter - org.springframework.web.filter.RequestContextFilter - - - requestContextFilter - /* - -... - + + ... + + requestContextFilter + org.springframework.web.filter.RequestContextFilter + + + requestContextFilter + /* + + ... + ---- `DispatcherServlet`, `RequestContextListener` and `RequestContextFilter` all do exactly @@ -3214,10 +3202,10 @@ down the call chain. ===== Request scope Consider the following bean definition: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- The Spring container creates a new instance of the `LoginAction` bean by using the @@ -3233,10 +3221,10 @@ bean that is scoped to the request is discarded. ===== Session scope Consider the following bean definition: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- The Spring container creates a new instance of the `UserPreferences` bean by using the @@ -3254,10 +3242,10 @@ HTTP `Session` is eventually discarded, the bean that is scoped to that particul ===== Global session scope Consider the following bean definition: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- The `global session` scope is similar to the standard HTTP `Session` scope @@ -3291,51 +3279,48 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - + + + + + - - - - - - - - - - - - + + + + + + ---- To create such a proxy, you insert a child `` element into a scoped -bean definition. See <> and +bean definition. See <> and <>.) Why do definitions of beans scoped at the `request`, `session`, `globalSession` and custom-scope levels require the `` 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - + + + ---- In the preceding example, the singleton bean `userManager` is injected with a reference @@ -3364,15 +3349,15 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - + + + + + + ---- [[beans-factory-scopes-other-injection-proxies]] @@ -3380,8 +3365,11 @@ Thus you need the following, correct and complete, configuration when injecting By default, when the Spring container creates a proxy for a bean that is marked up with the `` element, __a CGLIB-based class proxy is created__. -__Note: CGLIB proxies only intercept public method calls!__ Do not call non-public +[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 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 @@ -3392,16 +3380,16 @@ the scoped bean must implement at least one interface, and __that all__ collabor into which the scoped bean is injected must reference the bean through one of its interfaces. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - + + + + + + + ---- For more detailed information about choosing class-based or interface-based proxying, @@ -3411,7 +3399,7 @@ see <>. [[beans-factory-scopes-custom]] ==== Custom scopes -As of Spring 2.0, the bean scoping mechanism is extensible. You can define your own +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. @@ -3422,7 +3410,7 @@ To integrate your custom scope(s) into the Spring container, you need to impleme `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 -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/config/Scope.html[Scope +{javadoc-baseurl}/org/springframework/beans/factory/config/Scope.html[Scope Javadoc], 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 @@ -3433,10 +3421,10 @@ implementation, for example, returns the session-scoped bean (and if it does not the method returns a new instance of the bean, after having bound it to the session for future reference). -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -Object get(String name, ObjectFactory objectFactory) + Object get(String name, ObjectFactory objectFactory) ---- The following method removes the object from the underlying scope. The session scope @@ -3444,30 +3432,30 @@ implementation for example, removes the session-scoped bean from the underlying The object should be returned, but you can return null if the object with the specified name is not found. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -Object remove(String name) + 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 Javadoc or a Spring scope implementation for more information on destruction callbacks. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -void registerDestructionCallback(String name, Runnable destructionCallback) + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -String getConversationId() + String getConversationId() ---- @@ -3477,10 +3465,10 @@ After you write and test one or more custom `Scope` implementations, you need to 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -void registerScope(String scopeName, Scope scope); + void registerScope(String scopeName, Scope scope); ---- This method is declared on the `ConfigurableBeanFactory` interface, which is available @@ -3501,57 +3489,57 @@ registered by default. The instructions would be the same for your own custom `S implementations. ==== -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -Scope threadScope = new SimpleThreadScope(); -beanFactory.registerScope("thread", threadScope); + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - - - - - - - - + + + + + + + + + - - - - + + + + - - - + + + - + ---- [NOTE] @@ -3581,7 +3569,7 @@ to perform certain actions upon initialization and destruction of your beans. 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<>. +details see <>. 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. @@ -3606,53 +3594,55 @@ The `org.springframework.beans.factory.InitializingBean` interface allows a bean 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -void afterPropertiesSet() throws Exception; + 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<> or +the <> 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. For example, the following definition: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class ExampleBean { + public class ExampleBean { - public void init() { - // do some initialization work - } -} + public void init() { + // do some initialization work + } + + } ---- ...is exactly the same as... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class AnotherExampleBean implements InitializingBean { + public class AnotherExampleBean implements InitializingBean { - public void afterPropertiesSet() { - // do some initialization work - } -} + public void afterPropertiesSet() { + // do some initialization work + } + + } ---- but does not couple the code to Spring. @@ -3664,56 +3654,58 @@ Implementing the `org.springframework.beans.factory.DisposableBean` interface al bean to get a callback when the container containing it is destroyed. The `DisposableBean` interface specifies a single method: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -void destroy() throws Exception; + 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<> or +the <> 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 ``. For example, the following definition: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class ExampleBean { + public class ExampleBean { - public void cleanup() { - // do some destruction work (like releasing pooled connections) - } -} + public void cleanup() { + // do some destruction work (like releasing pooled connections) + } + + } ---- -...is exactly the same as... +is exactly the same as: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class AnotherExampleBean implements DisposableBean { + public class AnotherExampleBean implements DisposableBean { - public void destroy() { - // do some destruction work (like releasing pooled connections) - } -} + public void destroy() { + // do some destruction work (like releasing pooled connections) + } + + } ---- -... but does not couple the code to Spring. +but does not couple the code to Spring. [[beans-factory-lifecycle-default-init-destroy-methods]] @@ -3737,36 +3729,37 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class DefaultBlogService implements BlogService { + public class DefaultBlogService implements BlogService { - private BlogDao blogDao; + private BlogDao blogDao; - public void setBlogDao(BlogDao blogDao) { - this.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."); - } - } -} + // 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - + + + - + ---- The presence of the `default-init-method` attribute on the top-level `` element @@ -3831,18 +3824,18 @@ Destroy methods are called in the same order: 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface Lifecycle { + public interface Lifecycle { - void start(); + void start(); - void stop(); + void stop(); - boolean isRunning(); + boolean isRunning(); -} + } ---- Any Spring-managed object may implement that interface. Then, when the @@ -3850,16 +3843,16 @@ ApplicationContext itself starts and stops, it will cascade those calls to all L implementations defined within that context. It does this by delegating to a `LifecycleProcessor`: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface LifecycleProcessor extends Lifecycle { + public interface LifecycleProcessor extends Lifecycle { - void onRefresh(); + void onRefresh(); - void onClose(); + void onClose(); -} + } ---- Notice that the `LifecycleProcessor` is itself an extension of the `Lifecycle` @@ -3874,22 +3867,26 @@ prior to objects of another type. In those cases, the `SmartLifecycle` interface another option, namely the `getPhase()` method as defined on its super-interface, `Phased`. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface Phased { + public interface Phased { - int getPhase(); + int getPhase(); -} + } +---- -public interface SmartLifecycle extends Lifecycle, Phased { +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + public interface SmartLifecycle extends Lifecycle, Phased { - boolean isAutoStartup(); + boolean isAutoStartup(); - void stop(Runnable callback); + void stop(Runnable callback); -} + } ---- When starting, the objects with the lowest phase start first, and when stopping, the @@ -3913,13 +3910,13 @@ You can override the default lifecycle processor instance by defining a bean nam "lifecycleProcessor" within the context. If you only want to modify the timeout, then defining the following would be sufficient: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + ---- As mentioned, the `LifecycleProcessor` interface defines callback methods for the @@ -3955,44 +3952,47 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import org.springframework.context.support.AbstractApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; + import org.springframework.context.support.AbstractApplicationContext; + import org.springframework.context.support.ClassPathXmlApplicationContext; -public final class Boot { + public final class Boot { - public static void main(final String[] args) throws Exception { - AbstractApplicationContext ctx - = new ClassPathXmlApplicationContext(new String []{"beans.xml"}); + public static void main(final String[] args) throws Exception { - // add a shutdown hook for the above context... - ctx.registerShutdownHook(); + AbstractApplicationContext ctx = new ClassPathXmlApplicationContext( + new String []{"beans.xml"}); - // app runs here... + // add a shutdown hook for the above context... + ctx.registerShutdownHook(); - // main method exits, hook is called prior to the app shutting down... - } -} + // app runs here... + + // main method exits, hook is called prior to the app shutting down... + + } + } ---- [[beans-factory-aware]] -==== ApplicationContextAware and `BeanNameAware` +==== ApplicationContextAware and BeanNameAware When an `ApplicationContext` creates a class that implements the `org.springframework.context.ApplicationContextAware` interface, the class is provided with a reference to that `ApplicationContext`. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface ApplicationContextAware { + public interface ApplicationContextAware { - void setApplicationContext(ApplicationContext applicationContext) throws BeansException; -} + void setApplicationContext(ApplicationContext applicationContext) throws BeansException; + + } ---- Thus beans can manipulate programmatically the `ApplicationContext` that created them, @@ -4021,17 +4021,18 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface BeanNameAware { + public interface BeanNameAware { - void setBeanName(string name) throws BeansException; -} + void setBeanName(string name) throws BeansException; + + } ---- The callback is invoked after population of normal bean properties but before an -initialization callback such as `InitializingBean` s __afterPropertiesSet__ or a custom +initialization callback such as `InitializingBean` __afterPropertiesSet__ or a custom init-method. @@ -4052,19 +4053,19 @@ dependency type: | `ApplicationContextAware` | Declaring `ApplicationContext` -a| <> +| <> | `ApplicationEventPublisherAware` | Event publisher of the enclosing `ApplicationContext` -a| <> +| <> | `BeanClassLoaderAware` | Class loader used to load the bean classes. -a| <> +| <> | `BeanFactoryAware` | Declaring `BeanFactory` -a| <> +| <> | `BeanNameAware` | Name of the declaring bean @@ -4136,23 +4137,21 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + - - - - - - + + + + ---- A child bean definition uses the bean class from the parent definition if none is @@ -4171,19 +4170,19 @@ The preceding example explicitly marks the parent bean definition as abstract by 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + - - - - + + + + ---- The parent bean cannot be instantiated on its own because it is incomplete, and it is @@ -4278,9 +4277,9 @@ While the recommended approach for `BeanPostProcessor` registration is through 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 +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 +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. ==== @@ -4304,7 +4303,7 @@ eligible for getting processed by all BeanPostProcessor interfaces (for example: 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 +`@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 @@ -4325,84 +4324,86 @@ it is created by the container and prints the resulting string to the system con Find below the custom `BeanPostProcessor` implementation class definition: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package scripting; + package scripting; -import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.beans.BeansException; + import org.springframework.beans.factory.config.BeanPostProcessor; + import org.springframework.beans.BeansException; -public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor { + 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... - } + // 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; - } -} + public Object postProcessAfterInitialization(Object bean, + String beanName) throws BeansException { + System.out.println("Bean '" + beanName + "' created : " + bean.toString()); + return bean; + } + + } ---- -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - - + + + - - + + - + ---- 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 2.0 dynamic language support is detailed in the chapter entitled +script. The Spring dynamic language support is detailed in the chapter entitled <>.) The following simple Java application executes the preceding code and configuration: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import org.springframework.context.ApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; -import org.springframework.scripting.Messenger; + import org.springframework.context.ApplicationContext; + import org.springframework.context.support.ClassPathXmlApplicationContext; + import org.springframework.scripting.Messenger; -public final class Boot { + 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); - } -} + 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: -[source] +[literal] [subs="verbatim,quotes"] ---- Bean 'messenger' created : org.springframework.scripting.groovy.GroovyMessenger@272961 @@ -4428,9 +4429,9 @@ actually (configured to be) dependency-injected with a value. 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` s operate on the __bean configuration metadata__; -that is, the Spring IoC container allows `BeanFactoryPostProcessors` to read the -configuration metadata and potentially change it__before__ the container instantiates +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 @@ -4495,28 +4496,28 @@ Consider the following XML-based configuration metadata fragment, where a `DataS 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 / +are specified as __placeholders__ of the form `${property-name}` which follows the Ant / log4j / JSP EL style. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + - - - - - - + + + + + + ---- The actual values come from another file in the standard Java `Properties` format: -[source] +[literal] [subs="verbatim,quotes"] ---- jdbc.driverClassName=org.hsqldb.jdbcDriver @@ -4535,10 +4536,10 @@ With the `context` namespace introduced in Spring 2.5, it is possible to configu 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- The `PropertyPlaceholderConfigurer` not only looks for properties in the `Properties` @@ -4562,19 +4563,19 @@ You can use the `PropertyPlaceholderConfigurer` to substitute class names, which sometimes useful when you have to pick a particular implementation class at runtime. For example: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - classpath:com/foo/strategy.properties - - - custom.strategy.class=com.foo.DefaultStrategy - - + + + classpath:com/foo/strategy.properties + + + custom.strategy.class=com.foo.DefaultStrategy + + - + ---- If the class cannot be resolved at runtime to a valid class, resolution of the bean @@ -4599,7 +4600,7 @@ values for the same bean property, the last one wins, due to the overriding mech Properties file configuration lines take this format: -[source,java] +[literal] [subs="verbatim,quotes"] ---- beanName.property=value @@ -4607,7 +4608,7 @@ beanName.property=value For example: -[source,java] +[literal] [subs="verbatim,quotes"] ---- dataSource.driverClassName=com.mysql.jdbc.Driver @@ -4621,7 +4622,7 @@ Compound property names are also supported, as long as every component of the pa except the final property being overridden is already non-null (presumably initialized by the constructors). In this example... -[source] +[literal] [subs="verbatim,quotes"] ---- foo.fred.bob.sammy=123 @@ -4640,10 +4641,10 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- @@ -4685,13 +4686,14 @@ product of the `FactoryBean`; whereas, invoking `getBean("&myBean")` returns the [[beans-annotation-config]] -=== Annotation-based container configurationAre annotations better than XML for configuring Spring? -**** +=== 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 her better. Due to the way they are defined, annotations +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 @@ -4703,7 +4705,6 @@ It's worth pointing out that through its <> option, Sprin 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 http://www.springsource.com/products/sts[SpringSource Tool Suite]. - **** An alternative to XML setups is provided by annotation-based configuration which rely on @@ -4731,29 +4732,29 @@ As always, you can register them as individual bean definitions, but they can al implicitly registered by including the following tag in an XML-based Spring configuration (notice the inclusion of the `context` namespace): -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + - + ---- (The implicitly registered post-processors include -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.html[`AutowiredAnnotationBeanPostProcessor`], - http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.html[`CommonAnnotationBeanPostProcessor`], - http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.html[`PersistenceAnnotationBeanPostProcessor`], +{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 -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.html[`RequiredAnnotationBeanPostProcessor`].) +{javadoc-baseurl}/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.html[`RequiredAnnotationBeanPostProcessor`].) [NOTE] ==== @@ -4767,25 +4768,26 @@ it only checks for `@Autowired` beans in your controllers, and not your services [[beans-required-annotation]] -==== @Required +==== @Required The `@Required` annotation applies to bean property setter methods, as in the following example: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class SimpleMovieLister { + public class SimpleMovieLister { - private MovieFinder movieFinder; + private MovieFinder movieFinder; - @Required - public void setMovieFinder(MovieFinder movieFinder) { - this.movieFinder = movieFinder; - } + @Required + public void setMovieFinder(MovieFinder movieFinder) { + this.movieFinder = movieFinder; + } - // ... -} + // ... + + } ---- This annotation simply indicates that the affected bean property must be populated at @@ -4799,24 +4801,25 @@ references and values even when you use the class outside of a container. [[beans-autowired-annotation]] -==== @Autowired +==== @Autowired As expected, you can apply the `@Autowired` annotation to "traditional" setter methods: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class SimpleMovieLister { + public class SimpleMovieLister { - private MovieFinder movieFinder; + private MovieFinder movieFinder; - @Autowired - public void setMovieFinder(MovieFinder movieFinder) { - this.movieFinder = movieFinder; - } + @Autowired + public void setMovieFinder(MovieFinder movieFinder) { + this.movieFinder = movieFinder; + } - // ... -} + // ... + + } ---- [NOTE] @@ -4828,119 +4831,133 @@ the examples below. See <> for more details You can also apply the annotation to methods with arbitrary names and/or multiple arguments: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MovieRecommender { + public class MovieRecommender { - private MovieCatalog movieCatalog; + private MovieCatalog movieCatalog; - private CustomerPreferenceDao customerPreferenceDao; + private CustomerPreferenceDao customerPreferenceDao; - @Autowired - public void prepare(MovieCatalog movieCatalog, - CustomerPreferenceDao customerPreferenceDao) { - this.movieCatalog = movieCatalog; - this.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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MovieRecommender { + public class MovieRecommender { - @Autowired - private MovieCatalog movieCatalog; + @Autowired + private MovieCatalog movieCatalog; - private CustomerPreferenceDao customerPreferenceDao; + private CustomerPreferenceDao customerPreferenceDao; - @Autowired - public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) { - this.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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MovieRecommender { + public class MovieRecommender { - @Autowired - private MovieCatalog[] movieCatalogs; + @Autowired + private MovieCatalog[] movieCatalogs; - // ... -} + // ... + + } ---- The same applies for typed collections: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MovieRecommender { + public class MovieRecommender { - private Set movieCatalogs; + private Set movieCatalogs; - @Autowired - public void setMovieCatalogs(Set movieCatalogs) { - this.movieCatalogs = movieCatalogs; - } + @Autowired + public void setMovieCatalogs(Set movieCatalogs) { + this.movieCatalogs = movieCatalogs; + } - // ... -} + // ... + + } ---- +[TIP] +==== +Your beans can implement the `org.springframework.core.Ordered` interface or use the +the `@Ordered` 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MovieRecommender { + public class MovieRecommender { - private Map movieCatalogs; + private Map movieCatalogs; - @Autowired - public void setMovieCatalogs(Map movieCatalogs) { - this.movieCatalogs = movieCatalogs; - } + @Autowired + public void setMovieCatalogs(Map 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. +indicating __required__ dependencies. This behavior can be changed as demonstrated below. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class SimpleMovieLister { + public class SimpleMovieLister { - private MovieFinder movieFinder; + private MovieFinder movieFinder; - @Autowired(required=false) - public void setMovieFinder(MovieFinder movieFinder) { - this.movieFinder = movieFinder; - } + @Autowired(required=false) + public void setMovieFinder(MovieFinder movieFinder) { + this.movieFinder = movieFinder; + } - // ... -} + // ... + + } ---- [NOTE] @@ -4963,19 +4980,20 @@ dependencies: `BeanFactory`, `ApplicationContext`, `Environment`, `ResourceLoade interfaces, such as `ConfigurableApplicationContext` or `ResourcePatternResolver`, are automatically resolved, with no special setup necessary. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MovieRecommender { + public class MovieRecommender { - @Autowired - private ApplicationContext context; + @Autowired + private ApplicationContext context; - public MovieRecommender() { - } + public MovieRecommender() { + } - // ... -} + // ... + + } ---- [NOTE] @@ -4996,74 +5014,76 @@ Spring's `@Qualifier` annotation. You can associate qualifier values with specif 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MovieRecommender { + public class MovieRecommender { - @Autowired - **@Qualifier("main")** - private MovieCatalog movieCatalog; + @Autowired + **@Qualifier("main")** + private MovieCatalog movieCatalog; - // ... -} + // ... + + } ---- The `@Qualifier` annotation can also be specified on individual constructor arguments or method parameters: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MovieRecommender { + public class MovieRecommender { - private MovieCatalog movieCatalog; + private MovieCatalog movieCatalog; - private CustomerPreferenceDao customerPreferenceDao; + private CustomerPreferenceDao customerPreferenceDao; - @Autowired - public void prepare(**@Qualifier("main")**MovieCatalog movieCatalog, - CustomerPreferenceDao customerPreferenceDao) { - this.movieCatalog = movieCatalog; - this.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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + - - **** + + **** - - + + - - **** + + **** - - + + - + - + ---- For a fallback match, the bean name is considered a default qualifier value. Thus you @@ -5108,37 +5128,38 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Target({ElementType.FIELD, ElementType.PARAMETER}) -@Retention(RetentionPolicy.RUNTIME) -**@Qualifier** -public @interface Genre { + @Target({ElementType.FIELD, ElementType.PARAMETER}) + @Retention(RetentionPolicy.RUNTIME) + **@Qualifier** + public @interface Genre { - String value(); -} + String value(); + } ---- Then you can provide the custom qualifier on autowired fields and parameters: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MovieRecommender { + public class MovieRecommender { - @Autowired - **@Genre("Action")** - private MovieCatalog actionCatalog; - private MovieCatalog comedyCatalog; + @Autowired + **@Genre("Action")** + private MovieCatalog actionCatalog; + private MovieCatalog comedyCatalog; - @Autowired - public void setComedyCatalog(**@Genre("Comedy")** MovieCatalog comedyCatalog) { - this.comedyCatalog = comedyCatalog; - } + @Autowired + public void setComedyCatalog(**@Genre("Comedy")** MovieCatalog comedyCatalog) { + this.comedyCatalog = comedyCatalog; + } - // ... -} + // ... + + } ---- Next, provide the information for the candidate bean definitions. You can add @@ -5148,33 +5169,33 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + - - **** - - + + **** + + - - **_** - - + + **_** + + - + - + ---- In <>, you will see an annotation-based alternative to @@ -5186,41 +5207,42 @@ several different types of dependencies. For example, you may provide an __offli catalog that would be searched when no Internet connection is available. First define the simple annotation: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Target({ElementType.FIELD, ElementType.PARAMETER}) -@Retention(RetentionPolicy.RUNTIME) -@Qualifier -public @interface Offline { + @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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MovieRecommender { + public class MovieRecommender { - @Autowired - **@Offline** - private MovieCatalog offlineCatalog; + @Autowired + **@Offline** + private MovieCatalog offlineCatalog; - // ... -} + // ... + + } ---- Now the bean definition only needs a qualifier `type`: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - **** - - + + **** + + ---- You can also define custom qualifier annotations that accept named attributes in @@ -5229,57 +5251,58 @@ then specified on a field or parameter to be autowired, a bean definition must m __all__ such attribute values to be considered an autowire candidate. As an example, consider the following annotation definition: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Target({ElementType.FIELD, ElementType.PARAMETER}) -@Retention(RetentionPolicy.RUNTIME) -@Qualifier -public @interface MovieQualifier { + @Target({ElementType.FIELD, ElementType.PARAMETER}) + @Retention(RetentionPolicy.RUNTIME) + @Qualifier + public @interface MovieQualifier { - String genre(); + String genre(); - Format format(); -} + Format format(); + + } ---- In this case `Format` is an enum: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public enum Format { - - VHS, DVD, BLURAY -} + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MovieRecommender { + public class MovieRecommender { - @Autowired - @MovieQualifier(format=Format.VHS, genre="Action") - private MovieCatalog actionVhsCatalog; + @Autowired + @MovieQualifier(format=Format.VHS, genre="Action") + private MovieCatalog actionVhsCatalog; - @Autowired - @MovieQualifier(format=Format.VHS, genre="Comedy") - private MovieCatalog comedyVhsCatalog; + @Autowired + @MovieQualifier(format=Format.VHS, genre="Comedy") + private MovieCatalog comedyVhsCatalog; - @Autowired - @MovieQualifier(format=Format.DVD, genre="Action") - private MovieCatalog actionDvdCatalog; + @Autowired + @MovieQualifier(format=Format.DVD, genre="Action") + private MovieCatalog actionDvdCatalog; - @Autowired - @MovieQualifier(format=Format.BLURAY, genre="Comedy") - private MovieCatalog comedyBluRayCatalog; + @Autowired + @MovieQualifier(format=Format.BLURAY, genre="Comedy") + private MovieCatalog comedyBluRayCatalog; - // ... -} + // ... + + } ---- Finally, the bean definitions should contain matching qualifier values. This example @@ -5289,90 +5312,142 @@ precedence, but the autowiring mechanism falls back on the values provided withi `` tags if no such qualifier is present, as in the last two bean definitions in the following example. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + - - - - - - - + + + + + + + - - - - - - - + + + + + + + - - - - - + + + + + - - - - - + + + + + - + ---- +[[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` and +`Store`, 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 s1; // qualifier, injects the stringStore bean + + @Autowired + private Store s2; // 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 generic + // Store beans will not appear in this list + @Autowired + private List> s; +---- + + + + [[beans-custom-autowire-configurer]] -==== CustomAutowireConfigurer +==== CustomAutowireConfigurer The -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/annotation/CustomAutowireConfigurer.html[`CustomAutowireConfigurer`] +{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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - example.CustomQualifier - - - + + + + example.CustomQualifier + + + ---- -The particular implementation of `AutowireCandidateResolver` that is activated for the -application context depends on the Java version. In versions earlier than Java 5, the -qualifier annotations are not supported, and therefore autowire candidates are solely -determined by the `autowire-candidate` value of each bean definition as well as by any -`default-autowire-candidates` pattern(s) available on the `` element. In Java 5 -or later, the presence of `@Qualifier` annotations and any custom annotations registered -with the `CustomAutowireConfigurer` will also play a role. +The `AutowireCandidateResolver` determines autowire candidates by: -Regardless of the Java version, when multiple beans qualify as autowire candidates, the -determination of a "primary" candidate is the same: if exactly one bean definition among -the candidates has a `primary` attribute set to `true`, it will be selected. +* the `autowire-candidate` value of each bean definition +* any `default-autowire-candidates` pattern(s) available on the `` 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 +==== @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 @@ -5383,18 +5458,19 @@ Spring-managed objects as well. bean name to be injected. In other words, it follows __by-name__ semantics, as demonstrated in this example: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class SimpleMovieLister { + public class SimpleMovieLister { - private MovieFinder movieFinder; + private MovieFinder movieFinder; - **@Resource(name="myMovieFinder")** - public void setMovieFinder(MovieFinder movieFinder) { - this.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 @@ -5402,18 +5478,19 @@ setter method. In case of a field, it takes the field name; in case of a setter 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class SimpleMovieLister { + public class SimpleMovieLister { - private MovieFinder movieFinder; + private MovieFinder movieFinder; - **@Resource** - public void setMovieFinder(MovieFinder movieFinder) { - this.movieFinder = movieFinder; - } -} + **@Resource** + public void setMovieFinder(MovieFinder movieFinder) { + this.movieFinder = movieFinder; + } + + } ---- [NOTE] @@ -5421,7 +5498,7 @@ public class SimpleMovieLister { 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 -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/jndi/support/SimpleJndiBeanFactory.html[`SimpleJndiBeanFactory`] +{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. ==== @@ -5437,28 +5514,29 @@ named customerPreferenceDao, then falls back to a primary type match for the typ `CustomerPreferenceDao`. The "context" field is injected based on the known resolvable dependency type `ApplicationContext`. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MovieRecommender { + public class MovieRecommender { - @Resource - private CustomerPreferenceDao customerPreferenceDao; + @Resource + private CustomerPreferenceDao customerPreferenceDao; - @Resource - private ApplicationContext context; + @Resource + private ApplicationContext context; - public MovieRecommender() { - } + public MovieRecommender() { + } - // ... -} + // ... + + } ---- [[beans-postconstruct-and-predestroy-annotations]] -==== @PostConstruct and `@PreDestroy` +==== @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 @@ -5471,21 +5549,22 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class CachingMovieLister { + public class CachingMovieLister { - @PostConstruct - public void populateMovieCache() { - // populates the movie cache upon initialization... - } + @PostConstruct + public void populateMovieCache() { + // populates the movie cache upon initialization... + } - @PreDestroy - public void clearMovieCache() { - // clears the movie cache upon destruction... - } -} + @PreDestroy + public void clearMovieCache() { + // clears the movie cache upon destruction... + } + + } ---- [NOTE] @@ -5523,14 +5602,14 @@ than using the traditional XML files. Take a look at the `@Configuration`, `@Bea [[beans-stereotype-annotations]] -==== @Component and further stereotype annotations +==== @Component and further stereotype annotations -In Spring 2.0 and later, 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 <>. +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 +<>. -Spring 2.5 introduces further stereotype annotations: `@Component`, `@Service`, and +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 @@ -5546,34 +5625,84 @@ supported as a marker for automatic exception translation in your persistence la +[[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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Service -public class SimpleMovieLister { + @Service + public class SimpleMovieLister { - private MovieFinder movieFinder; + private MovieFinder movieFinder; - @Autowired - public SimpleMovieLister(MovieFinder movieFinder) { - this.movieFinder = movieFinder; - } -} + @Autowired + public SimpleMovieLister(MovieFinder movieFinder) { + this.movieFinder = movieFinder; + } + + } ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Repository -public class JpaMovieFinder implements MovieFinder { - // implementation elided for clarity -} + @Repository + public class JpaMovieFinder implements MovieFinder { + // implementation elided for clarity + } ---- To autodetect these classes and register the corresponding beans, you need to include @@ -5581,21 +5710,21 @@ the following element in XML, where the base-package element is a common parent for the two classes. (Alternatively, you can specify a comma-separated list that includes the parent package of each class.) -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + - + ---- [TIP] @@ -5665,18 +5794,17 @@ and `expression` attributes. The following table describes the filtering options The following example shows the XML configuration ignoring all `@Repository` annotations and using "stub" repositories instead. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - + + + + + + ---- [NOTE] @@ -5695,21 +5823,23 @@ Spring components can also contribute bean definition metadata to the container. this with the same `@Bean` annotation used to define bean metadata within `@Configuration` annotated classes. Here is a simple example: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Component -public class FactoryMethodComponent { + @Component + public class FactoryMethodComponent { - @Bean @Qualifier("public") - public TestBean publicInstance() { - return new TestBean("publicInstance"); - } + @Bean + @Qualifier("public") + public TestBean publicInstance() { + return new TestBean("publicInstance"); + } - public void doWork() { - // Component method implementation omitted - } -} + public void doWork() { + // Component method implementation omitted + } + + } ---- This class is a Spring component that has application-specific code contained in its @@ -5717,45 +5847,57 @@ This class is a Spring component that has application-specific code contained in 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. Autowired fields and methods are -supported as previously discussed, with additional support for autowiring of `@Bean` -methods: +`@Scope`, `@Lazy`, and custom qualifier annotations. -[source,java] +[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 { + @Component + public class FactoryMethodComponent { - private static int i; + private static int i; - @Bean @Qualifier("public") - public TestBean publicInstance() { - return new TestBean("publicInstance"); - } + @Bean + @Qualifier("public") + public TestBean publicInstance() { + return new TestBean("publicInstance"); + } - // use of a custom qualifier and autowiring of method parameters + // 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(tb); - tb.setCountry(country); - return tb; - } + @Bean + protected TestBean protectedInstance( + @Qualifier("public") TestBean spouse, + @Value("#{privateInstance.age}") String country) { + TestBean tb = new TestBean("protectedInstance", 1); + tb.setSpouse(tb); + tb.setCountry(country); + return tb; + } - @Bean @Scope(BeanDefinition.SCOPE_SINGLETON) - private TestBean privateInstance() { - return new TestBean("privateInstance", i++); - } + @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); - } -} + @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` @@ -5769,7 +5911,7 @@ counterparts inside a Spring `@Configuration` class. The difference is that `@Co 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 `@Configuration` classes `@Bean` methods create bean metadata references to collaborating objects. -Methods are__not__ invoked with normal Java semantics. In contrast, calling a method or +Methods are __not__ invoked with normal Java semantics. In contrast, calling a method or field within a `@Component` classes `@Bean` method __has__ standard Java semantics. @@ -5787,42 +5929,40 @@ as those discovered by custom filters), the default bean name generator returns uncapitalized non-qualified class name. For example, if the following two components were detected, the names would be myMovieLister and movieFinderImpl: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Service("myMovieLister") -public class SimpleMovieLister { - // ... -} + @Service("myMovieLister") + public class SimpleMovieLister { + // ... + } ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Repository -public class MovieFinderImpl implements MovieFinder { - // ... -} + @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 -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/support/BeanNameGenerator.html[`BeanNameGenerator`] +{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,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - + + + ---- As a general rule, consider specifying the name with the annotation whenever other @@ -5838,51 +5978,47 @@ autodetected components is singleton. However, sometimes you need other scopes, Spring 2.5 provides with a new `@Scope` annotation. Simply provide the name of the scope within the annotation: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Scope("prototype") -@Repository -public class MovieFinderImpl implements MovieFinder { - // ... -} + @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 -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/ScopeMetadataResolver.html[`ScopeMetadataResolver`] +{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,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - + + + ---- When using certain non-singleton scopes, it may be necessary to generate proxies for the -scoped objects. The reasoning is described in<>. +scoped objects. The reasoning is described in <>. 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,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - + + + ---- @@ -5899,34 +6035,34 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Component -**@Qualifier("Action")** -public class ActionMovieCatalog implements MovieCatalog { - // ... -} + @Component + **@Qualifier("Action")** + public class ActionMovieCatalog implements MovieCatalog { + // ... + } ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Component -**@Genre("Action")** -public class ActionMovieCatalog implements MovieCatalog { - // ... -} + @Component + **@Genre("Action")** + public class ActionMovieCatalog implements MovieCatalog { + // ... + } ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Component -**@Offline** -public class CachingMovieCatalog implements MovieCatalog { - // ... -} + @Component + **@Offline** + public class CachingMovieCatalog implements MovieCatalog { + // ... + } ---- [NOTE] @@ -5953,39 +6089,41 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - javax.inject - javax.inject - 1 - + + javax.inject + javax.inject + 1 + ---- ==== [[beans-inject-named]] -==== Dependency Injection with @Inject and `@Named` +==== Dependency Injection with @Inject and @Named Instead of `@Autowired`, `@javax.inject.Inject` may be used as follows: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import javax.inject.Inject; + import javax.inject.Inject; -public class SimpleMovieLister { + public class SimpleMovieLister { - private MovieFinder movieFinder; + private MovieFinder movieFinder; - @Inject - public void setMovieFinder(MovieFinder movieFinder) { - this.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, @@ -5993,82 +6131,88 @@ method-level and constructor-argument level. If you would like to use a qualifie for the dependency that should be injected, you should use the `@Named` annotation as follows: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import javax.inject.Inject; -import javax.inject.Named; + import javax.inject.Inject; + import javax.inject.Named; -public class SimpleMovieLister { + public class SimpleMovieLister { - private MovieFinder movieFinder; + private MovieFinder movieFinder; - @Inject - public void setMovieFinder(@Named("main") MovieFinder movieFinder) { - this.movieFinder = movieFinder; - } - // ... -} + @Inject + public void setMovieFinder(@Named("main") MovieFinder movieFinder) { + this.movieFinder = movieFinder; + } + + // ... + + } ---- [[beans-named]] -==== @Named: a standard equivalent to the `@Component` annotation +==== @Named: a standard equivalent to the @Component annotation Instead of `@Component`, `@javax.inject.Named` may be used as follows: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import javax.inject.Inject; -import javax.inject.Named; + import javax.inject.Inject; + import javax.inject.Named; -@Named("movieListener") -public class SimpleMovieLister { + @Named("movieListener") + public class SimpleMovieLister { - private MovieFinder movieFinder; + private MovieFinder movieFinder; - @Inject - public void setMovieFinder(MovieFinder movieFinder) { - this.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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import javax.inject.Inject; -import javax.inject.Named; + import javax.inject.Inject; + import javax.inject.Named; -@Named -public class SimpleMovieLister { + @Named + public class SimpleMovieLister { - private MovieFinder movieFinder; + private MovieFinder movieFinder; - @Inject - public void setMovieFinder(MovieFinder movieFinder) { - this.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,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- @@ -6093,7 +6237,7 @@ features are not available as shown in the table below: | @Scope("singleton") | @Singleton -| The JSR-330 default scope is like Spring's `prototype`. However, in order to keep it +| 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. @@ -6128,23 +6272,7 @@ Nevertheless, this one is only intended to be used for creating your own annotat [[beans-java-basic-concepts]] -==== Basic concepts: Full @Configuration vs 'lite' @Beans mode? @Bean and `@Configuration` - -**** -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. -**** +==== Basic concepts: @Bean and @Configuration The central artifacts in Spring's new Java-configuration support are `@Configuration`-annotated classes and `@Bean`-annotated methods. @@ -6160,32 +6288,50 @@ source of bean definitions. Furthermore, `@Configuration` classes allow inter-be 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -public class AppConfig { - @Bean - public MyService myService() { - return new MyServiceImpl(); - } -} + @Configuration + public class AppConfig { + + @Bean + public MyService myService() { + return new MyServiceImpl(); + } + + } ---- The `AppConfig` class above would be equivalent to the following Spring `` XML: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- 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]] @@ -6211,29 +6357,28 @@ In much the same way that Spring XML files are used as input when instantiating instantiating an `AnnotationConfigApplicationContext`. This allows for completely XML-free usage of the Spring container: -[source,java] +[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(); -} + 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: +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] +[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(); -} + 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 @@ -6247,17 +6392,17 @@ An `AnnotationConfigApplicationContext` may be instantiated using a no-arg const and then configured using the `register()` method. This approach is particularly useful when programmatically building an `AnnotationConfigApplicationContext`. -[source,java] +[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(); -} + 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(); + } ---- @@ -6267,12 +6412,12 @@ public static void main(String[] args) { Experienced Spring users will be familiar with the following commonly-used XML declaration from Spring's `context:` namespace -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- In the example above, the `com.acme` package will be scanned, looking for any @@ -6280,24 +6425,25 @@ In the example above, the `com.acme` package will be scanned, looking for any definitions within the container. `AnnotationConfigApplicationContext` exposes the `scan(String...)` method to allow for the same component-scanning functionality: -[source,java] +[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); -} + 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 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. +Remember that `@Configuration` classes are <> +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. ==== @@ -6311,58 +6457,58 @@ configuring the Spring `ContextLoaderListener` servlet listener, Spring MVC Spring MVC web application. Note the use of the `contextClass` context-param and init-param: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - contextClass - - org.springframework.web.context.support.AnnotationConfigWebApplicationContext - - + + + + contextClass + + org.springframework.web.context.support.AnnotationConfigWebApplicationContext + + - - - contextConfigLocation - com.acme.AppConfig - + + + contextConfigLocation + com.acme.AppConfig + - - - org.springframework.web.context.ContextLoaderListener - + + + org.springframework.web.context.ContextLoaderListener + - - - dispatcher - org.springframework.web.servlet.DispatcherServlet - - - contextClass - - org.springframework.web.context.support.AnnotationConfigWebApplicationContext - - - - - contextConfigLocation - com.acme.web.MvcConfig - - + + + dispatcher + org.springframework.web.servlet.DispatcherServlet + + + contextClass + + org.springframework.web.context.support.AnnotationConfigWebApplicationContext + + + + + contextConfigLocation + com.acme.web.MvcConfig + + - - - dispatcher - /app/* - - + + + dispatcher + /app/* + + ---- @@ -6387,34 +6533,34 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -public class AppConfig { + @Configuration + public class AppConfig { - @Bean - public TransferService transferService() { - return new TransferServiceImpl(); - } + @Bean + public TransferService transferService() { + return new TransferServiceImpl(); + } -} + } ---- The preceding configuration is exactly equivalent to the following Spring XML: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- Both declarations make a bean named `transferService` available in the `ApplicationContext`, bound to an object instance of type `TransferServiceImpl`: -[source] +[literal] [subs="verbatim,quotes"] ---- transferService -> com.acme.TransferServiceImpl @@ -6441,51 +6587,55 @@ The `@Bean` annotation supports specifying arbitrary initialization and destruct callback methods, much like Spring XML's `init-method` and `destroy-method` attributes on the `bean` element: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class Foo { - public void init() { - // initialization logic - } -} + public class Foo { + public void init() { + // initialization logic + } + } -public class Bar { - public void cleanup() { - // destruction 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(); - } -} + @Configuration + public class AppConfig { + + @Bean(initMethod = "init") + public Foo foo() { + return new Foo(); + } + + @Bean(destroyMethod = "cleanup") + public Bar bar() { + return new Bar(); + } + + } ---- Of course, in the case of `Foo` above, it would be equally as valid to call the `init()` method directly during construction: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -public class AppConfig { - @Bean - public Foo foo() { - Foo foo = new Foo(); - foo.init(); - return foo; - } + @Configuration + public class AppConfig { + @Bean + public Foo foo() { + Foo foo = new Foo(); + foo.init(); + return foo; + } - // ... -} + // ... + + } ---- [TIP] @@ -6508,21 +6658,23 @@ specific scope. You can use any of the standard scopes specified in the The default scope is `singleton`, but you can override this with the `@Scope` annotation: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -public class MyConfiguration { - @Bean - **@Scope("prototype")** - public Encryptor encryptor() { - // ... - } -} + @Configuration + public class MyConfiguration { + + @Bean + **@Scope("prototype")** + public Encryptor encryptor() { + // ... + } + + } ---- [[beans-java-scoped-proxy]] -====== @Scope and scoped-proxy +====== @Scope and scoped-proxy Spring offers a convenient way of working with scoped dependencies through <>. The easiest way to create such @@ -6534,23 +6686,23 @@ 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] +[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(); -} + // 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; -} + @Bean + public Service userService() { + UserService service = new SimpleUserService(); + // a reference to the proxied userPreferences bean + service.setUserPreferences(userPreferences()); + return service; + } ---- @@ -6559,18 +6711,18 @@ public Service userService() { 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -public class AppConfig { + @Configuration + public class AppConfig { - @Bean(name = "myFoo") - public Foo foo() { - return new Foo(); - } + @Bean(name = "myFoo") + public Foo foo() { + return new Foo(); + } -} + } ---- @@ -6580,21 +6732,45 @@ As discussed in <>, it is sometimes desirable to give a single b multiple names, otherwise known as__bean aliasing__. The `name` attribute of the `@Bean` annotation accepts a String array for this purpose. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -public class AppConfig { + @Configuration + public class AppConfig { - @Bean(name = { "dataSource", "subsystemA-dataSource", "subsystemB-dataSource" }) - public DataSource dataSource() { - // instantiate, configure and return DataSource bean... - } + @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 + **@Desciption("Provides a basic example of a bean")** + public Foo foo() { + return new Foo(); + } + + } +---- + [[beans-java-configuration-annotation]] ==== Using the @Configuration annotation @@ -6610,23 +6786,23 @@ inter-bean dependencies. See <> for a general introdu When `@Bean` s have dependencies on one another, expressing that dependency is as simple as having one bean method call another: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -public class AppConfig { + @Configuration + public class AppConfig { - @Bean - public Foo foo() { - return new Foo(bar()); - } + @Bean + public Foo foo() { + return new Foo(bar()); + } - @Bean - public Bar bar() { - return new Bar(); - } + @Bean + public Bar bar() { + return new Bar(); + } -} + } ---- In the example above, the `foo` bean receives a reference to `bar` via constructor @@ -6647,49 +6823,49 @@ 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] +[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(); + 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(); - } + // 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(); -} + // 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] +[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 + @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(); - } - } -} + @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(); + } + } + } ---- @@ -6697,30 +6873,32 @@ public CommandManager commandManager() { ===== Further information about how Java-based configuration works internally The following example shows a `@Bean` annotated method being called twice: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -public class AppConfig { + @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 ClientService clientService1() { + ClientServiceImpl clientService = new ClientServiceImpl(); + clientService.setClientDao(clientDao()); + return clientService; + } - @Bean - public ClientDao clientDao() { - return new ClientDaoImpl(); - } -} + @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()`. @@ -6762,40 +6940,49 @@ Much as the `` element is used within Spring XML files to aid in modula configurations, the `@Import` annotation allows for loading `@Bean` definitions from another configuration class: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -public class ConfigA { - public @Bean A a() { return new A(); } -} + @Configuration + public class ConfigA { -@Configuration -@Import(ConfigA.class) -public class ConfigB { - public @Bean B b() { return new B(); } -} + @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: +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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public static void main(String[] args) { - ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class); + 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); -} + // 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. +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 @@ -6814,39 +7001,52 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -public class ServiceConfig { - private @Autowired AccountRepository accountRepository; + @Configuration + public class ServiceConfig { - public @Bean TransferService transferService() { - return new TransferServiceImpl(accountRepository); - } -} + @Autowired + private AccountRepository accountRepository; -@Configuration -public class RepositoryConfig { - private @Autowired DataSource dataSource; + @Bean + public TransferService transferService() { + return new TransferServiceImpl(accountRepository); + } - public @Bean AccountRepository accountRepository() { - return new JdbcAccountRepository(dataSource); - } -} + } -@Configuration -@Import({ServiceConfig.class, RepositoryConfig.class}) -public class SystemTestConfig { - public @Bean DataSource dataSource() { /* return new DataSource */ } -} + @Configuration + public class RepositoryConfig { -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"); -} + @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 @@ -6865,61 +7065,78 @@ In cases where this ambiguity is not acceptable and you wish to have direct navi from within your IDE from one `@Configuration` class to another, consider autowiring the configuration classes themselves: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -public class ServiceConfig { - private @Autowired RepositoryConfig repositoryConfig; + @Configuration + public class ServiceConfig { - public @Bean TransferService transferService() { - // navigate 'through' the config class to the @Bean method! - return new TransferServiceImpl(repositoryConfig.accountRepository()); - } -} + @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: +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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -public class ServiceConfig { - private @Autowired RepositoryConfig repositoryConfig; + @Configuration + public class ServiceConfig { - public @Bean TransferService transferService() { - return new TransferServiceImpl(repositoryConfig.accountRepository()); - } -} + @Autowired + private RepositoryConfig repositoryConfig; -@Configuration -public interface RepositoryConfig { - @Bean AccountRepository accountRepository(); -} + @Bean + public TransferService transferService() { + return new TransferServiceImpl(repositoryConfig.accountRepository()); + } + } -@Configuration -public class DefaultRepositoryConfig implements RepositoryConfig { - public @Bean AccountRepository accountRepository() { - return new JdbcAccountRepository(...); - } -} + @Configuration + public interface RepositoryConfig { -@Configuration -@Import({ServiceConfig.class, DefaultRepositoryConfig.class}) // import the concrete config! -public class SystemTestConfig { - public @Bean DataSource dataSource() { /* return DataSource */ } -} + @Bean + AccountRepository accountRepository(); -public static void main(String[] args) { - ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class); - TransferService transferService = ctx.getBean(TransferService.class); - transferService.transfer(100.00, "A123", "C456"); -} + } + + @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 @@ -6930,6 +7147,48 @@ 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 <> +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 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 Javadoc] 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 @@ -6958,43 +7217,48 @@ include it within `system-test-config.xml` as a `` definition. Because `@Configuration` annotation, and process the `@Bean` methods declared in `AppConfig` properly. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -public class AppConfig { - private @Autowired DataSource dataSource; + @Configuration + public class AppConfig { - public @Bean AccountRepository accountRepository() { - return new JdbcAccountRepository(dataSource); - } + @Autowired + private DataSource dataSource; - public @Bean TransferService transferService() { - return new TransferService(accountRepository()); - } -} + @Bean + public AccountRepository accountRepository() { + return new JdbcAccountRepository(dataSource); + } + + @Bean + public TransferService transferService() { + return new TransferService(accountRepository()); + } + + } ---- -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -system-test-config.xml - - - - + system-test-config.xml + + + + - + - - - - - - + + + + + + ---- -[source] +[literal] [subs="verbatim,quotes"] ---- jdbc.properties @@ -7003,14 +7267,14 @@ jdbc.username=sa jdbc.password= ---- -[source,java] +[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); - // ... -} + public static void main(String[] args) { + ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml"); + TransferService transferService = ctx.getBean(TransferService.class); + // ... + } ---- [NOTE] @@ -7032,26 +7296,26 @@ Note that in this case, we don't need to explicitly declare ``, because `` enables all the same functionality. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -system-test-config.xml - - - - + system-test-config.xml + + + + - - - - - - + + + + + + ---- -- [[beans-java-combining-java-centric]] -====== @Configuration class-centric use of XML with `@ImportResource` +====== @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 @@ -7059,32 +7323,40 @@ scenarios, simply use `@ImportResource` and define only as much XML as is needed so achieves a "Java-centric" approach to configuring the container and keeps XML to a bare minimum. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -@ImportResource("classpath:/com/acme/properties-config.xml") -public class AppConfig { - private @Value("${jdbc.url}") String url; - private @Value("${jdbc.username}") String username; - private @Value("${jdbc.password}") String password; + @Configuration + @ImportResource("classpath:/com/acme/properties-config.xml") + public class AppConfig { - public @Bean DataSource dataSource() { - return new DriverManagerDataSource(url, username, password); - } -} + @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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -properties-config.xml - - - + properties-config.xml + + + ---- -[source] +[literal] [subs="verbatim,quotes"] ---- jdbc.properties @@ -7093,17 +7365,46 @@ jdbc.username=sa jdbc.password= ---- -[source,java] +[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); - // ... -} + public static void main(String[] args) { + ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); + TransferService transferService = ctx.getBean(TransferService.class); + // ... + } ---- +[[beans-definition-profiles]] +=== Bean definition profiles and environment abstraction +Bean definition profiles is a mechanism in the core container that allows for registration +of different beans in different environments. 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 + +Find out more about http://spring.io/blog/2011/02/11/spring-framework-3-1-m1-released/[Environment, +XML Profiles] and the +http://spring.io/blog/2011/02/14/spring-3-1-m1-introducing-profile/[@Profile annotation]. + + +[[beans-property-source-abstraction]] +=== PropertySource Abstraction + +Spring's <> provides search operations +over a configurable hierarchy of property sources. + +You can find out more about +http://spring.io/blog/2011/02/15/spring-3-1-m1-unified-property-management/[Unified +Property Management], the +{javadoc-baseurl}/org/springframework/core/env/PropertySource.html[PropertySource class] +and the {javadoc-baseurl}org/springframework/context/annotation/PropertySource.html[@PropertySource +annotation]. [[context-load-time-weaver]] @@ -7115,24 +7416,24 @@ loaded into the Java virtual machine (JVM). To enable load-time weaving add the `@EnableLoadTimeWeaving` to one of your `@Configuration` classes: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -@EnableLoadTimeWeaving -public class AppConfig { + @Configuration + @EnableLoadTimeWeaving + public class AppConfig { -} + } ---- Alternatively for XML configuration use the `context:load-time-weaver` element: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- Once configured for the `ApplicationContext`. Any bean within that `ApplicationContext` @@ -7151,7 +7452,7 @@ AspectJ load-time weaving, see <>. 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 -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/ApplicationContext.html[`ApplicationContext`] +{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 @@ -7208,21 +7509,21 @@ messaging. The `StaticMessageSource` is rarely used but provides programmatic wa add messages to the source. The `ResourceBundleMessageSource` is shown in the following example: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - format - exceptions - windows - - - - + + + + + format + exceptions + windows + + + + ---- In the example it is assumed you have three resource bundles defined in your classpath @@ -7231,37 +7532,37 @@ handled in the JDK standard way of resolving messages through ResourceBundles. F purposes of the example, assume the contents of two of the above resource bundle files are... -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -# in format.properties -message=Alligators rock! + # in format.properties + message=Alligators rock! ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -# in exceptions.properties -argument.required=The '{0}' argument is required. + # 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] +[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); -} + 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... -[source] +[literal] [subs="verbatim,quotes"] ---- Alligators rock! @@ -7277,47 +7578,47 @@ classpath and are called `format.properties`, `exceptions.properties`, and 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - - + + + + - - - - + + + + - + ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class Example { + public class Example { - private MessageSource messages; + private MessageSource messages; - public void setMessages(MessageSource messages) { - this.messages = 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); - } + 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... -[source] +[literal] [subs="verbatim,quotes"] ---- The userDao argument is required. @@ -7326,7 +7627,7 @@ 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 +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. @@ -7334,27 +7635,27 @@ 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. -[source] +[literal] [subs="verbatim,quotes"] ---- # in exceptions_en_GB.properties argument.required=Ebagum lad, the '{0}' argument is required, I say, required. ---- -[source,java] +[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); -} + 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... -[source] +[literal] [subs="verbatim,quotes"] ---- Ebagum lad, the 'userDao' argument is required, I say, required. @@ -7424,28 +7725,30 @@ following standard events: | `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 + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class BlackListEvent extends ApplicationEvent { - private final String address; - private final String test; + public class BlackListEvent extends ApplicationEvent { - public BlackListEvent(Object source, String address, String test) { - super(source); - this.address = address; - this.test = test; - } + private final String address; + private final String test; - // accessor and other methods... -} + 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 @@ -7453,31 +7756,32 @@ To publish a custom `ApplicationEvent`, call the `publishEvent()` method on an `ApplicationEventPublisherAware` and registering it as a Spring bean. The following example demonstrates such a class: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class EmailService implements ApplicationEventPublisherAware { + public class EmailService implements ApplicationEventPublisherAware { - private List blackList; - private ApplicationEventPublisher publisher; + private List blackList; + private ApplicationEventPublisher publisher; - public void setBlackList(List blackList) { - this.blackList = blackList; - } + public void setBlackList(List blackList) { + this.blackList = blackList; + } - public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { - this.publisher = publisher; - } + 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... - } -} + 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 @@ -7490,21 +7794,22 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class BlackListNotifier implements ApplicationListener { + public class BlackListNotifier implements ApplicationListener { - private String notificationAddress; + private String notificationAddress; - public void setNotificationAddress(String notificationAddress) { - this.notificationAddress = notificationAddress; - } + public void setNotificationAddress(String notificationAddress) { + this.notificationAddress = notificationAddress; + } - public void onApplicationEvent(BlackListEvent event) { -// notify appropriate parties via notificationAddress... - } -} + public void onApplicationEvent(BlackListEvent event) { + // notify appropriate parties via notificationAddress... + } + + } ---- Notice that `ApplicationListener` is generically parameterized with the type of your @@ -7521,22 +7826,22 @@ event publication becomes necessary, refer to the JavaDoc for Spring's The following example shows the bean definitions used to register and configure each of the classes above: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - known.spammer@example.org - known.hacker@example.org - john.doe@example.org - - - + + + + known.spammer@example.org + known.hacker@example.org + john.doe@example.org + + + - - - + + + ---- Putting it all together, when the `sendEmail()` method of the `emailService` bean is @@ -7610,20 +7915,25 @@ have a look at the Javadoc for the `ContextLoaderServlet`. You can register an `ApplicationContext` using the `ContextLoaderListener` as follows: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - -contextConfigLocation -/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml - + + contextConfigLocation + /WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml + - -org.springframework.web.context.ContextLoaderListener - + + org.springframework.web.context.ContextLoaderListener + - ---- The listener inspects the `contextConfigLocation` parameter. If the parameter does not @@ -7642,13 +7952,13 @@ the `contextConfigLocation` parameter just as the listener does. [[context-deploy-rar]] ==== Deploying a Spring ApplicationContext as a J2EE RAR file -In Spring 2.5 and later, 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 J2EE RAR deployment unit. This is the equivalent of bootstrapping a standalone -ApplicationContext, just hosted in J2EE environment, being able to access the J2EE -servers facilities. RAR deployment is a 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 J2EE environment. +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 J2EE RAR deployment +unit. This is the equivalent of bootstrapping a standalone ApplicationContext, just hosted +in J2EE environment, being able to access the J2EE servers facilities. RAR deployment is a +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 J2EE 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 @@ -7659,7 +7969,7 @@ 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 -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/jca/context/SpringContextResourceAdapter.html[SpringContextResourceAdapter] +{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 J2EE RAR file:__ package @@ -7702,7 +8012,7 @@ directly through a classic singleton lookup. [[context-introduction-ctx-vs-beanfactory]] -==== BeanFactory or `ApplicationContext`? +==== BeanFactory or ApplicationContext? Use an `ApplicationContext` unless you have a good reason for not doing so. @@ -7710,7 +8020,7 @@ Because the `ApplicationContext` includes all functionality of the `BeanFactory` generally recommended over the `BeanFactory`, except for a few situations such as in an `Applet` 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 2.0 and later makes__heavy__ +`ApplicationContext` is what you will want to use. Spring makes __heavy__ use of the <> (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 @@ -7749,32 +8059,32 @@ The following table lists features provided by the `BeanFactory` and To explicitly register a bean post-processor with a `BeanFactory` implementation, you must write code like this: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -ConfigurableBeanFactory factory = new XmlBeanFactory(...); + ConfigurableBeanFactory factory = new XmlBeanFactory(...); -// now register any needed BeanPostProcessor instances -MyBeanPostProcessor postProcessor = new MyBeanPostProcessor(); -factory.addBeanPostProcessor(postProcessor); + // now register any needed BeanPostProcessor instances + MyBeanPostProcessor postProcessor = new MyBeanPostProcessor(); + factory.addBeanPostProcessor(postProcessor); -// now start using the factory + // now start using the factory ---- To explicitly register a `BeanFactoryPostProcessor` when using a `BeanFactory` implementation, you must write code like this: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -XmlBeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml")); + 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")); + // 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); + // now actually do the replacement + cfg.postProcessBeanFactory(factory); ---- In both cases, the explicit registration step is inconvenient, which is one reason why @@ -7811,7 +8121,7 @@ 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 -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/access/ContextSingletonBeanFactoryLocator.html[`ContextSingletonBeanFactoryLocator`] +{javadoc-baseurl}/org/springframework/context/access/ContextSingletonBeanFactoryLocator.html[`ContextSingletonBeanFactoryLocator`] locator that is described in this http://blog.springsource.com/2007/06/11/using-a-shared-parent-application-context-in-a-multi-war-spring-application/[SpringSource team blog entry]. @@ -7846,34 +8156,36 @@ such as a method to check for the existence of the resource being pointed to. Spring's `Resource` interface is meant to be a more capable interface for abstracting access to low-level resources. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface Resource extends InputStreamSource { + public interface Resource extends InputStreamSource { - boolean exists(); + boolean exists(); - boolean isOpen(); + boolean isOpen(); - URL getURL() throws IOException; + URL getURL() throws IOException; - File getFile() throws IOException; + File getFile() throws IOException; - Resource createRelative(String relativePath) throws IOException; + Resource createRelative(String relativePath) throws IOException; - String getFilename(); + String getFilename(); - String getDescription(); -} + String getDescription(); + + } ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface InputStreamSource { + public interface InputStreamSource { - InputStream getInputStream() throws IOException; -} + InputStream getInputStream() throws IOException; + + } ---- Some of the most important methods from the `Resource` interface are: @@ -7925,7 +8237,7 @@ box in Spring: [[resources-implementations-urlresource]] -==== 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 @@ -7946,7 +8258,7 @@ create a `UrlResource`. [[resources-implementations-classpathresource]] -==== 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 @@ -7967,7 +8279,7 @@ path, and create a `ClassPathResource` in that case. [[resources-implementations-filesystemresource]] -==== FileSystemResource +==== FileSystemResource This is a `Resource` implementation for `java.io.File` handles. It obviously supports resolution as a `File`, and as a `URL`. @@ -7975,7 +8287,7 @@ resolution as a `File`, and as a `URL`. [[resources-implementations-servletcontextresource]] -==== ServletContextResource +==== ServletContextResource This is a `Resource` implementation for `ServletContext` resources, interpreting relative paths within the relevant web application's root directory. @@ -7989,7 +8301,7 @@ dependent on the Servlet container. [[resources-implementations-inputstreamresource]] -==== InputStreamResource +==== InputStreamResource A `Resource` implementation for a given `InputStream`. This should only be used if no specific `Resource` implementation is applicable. In particular, prefer @@ -8003,7 +8315,7 @@ times. [[resources-implementations-bytearrayresource]] -==== ByteArrayResource +==== ByteArrayResource This is a `Resource` implementation for a given byte array. It creates a `ByteArrayInputStream` for the given byte array. @@ -8020,12 +8332,14 @@ single-use `InputStreamResource`. The `ResourceLoader` interface is meant to be implemented by objects that can return (i.e. load) `Resource` instances. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface ResourceLoader { - Resource getResource(String location); -} + public interface ResourceLoader { + + Resource getResource(String location); + + } ---- All application contexts implement the `ResourceLoader` interface, and therefore all @@ -8036,10 +8350,10 @@ specified doesn't have a specific prefix, you will get back a `Resource` type th appropriate to that particular application context. For example, assume the following snippet of code was executed against a `ClassPathXmlApplicationContext` instance: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -Resource template = ctx.getResource("some/resource/path/myTemplate.txt"); + Resource template = ctx.getResource("some/resource/path/myTemplate.txt"); ---- What would be returned would be a `ClassPathResource`; if the same method was executed @@ -8053,25 +8367,25 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt"); + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -Resource template = ctx.getResource("file:/some/resource/path/myTemplate.txt"); + Resource template = ctx.getResource("file:/some/resource/path/myTemplate.txt"); ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -Resource template = ctx.getResource("http://myhost.com/resource/path/myTemplate.txt"); + Resource template = ctx.getResource("http://myhost.com/resource/path/myTemplate.txt"); ---- The following table summarizes the strategy for converting `String` s to `Resource` s: @@ -8082,21 +8396,21 @@ The following table summarizes the strategy for converting `String` s to `Resour | Prefix| Example| Explanation | classpath: -| `classpath:com/myapp/config.xml` +| `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 +| `file:/data/config.xml` +| Loaded as a `URL`, from the filesystem. footnote:[But see also pass:specialcharacters,macros[<>].] | http: -| `http://myserver/logo.png` +| `http://myserver/logo.png` | Loaded as a `URL`. | (none) -| `/data/config.xml` -| Depends on the underlying `ApplicationContext`. +| `/data/config.xml` +| Depends on the underlying `ApplicationContext`. |=== @@ -8108,13 +8422,13 @@ The following table summarizes the strategy for converting `String` s to `Resour The `ResourceLoaderAware` interface is a special marker interface, identifying objects that expect to be provided with a `ResourceLoader` reference. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface ResourceLoaderAware { + public interface ResourceLoaderAware { - void setResourceLoader(ResourceLoader resourceLoader); -} + void setResourceLoader(ResourceLoader resourceLoader); + } ---- When a class implements `ResourceLoaderAware` and is deployed into an application @@ -8145,7 +8459,7 @@ annotation. For more information, see <>. [[resources-as-dependencies]] -=== 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` @@ -8160,12 +8474,12 @@ register and use a special JavaBeans `PropertyEditor` which can convert `String` 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- Note that the resource path has no prefix, so because the application context itself is @@ -8177,16 +8491,16 @@ If there is a need to force a specific `Resource` type to be used, then a prefix 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- @@ -8208,20 +8522,20 @@ that path and used to load the bean definitions, depends on and is appropriate t specific application context. For example, if you create a `ClassPathXmlApplicationContext` as follows: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml"); + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -ApplicationContext ctx = - new FileSystemXmlApplicationContext("conf/appContext.xml"); + ApplicationContext ctx = + new FileSystemXmlApplicationContext("conf/appContext.xml"); ---- The bean definition will be loaded from a filesystem location, in this case relative to @@ -8231,11 +8545,11 @@ Note that the use of the special classpath prefix or a standard URL prefix on th location path will override the default type of `Resource` created to load the definition. So this `FileSystemXmlApplicationContext`... -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -ApplicationContext ctx = - new FileSystemXmlApplicationContext("classpath:conf/appContext.xml"); + ApplicationContext ctx = + new FileSystemXmlApplicationContext("classpath:conf/appContext.xml"); ---- ... will actually load its bean definitions from the classpath. However, it is still a @@ -8255,24 +8569,24 @@ will derive the path information from the supplied class. An example will hopefully make this clear. Consider a directory layout that looks like this: -[source] +[literal] [subs="verbatim,quotes"] ---- com/ foo/ - services.xml - daos.xml + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -ApplicationContext ctx = new ClassPathXmlApplicationContext( - new String[] {"services.xml", "daos.xml"}, MessengerService.class); + ApplicationContext ctx = new ClassPathXmlApplicationContext( + new String[] {"services.xml", "daos.xml"}, MessengerService.class); ---- Please do consult the Javadocs for the `ClassPathXmlApplicationContext` class for @@ -8304,13 +8618,13 @@ a resource points to just one resource at a time. ===== Ant-style Patterns When the path location contains an Ant-style pattern, for example: -[source] +[literal] [subs="verbatim,quotes"] ---- /WEB-INF/*-context.xml - com/mycompany/**/applicationContext.xml - file:C:/some/path/*-context.xml - classpath:com/mycompany/**/applicationContext.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 @@ -8351,11 +8665,11 @@ environment before you rely on it. When constructing an XML-based application context, a location string may use the special `classpath*:` prefix: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -ApplicationContext ctx = - new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml"); + ApplicationContext ctx = + new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml"); ---- This special prefix specifies that all classpath resources that match the given name @@ -8398,7 +8712,7 @@ Ant-style patterns with " `classpath:`" resources are not guaranteed to find mat resources if the root package to search is available in multiple class path locations. This is because a resource such as -[source] +[literal] [subs="verbatim,quotes"] ---- com/mycompany/package1/service-context.xml @@ -8406,7 +8720,7 @@ com/mycompany/package1/service-context.xml may be in only one location, but when a path such as -[source] +[literal] [subs="verbatim,quotes"] ---- classpath:com/mycompany/**/service-context.xml @@ -8421,7 +8735,7 @@ will search all class path locations that contain the root package. [[resources-filesystemresource-caveats]] -==== FileSystemResource caveats +==== FileSystemResource caveats A `FileSystemResource` that is not attached to a `FileSystemApplicationContext` (that is, a `FileSystemApplicationContext` is not the actual `ResourceLoader`) will treat @@ -8435,54 +8749,54 @@ For backwards compatibility (historical) reasons however, this changes when the 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -ApplicationContext ctx = - new FileSystemXmlApplicationContext("conf/context.xml"); + ApplicationContext ctx = + new FileSystemXmlApplicationContext("conf/context.xml"); ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -ApplicationContext ctx = - new FileSystemXmlApplicationContext("/conf/context.xml"); + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -FileSystemXmlApplicationContext ctx = ...; -ctx.getResource("some/resource/path/myTemplate.txt"); + FileSystemXmlApplicationContext ctx = ...; + ctx.getResource("some/resource/path/myTemplate.txt"); ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -FileSystemXmlApplicationContext ctx = ...; -ctx.getResource("/some/resource/path/myTemplate.txt"); + 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] +[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"); + // actual context type doesn't matter, the Resource will always be UrlResource + ctx.getResource("file:/some/resource/path/myTemplate.txt"); ---- -[source,java] +[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"); + // force this FileSystemXmlApplicationContext to load its definition via a UrlResource + ApplicationContext ctx = + new FileSystemXmlApplicationContext("file:/conf/context.xml"); ---- @@ -8496,8 +8810,9 @@ ApplicationContext ctx = [[validation-introduction]] -=== IntroductionJSR-303/JSR-349 Bean Validation +=== 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. @@ -8549,16 +8864,16 @@ validators can report validation failures to the `Errors` object. Let's consider a small data object: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class Person { + public class Person { - private String name; - private int age; + private String name; + private int age; - // the usual getters and setters... -} + // the usual getters and setters... + } ---- We're going to provide validation behavior for the `Person` class by implementing the @@ -8571,28 +8886,28 @@ following two methods of the `org.springframework.validation.Validator` interfac Implementing a `Validator` is fairly straightforward, especially when you know of the `ValidationUtils` helper class that the Spring Framework also provides. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class PersonValidator implements Validator { + public class PersonValidator implements Validator { - /** - * This Validator validates *just* Person instances - */ - public boolean supports(Class clazz) { - return Person.class.equals(clazz); - } + /** + * 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"); - } - } -} + 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 @@ -8611,44 +8926,44 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class CustomerValidator implements Validator { + public class CustomerValidator implements Validator { - private final Validator addressValidator; + 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; - } + 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); - } + /** + * 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(); - } - } -} + 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 @@ -8680,9 +8995,9 @@ convenience to aid developers in targeting error messages and suchlike. More information on the `MessageCodesResolver` and the default strategy can be found online with the Javadocs for -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/validation/MessageCodesResolver.html[MessageCodesResolver] +{javadoc-baseurl}/org/springframework/validation/MessageCodesResolver.html[MessageCodesResolver] and -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/validation/DefaultMessageCodesResolver.html[DefaultMessageCodesResolver] +{javadoc-baseurl}/org/springframework/validation/DefaultMessageCodesResolver.html[DefaultMessageCodesResolver] respectively. @@ -8755,70 +9070,79 @@ and their out-of-the-box implementation, you should skip ahead to the section ab Consider the following two classes: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class Company { - private String name; - private Employee managingDirector; + public class Company { - 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; - } -} + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class Employee { - private String name; - private float salary; + public class Employee { - 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; - } -} + 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] +[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); + 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()); + // 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"); + // retrieving the salary of the managingDirector through the company + Float salary = (Float) company.getPropertyValue("managingDirector.salary"); ---- @@ -8929,14 +9253,14 @@ name as that class, with `'Editor'` appended; for example, one could have the fo class and package structure, which would be sufficient for the `FooEditor` class to be recognized and used as the `PropertyEditor` for `Foo`-typed properties. -[source] +[literal] [subs="verbatim,quotes"] ---- com chank pop Foo - FooEditor // the PropertyEditor for the Foo class + FooEditor // the PropertyEditor for the Foo class ---- Note that you can also use the standard `BeanInfo` JavaBeans mechanism here as well @@ -8946,39 +9270,39 @@ not-amazing-detail here]). Find below an example of using the `BeanInfo` mechani explicitly registering one or more `PropertyEditor` instances with the properties of an associated class. -[source] +[literal] [subs="verbatim,quotes"] ---- com chank pop Foo - FooBeanInfo // the BeanInfo for the Foo class + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class FooBeanInfo extends SimpleBeanInfo { + 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()); - } - } -} + 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()); + } + } + } ---- @@ -9019,71 +9343,71 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package example; + package example; -public class ExoticType { + public class ExoticType { - private String name; + private String name; - public ExoticType(String name) { - this.name = name; - } -} + public ExoticType(String name) { + this.name = name; + } + } -public class DependsOnExoticType { + public class DependsOnExoticType { - private ExoticType type; + private ExoticType type; - public void setType(ExoticType type) { - this.type = 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- The `PropertyEditor` implementation could look similar to this: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// converts string representation to ExoticType object -package example; + // converts string representation to ExoticType object + package example; -public class ExoticTypeEditor extends PropertyEditorSupport { + public class ExoticTypeEditor extends PropertyEditorSupport { - public void setAsText(String text) { - setValue(new ExoticType(text.toUpperCase())); - } -} + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - + + + + + + + ---- [[beans-beans-conversion-customeditor-registration-per]] @@ -9106,21 +9430,21 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.foo.editors.spring; + package com.foo.editors.spring; -public final class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar { + public final class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar { - public void registerCustomEditors(PropertyEditorRegistry registry) { + public void registerCustomEditors(PropertyEditorRegistry registry) { - // it is expected that new PropertyEditor instances are created - registry.registerCustomEditor(ExoticType.class, new ExoticTypeEditor()); + // 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... - } -} + // you could register as many custom property editors as are required here... + } + } ---- See also the `org.springframework.beans.support.ResourceEditorRegistrar` for an example @@ -9130,19 +9454,19 @@ See also the `org.springframework.beans.support.ResourceEditorRegistrar` for an Next we configure a `CustomEditorConfigurer` and inject an instance of our `CustomPropertyEditorRegistrar` into it: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - + + + + + + + - + ---- Finally, and in a bit of a departure from the focus of this chapter, for those of you @@ -9151,24 +9475,24 @@ conjunction with data-binding `Controllers` (such as `SimpleFormController`) can convenient. Find below an example of using a `PropertyEditorRegistrar` in the implementation of an `initBinder(..)` method: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public final class RegisterUserController extends SimpleFormController { + public final class RegisterUserController extends SimpleFormController { - private final PropertyEditorRegistrar customPropertyEditorRegistrar; + private final PropertyEditorRegistrar customPropertyEditorRegistrar; - public RegisterUserController(PropertyEditorRegistrar propertyEditorRegistrar) { - this.customPropertyEditorRegistrar = propertyEditorRegistrar; - } + public RegisterUserController(PropertyEditorRegistrar propertyEditorRegistrar) { + this.customPropertyEditorRegistrar = propertyEditorRegistrar; + } - protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) - throws Exception { - **this.customPropertyEditorRegistrar.registerCustomEditors(binder);** - } + protected void initBinder(HttpServletRequest request, + ServletRequestDataBinder binder) throws Exception { + **this.customPropertyEditorRegistrar.registerCustomEditors(binder);** + } - // other methods to do with registering a User -} + // other methods to do with registering a User + } ---- This style of `PropertyEditor` registration can lead to concise code (the implementation @@ -9180,7 +9504,7 @@ registration code to be encapsulated in a class and then shared amongst as many [[core-convert]] -=== Spring 3 Type Conversion +=== 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 @@ -9194,16 +9518,16 @@ application where type conversion is needed. ==== Converter SPI The SPI to implement type conversion logic is simple and strongly typed: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.springframework.core.convert.converter; + package org.springframework.core.convert.converter; -public interface Converter { + public interface Converter { - T convert(S source); + T convert(S source); -} + } ---- To create your own Converter, simply implement the interface above. Parameterize S as @@ -9217,18 +9541,18 @@ Several converter implementations are provided in the `core.convert.support` pac a convenience. These include converters from Strings to Numbers and other common types. Consider `StringToInteger` as an example Converter implementation: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.springframework.core.convert.support; + package org.springframework.core.convert.support; -final class StringToInteger implements Converter { + final class StringToInteger implements Converter { - public Integer convert(String source) { - return Integer.valueOf(source); - } + public Integer convert(String source) { + return Integer.valueOf(source); + } -} + } ---- @@ -9239,16 +9563,16 @@ When you need to centralize the conversion logic for an entire class hierarchy, example, when converting from String to java.lang.Enum objects, implement `ConverterFactory`: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.springframework.core.convert.converter; + package org.springframework.core.convert.converter; -public interface ConverterFactory { + public interface ConverterFactory { - Converter getConverter(Class targetType); + Converter getConverter(Class targetType); -} + } ---- Parameterize S to be the type you are converting from and R to be the base type defining @@ -9257,30 +9581,30 @@ where T is a subclass of R. Consider the `StringToEnum` ConverterFactory as an example: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.springframework.core.convert.support; + package org.springframework.core.convert.support; -final class StringToEnumConverterFactory implements ConverterFactory { + final class StringToEnumConverterFactory implements ConverterFactory { - public Converter getConverter(Class targetType) { - return new StringToEnumConverter(targetType); - } + public Converter getConverter(Class targetType) { + return new StringToEnumConverter(targetType); + } - private final class StringToEnumConverter implements Converter { + private final class StringToEnumConverter implements Converter { - private Class enumType; + private Class enumType; - public StringToEnumConverter(Class enumType) { - this.enumType = enumType; - } + public StringToEnumConverter(Class enumType) { + this.enumType = enumType; + } - public T convert(String source) { - return (T) Enum.valueOf(this.enumType, source.trim()); - } - } -} + public T convert(String source) { + return (T) Enum.valueOf(this.enumType, source.trim()); + } + } + } ---- @@ -9294,18 +9618,18 @@ GenericConverter makes available source and target field context you can use whe 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.springframework.core.convert.converter; + package org.springframework.core.convert.converter; -public interface GenericConverter { + public interface GenericConverter { - public Set getConvertibleTypes(); + public Set getConvertibleTypes(); - Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType); + Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType); -} + } ---- To implement a GenericConverter, have getConvertibleTypes() return the supported @@ -9336,14 +9660,14 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface ConditionalGenericConverter extends GenericConverter { + public interface ConditionalGenericConverter extends GenericConverter { - boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType); + boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType); -} + } ---- A good example of a ConditionalGenericConverter is an EntityConverter that converts @@ -9359,22 +9683,22 @@ matches(TypeDescriptor, TypeDescriptor). The ConversionService defines a unified API for executing type conversion logic at runtime. Converters are often executed behind this facade interface: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.springframework.core.convert; + package org.springframework.core.convert; -public interface ConversionService { + public interface ConversionService { - boolean canConvert(Class sourceType, Class targetType); + boolean canConvert(Class sourceType, Class targetType); - T convert(Object source, Class targetType); + T convert(Object source, Class targetType); - boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType); + boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType); - Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType); + Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType); -} + } ---- Most ConversionService implementations also implement `ConverterRegistry`, which @@ -9406,11 +9730,11 @@ system is used. To register a default ConversionService with Spring, add the following bean definition with id `conversionService`: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- A default ConversionService can convert between strings, numbers, enums, collections, @@ -9418,17 +9742,17 @@ maps, and other common types. To supplement or override the default converters w own custom converter(s), set the `converters` property. Property values may implement either of the Converter, ConverterFactory, or GenericConverter interfaces. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - + + + + + + + ---- It is also common to use a ConversionService within a Spring MVC application. See @@ -9445,28 +9769,28 @@ In certain situations you may wish to apply formatting during conversion. See To work with a ConversionService instance programmatically, simply inject a reference to it like you would for any other bean: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Service -public class MyService { + @Service + public class MyService { - @Autowired - public MyService(ConversionService conversionService) { - this.conversionService = conversionService; - } + @Autowired + public MyService(ConversionService conversionService) { + this.conversionService = conversionService; + } - public void doIt() { - this.conversionService.convert(...) - } -} + public void doIt() { + this.conversionService.convert(...) + } + } ---- [[format]] -=== Spring 3 Field Formatting +=== Spring Field Formatting As discussed in the previous section, <> 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 @@ -9496,33 +9820,33 @@ ConversionService provides a unified type conversion API for both SPIs. ==== Formatter SPI The Formatter SPI to implement field formatting logic is simple and strongly typed: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.springframework.format; + package org.springframework.format; -public interface Formatter extends Printer, Parser { -} + public interface Formatter extends Printer, Parser { + } ---- Where Formatter extends from the Printer and Parser building-block interfaces: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface Printer { - String print(T fieldValue, Locale locale); -} + public interface Printer { + String print(T fieldValue, Locale locale); + } ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import java.text.ParseException; + import java.text.ParseException; -public interface Parser { - T parse(String clientValue, Locale locale) throws ParseException; -} + public interface Parser { + T parse(String clientValue, Locale locale) throws ParseException; + } ---- To create your own Formatter, simply implement the Formatter interface above. @@ -9534,48 +9858,48 @@ should throw a ParseException or IllegalArgumentException if a parse attempt fai 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 +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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.springframework.format.datetime; + package org.springframework.format.datetime; -public final class DateFormatter implements Formatter { + public final class DateFormatter implements Formatter { - private String pattern; + private String pattern; - public DateFormatter(String pattern) { - this.pattern = 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 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); - } + 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; - } + 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 @@ -9588,20 +9912,20 @@ http://jira.springframework.org[http://jira.springframework.org] to contribute. 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.springframework.format; + package org.springframework.format; -public interface AnnotationFormatterFactory { + public interface AnnotationFormatterFactory { - Set> getFieldTypes(); + Set> getFieldTypes(); - Printer getPrinter(A annotation, Class fieldType); + Printer getPrinter(A annotation, Class fieldType); - Parser getParser(A annotation, Class fieldType); + Parser getParser(A annotation, Class fieldType); -} + } ---- Parameterize A to be the field annotationType you wish to associate formatting logic @@ -9614,55 +9938,55 @@ The example AnnotationFormatterFactory implementation below binds the @NumberFor Annotation to a formatter. This annotation allows either a number style or pattern to be specified: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public final class NumberFormatAnnotationFormatterFactory - implements AnnotationFormatterFactory { + public final class NumberFormatAnnotationFormatterFactory + implements AnnotationFormatterFactory { - public Set> getFieldTypes() { - return new HashSet>(asList(new Class[] { - Short.class, Integer.class, Long.class, Float.class, - Double.class, BigDecimal.class, BigInteger.class })); - } + public Set> getFieldTypes() { + return new HashSet>(asList(new Class[] { + Short.class, Integer.class, Long.class, Float.class, + Double.class, BigDecimal.class, BigInteger.class })); + } - public Printer getPrinter(NumberFormat annotation, Class fieldType) { - return configureFormatterFrom(annotation, fieldType); - } + public Printer getPrinter(NumberFormat annotation, Class fieldType) { + return configureFormatterFrom(annotation, fieldType); + } - public Parser getParser(NumberFormat annotation, Class fieldType) { - return configureFormatterFrom(annotation, fieldType); - } + public Parser getParser(NumberFormat annotation, Class fieldType) { + return configureFormatterFrom(annotation, fieldType); + } - private Formatter 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(); - } - } - } -} + private Formatter 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MyModel { + public class MyModel { - @NumberFormat(style=Style.CURRENCY) - private BigDecimal decimal; + @NumberFormat(style=Style.CURRENCY) + private BigDecimal decimal; -} + } ---- @@ -9675,15 +9999,15 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MyModel { + public class MyModel { - @DateTimeFormat(iso=ISO.DATE) - private Date date; + @DateTimeFormat(iso=ISO.DATE) + private Date date; -} + } ---- @@ -9699,22 +10023,22 @@ for use with Spring's DataBinder and the Spring Expression Language (SpEL). Review the FormatterRegistry SPI below: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.springframework.format; + package org.springframework.format; -public interface FormatterRegistry extends ConverterRegistry { + public interface FormatterRegistry extends ConverterRegistry { - void addFormatterForFieldType(Class fieldType, Printer printer, Parser parser); + void addFormatterForFieldType(Class fieldType, Printer printer, Parser parser); - void addFormatterForFieldType(Class fieldType, Formatter formatter); + void addFormatterForFieldType(Class fieldType, Formatter formatter); - void addFormatterForFieldType(Formatter formatter); + void addFormatterForFieldType(Formatter formatter); - void addFormatterForAnnotation(AnnotationFormatterFactory factory); + void addFormatterForAnnotation(AnnotationFormatterFactory factory); -} + } ---- As shown above, Formatters can be registered by fieldType or annotation. @@ -9732,16 +10056,16 @@ these rules once and they are applied whenever formatting is needed. The FormatterRegistrar is an SPI for registering formatters and converters through the FormatterRegistry: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.springframework.format; + package org.springframework.format; -public interface FormatterRegistrar { + public interface FormatterRegistrar { - void registerFormatters(FormatterRegistry registry); + void registerFormatters(FormatterRegistry registry); -} + } ---- A FormatterRegistrar is useful when registering multiple related converters and @@ -9764,22 +10088,22 @@ register default formatters and converters for common types such as numbers and To rely on default formatting rules, no custom configuration is required in your Spring MVC config XML: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + - + ---- With this one-line of configuration, default formatters for Numbers and Date types will @@ -9791,42 +10115,42 @@ To inject a ConversionService instance with custom formatters and converters reg set the conversion-service attribute and then specify custom converters, formatters, or FormatterRegistrars as properties of the FormattingConversionServiceFactoryBean: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - + ---- [NOTE] @@ -9853,65 +10177,65 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -public class AppConfig { + @Configuration + public class AppConfig { - @Bean - public FormattingConversionService conversionService() { + @Bean + public FormattingConversionService conversionService() { - // Use the DefaultFormattingConversionService but do not register defaults - DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(false); + // Use the DefaultFormattingConversionService but do not register defaults + DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(false); - // Ensure @NumberFormat is still supported - conversionService.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory()); + // 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); + // Register date conversion with a specific global format + DateFormatterRegistrar registrar = new DateFormatterRegistrar(); + registrar.setFormatter(new DateFormatter("yyyyMMdd")); + registrar.registerFormatters(conversionService); - return 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + ---- [NOTE] @@ -9932,7 +10256,7 @@ For XML you should use the `'conversion-service'` attribute of the [[validation-beanvalidation]] -=== Spring 3 Validation +=== 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 @@ -9949,30 +10273,30 @@ constraints you can take advantage of. You may also define your own custom const To illustrate, consider a simple PersonForm model with two properties: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class PersonForm { - private String name; - private int age; -} + public class PersonForm { + private String name; + private int age; + } ---- JSR-303 allows you to define declarative validation constraints against such properties: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class PersonForm { + public class PersonForm { - @NotNull - @Size(max=64) - private String name; + @NotNull + @Size(max=64) + private String name; - @Min(0) - private int age; + @Min(0) + private int age; -} + } ---- When an instance of this class is validated by a JSR-303 Validator, these constraints @@ -9995,11 +10319,11 @@ be injected wherever validation is needed in your application. Use the `LocalValidatorFactoryBean` to configure a default Validator as a Spring bean: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- The basic configuration above will trigger Bean Validation to initialize using its @@ -10017,33 +10341,33 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import javax.validation.Validator; + import javax.validation.Validator; -@Service -public class MyService { + @Service + public class MyService { - @Autowired - private Validator validator; + @Autowired + private Validator validator; ---- Inject a reference to `org.springframework.validation.Validator` if your bean requires the Spring Validation API: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import org.springframework.validation.Validator; + import org.springframework.validation.Validator; -@Service -public class MyService { + @Service + public class MyService { - @Autowired - private Validator validator; + @Autowired + private Validator validator; -} + } ---- @@ -10065,28 +10389,28 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Target({ElementType.METHOD, ElementType.FIELD}) -@Retention(RetentionPolicy.RUNTIME) -@Constraint(validatedBy=MyConstraintValidator.class) -public @interface MyConstraint { -} + @Target({ElementType.METHOD, ElementType.FIELD}) + @Retention(RetentionPolicy.RUNTIME) + @Constraint(validatedBy=MyConstraintValidator.class) + public @interface MyConstraint { + } ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import javax.validation.ConstraintValidator; + import javax.validation.ConstraintValidator; -public class MyConstraintValidator implements ConstraintValidator { + public class MyConstraintValidator implements ConstraintValidator { - @Autowired; - private Foo aDependency; + @Autowired; + private Foo aDependency; - ... -} + ... + } ---- As you can see, a ConstraintValidator implementation may have its dependencies @@ -10111,21 +10435,21 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -Foo target = new Foo(); -DataBinder binder = new DataBinder(target); -binder.setValidator(new FooValidator()); + Foo target = new Foo(); + DataBinder binder = new DataBinder(target); + binder.setValidator(new FooValidator()); -// bind to the target object -binder.bind(propertyValues); + // bind to the target object + binder.bind(propertyValues); -// validate the target object -binder.validate(); + // validate the target object + binder.validate(); -// get BindingResult that includes any validation errors -BindingResult results = binder.getBindingResult(); + // get BindingResult that includes any validation errors + BindingResult results = binder.getBindingResult(); ---- A DataBinder can also be configured with multiple `Validator` instances via @@ -10147,14 +10471,14 @@ validation logic. To trigger validation of a @Controller input, simply annotate the input argument as @Valid: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Controller -public class MyController { + @Controller + public class MyController { - @RequestMapping("/foo", method=RequestMethod.POST) - public void processFoo(**@Valid** Foo foo) { /* ... */ } + @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 @@ -10174,60 +10498,60 @@ configured in two ways. First, you may call binder.setValidator(Validator) withi @Controller's @InitBinder callback. This allows you to configure a Validator instance per @Controller class: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Controller -public class MyController { + @Controller + public class MyController { - @InitBinder - protected void initBinder(WebDataBinder binder) { - binder.setValidator(new FooValidator()); - } + @InitBinder + protected void initBinder(WebDataBinder binder) { + binder.setValidator(new FooValidator()); + } - @RequestMapping("/foo", method=RequestMethod.POST) - public void processFoo(@Valid Foo foo) { ... } + @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 @Controllers. This can be achieved easily by using the Spring MVC namespace: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + - + ---- To combine a global and a local validator, configure the global validator as shown above and then add a local validator: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Controller -public class MyController { + @Controller + public class MyController { - @InitBinder - protected void initBinder(WebDataBinder binder) { - binder.addValidators(new FooValidator()); - } + @InitBinder + protected void initBinder(WebDataBinder binder) { + binder.addValidators(new FooValidator()); + } -} + } ---- @@ -10241,23 +10565,23 @@ enable Bean Validation support across all Controllers. The Spring MVC configuration required to enable Bean Validation support is shown below: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - + + - + ---- With this minimal configuration, anytime a @Valid @Controller input is encountered, it @@ -10344,12 +10668,12 @@ The complete language reference can be found in the section The following code introduces the SpEL API to evaluate the literal string expression 'Hello World'. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -ExpressionParser parser = new SpelExpressionParser(); -Expression exp = parser.parseExpression("**\'Hello World'**"); -String message = (String) exp.getValue(); + 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'. @@ -10369,12 +10693,12 @@ and calling constructors. As an example of method invocation, we call the 'concat' method on the string literal. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -ExpressionParser parser = new SpelExpressionParser(); -Expression exp = parser.parseExpression("**\'Hello World'.concat(\'!')**"); -String message = (String) exp.getValue(); + ExpressionParser parser = new SpelExpressionParser(); + Expression exp = parser.parseExpression("**\'Hello World'.concat(\'!')**"); + String message = (String) exp.getValue(); ---- The value of message is now 'Hello World!'. @@ -10382,14 +10706,14 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -ExpressionParser parser = new SpelExpressionParser(); + ExpressionParser parser = new SpelExpressionParser(); -// invokes 'getBytes()' -Expression exp = parser.parseExpression("**\'Hello World'.bytes**"); -byte[] bytes = (byte[]) exp.getValue(); + // 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. @@ -10397,24 +10721,24 @@ prop1.prop2.prop3 and the setting of property values Public fields may also be accessed. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -ExpressionParser parser = new SpelExpressionParser(); + ExpressionParser parser = new SpelExpressionParser(); -// invokes 'getBytes().length' -Expression exp = parser.parseExpression("**\'Hello World'.bytes.length**"); -int length = (Integer) exp.getValue(); + // 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] +[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); + 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 getValue(Class desiredResultType)`. @@ -10425,46 +10749,46 @@ 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 +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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// Create and set a calendar -GregorianCalendar c = new GregorianCalendar(); -c.set(1856, 7, 9); + // 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"); + // 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**"); + ExpressionParser parser = new SpelExpressionParser(); + Expression exp = parser.parseExpression("**name**"); -EvaluationContext context = new StandardEvaluationContext(tesla); -String name = (String) exp.getValue(context); + 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 +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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -/ Create and set a calendar -GregorianCalendar c = new GregorianCalendar(); -c.set(1856, 7, 9); + / 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"); + // 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); + 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 @@ -10473,7 +10797,7 @@ 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 +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 @@ -10493,11 +10817,11 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -Expression exp = parser.parseExpression("name == 'Nikola Tesla'"); -boolean result = exp.getValue(context, Boolean.class); // evaluates to true + Expression exp = parser.parseExpression("name == 'Nikola Tesla'"); + boolean result = exp.getValue(context, Boolean.class); // evaluates to true ---- @@ -10536,25 +10860,25 @@ to set a `List` property. The type of the property is actually `List`. will recognize that the elements of the list need to be converted to `Boolean` before being placed in it. A simple example: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -class Simple { - public List booleanList = new ArrayList(); -} + class Simple { + public List booleanList = new ArrayList(); + } -Simple simple = new Simple(); + Simple simple = new Simple(); -simple.booleanList.add(true); + simple.booleanList.add(true); -StandardEvaluationContext simpleContext = new StandardEvaluationContext(simple); + 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"); + // 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); + // b will be false + Boolean b = simple.booleanList.get(0); ---- @@ -10572,46 +10896,46 @@ form `#{ }`. ==== XML based configuration A property or constructor-arg value can be set using expressions as shown below -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - + + ---- 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - + + ---- You can also refer to other bean properties by name, for example. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - + + - - + + - - + + ---- @@ -10623,89 +10947,85 @@ parameters to specify a default value. Here is an example to set the default value of a field variable. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public static class FieldValueTestBean + public static class FieldValueTestBean - @Value("#{ systemProperties['user.region'] }") - private String defaultLocale; + @Value("#{ systemProperties['user.region'] }") + private String defaultLocale; - public void setDefaultLocale(String defaultLocale) - { - this.defaultLocale = defaultLocale; - } + public void setDefaultLocale(String defaultLocale) { + this.defaultLocale = defaultLocale; + } - public String getDefaultLocale() - { - return this.defaultLocale; - } + public String getDefaultLocale() { + return this.defaultLocale; + } -} + } ---- The equivalent but on a property setter method is shown below. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public static class PropertyValueTestBean + public static class PropertyValueTestBean - private String defaultLocale; + private String defaultLocale; - @Value("#{ systemProperties['user.region'] }") - public void setDefaultLocale(String defaultLocale) - { - this.defaultLocale = defaultLocale; - } + @Value("#{ systemProperties['user.region'] }") + public void setDefaultLocale(String defaultLocale) { + this.defaultLocale = defaultLocale; + } - public String getDefaultLocale() - { - return this.defaultLocale; - } + public String getDefaultLocale() { + return this.defaultLocale; + } -} + } ---- Autowired methods and constructors can also use the `@Value` annotation. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class SimpleMovieLister { + public class SimpleMovieLister { - private MovieFinder movieFinder; - private String defaultLocale; + private MovieFinder movieFinder; + private String defaultLocale; - @Autowired - public void configure(MovieFinder movieFinder, - @Value("#{ systemProperties['user.region'] }") String defaultLocale) { - this.movieFinder = movieFinder; - this.defaultLocale = defaultLocale; - } + @Autowired + public void configure(MovieFinder movieFinder, + @Value("#{ systemProperties['user.region'] }") String defaultLocale) { + this.movieFinder = movieFinder; + this.defaultLocale = defaultLocale; + } - // ... -} + // ... + } ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MovieRecommender { + public class MovieRecommender { - private String defaultLocale; + private String defaultLocale; - private CustomerPreferenceDao customerPreferenceDao; + private CustomerPreferenceDao customerPreferenceDao; - @Autowired - public MovieRecommender(CustomerPreferenceDao customerPreferenceDao, - @Value("#{systemProperties['user.country']}") String defaultLocale) { - this.customerPreferenceDao = customerPreferenceDao; - this.defaultLocale = defaultLocale; - } + @Autowired + public MovieRecommender(CustomerPreferenceDao customerPreferenceDao, + @Value("#{systemProperties['user.country']}") String defaultLocale) { + this.customerPreferenceDao = customerPreferenceDao; + this.defaultLocale = defaultLocale; + } - // ... -} + // ... + } ---- @@ -10725,22 +11045,22 @@ shows simple usage of literals. Typically they would not be used in isolation li but as part of a more complex expression, for example using a literal on one side of a logical comparison operator. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -ExpressionParser parser = new SpelExpressionParser(); + ExpressionParser parser = new SpelExpressionParser(); -// evals to "Hello World" -String helloWorld = (String) parser.parseExpression("'Hello World'").getValue(); + // evals to "Hello World" + String helloWorld = (String) parser.parseExpression("'Hello World'").getValue(); -double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue(); + double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue(); -// evals to 2147483647 -int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue(); + // evals to 2147483647 + int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue(); -boolean trueValue = (Boolean) parser.parseExpression("true").getValue(); + boolean trueValue = (Boolean) parser.parseExpression("true").getValue(); -Object nullValue = parser.parseExpression("null").getValue(); + Object nullValue = parser.parseExpression("null").getValue(); ---- Numbers support the use of the negative sign, exponential notation, and decimal points. @@ -10756,62 +11076,62 @@ data listed in the section <'), le ('<='), ge ('>='), eq ('=='), ne ('!='), div ('/'), mod ('%'), not ('!'). These are case insensitive. @@ -10927,35 +11248,35 @@ mod ('%'), not ('!'). These are case insensitive. The logical operators that are supported are and, or, and not. Their use is demonstrated below. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// -- AND -- + // -- AND -- -// evaluates to false -boolean falseValue = parser.parseExpression("true and false").getValue(Boolean.class); + // 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); + // evaluates to true + String expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')"; + boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class); -// -- OR -- + // -- OR -- -// evaluates to true -boolean trueValue = parser.parseExpression("true or false").getValue(Boolean.class); + // 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); + // evaluates to true + String expression = "isMember('Nikola Tesla') or isMember('Albert Einstein')"; + boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class); -// -- NOT -- + // -- NOT -- -// evaluates to false -boolean falseValue = parser.parseExpression("!true").getValue(Boolean.class); + // 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); + // -- AND and NOT -- + String expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')"; + boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class); ---- @@ -10966,37 +11287,37 @@ and division can be used only on numbers. Other mathematical operators supported modulus (%) and exponential power (^). Standard operator precedence is enforced. These operators are demonstrated below. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// Addition -int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2 + // Addition + int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2 -String testString = - parser.parseExpression("'test' + ' ' + 'string'").getValue(String.class); // 'test string' + String testString = parser.parseExpression( + "'test' + ' ' + 'string'").getValue(String.class); // 'test string' -// Subtraction -int four = parser.parseExpression("1 - -3").getValue(Integer.class); // 4 + // Subtraction + int four = parser.parseExpression("1 - -3").getValue(Integer.class); // 4 -double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class); // -9000 + double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class); // -9000 -// Multiplication -int six = parser.parseExpression("-2 * -3").getValue(Integer.class); // 6 + // Multiplication + int six = parser.parseExpression("-2 * -3").getValue(Integer.class); // 6 -double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); // 24.0 + double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); // 24.0 -// Division -int minusTwo = parser.parseExpression("6 / -3").getValue(Integer.class); // -2 + // Division + int minusTwo = parser.parseExpression("6 / -3").getValue(Integer.class); // -2 -double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class); // 1.0 + double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class); // 1.0 -// Modulus -int three = parser.parseExpression("7 % 4").getValue(Integer.class); // 3 + // Modulus + int three = parser.parseExpression("7 % 4").getValue(Integer.class); // 3 -int one = parser.parseExpression("8 / 5 % 2").getValue(Integer.class); // 1 + 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 + // Operator precedence + int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21 ---- @@ -11006,18 +11327,18 @@ int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -Inventor inventor = new Inventor(); -StandardEvaluationContext inventorContext = new StandardEvaluationContext(inventor); + Inventor inventor = new Inventor(); + StandardEvaluationContext inventorContext = new StandardEvaluationContext(inventor); -parser.parseExpression("Name").setValue(inventorContext, "Alexander Seovic2"); + parser.parseExpression("Name").setValue(inventorContext, "Alexander Seovic2"); -// alternatively + // alternatively -String aleks = parser.parseExpression("Name = 'Alexandar Seovic'").getValue(inventorContext, - String.class); + String aleks = parser.parseExpression( + "Name = 'Alexandar Seovic'").getValue(inventorContext, String.class); ---- @@ -11031,16 +11352,16 @@ The special 'T' operator can be used to specify an instance of java.lang.Class ( 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class); + Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class); -Class stringClass = parser.parseExpression("T(String)").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); + boolean trueValue = parser.parseExpression( + "T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR") + .getValue(Boolean.class); ---- @@ -11051,18 +11372,17 @@ Constructors can be invoked using the new operator. The fully qualified class na should be used for all but the primitive type and String (where int, float, etc, can be used). -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -Inventor einstein = - p.parseExpression("new org.spring.samples.spel.inventor.Inventor('Albert Einstein', - 'German')") - .getValue(Inventor.class); + 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); + //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); ---- @@ -11072,16 +11392,16 @@ p.parseExpression("Members.add(new org.spring.samples.spel.inventor.Inventor('Al Variables can be referenced in the expression using the syntax #variableName. Variables are set using the method setVariable on the StandardEvaluationContext. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); -StandardEvaluationContext context = new StandardEvaluationContext(tesla); -context.setVariable("newName", "Mike Tesla"); + Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); + StandardEvaluationContext context = new StandardEvaluationContext(tesla); + context.setVariable("newName", "Mike Tesla"); -parser.parseExpression("Name = #newName").getValue(context); + parser.parseExpression("Name = #newName").getValue(context); -System.out.println(tesla.getName()) // "Mike Tesla" + System.out.println(tesla.getName()) // "Mike Tesla" ---- @@ -11089,25 +11409,25 @@ System.out.println(tesla.getName()) // "Mike Tesla" ===== 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 +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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// create an array of integers -List primes = new ArrayList(); -primes.addAll(Arrays.asList(2,3,5,7,11,13,17)); + // create an array of integers + List primes = new ArrayList(); + 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); + // 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 primesGreaterThanTen = - (List) parser.parseExpression("#primes.?[#this>10]").getValue(context); + // all prime numbers > 10 from the list (using selection ?{...}) + // evaluates to [11, 13, 17] + List primesGreaterThanTen = (List) parser.parseExpression( + "#primes.?[#this>10]").getValue(context); ---- @@ -11118,45 +11438,44 @@ You can extend SpEL by registering user defined functions that can be called wit expression string. The function is registered with the `StandardEvaluationContext` using the method. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public void registerFunction(String name, Method m) + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public abstract class StringUtils { + 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(); - } -} + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -ExpressionParser parser = new SpelExpressionParser(); -StandardEvaluationContext context = new StandardEvaluationContext(); + ExpressionParser parser = new SpelExpressionParser(); + StandardEvaluationContext context = new StandardEvaluationContext(); -context.registerFunction("reverseString", - StringUtils.class.getDeclaredMethod("reverseString", - new Class[] { String.class })); + context.registerFunction("reverseString", + StringUtils.class.getDeclaredMethod("reverseString", new Class[] { String.class })); -String helloWorldReversed = - parser.parseExpression("#reverseString('hello')").getValue(context, String.class); + String helloWorldReversed = parser.parseExpression( + "#reverseString('hello')").getValue(context, String.class); ---- @@ -11166,15 +11485,15 @@ String helloWorldReversed = 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -ExpressionParser parser = new SpelExpressionParser(); -StandardEvaluationContext context = new StandardEvaluationContext(); -context.setBeanResolver(new MyBeanResolver()); + 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); + // This will end up calling resolve(context,"foo") on MyBeanResolver during evaluation + Object bean = parser.parseExpression("@foo").getValue(context); ---- @@ -11184,28 +11503,28 @@ Object bean = parser.parseExpression("@foo").getValue(context); You can use the ternary operator for performing if-then-else conditional logic inside the expression. A minimal example is: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -String falseString = - parser.parseExpression("false ? 'trueExp' : 'falseExp'").getValue(String.class); + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -parser.parseExpression("Name").setValue(societyContext, "IEEE"); -societyContext.setVariable("queryName", "Nikola Tesla"); + 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'"; + 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" + 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 @@ -11220,44 +11539,44 @@ http://groovy.codehaus.org/Operators#Operators-ElvisOperator(%3F%3A)[Groovy] lan With the ternary operator syntax you usually have to repeat a variable twice, for example: -[source] +[source,groovy,indent=0] [subs="verbatim,quotes"] ---- -String name = "Elvis Presley"; -String displayName = name != null ? name : "Unknown"; + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -ExpressionParser parser = new SpelExpressionParser(); + ExpressionParser parser = new SpelExpressionParser(); -String name = parser.parseExpression("null?:'Unknown'").getValue(String.class); + String name = parser.parseExpression("null?:'Unknown'").getValue(String.class); -System.out.println(name); // 'Unknown' + System.out.println(name); // 'Unknown' ---- Here is a more complex example. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -ExpressionParser parser = new SpelExpressionParser(); + ExpressionParser parser = new SpelExpressionParser(); -Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); -StandardEvaluationContext context = new StandardEvaluationContext(tesla); + Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); + StandardEvaluationContext context = new StandardEvaluationContext(tesla); -String name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, String.class); + String name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, String.class); -System.out.println(name); // Nikola Tesla + System.out.println(name); // Nikola Tesla -tesla.setName(null); + tesla.setName(null); -name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, String.class); + name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, String.class); -System.out.println(name); // Elvis Presley + System.out.println(name); // Elvis Presley ---- @@ -11270,24 +11589,24 @@ language. Typically when you have a reference to an object you might need to ver 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -ExpressionParser parser = new SpelExpressionParser(); + ExpressionParser parser = new SpelExpressionParser(); -Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); -tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan")); + Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); + tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan")); -StandardEvaluationContext context = new StandardEvaluationContext(tesla); + StandardEvaluationContext context = new StandardEvaluationContext(tesla); -String city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, String.class); -System.out.println(city); // Smiljan + String city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, String.class); + System.out.println(city); // Smiljan -tesla.setPlaceOfBirth(null); + tesla.setPlaceOfBirth(null); -city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, String.class); + city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, String.class); -System.out.println(city); // null - does not throw NullPointerException!!! + System.out.println(city); // null - does not throw NullPointerException!!! ---- [NOTE] @@ -11295,10 +11614,10 @@ System.out.println(city); // null - does not throw NullPointerException!!! The Elvis operator can be used to apply default values in expressions, e.g. in an `@Value` expression: -[source] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Value("#{systemProperties['pop3.port'] ?: 25}") + @Value("#{systemProperties['pop3.port'] ?: 25}") ---- This will inject a system property `pop3.port` if it is defined or 25 if not. @@ -11315,11 +11634,11 @@ Selection uses the syntax `?[selectionExpression]`. This will filter the collect 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -List list = (List) - parser.parseExpression("Members.?[Nationality == 'Serbian']").getValue(societyContext); + List list = (List) parser.parseExpression( + "Members.?[Nationality == 'Serbian']").getValue(societyContext); ---- Selection is possible upon both lists and maps. In the former case the selection @@ -11331,10 +11650,10 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -Map newMap = parser.parseExpression("map.?[value<27]").getValue(); + Map newMap = parser.parseExpression("map.?[value<27]").getValue(); ---- In addition to returning all the selected elements, it is possible to retrieve just the @@ -11351,11 +11670,11 @@ easily understood by example, suppose we have a list of inventors but want the l cities where they were born. Effectively we want to evaluate 'placeOfBirth.city' for every entry in the inventor list. Using projection: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// returns ['Smiljan', 'Idvor' ] -List placesOfBirth = (List)parser.parseExpression("Members.![placeOfBirth.city]"); + // 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 @@ -11371,14 +11690,14 @@ Expression templates allow a mixing of literal text with one or more evaluation 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -String randomPhrase = - parser.parseExpression("random number is #{T(java.lang.Math).random()}", - new TemplateParserContext()).getValue(String.class); + String randomPhrase = parser.parseExpression( + "random number is #{T(java.lang.Math).random()}", + new TemplateParserContext()).getValue(String.class); -// evaluates to "random number is 0.7038186818312008" + // evaluates to "random number is 0.7038186818312008" ---- The string is evaluated by concatenating the literal text 'random number is ' with the @@ -11388,23 +11707,23 @@ is of the type `ParserContext`. The `ParserContext` interface is used to influen the expression is parsed in order to support the expression templating functionality. The definition of `TemplateParserContext` is shown below. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class TemplateParserContext implements ParserContext { + public class TemplateParserContext implements ParserContext { - public String getExpressionPrefix() { - return "#{"; - } + public String getExpressionPrefix() { + return "#{"; + } - public String getExpressionSuffix() { - return "}"; - } + public String getExpressionSuffix() { + return "}"; + } - public boolean isTemplate() { - return true; - } -} + public boolean isTemplate() { + return true; + } + } ---- @@ -11414,157 +11733,165 @@ public class TemplateParserContext implements ParserContext { === Classes used in the examples Inventor.java -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.spring.samples.spel.inventor; + package org.spring.samples.spel.inventor; -import java.util.Date; -import java.util.GregorianCalendar; + import java.util.Date; + import java.util.GregorianCalendar; -public class Inventor { + public class Inventor { - private String name; - private String nationality; - private String[] inventions; - private Date birthdate; - private PlaceOfBirth placeOfBirth; + 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(String name, String nationality) { + GregorianCalendar c= new GregorianCalendar(); + this.name = name; + this.nationality = nationality; + this.birthdate = c.getTime(); + } - public Inventor() { - } + public Inventor(String name, Date birthdate, String nationality) { + this.name = name; + this.nationality = nationality; + this.birthdate = birthdate; + } - 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; - } -} + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.spring.samples.spel.inventor; + package org.spring.samples.spel.inventor; -public class PlaceOfBirth { + public class PlaceOfBirth { - private String city; - private String country; + 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 PlaceOfBirth(String city) { + this.city=city; + } - 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; - } + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.spring.samples.spel.inventor; + package org.spring.samples.spel.inventor; -import java.util.*; + import java.util.*; -public class Society { + public class Society { - private String name; + private String name; - public static String Advisors = "advisors"; - public static String President = "president"; + public static String Advisors = "advisors"; + public static String President = "president"; - private List members = new ArrayList(); - private Map officers = new HashMap(); + private List members = new ArrayList(); + private Map officers = new HashMap(); - public List getMembers() { - return members; - } + public List getMembers() { + return members; + } - public Map getOfficers() { - return officers; - } + public Map getOfficers() { + return officers; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public void setName(String name) { - this.name = name; - } + public void setName(String name) { + this.name = name; + } - public boolean isMember(String name) - { - boolean found = false; - for (Inventor inventor : members) { - if (inventor.getName().equals(name)) - { - found = true; - break; - } - } - return found; - } + public boolean isMember(String name) { + for (Inventor inventor : members) { + if (inventor.getName().equals(name)) { + return true; + } + } + return false; + } -} + } ---- @@ -11578,7 +11905,7 @@ public class Society { [[aop-introduction]] -=== IntroductionSpring 2.0 AOP +=== 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 @@ -11590,6 +11917,7 @@ One of the key components of Spring is the __AOP framework__. While the Spring I 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 <> or the <>) or regular classes annotated with the + (the <>) or regular classes annotated with the `@Aspect` annotation (the <>). * __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__, + 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, @@ -11647,8 +11975,8 @@ however, it would be even more confusing if Spring used its own terminology. `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. + 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. @@ -11723,7 +12051,7 @@ 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 2.0 seamlessly integrates Spring AOP and IoC with AspectJ, to enable +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 <> for a @@ -11777,9 +12105,9 @@ implementation detail actually means. [[aop-ataspectj]] === @AspectJ support @AspectJ refers to a style of declaring aspects as regular Java classes annotated with -Java 5 annotations. The @AspectJ style was introduced by the +annotations. The @AspectJ style was introduced by the http://www.eclipse.org/aspectj[AspectJ project] as part of the AspectJ 5 release. Spring -2.0 interprets the same annotations as AspectJ 5, using a library supplied by AspectJ +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. @@ -11800,7 +12128,7 @@ determines that a bean is advised by one or more aspects, it will automatically 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 +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. @@ -11811,14 +12139,14 @@ classpath of your application (version 1.6.8 or later). This library is availabl To enable @AspectJ support with Java `@Configuration` add the `@EnableAspectJAutoProxy` annotation: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -@EnableAspectJAutoProxy -public class AppConfig { + @Configuration + @EnableAspectJAutoProxy + public class AppConfig { -} + } ---- @@ -11827,24 +12155,15 @@ public class AppConfig { To enable @AspectJ support with XML based configuration use the `aop:aspectj-autoproxy` element: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- This assumes that you are using schema support as described in <>. See <> for how to import the tags in the aop namespace. -If you are using the DTD, it is still possible to enable @AspectJ support by adding the -following definition to your application context: - -[source,xml] -[subs="verbatim,quotes"] ----- - ----- - [[aop-at-aspectj]] @@ -11858,27 +12177,27 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- And the `NotVeryUsefulAspect` class definition, annotated with `org.aspectj.lang.annotation.Aspect` annotation; -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.xyz; -import org.aspectj.lang.annotation.Aspect; + package org.xyz; + import org.aspectj.lang.annotation.Aspect; -@Aspect -public class NotVeryUsefulAspect { + @Aspect + public class NotVeryUsefulAspect { -} + } ---- Aspects (classes annotated with `@Aspect`) may have methods and fields just like any @@ -11900,7 +12219,7 @@ Spring's component scanner). [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 +from other aspects. The __@Aspect__ annotation on a class marks it as an aspect, and hence excludes it from auto-proxying. ==== @@ -11922,27 +12241,28 @@ An example will help make this distinction between a pointcut signature and a po expression clear. The following example defines a pointcut named `'anyOldTransfer'` that will match the execution of any method named `'transfer'`: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Pointcut("execution(* transfer(..))")// the pointcut expression -private void anyOldTransfer() {}// the pointcut signature + @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 Java 5 based extensions, the +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 DesignatorsOther pointcut types +===== 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, @@ -11994,7 +12314,7 @@ purposes). As a consequence, any given pointcut will be matched against __public only__! If your interception needs include protected/private methods or even constructors, -consider the use of Spring-driven<> instead of +consider the use of Spring-driven <> 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. @@ -12004,10 +12324,10 @@ Spring AOP also supports an additional PCD named ' `bean`'. This PCD allows you 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -bean(idOrNameOfBean) + bean(idOrNameOfBean) ---- The ' `idOrNameOfBean`' token can be the name of any Spring bean: limited wildcard @@ -12039,17 +12359,17 @@ represents the execution of any public method); `inTrading` (which matches if a execution is in the trading module), and `tradingOperation` (which matches if a method execution represents any public method in the trading module). -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Pointcut("execution(public * *(..))") - private void anyPublicOperation() {} + @Pointcut("execution(public * *(..))") + private void anyPublicOperation() {} - @Pointcut("within(com.xyz.someapp.trading..*)") - private void inTrading() {} + @Pointcut("within(com.xyz.someapp.trading..*)") + private void inTrading() {} - @Pointcut("anyPublicOperation() && inTrading()") - private void tradingOperation() {} + @Pointcut("anyPublicOperation() && inTrading()") + private void tradingOperation() {} ---- It is a best practice to build more complex pointcut expressions out of smaller named @@ -12066,87 +12386,87 @@ application and particular sets of operations from within several aspects. We re defining a "SystemArchitecture" aspect that captures common pointcut expressions for this purpose. A typical such aspect would look as follows: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.xyz.someapp; + package com.xyz.someapp; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Pointcut; + import org.aspectj.lang.annotation.Aspect; + import org.aspectj.lang.annotation.Pointcut; -@Aspect -public class SystemArchitecture { + @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 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 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 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.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 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.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() {} + /** + * 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + - - - - - + + + + + ---- The `` and `` elements are discussed in <>. The @@ -12158,11 +12478,11 @@ transaction elements are discussed in <>. Spring AOP users are likely to use the `execution` pointcut designator the most often. The format of an execution expression is: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) - throws-pattern?) + 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), @@ -12183,68 +12503,68 @@ Some examples of common pointcut expressions are given below. * the execution of any public method: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -execution(public * *(..)) + execution(public * *(..)) ---- * the execution of any method with a name beginning with "set": -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -execution(* set*(..)) + execution(* set*(..)) ---- * the execution of any method defined by the `AccountService` interface: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -execution(* com.xyz.service.AccountService.*(..)) + execution(* com.xyz.service.AccountService.*(..)) ---- * the execution of any method defined in the service package: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -execution(* com.xyz.service.*.*(..)) + execution(* com.xyz.service.*.*(..)) ---- * the execution of any method defined in the service package or a sub-package: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -execution(* com.xyz.service..*.*(..)) + execution(* com.xyz.service..*.*(..)) ---- * any join point (method execution only in Spring AOP) within the service package: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -within(com.xyz.service.*) + within(com.xyz.service.*) ---- * any join point (method execution only in Spring AOP) within the service package or a sub-package: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -within(com.xyz.service..*) + within(com.xyz.service..*) ---- * any join point (method execution only in Spring AOP) where the proxy implements the `AccountService` interface: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -this(com.xyz.service.AccountService) + this(com.xyz.service.AccountService) ---- [NOTE] @@ -12256,10 +12576,10 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -target(com.xyz.service.AccountService) + target(com.xyz.service.AccountService) ---- [NOTE] @@ -12271,10 +12591,10 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -args(java.io.Serializable) + args(java.io.Serializable) ---- [NOTE] @@ -12291,10 +12611,10 @@ parameter of type `Serializable`. * any join point (method execution only in Spring AOP) where the target object has an `@Transactional` annotation: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@target(org.springframework.transaction.annotation.Transactional) + @target(org.springframework.transaction.annotation.Transactional) ---- [NOTE] @@ -12306,10 +12626,10 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@within(org.springframework.transaction.annotation.Transactional) + @within(org.springframework.transaction.annotation.Transactional) ---- [NOTE] @@ -12321,10 +12641,10 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@annotation(org.springframework.transaction.annotation.Transactional) + @annotation(org.springframework.transaction.annotation.Transactional) ---- [NOTE] @@ -12336,10 +12656,10 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@args(com.xyz.security.Classified) + @args(com.xyz.security.Classified) ---- [NOTE] @@ -12351,19 +12671,19 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -bean(tradeService) + bean(tradeService) ---- * any join point (method execution only in Spring AOP) on Spring beans having names that match the wildcard expression ' `*Service`': -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -bean(*Service) + bean(*Service) ---- @@ -12415,40 +12735,40 @@ simple reference to a named pointcut, or a pointcut expression declared in place ===== Before advice Before advice is declared in an aspect using the `@Before` annotation: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Before; + import org.aspectj.lang.annotation.Aspect; + import org.aspectj.lang.annotation.Before; -@Aspect -public class BeforeExample { + @Aspect + public class BeforeExample { - @Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation()") - public void doAccessCheck() { - // ... - } + @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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Before; + import org.aspectj.lang.annotation.Aspect; + import org.aspectj.lang.annotation.Before; -@Aspect -public class BeforeExample { + @Aspect + public class BeforeExample { - @Before("execution(* com.xyz.myapp.dao.*.*(..))") - public void doAccessCheck() { - // ... - } + @Before("execution(* com.xyz.myapp.dao.*.*(..))") + public void doAccessCheck() { + // ... + } -} + } ---- @@ -12457,21 +12777,21 @@ public class BeforeExample { After returning advice runs when a matched method execution returns normally. It is declared using the `@AfterReturning` annotation: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.AfterReturning; + import org.aspectj.lang.annotation.Aspect; + import org.aspectj.lang.annotation.AfterReturning; -@Aspect -public class AfterReturningExample { + @Aspect + public class AfterReturningExample { - @AfterReturning("com.xyz.myapp.SystemArchitecture.dataAccessOperation()") - public void doAccessCheck() { - // ... - } + @AfterReturning("com.xyz.myapp.SystemArchitecture.dataAccessOperation()") + public void doAccessCheck() { + // ... + } -} + } ---- [NOTE] @@ -12484,23 +12804,23 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.AfterReturning; + import org.aspectj.lang.annotation.Aspect; + import org.aspectj.lang.annotation.AfterReturning; -@Aspect -public class AfterReturningExample { + @Aspect + public class AfterReturningExample { - @AfterReturning( - pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()", - returning="retVal") - public void doAccessCheck(Object retVal) { - // ... - } + @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 @@ -12518,21 +12838,21 @@ using after-returning advice. After throwing advice runs when a matched method execution exits by throwing an exception. It is declared using the `@AfterThrowing` annotation: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.AfterThrowing; + import org.aspectj.lang.annotation.Aspect; + import org.aspectj.lang.annotation.AfterThrowing; -@Aspect -public class AfterThrowingExample { + @Aspect + public class AfterThrowingExample { - @AfterThrowing("com.xyz.myapp.SystemArchitecture.dataAccessOperation()") - public void doRecoveryActions() { - // ... - } + @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 @@ -12540,23 +12860,23 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.AfterThrowing; + import org.aspectj.lang.annotation.Aspect; + import org.aspectj.lang.annotation.AfterThrowing; -@Aspect -public class AfterThrowingExample { + @Aspect + public class AfterThrowingExample { - @AfterThrowing( - pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()", - throwing="ex") - public void doRecoveryActions(DataAccessException ex) { - // ... - } + @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 @@ -12572,21 +12892,21 @@ After (finally) advice runs however a matched method execution exits. It is decl 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.After; + import org.aspectj.lang.annotation.Aspect; + import org.aspectj.lang.annotation.After; -@Aspect -public class AfterFinallyExample { + @Aspect + public class AfterFinallyExample { - @After("com.xyz.myapp.SystemArchitecture.dataAccessOperation()") - public void doReleaseLock() { - // ... - } + @After("com.xyz.myapp.SystemArchitecture.dataAccessOperation()") + public void doReleaseLock() { + // ... + } -} + } ---- @@ -12623,37 +12943,37 @@ Spring AOP and AspectJ, and this is discussed in the following section on advice parameters. ==== -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.ProceedingJoinPoint; + import org.aspectj.lang.annotation.Aspect; + import org.aspectj.lang.annotation.Around; + import org.aspectj.lang.ProceedingJoinPoint; -@Aspect -public class AroundExample { + @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; - } + @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, +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 2.0 offers fully typed advice - meaning that you declare the parameters you need +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 @@ -12683,14 +13003,13 @@ clearer. Suppose you want to advise the execution of dao operations that take an object as the first parameter, and you need access to the account in the advice body. You could write the following: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation() &&" + - "args(account,..)") -public void validateAccount(Account account) { - // ... -} + @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 @@ -12703,17 +13022,16 @@ Another way of writing this is to declare a pointcut that "provides" the `Accoun 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Pointcut("com.xyz.myapp.SystemArchitecture.dataAccessOperation() &&" + - "args(account,..)") -private void accountDataAccessOperation(Account account) {} + @Pointcut("com.xyz.myapp.SystemArchitecture.dataAccessOperation() && args(account,..)") + private void accountDataAccessOperation(Account account) {} -@Before("accountDataAccessOperation(account)") -public void validateAccount(Account account) { - // ... -} + @Before("accountDataAccessOperation(account)") + public void validateAccount(Account account) { + // ... + } ---- The interested reader is once more referred to the AspectJ programming guide for more @@ -12726,27 +13044,26 @@ example shows how you could match the execution of methods annotated with an First the definition of the `@Auditable` annotation: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface Auditable { - AuditCode value(); -} + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public @interface Auditable { + AuditCode value(); + } ---- And then the advice that matches the execution of `@Auditable` methods: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Before("com.xyz.lib.Pointcuts.anyPublicMethod() && " + - "@annotation(auditable)") -public void audit(Auditable auditable) { - AuditCode code = auditable.value(); - // ... -} + @Before("com.xyz.lib.Pointcuts.anyPublicMethod() && @annotation(auditable)") + public void audit(Auditable auditable) { + AuditCode code = auditable.value(); + // ... + } ---- [[aop-ataspectj-advice-params-generics]] @@ -12754,38 +13071,38 @@ public void audit(Auditable auditable) { Spring AOP can handle generics used in class declarations and method parameters. Suppose you have a generic type like this: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface Sample { - void sampleGenericMethod(T param); - void sampleGenericCollectionMethod(Collection>T> param); -} + public interface Sample { + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)") -public void beforeSampleMethod(MyType param) { - // Advice implementation -} + @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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)") -public void beforeSampleMethod(Collection param) { - // Advice implementation -} + @Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)") + public void beforeSampleMethod(Collection param) { + // Advice implementation + } ---- To make this work we would have to inspect every element of the collection, which is not @@ -12800,21 +13117,20 @@ expressions to declared parameter names in (advice and pointcut) method signatur 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 +* 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: + the annotated method - these argument names __are__ available at runtime. For example: -[source,java] +[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 -} + @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 @@ -12822,16 +13138,15 @@ If the first parameter is of the `JoinPoint`, `ProceedingJoinPoint`, or 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] +[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 -} + @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`, @@ -12840,14 +13155,13 @@ advice that do not collect any other join point context. In such situations, you simply omit the "argNames" attribute. For example, the following advice need not declare the "argNames" attribute: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Before( - "com.xyz.lib.Pointcuts.anyPublicMethod()") -public void audit(JoinPoint jp) { - // ... use jp -} + @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 @@ -12882,17 +13196,17 @@ arguments__ that works consistently across Spring AOP and AspectJ. The solution simply to ensure that the advice signature binds each of the method parameters in order. For example: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Around("execution(List 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}); -} + @Around("execution(List 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). @@ -12935,23 +13249,21 @@ interface `UsageTracked`, and an implementation of that interface `DefaultUsageT 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Aspect -public class UsageTracking { + @Aspect + public class UsageTracking { - @DeclareParents(value="com.xzy.myapp.service.*+", - defaultImpl=DefaultUsageTracked.class) - public static UsageTracked mixin; + @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(); - } + @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 @@ -12961,10 +13273,10 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -UsageTracked usageTracked = (UsageTracked) context.getBean("myService"); + UsageTracked usageTracked = (UsageTracked) context.getBean("myService"); ---- @@ -12986,20 +13298,20 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Aspect("perthis(com.xyz.myapp.SystemArchitecture.businessService())") -public class MyAspect { + @Aspect("perthis(com.xyz.myapp.SystemArchitecture.businessService())") + public class MyAspect { - private int someState; + private int someState; - @Before(com.xyz.myapp.SystemArchitecture.businessService()) - public void recordServiceUsage() { - // ... - } + @Before(com.xyz.myapp.SystemArchitecture.businessService()) + public void recordServiceUsage() { + // ... + } -} + } ---- The effect of the `'perthis'` clause is that one aspect instance will be created for @@ -13034,47 +13346,46 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Aspect -public class ConcurrentOperationExecutor implements Ordered { + @Aspect + public class ConcurrentOperationExecutor implements Ordered { - private static final int DEFAULT_MAX_RETRIES = 2; + private static final int DEFAULT_MAX_RETRIES = 2; - private int maxRetries = DEFAULT_MAX_RETRIES; - private int order = 1; + private int maxRetries = DEFAULT_MAX_RETRIES; + private int order = 1; - public void setMaxRetries(int maxRetries) { - this.maxRetries = maxRetries; - } + public void setMaxRetries(int maxRetries) { + this.maxRetries = maxRetries; + } - public int getOrder() { - return this.order; - } + public int getOrder() { + return this.order; + } - public void setOrder(int order) { - this.order = 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; - } + @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 @@ -13087,42 +13398,41 @@ we have exhausted all of our retry attempts. The corresponding Spring configuration is: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - - + + + + ---- To refine the aspect so that it only retries idempotent operations, we might define an `Idempotent` annotation: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Retention(RetentionPolicy.RUNTIME) -public @interface Idempotent { - // marker annotation -} + @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] +[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 { - ... -} + @Around("com.xyz.myapp.SystemArchitecture.businessService() && " + + "@annotation(com.xyz.myapp.service.Idempotent)") + public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable { + ... + } ---- @@ -13130,12 +13440,12 @@ public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable { [[aop-schema]] === Schema-based AOP support -If you are unable to use Java 5, or simply prefer an XML-based format, then Spring 2.0 -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 (<>) for an understanding of -writing pointcut expressions and the binding of advice parameters. +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 +(<>) 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 <>. See <> @@ -13167,18 +13477,18 @@ methods of the object, and the pointcut and advice information is captured in th An aspect is declared using the element, and the backing bean is referenced using the `ref` attribute: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - ... - - + + + ... + + - - ... - + + ... + ---- The bean backing the aspect (" `aBean`" in this case) can of course be configured and @@ -13194,105 +13504,108 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - + ---- Note that the pointcut expression itself is using the same AspectJ pointcut expression language as described in <>. If you are using the schema based -declaration style with Java 5, you can refer to named pointcuts defined in types -(@Aspects) within the pointcut expression, but this feature is not available on JDK 1.4 -and below (it relies on the Java 5 specific AspectJ reflection APIs). On JDK 1.5 -therefore, another way of defining the above pointcut would be: +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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - + ---- Assuming you have a `SystemArchitecture` aspect as described in <>. Declaring a pointcut inside an aspect is very similar to declaring a top-level pointcut: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - + - ... + ... - + - + ---- 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - - - ... + - + - + ... + + + + ---- The advice must be declared to receive the collected join point context by including parameters of the matching names: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public void monitor(Object service) { - ... -} + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - - - ... - - + + + + + ... + + ---- Note that pointcuts defined in this way are referred to by their XML id and cannot be @@ -13313,36 +13626,36 @@ exactly the same semantics. Before advice runs before a matched method execution. It is declared inside an `` using the element. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - ... + ... - + ---- Here `dataAccessOperation` is the id of a pointcut defined at the top ( ``) level. To define the pointcut inline instead, replace the `pointcut-ref` attribute with a `pointcut` attribute: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - ... + ... - + ---- As we noted in the discussion of the @AspectJ style, using named pointcuts can @@ -13360,47 +13673,47 @@ bean will be invoked. After returning advice runs when a matched method execution completes normally. It is declared inside an `` in the same way as before advice. For example: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - ... + ... - + ---- 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - ... + ... - + ---- 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public void doAccessCheck(Object retVal) {... + public void doAccessCheck(Object retVal) {... ---- @@ -13409,47 +13722,47 @@ public void doAccessCheck(Object retVal) {... After throwing advice executes when a matched method execution exits by throwing an exception. It is declared inside an `` using the after-throwing element: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - ... + ... - + ---- 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - ... + ... - + ---- 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public void doRecoveryActions(DataAccessException dataAccessEx) {... + public void doRecoveryActions(DataAccessException dataAccessEx) {... ---- @@ -13458,18 +13771,18 @@ public void doRecoveryActions(DataAccessException dataAccessEx) {... After (finally) advice runs however a matched method execution exits. It is declared using the `after` element: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - ... + ... - + ---- @@ -13490,32 +13803,32 @@ execute. The `proceed` method may also be calling passing in an `Object[]` - the in the array will be used as the arguments to the method execution when it proceeds. See <> for notes on calling proceed with an `Object[]`. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - ... + ... - + ---- The implementation of the `doBasicProfiling` advice would be exactly the same as in the @AspectJ example (minus the annotation of course): -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { - // start stopwatch - Object retVal = pjp.proceed(); - // stop stopwatch - return retVal; -} + public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { + // start stopwatch + Object retVal = pjp.proceed(); + // stop stopwatch + return retVal; + } ---- @@ -13530,13 +13843,13 @@ attribute of the advice element, which is treated in the same manner to the "arg attribute in an advice annotation as described in <>. For example: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- The `arg-names` attribute accepts a comma-delimited list of parameter names. @@ -13544,22 +13857,22 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package x.y.service; + package x.y.service; -public interface FooService { + public interface FooService { - Foo getFoo(String fooName, int age); -} + Foo getFoo(String fooName, int age); + } -public class DefaultFooService implements FooService { + public class DefaultFooService implements FooService { - public Foo getFoo(String name, int age) { - return new Foo(name, age); - } -} + 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 @@ -13567,86 +13880,85 @@ strongly-typed parameters, the first of which happens to be the join point used 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package x.y; + package x.y; -import org.aspectj.lang.ProceedingJoinPoint; -import org.springframework.util.StopWatch; + import org.aspectj.lang.ProceedingJoinPoint; + import org.springframework.util.StopWatch; -public class SimpleProfiler { + 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()); - } - } -} + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - + + - - + + - - + + - + - + - - + + - + ---- If we had the following driver script, we would get output something like this on standard output: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import org.springframework.beans.factory.BeanFactory; -import org.springframework.context.support.ClassPathXmlApplicationContext; -import x.y.service.FooService; + import org.springframework.beans.factory.BeanFactory; + import org.springframework.context.support.ClassPathXmlApplicationContext; + import x.y.service.FooService; -public final class Boot { + 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); - } -} + 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); + } + } ---- -[source] +[literal] [subs="verbatim,quotes"] ---- StopWatch 'Profiling for 'Pengo' and '12'': running time (millis) = 0 @@ -13679,32 +13991,32 @@ For example, given an interface `UsageTracked`, and an implementation of that in interfaces also implement the `UsageTracked` interface. (In order to expose statistics via JMX for example.) -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - + - + ---- The class backing the `usageTracking` bean would contain the method: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public void recordUsage(UsageTracked usageTracked) { - usageTracked.incrementUseCount(); -} + public void recordUsage(UsageTracked usageTracked) { + usageTracked.incrementUseCount(); + } ---- The interface to be implemented is determined by `implement-interface` attribute. The @@ -13714,10 +14026,10 @@ advice of the above example, service beans can be directly used as implementatio the `UsageTracked` interface. If accessing a bean programmatically you would write the following: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -UsageTracked usageTracked = (UsageTracked) context.getBean("myService"); + UsageTracked usageTracked = (UsageTracked) context.getBean("myService"); ---- @@ -13738,29 +14050,29 @@ represented by a bean, and must implement one of the advice interfaces described <>. Advisors can take advantage of AspectJ pointcut expressions though. -Spring 2.0 supports the advisor concept with the `` element. You will most +Spring supports the advisor concept with the `` element. You will most commonly see it used in conjunction with transactional advice, which also has its own -namespace support in Spring 2.0. Here's how it looks: +namespace support in Spring. Here's how it looks: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - + - + - - - - - + + + + + ---- As well as the `pointcut-ref` attribute used in the above example, you can also use the @@ -13789,45 +14101,44 @@ Because we want to retry the operation, we'll need to use around advice so that 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class ConcurrentOperationExecutor implements Ordered { + public class ConcurrentOperationExecutor implements Ordered { - private static final int DEFAULT_MAX_RETRIES = 2; + private static final int DEFAULT_MAX_RETRIES = 2; - private int maxRetries = DEFAULT_MAX_RETRIES; - private int order = 1; + private int maxRetries = DEFAULT_MAX_RETRIES; + private int order = 1; - public void setMaxRetries(int maxRetries) { - this.maxRetries = maxRetries; - } + public void setMaxRetries(int maxRetries) { + this.maxRetries = maxRetries; + } - public int getOrder() { - return this.order; - } + public int getOrder() { + return this.order; + } - public void setOrder(int order) { - this.order = 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; - } + 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 @@ -13845,54 +14156,54 @@ annotations removed. The corresponding Spring configuration is: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - + - + - + - + - - - - + + + + ---- 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Retention(RetentionPolicy.RUNTIME) -public @interface Idempotent { - // marker annotation -} + @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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- @@ -13934,16 +14245,13 @@ 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. -Clearly if you are not running on Java 5+, then the XML style is the appropriate choice; -for Java 5 projects there are various tradeoffs to consider. +There are various tradeoffs to consider. -The XML style will be most familiar to existing Spring users. It can be used with any -JDK level (referring to named pointcuts from within pointcut expressions does still -require Java 5+ though) and 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 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 @@ -13957,29 +14265,29 @@ is slightly more limited in what it can express than the @AspectJ style: only th named pointcuts declared in XML. For example, in the @AspectJ style you can write something like: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Pointcut(execution(* get*())) - public void propertyAccess() {} + @Pointcut(execution(* get*())) + public void propertyAccess() {} - @Pointcut(execution(org.xyz.Account+ *(..)) - public void operationReturningAnAccount() {} + @Pointcut(execution(org.xyz.Account+ *(..)) + public void operationReturningAnAccount() {} - @Pointcut(propertyAccess() && operationReturningAnAccount()) - public void accountPropertyAccess() {} + @Pointcut(propertyAccess() && operationReturningAnAccount()) + public void accountPropertyAccess() {} ---- In the XML style I can declare the first two pointcuts: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + ---- The downside of the XML approach is that you cannot define the ' @@ -14037,21 +14345,21 @@ so. However, there are some issues to consider: To force the use of CGLIB proxies set the value of the `proxy-target-class` attribute of the `` element to true: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- To force CGLIB proxying when using the @AspectJ autoproxy support, set the `'proxy-target-class'` attribute of the `` element to `true`: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- [NOTE] @@ -14079,65 +14387,64 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class SimplePojo implements Pojo { + public class SimplePojo implements Pojo { - public void foo() { - // this next method invocation is a direct - call on the 'this' reference - this.bar(); - } + public void foo() { + // this next method invocation is a direct call on the 'this' reference + this.bar(); + } - public void bar() { - // some logic... - } -} + 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[] +image::images/aop-proxy-plain-pojo-call.png[width=400] -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class Main { + public class Main { - public static void main(String[] args) { + public static void main(String[] args) { - Pojo pojo = new SimplePojo(); + Pojo pojo = new SimplePojo(); - // this is a direct method call on the 'pojo' reference - pojo.foo(); - } -} + // 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[] +image::images/aop-proxy-call.png[width=400] -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class Main { + public class Main { - public static void main(String[] args) { + public static void main(String[] args) { - ProxyFactory factory = new ProxyFactory(new SimplePojo()); - factory.addInterface(Pojo.class); - factory.addAdvice(new RetryAdvice()); + ProxyFactory factory = new ProxyFactory(new SimplePojo()); + factory.addInterface(Pojo.class); + factory.addAdvice(new RetryAdvice()); - Pojo pojo = (Pojo) factory.getProxy(); + Pojo pojo = (Pojo) factory.getProxy(); - // this is a method call on the proxy! - pojo.foo(); - } -} + // 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 @@ -14158,44 +14465,44 @@ The next approach is absolutely horrendous, and I am almost reticent to point it precisely because it is so horrendous. You can (choke!) totally tie the logic within your class to Spring AOP by doing this: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class SimplePojo implements Pojo { + public class SimplePojo implements Pojo { - public void foo() { - // this works, but... gah! - ((Pojo) AopContext.currentProxy()).bar(); - } + public void foo() { + // this works, but... gah! + ((Pojo) AopContext.currentProxy()).bar(); + } - public void bar() { - // some logic... - } -} + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class Main { + public class Main { - public static void main(String[] args) { + public static void main(String[] args) { - ProxyFactory factory = new ProxyFactory(new SimplePojo()); - factory.adddInterface(Pojo.class); - factory.addAdvice(new RetryAdvice()); - factory.setExposeProxy(true); + ProxyFactory factory = new ProxyFactory(new SimplePojo()); + factory.adddInterface(Pojo.class); + factory.addAdvice(new RetryAdvice()); + factory.setExposeProxy(true); - Pojo pojo = (Pojo) factory.getProxy(); + Pojo pojo = (Pojo) factory.getProxy(); - // this is a method call on the proxy! - pojo.foo(); - } -} + // 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 @@ -14217,21 +14524,21 @@ to create a proxy for a target object that is advised by one or more @AspectJ as Basic usage for this class is very simple, as illustrated below. See the Javadocs for full information. -[source,java] +[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); + // 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); + // 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); + // 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(); + // now get the proxy object... + MyInterfaceType proxy = factory.getProxy(); ---- @@ -14268,17 +14575,17 @@ often fall into this category because they are often created programmatically us 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.xyz.myapp.domain; + package com.xyz.myapp.domain; -import org.springframework.beans.factory.annotation.Configurable; + import org.springframework.beans.factory.annotation.Configurable; -@Configurable -public class Account { - // ... -} + @Configurable + public class Account { + // ... + } ---- When used as a marker interface in this way, Spring will configure new instances of the @@ -14288,28 +14595,28 @@ prototype-scoped) with the same name as the fully-qualified type name ( fully-qualified name of its type, a convenient way to declare the prototype definition is simply to omit the `id` attribute: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.xyz.myapp.domain; + package com.xyz.myapp.domain; -import org.springframework.beans.factory.annotation.Configurable; + import org.springframework.beans.factory.annotation.Configurable; -@Configurable("account") -public class Account { - // ... -} + @Configurable("account") + public class Account { + // ... + } ---- Spring will now look for a bean definition named " `account`" and use that as the @@ -14351,10 +14658,10 @@ dependencies to be injected __before__ the constructor bodies execute, and thus available for use in the body of the constructors, then you need to define this on the `@Configurable` declaration like so: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configurable(preConstruction=true) + @Configurable(preConstruction=true) ---- You can find out more information about the language semantics of the various pointcut @@ -14373,33 +14680,23 @@ a reference to the bean factory that is to be used to configure new objects). If using Java based configuration simply add `@EnableSpringConfigured` to any `@Configuration` class. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -@EnableSpringConfigured -public class AppConfig { + @Configuration + @EnableSpringConfigured + public class AppConfig { -} + } ---- If you prefer XML based configuration, the Spring <> defines a convenient `context:spring-configured` element: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - ----- - -If you are using the DTD instead of schema, the equivalent definition is: - -[source,xml] -[subs="verbatim,quotes"] ----- - + ---- Instances of `@Configurable` objects created __before__ the aspect has been configured @@ -14409,16 +14706,16 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - + ---- [NOTE] @@ -14504,22 +14801,22 @@ excerpt shows how you could write an aspect to configure all instances of object defined in the domain model using prototype bean definitions that match the fully-qualified class names: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public aspect DomainObjectConfiguration extends AbstractBeanConfigurerAspect { + public aspect DomainObjectConfiguration extends AbstractBeanConfigurerAspect { - public DomainObjectConfiguration() { - setBeanWiringInfoResolver(new ClassNameBeanWiringInfoResolver()); - } + 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); + // the creation of a new bean (any object in the domain model) + protected pointcut beanCreation(Object beanInstance) : + initialization(new(..)) && + SystemArchitecture.inDomainModel() && + this(beanInstance); -} + } ---- @@ -14538,14 +14835,14 @@ normal, and include the bean attribute `'factory-method="aspectOf"'`. This ensur Spring obtains the aspect instance by asking AspectJ for it rather than trying to create an instance itself. For example: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - + + ---- Non-singleton aspects are harder to configure: however it is possible to do so by @@ -14563,13 +14860,13 @@ declaration. Each `` element specifies a name pattern, and only beans names matched by at least one of the patterns will be used for Spring AOP autoproxy configuration: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + ---- [NOTE] @@ -14612,8 +14909,8 @@ typically are in charge of the deployment configuration such as the launch scrip 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 Petclinic -<> application. +following example. For a complete example, please see the +https://github.com/spring-projects/spring-petclinic[Petclinic sample application]. [[aop-aj-ltw-first-example]] @@ -14627,7 +14924,7 @@ 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<>. Specifically the +configure and use @AspectJ with <>. Specifically the `@EnableLoadTimeWeaving` annotation can be used as an alternative to `` (see <> for details). ==== @@ -14635,36 +14932,36 @@ configure and use @AspectJ with<>. Specifically t 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package foo; + 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; + 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 { + @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()); - } - } + @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(){} -} + @Pointcut("execution(public * foo..*.*(..))") + public void methodsToBeProfiled(){} + } ---- We will also need to create an ' `META-INF/aop.xml`' file, to inform the AspectJ weaver @@ -14672,28 +14969,23 @@ that we want to weave our `ProfilingAspect` into our classes. This file conventi namely the presence of a file (or files) on the Java classpath called ' `META-INF/aop.xml`' is standard AspectJ. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + + + + - - + + + + - - - - - - - - - - + ---- Now to the Spring-specific portion of the configuration. We need to configure a @@ -14703,52 +14995,52 @@ one or more ' `META-INF/aop.xml`' files into the classes in your application. Th 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - + + - - **** - + + **** + ---- 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package foo; + package foo; -import org.springframework.context.support.ClassPathXmlApplicationContext; + import org.springframework.context.support.ClassPathXmlApplicationContext; -public final class Main { + public final class Main { - public static void main(String[] args) { + public static void main(String[] args) { - ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml", Main.class); + ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml", Main.class); - EntitlementCalculationService entitlementCalculationService - = (EntitlementCalculationService) ctx.getBean("entitlementCalculationService"); + EntitlementCalculationService entitlementCalculationService + = (EntitlementCalculationService) ctx.getBean("entitlementCalculationService"); - // the profiling aspect is 'woven' around this method execution - entitlementCalculationService.calculateEntitlement(); - } -} + // 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 @@ -14756,14 +15048,14 @@ switch on LTW selectively on a per- `ClassLoader` basis with Spring, and this is 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: -[source] +[literal] [subs="verbatim,quotes"] ---- java -javaagent:C:/projects/foo/lib/global/spring-instrument.jar foo.Main ---- -The ' `-javaagent`' is a Java 5+ flag for specifying and enabling -http://java.sun.com/j2se/1.5.0/docs/api/java/lang/instrument/package-summary.html[agents +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 @@ -14774,7 +15066,7 @@ The output from the execution of the `Main` program will look something like tha implementation so that the profiler actually captures something other than 0 milliseconds - the `01234` milliseconds is __not__ an overhead introduced by the AOP :) ) -[source] +[literal] [subs="verbatim,quotes"] ---- Calculating entitlement @@ -14790,26 +15082,26 @@ Since this LTW is effected using full-blown AspectJ, we are not just limited to Spring beans; the following slight variation on the `Main` program will yield the same result. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package foo; + package foo; -import org.springframework.context.support.ClassPathXmlApplicationContext; + import org.springframework.context.support.ClassPathXmlApplicationContext; -public final class Main { + public final class Main { - public static void main(String[] args) { + public static void main(String[] args) { - new ClassPathXmlApplicationContext("beans.xml", Main.class); + new ClassPathXmlApplicationContext("beans.xml", Main.class); - EntitlementCalculationService entitlementCalculationService = - new StubEntitlementCalculationService(); + EntitlementCalculationService entitlementCalculationService = + new StubEntitlementCalculationService(); - // the profiling aspect will be 'woven' around this method execution - entitlementCalculationService.calculateEntitlement(); - } -} + // 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 @@ -14833,9 +15125,8 @@ into UAT or production. ===== 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. -The latter option is of course only an option if you are using Java 5+, but it does mean -that your aspects are then both valid AspectJ __and__ Spring AOP aspects. Furthermore, -the compiled aspect classes need to be available on the classpath. +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]] @@ -14895,36 +15186,36 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -@EnableLoadTimeWeaving -public class AppConfig { + @Configuration + @EnableLoadTimeWeaving + public class AppConfig { -} + } ---- Alternatively, if you prefer XML based configuration, use the `` element. Note that the element is defined in the ' `context`' namespace. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + - + ---- The above configuration will define and register a number of LTW-specific infrastructure @@ -14970,40 +15261,41 @@ Note that these are just the `LoadTimeWeavers` that are autodetected when using To specify a specific `LoadTimeWeaver` with Java configuration implement the `LoadTimeWeavingConfigurer` interface and override the `getLoadTimeWeaver()` method: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -@EnableLoadTimeWeaving -public class AppConfig implements LoadTimeWeavingConfigurer { - @Override - public LoadTimeWeaver getLoadTimeWeaver() { - return new ReflectiveLoadTimeWeaver(); - } -} + @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 `` element: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + - + ---- The `LoadTimeWeaver` that is defined and registered by the configuration can be later @@ -15060,13 +15352,13 @@ above and can be registered individually for __each__ web application as follows * Instruct Tomcat to use the custom class loader (instead of the default) by editing the web application context file: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- Apache Tomcat 6.0.x (similar to 5.0.x/5.5.x) series supports several context locations: @@ -15090,13 +15382,13 @@ details about available context locations. * Instruct Tomcat to use the custom class loader instead of the default one by editing the web application context file: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- Tomcat 5.0.x and 5.5.x series supports several context locations: @@ -15122,14 +15414,14 @@ details]. In Tomcat 5.5.x, versions 5.5.20 or later, you should set __useSystemClassLoaderAsParent__ to `false` to fix this problem: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- This setting is not needed on Tomcat 6 or higher. @@ -15155,10 +15447,10 @@ Note that on JBoss 6.x, the app server scanning needs to be disabled to prevent 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- [[aop-aj-ltw-environment-generic]] @@ -15172,7 +15464,7 @@ requires a Spring-specific (but very general) VM agent, To use it, you must start the virtual machine with the Spring agent, by supplying the following JVM options: -[source] +[literal] [subs="verbatim,quotes"] ---- -javaagent:/path/to/org.springframework.instrument-{version}.jar @@ -15213,14 +15505,14 @@ explored (in some depth). [[aop-api-introduction]] === Introduction -The previous chapter described the Spring 2.0 and later version's support for AOP using +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 3.0 is backwards +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 3.0. +in Spring 4.0. @@ -15239,16 +15531,16 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface Pointcut { + public interface Pointcut { - ClassFilter getClassFilter(); + ClassFilter getClassFilter(); - MethodMatcher getMethodMatcher(); + MethodMatcher getMethodMatcher(); -} + } ---- Splitting the `Pointcut` interface into two parts allows reuse of class and method @@ -15259,29 +15551,29 @@ The `ClassFilter` interface is used to restrict the pointcut to a given set of t classes. If the `matches()` method always returns true, all target classes will be matched: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface ClassFilter { + public interface ClassFilter { - boolean matches(Class clazz); -} + boolean matches(Class clazz); + } ---- The `MethodMatcher` interface is normally more important. The complete interface is shown below: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface MethodMatcher { + public interface MethodMatcher { - boolean matches(Method m, Class targetClass); + boolean matches(Method m, Class targetClass); - boolean isRuntime(); + boolean isRuntime(); - boolean matches(Method m, Class targetClass, Object[] args); -} + boolean matches(Method m, Class targetClass, Object[] args); + } ---- The `matches(Method, Class)` method is used to test whether this pointcut will ever @@ -15357,18 +15649,18 @@ effectively the union of these pointcuts.) The usage is shown below: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - .*set.* - .*absquatulate - - - + + + + .*set.* + .*absquatulate + + + ---- Spring provides a convenience class, `RegexpMethodPointcutAdvisor`, that allows us to @@ -15377,21 +15669,21 @@ throws advice etc.). Behind the scenes, Spring will use a `JdkRegexpMethodPointc Using `RegexpMethodPointcutAdvisor` simplifies wiring, as the one bean encapsulates both pointcut and advice, as shown below: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - .*set.* - .*absquatulate - - - + + + + + + + .*set.* + .*absquatulate + + + ---- __RegexpMethodPointcutAdvisor__ can be used with any Advice type. @@ -15405,7 +15697,7 @@ 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 +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. @@ -15436,15 +15728,15 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -class TestStaticPointcut extends StaticMethodMatcherPointcut { + class TestStaticPointcut extends StaticMethodMatcherPointcut { - public boolean matches(Method m, Class targetClass) { - // return true if custom criteria match - } -} + public boolean matches(Method m, Class targetClass) { + // return true if custom criteria match + } + } ---- There are also superclasses for dynamic pointcuts. @@ -15506,13 +15798,13 @@ Spring is compliant with the AOP Alliance interface for around advice using meth interception. MethodInterceptors implementing around advice should implement the following interface: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface MethodInterceptor extends Interceptor { + public interface MethodInterceptor extends Interceptor { - Object invoke(MethodInvocation invocation) throws Throwable; -} + Object invoke(MethodInvocation invocation) throws Throwable; + } ---- The `MethodInvocation` argument to the `invoke()` method exposes the method being @@ -15522,18 +15814,18 @@ point. A simple `MethodInterceptor` implementation looks as follows: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class DebugInterceptor implements MethodInterceptor { + 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; - } -} + 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 @@ -15567,13 +15859,13 @@ The `MethodBeforeAdvice` interface is shown below. (Spring's API design would al field before advice, although the usual objects apply to field interception and it's unlikely that Spring will ever implement it). -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface MethodBeforeAdvice extends BeforeAdvice { + public interface MethodBeforeAdvice extends BeforeAdvice { - void before(Method m, Object[] args, Object target) throws Throwable; -} + 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 @@ -15585,21 +15877,21 @@ wrapped in an unchecked exception by the AOP proxy. An example of a before advice in Spring, which counts all method invocations: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class CountingBeforeAdvice implements MethodBeforeAdvice { + public class CountingBeforeAdvice implements MethodBeforeAdvice { - private int count; + private int count; - public void before(Method m, Object[] args, Object target) throws Throwable { - ++count; - } + public void before(Method m, Object[] args, Object target) throws Throwable { + ++count; + } - public int getCount() { - return count; - } -} + public int getCount() { + return count; + } + } ---- [TIP] @@ -15617,10 +15909,10 @@ an exception. Spring offers typed throws advice. Note that this means that the tag interface identifying that the given object implements one or more typed throws advice methods. These should be in the form of: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -afterThrowing([Method, args, target], subclassOfThrowable) + afterThrowing([Method, args, target], subclassOfThrowable) ---- Only the last argument is required. The method signatures may have either one or four @@ -15629,58 +15921,61 @@ arguments. The following classes are examples of throws advice. The advice below is invoked if a `RemoteException` is thrown (including subclasses): -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class RemoteThrowsAdvice implements ThrowsAdvice { + public class RemoteThrowsAdvice implements ThrowsAdvice { - public void afterThrowing(RemoteException ex) throws Throwable { - // Do something with remote exception - } -} + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class ServletThrowsAdviceWithArguments implements ThrowsAdvice { + public class ServletThrowsAdviceWithArguments implements ThrowsAdvice { - public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) { - // Do something with all arguments - } -} + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public static class CombinedThrowsAdvice implements ThrowsAdvice { + public static class CombinedThrowsAdvice implements ThrowsAdvice { - public void afterThrowing(RemoteException ex) throws Throwable { - // Do something with remote exception - } + 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 - } -} + 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 +[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] ==== @@ -15694,14 +15989,14 @@ Throws advice can be used with any pointcut. An after returning advice in Spring must implement the __org.springframework.aop.AfterReturningAdvice__ interface, shown below: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface AfterReturningAdvice extends Advice { + public interface AfterReturningAdvice extends Advice { - void afterReturning(Object returnValue, Method m, Object[] args, Object target) - throws Throwable; -} + 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), @@ -15710,22 +16005,22 @@ invoked method, methods arguments and target. The following after returning advice counts all successful method invocations that have not thrown exceptions: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class CountingAfterReturningAdvice implements AfterReturningAdvice { + public class CountingAfterReturningAdvice implements AfterReturningAdvice { - private int count; + private int count; - public void afterReturning(Object returnValue, Method m, Object[] args, Object target) - throws Throwable { - ++count; - } + public void afterReturning(Object returnValue, Method m, Object[] args, Object target) + throws Throwable { + ++count; + } - public int getCount() { - return count; - } -} + public int getCount() { + return count; + } + } ---- This advice doesn't change the execution path. If it throws an exception, this will be @@ -15745,13 +16040,13 @@ Spring treats introduction advice as a special kind of interception advice. Introduction requires an `IntroductionAdvisor`, and an `IntroductionInterceptor`, implementing the following interface: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface IntroductionInterceptor extends MethodInterceptor { + public interface IntroductionInterceptor extends MethodInterceptor { - boolean implementsInterface(Class intf); -} + boolean implementsInterface(Class intf); + } ---- The `invoke()` method inherited from the AOP Alliance `MethodInterceptor` interface must @@ -15763,20 +16058,20 @@ Introduction advice cannot be used with any pointcut, as it applies only at clas rather than method, level. You can only use introduction advice with the `IntroductionAdvisor`, which has the following methods: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface IntroductionAdvisor extends Advisor, IntroductionInfo { + public interface IntroductionAdvisor extends Advisor, IntroductionInfo { - ClassFilter getClassFilter(); + ClassFilter getClassFilter(); - void validateInterfaces() throws IllegalArgumentException; -} + void validateInterfaces() throws IllegalArgumentException; + } -public interface IntroductionInfo { + public interface IntroductionInfo { - Class[] getInterfaces(); -} + Class[] getInterfaces(); + } ---- There is no `MethodMatcher`, and hence no `Pointcut`, associated with introduction @@ -15790,14 +16085,14 @@ introduced interfaces can be implemented by the configured `IntroductionIntercep 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface Lockable { - void lock(); - void unlock(); - boolean locked(); -} + public interface Lockable { + void lock(); + void unlock(); + boolean locked(); + } ---- This illustrates a __mixin__. We want to be able to cast advised objects to Lockable, @@ -15833,33 +16128,33 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class LockMixin extends DelegatingIntroductionInterceptor - implements Lockable { + public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable { - private boolean locked; + private boolean locked; - public void lock() { - this.locked = true; - } + public void lock() { + this.locked = true; + } - public void unlock() { - this.locked = false; - } + public void unlock() { + this.locked = false; + } - public boolean locked() { - return this.locked; - } + 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); - } + 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 @@ -15874,15 +16169,15 @@ The introduction advisor required is simple. All it needs to do is hold a distin 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class LockMixinAdvisor extends DefaultIntroductionAdvisor { + public class LockMixinAdvisor extends DefaultIntroductionAdvisor { - public LockMixinAdvisor() { - super(new LockMixin(), Lockable.class); - } -} + public LockMixinAdvisor() { + super(new LockMixin(), Lockable.class); + } + } ---- We can apply this advisor very simply: it requires no configuration. (However, it __is__ @@ -15927,7 +16222,7 @@ it to create objects of a different type.) [NOTE] ==== -The Spring 2.0 AOP support also uses factory beans under the covers. +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 @@ -16069,33 +16364,33 @@ Let's look at a simple example of `ProxyFactoryBean` in action. This example inv * 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + - - - + + + - - + + - - + + - - - - myAdvisor - debugInterceptor - - - + + + + myAdvisor + debugInterceptor + + + ---- Note that the `interceptorNames` property takes a list of String: the bean names of the @@ -16114,21 +16409,21 @@ an instance of the prototype from the factory; holding a reference isn't suffici The "person" bean definition above can be used in place of a Person implementation, as follows: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -Person person = (Person) factory.getBean("person"); + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- The `PersonUser` class in this example would expose a property of type Person. As far as @@ -16140,36 +16435,36 @@ It's possible to conceal the distinction between target and proxy using an anony __inner bean__, as follows. Only the `ProxyFactoryBean` definition is different; the advice is included only for completeness: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + - + - - - - - - - - - - - - myAdvisor - debugInterceptor - - - + + + + + + + + + + + + myAdvisor + debugInterceptor + + + ---- 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 +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. @@ -16216,20 +16511,20 @@ By appending an asterisk to an interceptor name, all advisors with bean names ma 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - global* - - - + + + + + global* + + + - - + + ---- @@ -16243,55 +16538,55 @@ definitions, can result in much cleaner and more concise proxy definitions. First a parent, __template__, bean definition is created for the proxy: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - PROPAGATION_REQUIRED - - - + + + + + PROPAGATION_REQUIRED + + + ---- 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - + + + + + + ---- It is of course possible to override properties from the parent template, such as in this case, the transaction propagation settings: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - PROPAGATION_REQUIRED,readOnly - PROPAGATION_REQUIRED,readOnly - PROPAGATION_REQUIRED,readOnly - PROPAGATION_REQUIRED - - - + + + + + + + + PROPAGATION_REQUIRED,readOnly + PROPAGATION_REQUIRED,readOnly + PROPAGATION_REQUIRED,readOnly + PROPAGATION_REQUIRED + + + ---- Note that in the example above, we have explicitly marked the parent bean definition as @@ -16300,7 +16595,7 @@ __abstract__ by using the __abstract__ attribute, as described 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__ +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. @@ -16316,13 +16611,13 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl); -factory.addAdvice(myMethodInterceptor); -factory.addAdvisor(myAdvisor); -MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy(); + 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 @@ -16357,29 +16652,28 @@ However you create AOP proxies, you can manipulate them using the interface, whichever other interfaces it implements. This interface includes the following methods: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -Advisor[] getAdvisors(); + Advisor[] getAdvisors(); -void addAdvice(Advice advice) throws AopConfigException; + void addAdvice(Advice advice) throws AopConfigException; -void addAdvice(int pos, Advice advice) - throws AopConfigException; + void addAdvice(int pos, Advice advice) throws AopConfigException; -void addAdvisor(Advisor advisor) throws AopConfigException; + void addAdvisor(Advisor advisor) throws AopConfigException; -void addAdvisor(int pos, Advisor advisor) throws AopConfigException; + void addAdvisor(int pos, Advisor advisor) throws AopConfigException; -int indexOf(Advisor advisor); + int indexOf(Advisor advisor); -boolean removeAdvisor(Advisor advisor) throws AopConfigException; + boolean removeAdvisor(Advisor advisor) throws AopConfigException; -void removeAdvisor(int index) throws AopConfigException; + void removeAdvisor(int index) throws AopConfigException; -boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException; + boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException; -boolean isFrozen(); + boolean isFrozen(); ---- The `getAdvisors()` method will return an Advisor for every advisor, interceptor or @@ -16402,24 +16696,23 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -Advised advised = (Advised) myObject; -Advisor[] advisors = advised.getAdvisors(); -int oldAdvisorCount = advisors.length; -System.out.println(oldAdvisorCount + " advisors"); + 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 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)); + // Add selective advice using a pointcut + advised.addAdvisor(new DefaultPointcutAdvisor(mySpecialPointcut, myAdvice)); -assertEquals("Added two advisors", - oldAdvisorCount + 2, advised.getAdvisors().length); + assertEquals("Added two advisors", oldAdvisorCount + 2, advised.getAdvisors().length); ---- [NOTE] @@ -16476,17 +16769,17 @@ standard auto-proxy creators. The `BeanNameAutoProxyCreator` class is a `BeanPostProcessor` that automatically creates AOP proxies for beans with names matching literal values or wildcards. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - myInterceptor - - - + + + + + myInterceptor + + + ---- As with `ProxyFactoryBean`, there is an `interceptorNames` property rather than a list @@ -16535,22 +16828,22 @@ dependencies to obtain an un-advised object. Calling getBean("businessObject1") ApplicationContext will return an AOP proxy, not the target business object. (The "inner bean" idiom shown earlier also offers this benefit.) -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - + + + - + - - - + + + - + ---- The `DefaultAdvisorAutoProxyCreator` is very useful if you want to apply the same advice @@ -16598,26 +16891,26 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - + + + - - - - - - - - + + + + + + + + - + ---- The `DefaultAdvisorAutoProxyCreator` bean definition (the name is not significant, hence @@ -16637,22 +16930,22 @@ example for auto-proxying driven by JDK 1.5+ annotations. The following configur enables automatic detection of Spring's `Transactional` annotation, leading to implicit proxies for beans containing that annotation: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - + + + - - - - - - + + + + + + ---- The `TransactionInterceptor` defined here depends on a `PlatformTransactionManager` @@ -16660,11 +16953,11 @@ definition, which is not included in this generic file (although it could be) be will be specific to the application's transaction requirements (typically JTA, as in this example, or Hibernate, JDO or JDBC): -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- [TIP] @@ -16692,19 +16985,19 @@ suite, shown above, could be used in conjunction with an attribute-driven pointc target a mixin, as shown here. We use the generic `DefaultPointcutAdvisor`, configured using JavaBean properties: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - - + + + + - + - - - + + + - - - + + + ---- The above `swap()` call changes the target of the swappable bean. Clients who hold a @@ -16805,23 +17097,23 @@ pooling API. Sample configuration is shown below: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - ... properties omitted - + + ... properties omitted + - - - - + + + + - - - - + + + + ---- Note that the target object - "businessObjectTarget" in the example - __must__ be a @@ -16840,13 +17132,13 @@ It's possible to configure Spring so as to be able to cast any pooled object to about the configuration and current size of the pool through an introduction. You'll need to define an advisor like this: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + ---- This advisor is obtained by calling a convenience method on the @@ -16856,11 +17148,11 @@ the ProxyFactoryBean exposing the pooled object. The cast will look as follows: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject"); -System.out.println("Max pool size is " + conf.getMaxSize()); + PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject"); + System.out.println("Max pool size is " + conf.getMaxSize()); ---- [NOTE] @@ -16886,12 +17178,12 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- There's only one property: the name of the target bean. Inheritance is used in the @@ -16901,7 +17193,7 @@ source, the target bean must be a prototype bean definition. [[aop-ts-threadlocal]] -==== ThreadLocal target sources +==== 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 @@ -16909,12 +17201,12 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- [NOTE] @@ -17011,10 +17303,10 @@ support classes. [[mock-objects-env]] ===== Environment The `org.springframework.mock.env` package contains mock implementations of the -`Environment` and `PropertySource` abstractions introduced in Spring 3.1 (see -<> and <>). -`MockEnvironment` and `MockPropertySource` are useful for developing -__out-of-container__ tests for code that depends on environment-specific properties. +`Environment` and `PropertySource` abstractions (see <> +and <>). `MockEnvironment` and +`MockPropertySource` are useful for developing __out-of-container__ tests for code that +depends on environment-specific properties. [[mock-objects-jndi]] @@ -17272,24 +17564,24 @@ component classes, etc. + -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -**@ContextConfiguration**("/test-config.xml") -public class XmlApplicationContextTests { - // class body... -} + **@ContextConfiguration**("/test-config.xml") + public class XmlApplicationContextTests { + // class body... + } ---- + -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -**@ContextConfiguration**(**classes** = TestConfig.class) -public class ConfigClassApplicationContextTests { - // class body... -} + **@ContextConfiguration**(**classes** = TestConfig.class) + public class ConfigClassApplicationContextTests { + // class body... + } ---- + @@ -17299,13 +17591,13 @@ As an alternative or in addition to declaring resource locations or annotated cl + -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -**@ContextConfiguration**(**initializers** = CustomContextIntializer.class) -public class ContextInitializerTests { - // class body... -} + **@ContextConfiguration**(**initializers** = CustomContextIntializer.class) + public class ContextInitializerTests { + // class body... + } ---- + @@ -17317,13 +17609,13 @@ loader since the default loader supports either resource `locations` or annotate + -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -**@ContextConfiguration**(**locations** = "/test-context.xml", **loader** = CustomContextLoader.class) -public class CustomLoaderXmlApplicationContextTests { - // class body... -} + **@ContextConfiguration**(**locations** = "/test-context.xml", **loader** = CustomContextLoader.class) + public class CustomLoaderXmlApplicationContextTests { + // class body... + } ---- + @@ -17354,14 +17646,14 @@ path is used behind the scenes to create a `MockServletContext` which serves as + -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@ContextConfiguration -**@WebAppConfiguration** -public class WebAppTests { - // class body... -} + @ContextConfiguration + **@WebAppConfiguration** + public class WebAppTests { + // class body... + } ---- + @@ -17372,14 +17664,14 @@ resource prefix is supplied the path is assumed to be a file system resource. + -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@ContextConfiguration -**@WebAppConfiguration("classpath:test-web-resources")** -public class WebAppTests { - // class body... -} + @ContextConfiguration + **@WebAppConfiguration("classpath:test-web-resources")** + public class WebAppTests { + // class body... + } ---- + @@ -17403,31 +17695,31 @@ hierarchy. + -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@ContextHierarchy({ - @ContextConfiguration("/parent-config.xml"), - @ContextConfiguration("/child-config.xml") -}) -public class ContextHierarchyTests { - // class body... -} + @ContextHierarchy({ + @ContextConfiguration("/parent-config.xml"), + @ContextConfiguration("/child-config.xml") + }) + public class ContextHierarchyTests { + // class body... + } ---- + -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@WebAppConfiguration -@ContextHierarchy({ - @ContextConfiguration(classes = AppConfig.class), - @ContextConfiguration(classes = WebConfig.class) -}) -public class WebIntegrationTests { - // class body... -} + @WebAppConfiguration + @ContextHierarchy({ + @ContextConfiguration(classes = AppConfig.class), + @ContextConfiguration(classes = WebConfig.class) + }) + public class WebIntegrationTests { + // class body... + } ---- + @@ -17448,26 +17740,26 @@ should be active when loading an `ApplicationContext` for test classes. + -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@ContextConfiguration -**@ActiveProfiles**("dev") -public class DeveloperTests { - // class body... -} + @ContextConfiguration + **@ActiveProfiles**("dev") + public class DeveloperTests { + // class body... + } ---- + -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@ContextConfiguration -**@ActiveProfiles**({"dev", "integration"}) -public class DeveloperIntegrationTests { - // class body... -} + @ContextConfiguration + **@ActiveProfiles**({"dev", "integration"}) + public class DeveloperIntegrationTests { + // class body... + } ---- + @@ -17492,7 +17784,7 @@ for examples and further details. 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 +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. @@ -17517,13 +17809,13 @@ configuration scenarios: + -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -**@DirtiesContext** -public class ContextDirtyingTests { - // some tests that result in the Spring container being dirtied -} + **@DirtiesContext** + public class ContextDirtyingTests { + // some tests that result in the Spring container being dirtied + } ---- + @@ -17533,13 +17825,13 @@ mode set to `AFTER_EACH_TEST_METHOD.` + -[source,java] +[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 -} + **@DirtiesContext**(**classMode** = ClassMode.AFTER_EACH_TEST_METHOD) + public class ContextDirtyingTests { + // some tests that result in the Spring container being dirtied + } ---- + @@ -17548,21 +17840,21 @@ public class ContextDirtyingTests { + -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -**@DirtiesContext** -@Test -public void testProcessWhichDirtiesAppCtx() { - // some logic that results in the Spring container being dirtied -} + **@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 +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 @@ -17572,25 +17864,25 @@ specified instead, as seen below. + -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@ContextHierarchy({ - @ContextConfiguration("/parent-config.xml"), - @ContextConfiguration("/child-config.xml") -}) -public class BaseTests { - // class body... -} + @ContextHierarchy({ + @ContextConfiguration("/parent-config.xml"), + @ContextConfiguration("/child-config.xml") + }) + public class BaseTests { + // class body... + } -public class ExtendedTests extends BaseTests { + public class ExtendedTests extends BaseTests { - @Test - @DirtiesContext(**hierarchyMode = HierarchyMode.CURRENT_LEVEL**) - public void test() { - // some logic that results in the child context being dirtied - } -} + @Test + @DirtiesContext(**hierarchyMode = HierarchyMode.CURRENT_LEVEL**) + public void test() { + // some logic that results in the child context being dirtied + } + } ---- + @@ -17608,14 +17900,14 @@ in conjunction with `@ContextConfiguration`. + -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@ContextConfiguration -**@TestExecutionListeners**({CustomTestExecutionListener.class, AnotherTestExecutionListener.class}) -public class CustomTestExecutionListenerTests { - // class body... -} + @ContextConfiguration + **@TestExecutionListeners**({CustomTestExecutionListener.class, AnotherTestExecutionListener.class}) + public class CustomTestExecutionListenerTests { + // class body... + } ---- + @@ -17637,14 +17929,14 @@ conjunction with `@ContextConfiguration`. + -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@ContextConfiguration -**@TransactionConfiguration**(**transactionManager** = "txMgr", **defaultRollback** = false) -public class CustomConfiguredTransactionalTests { - // class body... -} + @ContextConfiguration + **@TransactionConfiguration**(**transactionManager** = "txMgr", **defaultRollback** = false) + public class CustomConfiguredTransactionalTests { + // class body... + } ---- + @@ -17673,14 +17965,14 @@ rollback flag configured at the class level. + -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -**@Rollback**(false) -@Test -public void testProcessWithoutRollback() { - // ... -} + **@Rollback**(false) + @Test + public void testProcessWithoutRollback() { + // ... + } ---- * `@BeforeTransaction` @@ -17693,13 +17985,13 @@ transaction is started for test methods configured to run within a transaction v + -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -**@BeforeTransaction** -public void beforeTransaction() { - // logic to be executed before a transaction is started -} + **@BeforeTransaction** + public void beforeTransaction() { + // logic to be executed before a transaction is started + } ---- * `@AfterTransaction` @@ -17712,13 +18004,13 @@ transaction has ended for test methods configured to run within a transaction vi + -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -**@AfterTransaction** -public void afterTransaction() { - // logic to be executed after a transaction has ended -} + **@AfterTransaction** + public void afterTransaction() { + // logic to be executed after a transaction has ended + } ---- @@ -17772,14 +18064,14 @@ methods. Class-level usage overrides method-level usage. + -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -**@IfProfileValue**(**name**="java.vendor", **value**="Sun Microsystems Inc.") -@Test -public void testProcessWhichRunsOnlyOnSunJvm() { - // some logic that should run only on Java VMs from Sun Microsystems -} + **@IfProfileValue**(**name**="java.vendor", **value**="Sun Microsystems Inc.") + @Test + public void testProcessWhichRunsOnlyOnSunJvm() { + // some logic that should run only on Java VMs from Sun Microsystems + } ---- + @@ -17790,14 +18082,14 @@ Consider the following example: + -[source,java] +[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 -} + **@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 + } ---- + @@ -17813,13 +18105,13 @@ retrieving __profile values__ configured through the `@IfProfileValue` annotatio + -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -**@ProfileValueSourceConfiguration**(CustomProfileValueSource.class) -public class CustomProfileValueSourceTests { - // class body... -} + **@ProfileValueSourceConfiguration**(CustomProfileValueSource.class) + public class CustomProfileValueSourceTests { + // class body... + } ---- * `@Timed` @@ -17837,13 +18129,13 @@ test (see `@Repeat`), as well as any __set up__ or __tear down__ of the test fix + -[source,java] +[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 -} + **@Timed**(millis=1000) + public void testProcessWithOneSecondTimeout() { + // some logic that should not take longer than 1 second to execute + } ---- + @@ -17870,14 +18162,14 @@ well as any __set up__ or __tear down__ of the test fixture. + -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -**@Repeat**(10) -@Test -public void testProcessRepeatedly() { - // ... -} + **@Repeat**(10) + @Test + public void testProcessRepeatedly() { + // ... + } ---- @@ -18012,35 +18304,35 @@ As an alternative to implementing the `ApplicationContextAware` interface, you c inject the application context for your test class through the `@Autowired` annotation on either a field or setter method. For example: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration -public class MyTest { + @RunWith(SpringJUnit4ClassRunner.class) + @ContextConfiguration + public class MyTest { - **@Autowired** - private ApplicationContext applicationContext; + **@Autowired** + private ApplicationContext applicationContext; - // class body... -} + // 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RunWith(SpringJUnit4ClassRunner.class) -**@WebAppConfiguration** -@ContextConfiguration -public class MyWebAppTest { - **@Autowired** - private WebApplicationContext wac; + @RunWith(SpringJUnit4ClassRunner.class) + **@WebAppConfiguration** + @ContextConfiguration + public class MyWebAppTest { + **@Autowired** + private WebApplicationContext wac; - // class body... -} + // class body... + } ---- Dependency injection via `@Autowired` is provided by the @@ -18073,16 +18365,16 @@ 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] +[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... -} + @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 @@ -18091,14 +18383,14 @@ attributes in `@ContextConfiguration`, you can omit the declaration of the `loca attribute name and declare the resource locations by using the shorthand format demonstrated in the following example. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RunWith(SpringJUnit4ClassRunner.class) -**@ContextConfiguration({"/app-config.xml", "/test-config.xml"})** -public class MyTest { - // class body... -} + @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` @@ -18108,18 +18400,18 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.example; + package com.example; -@RunWith(SpringJUnit4ClassRunner.class) -// ApplicationContext will be loaded from -// "classpath:/com/example/MyTest-context.xml" -**@ContextConfiguration** -public class MyTest { - // class body... -} + @RunWith(SpringJUnit4ClassRunner.class) + // ApplicationContext will be loaded from + // "classpath:/com/example/MyTest-context.xml" + **@ContextConfiguration** + public class MyTest { + // class body... + } ---- [[testcontext-ctx-management-javaconfig]] @@ -18128,15 +18420,15 @@ To load an `ApplicationContext` for your tests using __annotated classes__ (see <>), annotate your test class with `@ContextConfiguration` and configure the `classes` attribute with an array that contains references to annotated classes. -[source,java] +[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... -} + @RunWith(SpringJUnit4ClassRunner.class) + // ApplicationContext will be loaded from AppConfig and TestConfig + **@ContextConfiguration(classes = {AppConfig.class, TestConfig.class})** + public class MyTest { + // class body... + } ---- .Annotated Classes @@ -18165,36 +18457,36 @@ example, the `OrderServiceTest` class declares a static inner configuration clas 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RunWith(SpringJUnit4ClassRunner.class) -// ApplicationContext will be loaded from the -// static inner Config class -**@ContextConfiguration** -public class OrderServiceTest { + @RunWith(SpringJUnit4ClassRunner.class) + // ApplicationContext will be loaded from the + // static inner Config class + **@ContextConfiguration** + public class OrderServiceTest { - @Configuration - static class Config { + @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; - } - } + // 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; + @Autowired + private OrderService orderService; - @Test - public void testOrderService() { - // test the orderService - } + @Test + public void testOrderService() { + // test the orderService + } -} + } ---- [[testcontext-ctx-management-mixed-config]] @@ -18232,18 +18524,18 @@ Furthermore, the order in which the initializers are invoked depends on whether implement Spring's `Ordered` interface or are annotated with Spring's `@Order` annotation. -[source,java] +[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... -} + @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 @@ -18252,16 +18544,16 @@ classes in `@ContextConfiguration` entirely and instead declare only in the context -- for example, by programmatically loading bean definitions from XML files or configuration classes. -[source,java] +[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... -} + @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]] @@ -18275,35 +18567,35 @@ Specifically, the resource locations or annotated classes for a test class are a 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. +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 +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 +__"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] +[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... -} + @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... -} + // 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 @@ -18311,21 +18603,21 @@ Similarly, in the following example that uses annotated classes, the `ExtendedConfig` classes, in that order. Beans defined in `ExtendedConfig` may therefore override (i.e., replace) those defined in `BaseConfig`. -[source,java] +[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... -} + @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... -} + // 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 @@ -18334,22 +18626,22 @@ In the following example that uses context initializers, the `ApplicationContext invoked depends on whether they implement Spring's `Ordered` interface or are annotated with Spring's `@Order` annotation. -[source,java] +[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... -} + @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... -} + // ApplicationContext will be initialized by BaseInitializer + // and ExtendedInitializer + **@ContextConfiguration(initializers=ExtendedInitializer.class)** + public class ExtendedTest extends BaseTest { + // class body... + } ---- [[testcontext-ctx-management-env-profiles]] @@ -18370,66 +18662,65 @@ SPI, but `@ActiveProfiles` is not supported with implementations of the older Let's take a look at some examples with XML configuration and `@Configuration` classes. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - - - + + + + - - - + + + - + - - - - - - + + + + + + - - - + + + - + ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.bank.service; + 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 { + @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; + @Autowired + private TransferService transferService; - @Test - public void testTransferService() { - // test the transferService - } -} + @Test + public void testTransferService() { + // test the transferService + } + } ---- When `TransferServiceTest` is run, its `ApplicationContext` will be loaded from the @@ -18448,88 +18739,86 @@ And that's likely what we want in an integration test. The following code listings demonstrate how to implement the same configuration and integration test but using `@Configuration` classes instead of XML. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -@Profile("dev") -public class StandaloneDataConfig { + @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(); - } -} + @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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -@Profile("production") -public class JndiDataConfig { + @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"); - } -} + @Bean + public DataSource dataSource() throws Exception { + Context ctx = new InitialContext(); + return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource"); + } + } ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -public class TransferServiceConfig { + @Configuration + public class TransferServiceConfig { - @Autowired DataSource dataSource; + @Autowired DataSource dataSource; - @Bean - public TransferService transferService() { - return new DefaultTransferService(accountRepository(), - feePolicy()); - } + @Bean + public TransferService transferService() { + return new DefaultTransferService(accountRepository(), feePolicy()); + } - @Bean - public AccountRepository accountRepository() { - return new JdbcAccountRepository(dataSource); - } + @Bean + public AccountRepository accountRepository() { + return new JdbcAccountRepository(dataSource); + } - @Bean - public FeePolicy feePolicy() { - return new ZeroFeePolicy(); - } + @Bean + public FeePolicy feePolicy() { + return new ZeroFeePolicy(); + } -} + } ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.bank.service; + package com.bank.service; -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration( - classes = { - TransferServiceConfig.class, - StandaloneDataConfig.class, - JndiDataConfig.class}) -@ActiveProfiles("dev") -public class TransferServiceTest { + @RunWith(SpringJUnit4ClassRunner.class) + @ContextConfiguration(classes = { + TransferServiceConfig.class, + StandaloneDataConfig.class, + JndiDataConfig.class}) + @ActiveProfiles("dev") + public class TransferServiceTest { - @Autowired - private TransferService transferService; + @Autowired + private TransferService transferService; - @Test - public void testTransferService() { - // test the transferService - } -} + @Test + public void testTransferService() { + // test the transferService + } + } ---- In this variation, we have split the XML configuration into three independent @@ -18554,53 +18843,52 @@ subclasses will automatically inherit the `@ActiveProfiles` configuration from t class. In the following example, the declaration of `@ActiveProfiles` (as well as other annotations) has been moved to an abstract superclass, `AbstractIntegrationTest`. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.bank.service; + package com.bank.service; -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration( - classes = { - TransferServiceConfig.class, - StandaloneDataConfig.class, - JndiDataConfig.class}) -@ActiveProfiles("dev") -public abstract class AbstractIntegrationTest { -} + @RunWith(SpringJUnit4ClassRunner.class) + @ContextConfiguration(classes = { + TransferServiceConfig.class, + StandaloneDataConfig.class, + JndiDataConfig.class}) + @ActiveProfiles("dev") + public abstract class AbstractIntegrationTest { + } ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.bank.service; + package com.bank.service; -// "dev" profile inherited from superclass -public class TransferServiceTest extends AbstractIntegrationTest { + // "dev" profile inherited from superclass + public class TransferServiceTest extends AbstractIntegrationTest { - @Autowired - private TransferService transferService; + @Autowired + private TransferService transferService; - @Test - public void testTransferService() { - // test the 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.bank.service; + package com.bank.service; -// "dev" profile overridden with "production" -@ActiveProfiles(profiles = "production", inheritProfiles = false) -public class ProductionTransferServiceTest extends AbstractIntegrationTest { - // test body -} + // "dev" profile overridden with "production" + @ActiveProfiles(profiles = "production", inheritProfiles = false) + public class ProductionTransferServiceTest extends AbstractIntegrationTest { + // test body + } ---- Furthermore, it is sometimes necessary to resolve active profiles for tests @@ -18618,34 +18906,34 @@ To resolve active bean definition profiles programmatically, simply implement a custom `OperatingSystemActiveProfilesResolver`. For further information, refer to the respective Javadoc. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.bank.service; + 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 -} + // "dev" profile overridden programmatically via a custom resolver + @ActiveProfiles( + resolver = OperatingSystemActiveProfilesResolver.class, + inheritProfiles = false) + public class TransferServiceTest extends AbstractIntegrationTest { + // test body + } ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.bank.service.test; + package com.bank.service.test; -public class OperatingSystemActiveProfilesResolver implements ActiveProfilesResolver { + 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}; - } -} + @Override + String[] resolve(Class testClass) { + String profile = ...; + // determine the value of profile based on the operating system + return new String[] {profile}; + } + } ---- [[testcontext-ctx-management-web]] @@ -18662,7 +18950,7 @@ created and supplied to your test's WAC. By default the base resource path for y `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 +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 @@ -18679,47 +18967,47 @@ The following examples demonstrate some of the various configuration options for a `WebApplicationContext`. .Conventions -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RunWith(SpringJUnit4ClassRunner.class) + @RunWith(SpringJUnit4ClassRunner.class) -// defaults to "file:src/main/webapp" -@WebAppConfiguration + // defaults to "file:src/main/webapp" + @WebAppConfiguration -// detects "WacTests-context.xml" in same package -// or static nested @Configuration class -@ContextConfiguration + // detects "WacTests-context.xml" in same package + // or static nested @Configuration class + @ContextConfiguration -public class WacTests { - //... -} + 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 +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 +(i.e., __"WacTests-context.xml"__ in the same package as the `WacTests` class or static nested `@Configuration` classes). .Default resource semantics -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RunWith(SpringJUnit4ClassRunner.class) + @RunWith(SpringJUnit4ClassRunner.class) -// file system resource -@WebAppConfiguration("webapp") + // file system resource + @WebAppConfiguration("webapp") -// classpath resource -@ContextConfiguration("/spring/test-servlet-config.xml") + // classpath resource + @ContextConfiguration("/spring/test-servlet-config.xml") -public class WacTests { - //... -} + public class WacTests { + //... + } ---- This example demonstrates how to explicitly declare a resource base path with @@ -18729,20 +19017,20 @@ annotations. By default, `@WebAppConfiguration` resource paths are file system b whereas, `@ContextConfiguration` resource locations are classpath based. .Explicit resource semantics -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RunWith(SpringJUnit4ClassRunner.class) + @RunWith(SpringJUnit4ClassRunner.class) -// classpath resource -@WebAppConfiguration("classpath:test-web-resources") + // classpath resource + @WebAppConfiguration("classpath:test-web-resources") -// file system resource -@ContextConfiguration("file:src/main/webapp/WEB-INF/servlet-config.xml") + // file system resource + @ContextConfiguration("file:src/main/webapp/WEB-INF/servlet-config.xml") -public class WacTests { - //... -} + public class WacTests { + //... + } ---- In this third example, we see that we can override the default resource semantics for @@ -18770,27 +19058,33 @@ whereas, the other mocks are managed per test method by the `ServletTestExecutionListener`. .Injecting mocks -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@WebAppConfiguration -@ContextConfiguration -public class WacTests { + @WebAppConfiguration + @ContextConfiguration + public class WacTests { - @Autowired WebApplicationContext wac; // cached + @Autowired + WebApplicationContext wac; // cached - @Autowired MockServletContext servletContext; // cached + @Autowired + MockServletContext servletContext; // cached - @Autowired MockHttpSession session; + @Autowired + MockHttpSession session; - @Autowired MockHttpServletRequest request; + @Autowired + MockHttpServletRequest request; - @Autowired MockHttpServletResponse response; + @Autowired + MockHttpServletResponse response; - @Autowired ServletWebRequest webRequest; + @Autowired + ServletWebRequest webRequest; - //... -} + //... + } ---- -- @@ -18892,22 +19186,22 @@ one for the __root__ WebApplicationContext (loaded using the `TestAppConfig` is __autowired__ into the test instance is the one for the child context (i.e., the lowest context in the hierarchy). -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RunWith(SpringJUnit4ClassRunner.class) -@WebAppConfiguration -@ContextHierarchy({ - @ContextConfiguration(classes = TestAppConfig.class), - @ContextConfiguration(classes = WebConfig.class) -}) -public class ControllerIntegrationTests { + @RunWith(SpringJUnit4ClassRunner.class) + @WebAppConfiguration + @ContextHierarchy({ + @ContextConfiguration(classes = TestAppConfig.class), + @ContextConfiguration(classes = WebConfig.class) + }) + public class ControllerIntegrationTests { - @Autowired - private WebApplicationContext wac; + @Autowired + private WebApplicationContext wac; - // ... -} + // ... + } ---- -- @@ -18927,19 +19221,19 @@ that three application contexts will be loaded (one for each declaration of in `AbstractWebTests` will be set as the parent context for each of the contexts loaded for the concrete subclasses. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RunWith(SpringJUnit4ClassRunner.class) -@WebAppConfiguration -@ContextConfiguration("file:src/main/webapp/WEB-INF/applicationContext.xml") -public abstract class AbstractWebTests {} + @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/soap-ws-config.xml") + public class SoapWebServiceTests extends AbstractWebTests {} -@ContextHierarchy(@ContextConfiguration("/spring/rest-ws-config.xml") -public class RestWebServiceTests extends AbstractWebTests {} + @ContextHierarchy(@ContextConfiguration("/spring/rest-ws-config.xml") + public class RestWebServiceTests extends AbstractWebTests {} ---- -- @@ -18958,20 +19252,20 @@ the previous example, the application context loaded from `"/app-config.xml"` wi set as the parent context for the contexts loaded from `"/user-config.xml"` and `{"/user-config.xml", "/order-config.xml"}`. -[source,java] +[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 {} + @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 {} + @ContextHierarchy( + @ContextConfiguration(name = "child", locations = "/order-config.xml") + ) + public class ExtendedTests extends BaseTests {} ---- -- @@ -18984,23 +19278,23 @@ 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] +[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 {} + @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 {} + @ContextHierarchy( + @ContextConfiguration( + name = "child", + locations = "/test-user-config.xml", + inheritLocations = false + )) + public class ExtendedTests extends BaseTests {} ---- .Dirtying a context within a context hierarchy @@ -19017,7 +19311,7 @@ cleared. For further details consult the discussion of `@DirtiesContext` in [[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 +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 @@ -19065,73 +19359,76 @@ example. The first code listing shows a JUnit-based implementation of the test class that uses `@Autowired` for field injection. -[source,java] +[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; + @RunWith(SpringJUnit4ClassRunner.class) + // specifies the Spring configuration to load for this test fixture + **@ContextConfiguration("repository-config.xml")** + public class HibernateTitleRepositoryTests { - @Test - public void findById() { - Title title = titleRepository.findById(new Long(10)); - assertNotNull(title); - } -} + // 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] +[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; + @RunWith(SpringJUnit4ClassRunner.class) + // specifies the Spring configuration to load for this test fixture + **@ContextConfiguration("repository-config.xml")** + public class HibernateTitleRepositoryTests { - **@Autowired** - public void setTitleRepository(HibernateTitleRepository titleRepository) { - this.titleRepository = titleRepository; - } + // this instance will be dependency injected by type + private HibernateTitleRepository titleRepository; - @Test - public void findById() { - Title title = titleRepository.findById(new Long(10)); - assertNotNull(title); - } -} + **@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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - - - - - - + + + + - + + + + + ---- [NOTE] @@ -19143,18 +19440,18 @@ such a case, you can override the setter method and use the `@Qualifier` annotat indicate a specific target bean as follows, but make sure to delegate to the overridden method in the superclass as well. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// ... + // ... - @Autowired - @Override - public void setDataSource(**@Qualifier("myDataSource")** DataSource dataSource) { - **super**.setDataSource(dataSource); - } + @Autowired + @Override + public void setDataSource(**@Qualifier("myDataSource")** DataSource dataSource) { + **super**.setDataSource(dataSource); + } -// ... + // ... ---- The specified qualifier value indicates the specific `DataSource` bean to inject, @@ -19189,23 +19486,23 @@ want to configure these request parameters via the mock managed by the TestConte framework. .Request-scoped bean configuration -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - - - + + + - + ---- In `RequestScopedBeanTests` we inject both the `UserService` (i.e., the subject under @@ -19218,28 +19515,28 @@ parameters in). We can then perform assertions against the results based on the inputs for the username and password. .Request-scoped bean test -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration -@WebAppConfiguration -public class RequestScopedBeanTests { + @RunWith(SpringJUnit4ClassRunner.class) + @ContextConfiguration + @WebAppConfiguration + public class RequestScopedBeanTests { - @Autowired UserService userService; - @Autowired MockHttpServletRequest request; + @Autowired UserService userService; + @Autowired MockHttpServletRequest request; - @Test - public void requestScope() { + @Test + public void requestScope() { - request.setParameter("user", "enigma"); - request.setParameter("pswd", "$pr!ng"); + request.setParameter("user", "enigma"); + request.setParameter("pswd", "$pr!ng"); - LoginResults results = userService.loginUser(); + LoginResults results = userService.loginUser(); - // assert results - } -} + // assert results + } + } ---- The following code snippet is similar to the one we saw above for a request-scoped bean; @@ -19250,23 +19547,23 @@ we will need to configure a theme in the mock session managed by the TestContext framework. .Session-scoped bean configuration -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - - - + + + - + ---- In `SessionScopedBeanTests` we inject the `UserService` and the `MockHttpSession` into @@ -19278,27 +19575,27 @@ the user service has access to the session-scoped `userPreferences` for the curr configured theme. .Session-scoped bean test -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration -@WebAppConfiguration -public class SessionScopedBeanTests { + @RunWith(SpringJUnit4ClassRunner.class) + @ContextConfiguration + @WebAppConfiguration + public class SessionScopedBeanTests { - @Autowired UserService userService; - @Autowired MockHttpSession session; + @Autowired UserService userService; + @Autowired MockHttpSession session; - @Test - public void sessionScope() throws Exception { + @Test + public void sessionScope() throws Exception { - session.setAttribute("theme", "blue"); + session.setAttribute("theme", "blue"); - Results results = userService.processUserPreferences(); + Results results = userService.processUserPreferences(); - // assert results - } -} + // assert results + } + } ---- @@ -19351,43 +19648,43 @@ highlighting several transaction-related annotations. Consult the <> section for further information and configuration examples. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration -**@TransactionConfiguration(transactionManager="txMgr", defaultRollback=false) -@Transactional** -public class FictitiousTransactionalTest { + @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 - } + **@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 - } + @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 - } + @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 - } + @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 - } + **@AfterTransaction** + public void verifyFinalDatabaseState() { + // logic to verify the final state after transaction has rolled back + } -} + } ---- .Avoid false positives when testing ORM code @@ -19402,29 +19699,29 @@ and the other method correctly exposes the results of flushing the session. Note this applies to JPA and any other ORM frameworks that maintain an in-memory __unit of work__. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// ... + // ... -@Autowired -private SessionFactory sessionFactory; + @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 // 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(); -} + @Test(expected = GenericJDBCException.class) + public void updateWithSessionFlush() { + updateEntityInHibernateSession(); + // Manual flush is required to avoid false positive in test + sessionFactory.getCurrentSession().flush(); + } -// ... + // ... ---- ==== @@ -19488,18 +19785,18 @@ Spring Runner. `@TestExecutionListeners` is configured with an empty list in ord disable the default listeners, which otherwise would require an ApplicationContext to be configured through `@ContextConfiguration`. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RunWith(SpringJUnit4ClassRunner.class) -@TestExecutionListeners({}) -public class SimpleTest { + @RunWith(SpringJUnit4ClassRunner.class) + @TestExecutionListeners({}) + public class SimpleTest { - @Test - public void testMethod() { - // execute test logic... - } -} + @Test + public void testMethod() { + // execute test logic... + } + } ---- [[testcontext-support-classes-testng]] @@ -19559,7 +19856,7 @@ Before inclusion in Spring Framework 3.2, the Spring MVC Test framework had alre 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/SpringSource/spring-test-mvc[spring-test-mvc project] +The standalone https://github.com/SpringSource/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 should replace the `spring-test-mvc` dependency with a dependency on `spring-test`. @@ -19615,35 +19912,35 @@ and so on should work as expected, and the response will contain the generated c Below is an example of a test requesting account information in JSON format: -[source,java] +[source,java,indent=0] ---- -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + 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 { + @RunWith(SpringJUnit4ClassRunner.class) + @WebAppConfiguration + @ContextConfiguration("test-servlet-context.xml") + public class ExampleTests { - @Autowired - private WebApplicationContext wac; + @Autowired + private WebApplicationContext wac; - private MockMvc mockMvc; + private MockMvc mockMvc; - @Before - public void setup() { - this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); - } + @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")); - } + @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__. @@ -19654,7 +19951,7 @@ as the test class (also supports JavaConfig) and injects the created 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 +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. @@ -19662,7 +19959,7 @@ request and those will be discussed later. ====== 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 +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. @@ -19678,27 +19975,27 @@ The first option is to point to Spring MVC configuration through the __TestConte framework__, which loads the Spring configuration and injects a `WebApplicationContext` into the test to use to create a `MockMvc`: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RunWith(SpringJUnit4ClassRunner.class) -@WebAppConfiguration -@ContextConfiguration("my-servlet-context.xml") -public class MyWebTests { + @RunWith(SpringJUnit4ClassRunner.class) + @WebAppConfiguration + @ContextConfiguration("my-servlet-context.xml") + public class MyWebTests { - @Autowired - private WebApplicationContext wac; + @Autowired + private WebApplicationContext wac; - private MockMvc mockMvc; + private MockMvc mockMvc; - @Before - public void setup() { - this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); - } + @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 @@ -19707,21 +20004,21 @@ controllers is automatically created. The created configuration is comparable to the MVC JavaConfig (and the MVC namespace) and can be customized to a degree through builder-style methods: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MyWebTests { + public class MyWebTests { - private MockMvc mockMvc; + private MockMvc mockMvc; - @Before - public void setup() { - this.mockMvc = MockMvcBuilders.standaloneSetup(new AccountController()).build(); - } + @Before + public void setup() { + this.mockMvc = MockMvcBuilders.standaloneSetup(new AccountController()).build(); + } - // ... + // ... -} + } ---- Which option should you use? @@ -19733,36 +20030,36 @@ Furthermore, you can inject mock services into controllers through Spring config in order to remain focused on testing the web layer. Here is an example of declaring a mock service with Mockito: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- Then you can inject the mock service into the test in order set up and verify expectations: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RunWith(SpringJUnit4ClassRunner.class) -@WebAppConfiguration -@ContextConfiguration("test-servlet-context.xml") -public class AccountTests { + @RunWith(SpringJUnit4ClassRunner.class) + @WebAppConfiguration + @ContextConfiguration("test-servlet-context.xml") + public class AccountTests { - @Autowired - private WebApplicationContext wac; + @Autowired + private WebApplicationContext wac; - private MockMvc mockMvc; + private MockMvc mockMvc; - @Autowired - private AccountService accountService; + @Autowired + private AccountService accountService; - // ... + // ... -} + } ---- The __"standaloneSetup"__ on the other hand is a little closer to a unit test. It tests @@ -19783,35 +20080,35 @@ verify the Spring MVC configuration. Alternatively, you can decide write all tes To perform requests, use the appropriate HTTP method and additional builder-style methods corresponding to properties of `MockHttpServletRequest`. For example: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -mockMvc.perform(post("/hotels/{id}", 42).accept(MediaType.APPLICATION_JSON)); + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -mockMvc.perform(fileUpload("/doc").file("a1", "ABC".getBytes("UTF-8"))); + mockMvc.perform(fileUpload("/doc").file("a1", "ABC".getBytes("UTF-8"))); ---- Query string parameters can be specified in the URI template: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -mockMvc.perform(get("/hotels?foo={foo}", "bar")); + mockMvc.perform(get("/hotels?foo={foo}", "bar")); ---- Or by adding Servlet request parameters: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -mockMvc.perform(get("/hotels").param("foo", "bar")); + mockMvc.perform(get("/hotels").param("foo", "bar")); ---- If application code relies on Servlet request parameters, and doesn't check the query @@ -19823,30 +20120,30 @@ In most cases it's preferable to leave out the context path and the Servlet path 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -mockMvc.perform(get("/app/main/hotels/{id}").contextPath("/app").servletPath("/main")) + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MyWebTests { + public class MyWebTests { - private MockMvc mockMvc; + private MockMvc mockMvc; - @Before - public void setup() { - mockMvc = standaloneSetup(new AccountController()) - .defaultRequest(get("/") - .contextPath("/app").servletPath("/main") - .accept(MediaType.APPLICATION_JSON).build(); - } + @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 @@ -19859,10 +20156,10 @@ properties, since they must be specified on every request. Expectations can be defined by appending one or more `.andExpect(..)` after call to perform the request: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -mockMvc.perform(get("/accounts/1")).andExpect(status().isOk()); + mockMvc.perform(get("/accounts/1")).andExpect(status().isOk()); ---- `MockMvcResultMatchers.*` defines a number of static members, some of which return types @@ -19879,25 +20176,25 @@ selected, what flash attributes were added, and so on. It is also possible to ve Servlet specific constructs such as request and session attributes. The following test asserts that binding/validation failed: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -mockMvc.perform(post("/persons")) - .andExpect(status().isOk()) - .andExpect(model().attributeHasErrors("person")); + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -mockMvc.perform(post("/persons")) - .andDo(print()) - .andExpect(status().isOk()) - .andExpect(model().attributeHasErrors("person")); + 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 @@ -19907,23 +20204,23 @@ In some cases, you may want to get direct access to the result and verify someth cannot be verified otherwise. This can be done by appending `.andReturn()` at the end after all expectations: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -MvcResult mvcResult = mockMvc.perform(post("/persons")).andExpect(status().isOk()).andReturn(); -// ... + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -standaloneSetup(new SimpleController()) - .alwaysExpect(status().isOk()) - .alwaysExpect(content().contentType("application/json;charset=UTF-8")) - .build() + 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 @@ -19933,33 +20230,33 @@ When JSON response content contains hypermedia links created with https://github.com/SpringSource/spring-hateoas[Spring HATEOAS], the resulting links can be verified: -[source,java] +[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")); + 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/SpringSource/spring-hateoas[Spring HATEOAS], the resulting links can be verified: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -Map 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")); + Map 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -mockMvc = standaloneSetup(new PersonController()).addFilters(new CharacterEncodingFilter()).build(); + mockMvc = standaloneSetup(new PersonController()).addFilters(new CharacterEncodingFilter()).build(); ---- Registered filters will be invoked through `MockFilterChain` from `spring-test` and the @@ -19980,17 +20277,17 @@ coverage based on Spring MVC Test. Client-side tests are for code using the `RestTemplate`. The goal is to define expected requests and provide "stub" responses: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -RestTemplate restTemplate = new RestTemplate(); + RestTemplate restTemplate = new RestTemplate(); -MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate); -mockServer.expect(requestTo("/greeting")).andRespond(withSuccess("Hello world", "text/plain")); + MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate); + mockServer.expect(requestTo("/greeting")).andRespond(withSuccess("Hello world", "text/plain")); -// use RestTemplate ... + // use RestTemplate ... -mockServer.verify(); + mockServer.verify(); ---- In the above example, `MockRestServiceServer` -- the central class for client-side REST @@ -20024,37 +20321,38 @@ tests] of client-side REST tests. [[testing-examples-petclinic]] ==== PetClinic Example -The PetClinic application, available from the <>, -illustrates several features of the __Spring TestContext Framework__ in a JUnit -4.5+ environment. Most test functionality is included in the `AbstractClinicTests`, for -which a partial listing is shown below: +The PetClinic application, available on +https://github.com/spring-projects/spring-petclinic[Github], illustrates several features +of the __Spring TestContext Framework__ in a JUnit 4.5+ environment. Most test +functionality is included in the `AbstractClinicTests`, for which a partial listing +is shown below: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import static org.junit.Assert.assertEquals; -// import ... + import static org.junit.Assert.assertEquals; + // import ... -**@ContextConfiguration** -public abstract class AbstractClinicTests **extends AbstractTransactionalJUnit4SpringContextTests** { + **@ContextConfiguration** + public abstract class AbstractClinicTests **extends AbstractTransactionalJUnit4SpringContextTests** { - **@Autowired** - protected Clinic clinic; + **@Autowired** + protected Clinic clinic; - @Test - public void getVets() { - Collection 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()); - // ... - } + @Test + public void getVets() { + Collection 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: @@ -20093,11 +20391,11 @@ inherited locations) and `HibernateClinicTests-context.xml`, with `HibernateClinicTests-context.xml` possibly overriding beans defined in `AbstractClinicTests-context.xml`. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -**@ContextConfiguration** -public class HibernateClinicTests extends AbstractClinicTests { } + **@ContextConfiguration** + public class HibernateClinicTests extends AbstractClinicTests { } ---- In a large-scale application, the Spring configuration is often split across multiple @@ -20131,7 +20429,7 @@ 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 Java 5 annotations, test groups, data-driven testing, distributed testing, etc. + 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. @@ -20265,7 +20563,7 @@ to the 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__. +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 @@ -20312,18 +20610,18 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface PlatformTransactionManager { + public interface PlatformTransactionManager { - TransactionStatus getTransaction(TransactionDefinition definition) - throws TransactionException; + TransactionStatus getTransaction( + TransactionDefinition definition) throws TransactionException; - void commit(TransactionStatus status) throws TransactionException; + void commit(TransactionStatus status) throws TransactionException; - void rollback(TransactionStatus status) throws TransactionException; -} + void rollback(TransactionStatus status) throws TransactionException; + } ---- This is primarily a service provider interface (SPI), although it can be used @@ -20377,24 +20675,24 @@ The `TransactionStatus` interface provides a simple way for transactional code t control transaction execution and query transaction status. The concepts should be familiar, as they are common to all transaction APIs: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface TransactionStatus extends SavepointManager { + public interface TransactionStatus extends SavepointManager { - boolean isNewTransaction(); + boolean isNewTransaction(); - boolean hasSavepoint(); + boolean hasSavepoint(); - void setRollbackOnly(); + void setRollbackOnly(); - boolean isRollbackOnly(); + boolean isRollbackOnly(); - void flush(); + void flush(); - boolean isCompleted(); + boolean isCompleted(); -} + } ---- Regardless of whether you opt for declarative or programmatic transaction management in @@ -20408,52 +20706,52 @@ example works with plain JDBC.) You define a JDBC `DataSource` -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - + + + + + + ---- The related `PlatformTransactionManager` bean definition will then have a reference to the `DataSource` definition. It will look like this: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + - + - + - + ---- The `JtaTransactionManager` does not need to know about the `DataSource`, or any other @@ -20486,36 +20784,36 @@ The `txManager` bean in this case is of the `HibernateTransactionManager` type. same way as the `DataSourceTransactionManager` needs a reference to the `DataSource`, the `HibernateTransactionManager` needs a reference to the `SessionFactory`. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml - - - - - hibernate.dialect=${hibernate.dialect} - - - + + + + + org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml + + + + + hibernate.dialect=${hibernate.dialect} + + + - - - + + + ---- 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- [NOTE] @@ -20574,10 +20872,10 @@ For example, in the case of JDBC, instead of the traditional JDBC approach of ca the `getConnection()` method on the `DataSource`, you instead use Spring's `org.springframework.jdbc.datasource.DataSourceUtils` class as follows: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -Connection conn = DataSourceUtils.getConnection(dataSource); + Connection conn = DataSourceUtils.getConnection(dataSource); ---- If an existing transaction already has a connection synchronized (linked) to it, that @@ -20603,7 +20901,7 @@ behind the scenes and you won't need to write any special code. [[tx-resource-synchronization-tadsp]] -==== TransactionAwareDataSourceProxy +==== 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 @@ -20620,7 +20918,7 @@ abstractions mentioned above. [[transaction-declarative]] -=== Declarative transaction managementWhere is +=== Declarative transaction management [NOTE] ==== Most Spring Framework users choose declarative transaction management. This option has @@ -20657,8 +20955,8 @@ necessary. The differences between the two types of transaction management are: 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? **** -`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. @@ -20708,7 +21006,7 @@ Spring AOP is covered in <>. Conceptually, calling a method on a transactional proxy looks like this... -image::images/tx.png[] +image::images/tx.png[width=400] @@ -20722,52 +21020,52 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// the service interface that we want to make transactional + // the service interface that we want to make transactional -package x.y.service; + package x.y.service; -public interface FooService { + public interface FooService { - Foo getFoo(String fooName); + Foo getFoo(String fooName); - Foo getFoo(String fooName, String barName); + Foo getFoo(String fooName, String barName); - void insertFoo(Foo foo); + void insertFoo(Foo foo); - void updateFoo(Foo foo); + void updateFoo(Foo foo); -} + } ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// an implementation of the above interface + // an implementation of the above interface -package x.y.service; + package x.y.service; -public class DefaultFooService implements FooService { + public class DefaultFooService implements FooService { - public Foo getFoo(String fooName) { - throw new UnsupportedOperationException(); - } + public Foo getFoo(String fooName) { + throw new UnsupportedOperationException(); + } - public Foo getFoo(String fooName, String barName) { - throw new UnsupportedOperationException(); - } + public Foo getFoo(String fooName, String barName) { + throw new UnsupportedOperationException(); + } - public void insertFoo(Foo foo) { - throw new UnsupportedOperationException(); - } + public void insertFoo(Foo foo) { + throw new UnsupportedOperationException(); + } - public void updateFoo(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 @@ -20776,60 +21074,60 @@ semantics, and that the other methods, `insertFoo(Foo)` and `updateFoo(Foo)`, mu 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + - - + + - - - - - - - - - - + + + + + + + + + + - - - - - + + + + + - - - - - - - + + + + + + + - - - - + + + + - + - + ---- Examine the preceding configuration. You want to make a service object, the `fooService` @@ -20859,19 +21157,19 @@ using an advisor. The result indicates that at the execution of a `fooServiceOpe the advice defined by `txAdvice` will be run. The expression defined within the `` element is an AspectJ pointcut -expression; see <> for more details on pointcut expressions in Spring 2.0. +expression; see <> 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + ---- [NOTE] @@ -20890,57 +21188,52 @@ proxy__, a transaction is started, suspended, marked as read-only, and so on, de on the transaction configuration associated with that method. Consider the following program that test drives the above configuration: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public final class Boot { + 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()); - } -} + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - -[AspectJInvocationContextExposingAdvisorAutoProxyCreator] - Creating implicit proxy - for bean 'fooService' with 0 common interceptors and 1 specific interceptors - -[JdkDynamicAopProxy] - Creating JDK dynamic proxy for [x.y.service.DefaultFooService] + + [AspectJInvocationContextExposingAdvisorAutoProxyCreator] - Creating implicit proxy for bean 'fooService' with 0 common interceptors and 1 specific interceptors - + + [JdkDynamicAopProxy] - Creating JDK dynamic proxy for [x.y.service.DefaultFooService] -[TransactionInterceptor] - Getting transaction for x.y.service.FooService.insertFoo - -[DataSourceTransactionManager] - Creating new transaction with name [x.y.service.FooService.insertFoo] -[DataSourceTransactionManager] - Acquired Connection - [org.apache.commons.dbcp.PoolableConnection@a53de4] for JDBC transaction + + [TransactionInterceptor] - Getting transaction for x.y.service.FooService.insertFoo - -[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] + + [DataSourceTransactionManager] - Creating new transaction with name [x.y.service.FooService.insertFoo] + [DataSourceTransactionManager] - Acquired Connection [org.apache.commons.dbcp.PoolableConnection@a53de4] for JDBC transaction - -[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 + + [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] -Exception in thread "main" java.lang.UnsupportedOperationException - at x.y.service.DefaultFooService.insertFoo(DefaultFooService.java:14) - - at $Proxy0.insertFoo(Unknown Source) - at Boot.main(Boot.java:11) + + [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) + + at $Proxy0.insertFoo(Unknown Source) + at Boot.main(Boot.java:11) ---- @@ -20969,15 +21262,15 @@ You can configure exactly which `Exception` types mark a transaction for rollbac including checked exceptions. The following XML snippet demonstrates how you configure rollback for a checked, application-specific `Exception` type. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - + + + + + + ---- You can also specify 'no rollback rules', if you do __not__ want a transaction rolled @@ -20985,15 +21278,15 @@ back when an exception is thrown. The following example tells the Spring Framewo transaction infrastructure to commit the attendant transaction even in the face of an unhandled `InstrumentNotFoundException`. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - + + + + + + ---- When the Spring Framework's transaction infrastructure catches an exception and is @@ -21002,31 +21295,31 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - + + + + + ---- 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public void resolvePosition() { - try { - // some business logic... - } catch (NoProductInStockException ex) { - // trigger rollback programmatically - TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); - } -} + 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 @@ -21047,112 +21340,112 @@ defined in a root `x.y.service` package. To make all beans that are instances of 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + - + - + - + - - - + + + - - - + + + - - - - - - + + + + + + - + - + ---- The following example shows how to configure two distinct beans with totally different transactional settings. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + - + - + - + - + - + - - + + - - + + - - - - - - + + + + + + - - - - - + + + + + - + - + ---- [[transaction-declarative-txadvice-settings]] -==== settings +==== settings This section summarizes the various transactional settings that can be specified using the `` tag. The default `` settings are: @@ -21216,7 +21509,7 @@ that are nested within `` and `` tags are summarized [[transaction-declarative-annotations]] -==== Using Method visibility and @Transactional +==== 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 @@ -21228,56 +21521,56 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// the service class that we want to make transactional -**@Transactional** -public class DefaultFooService implements FooService { + // the service class that we want to make transactional + **@Transactional** + public class DefaultFooService implements FooService { - Foo getFoo(String fooName); + Foo getFoo(String fooName); - Foo getFoo(String fooName, String barName); + Foo getFoo(String fooName, String barName); - void insertFoo(Foo foo); + void insertFoo(Foo foo); - void updateFoo(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: +can be made transactional by adding merely __one__ line of XML configuration: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + - - + + - - ____ - - - - + + ____ + + + + - + - + ---- [TIP] @@ -21296,8 +21589,8 @@ using Java based configuration. Simply add the annotation to a `@Configuration` See Javadoc for full details. ==== +.Method visibility and @Transactional **** -`@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 @@ -21325,7 +21618,7 @@ proxies. The fact that Java annotations are __not inherited from interfaces__ me 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__. +transactional proxy, which would be decidedly __bad__. ==== [NOTE] @@ -21409,27 +21702,27 @@ annotated at the class level with the settings for a read-only transaction, but `@Transactional` annotation on the `updateFoo(Foo)` method in the same class takes precedence over the transactional settings defined at the class level. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Transactional(readOnly = true) -public class DefaultFooService implements FooService { + @Transactional(readOnly = true) + public class DefaultFooService implements FooService { - public Foo getFoo(String fooName) { - // do something - } + 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 - } -} + // 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 +===== @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 @@ -21451,11 +21744,11 @@ annotation are summarized in the following table: |=== | Property| Type| Description -a| <> +| <> | String | Optional qualifier specifying the transaction manager to be used. -a| <> +| <> | enum: `Propagation` | Optional propagation setting. @@ -21506,36 +21799,36 @@ optionally specify the identity of the `PlatformTransactionManager` to be used. 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class TransactionalService { + public class TransactionalService { - @Transactional("order") - public void setSomething(String name) { ... } + @Transactional("order") + public void setSomething(String name) { ... } - @Transactional("account") - public void doSomething() { ... } - } + @Transactional("account") + public void doSomething() { ... } + } ---- could be combined with the following transaction manager bean declarations in the application context. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - ... - - + + ... + + - - ... - - + + ... + + ---- In this case, the two methods on `TransactionalService` will run under separate @@ -21547,44 +21840,43 @@ 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 Spring's meta-annotation support allows you to define custom -shortcut annotations for your specific use cases. For example, defining the following -annotations +different methods, then <> allows +you to define custom shortcut annotations for your specific use cases. For example, +defining the following annotations -[source,java] +[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("order") + public @interface OrderTx { + } - @Target({ElementType.METHOD, ElementType.TYPE}) - @Retention(RetentionPolicy.RUNTIME) - @Transactional("account") - public @interface AccountTx { - } + @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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class TransactionalService { + public class TransactionalService { - @OrderTx - public void setSomething(String name) { ... } + @OrderTx + public void setSomething(String name) { ... } - @AccountTx - public void doSomething() { ... } - } + @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. +have included propagation behavior, rollback rules, timeouts etc. @@ -21600,7 +21892,7 @@ __logical__ transactions, and how the propagation setting applies to this differ [[tx-propagation-required]] ===== Required -image::images/tx_prop_required.png[] +image::images/tx_prop_required.png[width=400] PROPAGATION_REQUIRED @@ -21626,7 +21918,7 @@ indicate clearly that a rollback was performed instead. [[tx-propagation-requires_new]] ===== RequiresNew -image::images/tx_prop_requires_new.png[] +image::images/tx_prop_requires_new.png[width=400] PROPAGATION_REQUIRES_NEW @@ -21672,85 +21964,91 @@ Here is the code for a simple profiling aspect discussed above. The ordering of is controlled through the `Ordered` interface. For full details on advice ordering, see <>. . -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package x.y; + package x.y; -import org.aspectj.lang.ProceedingJoinPoint; -import org.springframework.util.StopWatch; -import org.springframework.core.Ordered; + import org.aspectj.lang.ProceedingJoinPoint; + import org.springframework.util.StopWatch; + import org.springframework.core.Ordered; -public class SimpleProfiler implements Ordered { + public class SimpleProfiler implements Ordered { - private int order; + private int order; - // allows us to control the ordering of advice - public int getOrder() { - return this.order; - } + // allows us to control the ordering of advice + public int getOrder() { + return this.order; + } - public void setOrder(int order) { - this.order = 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; - } -} + // 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + - - - - - - - - - + + + + + - - - - - - + - - - + + + + + + + - + + + + + + + + + + + + ---- The result of the above configuration is a `fooService` bean that has profiling and @@ -21760,51 +22058,55 @@ of additional aspects in similar fashion. The following example effects the same setup as above, but uses the purely XML declarative approach. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + - - - - __ - + + + + __ + - - - - + + + - + + - - - - - - + + + + - + - + + + + + + + + + + ---- The result of the above configuration will be a `fooService` bean that has profiling and @@ -21840,14 +22142,14 @@ Prior to continuing, you may want to read < <> respectively. ==== -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// construct an appropriate transaction manager -DataSourceTransactionManager txManager = new DataSourceTransactionManager(getDataSource()); + // 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); + // configure the AnnotationTransactionAspect to use it; this must be done before executing any transactional methods + AnnotationTransactionAspect.aspectOf().setTransactionManager(txManager); ---- [NOTE] @@ -21910,64 +22212,63 @@ anonymous inner class) that contains the code that you need to execute in the co a transaction. You then pass an instance of your custom `TransactionCallback` to the `execute(..)` method exposed on the `TransactionTemplate`. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class SimpleService implements Service { + public class SimpleService implements Service { - // single TransactionTemplate shared amongst all methods in this instance - private final TransactionTemplate transactionTemplate; + // 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); - } + // 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(); - } - }); - } -} + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -transactionTemplate.execute(new **TransactionCallbackWithoutResult**() { - protected void doInTransactionWithoutResult(TransactionStatus status) { - updateOperation1(); - updateOperation2(); - } -}); + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -transactionTemplate.execute(new TransactionCallbackWithoutResult() { + transactionTemplate.execute(new TransactionCallbackWithoutResult() { - protected void doInTransactionWithoutResult(TransactionStatus status) { - try { - updateOperation1(); - updateOperation2(); - } catch (SomeBusinessExeption ex) { - **status.setRollbackOnly();** - } - } -}); + protected void doInTransactionWithoutResult(TransactionStatus status) { + try { + updateOperation1(); + updateOperation2(); + } catch (SomeBusinessExeption ex) { + **status.setRollbackOnly();** + } + } + }); ---- @@ -21980,37 +22281,37 @@ configuration. `TransactionTemplate` instances by default have the following example shows the programmatic customization of the transactional settings for a specific `TransactionTemplate:` -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class SimpleService implements Service { + public class SimpleService implements Service { - private final TransactionTemplate transactionTemplate; + private final TransactionTemplate transactionTemplate; - public SimpleService(PlatformTransactionManager transactionManager) { - Assert.notNull(transactionManager, "The 'transactionManager' argument must not be null."); - this.transactionTemplate = new TransactionTemplate(transactionManager); + 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 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - -" + + + + " ---- Finally, instances of the `TransactionTemplate` class are threadsafe, in that instances @@ -22031,23 +22332,23 @@ directly to manage your transaction. Simply pass the implementation of the using the `TransactionDefinition` and `TransactionStatus` objects you can initiate transactions, roll back, and commit. -[source,java] +[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); + 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); + TransactionStatus status = txManager.getTransaction(def); + try { + // execute your business logic here + } + catch (MyException ex) { + txManager.rollback(status); + throw ex; + } + txManager.commit(status); ---- @@ -22132,7 +22433,7 @@ per-transaction isolation levels, and proper resuming of transactions in all cas 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 +transactions, you __must__ use the `org.springframework.transaction.jta.JtaTransactionManager` class (or an <> of it) for all your transactional operations. Otherwise the transaction infrastructure @@ -22208,7 +22509,7 @@ The exception hierarchy that Spring provides can be seen below. (Please note tha class hierarchy detailed in the image shows only a subset of the entire `DataAccessException` hierarchy.) -image::images/DataAccessException.gif[] +image::images/DataAccessException.gif[width=400] @@ -22220,13 +22521,13 @@ exception translation is to use the `@Repository` annotation. This annotation al allows the component scanning support to find and configure your DAOs and repositories without having to provide XML configuration entries for them. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -**@Repository** -public class SomeMovieFinder implements MovieFinder { - // ... -} + **@Repository** + public class SomeMovieFinder implements MovieFinder { + // ... + } ---- Any DAO or repository implementation will need to access to a persistence resource, @@ -22236,38 +22537,38 @@ need access to a JDBC `DataSource`; a JPA-based repository will need access to a injected using one of the `@Autowired,`, `@Inject`, `@Resource` or `@PersistenceContext` annotations. Here is an example for a JPA repository: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Repository -public class JpaMovieFinder implements MovieFinder { + @Repository + public class JpaMovieFinder implements MovieFinder { - @PersistenceContext - private EntityManager entityManager; + @PersistenceContext + private EntityManager entityManager; - // ... + // ... -} + } ---- If you are using the classic Hibernate APIs than you can inject the SessionFactory: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Repository -public class HibernateMovieFinder implements MovieFinder { + @Repository + public class HibernateMovieFinder implements MovieFinder { - private SessionFactory sessionFactory; + private SessionFactory sessionFactory; - @Autowired - public void setSessionFactory(SessionFactory sessionFactory) { - this.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 @@ -22275,22 +22576,22 @@ Last example we will show here is for typical JDBC support. You would have the `JdbcTemplate` and other data access support classes like `SimpleJdbcCall` etc using this `DataSource`. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Repository -public class JdbcMovieFinder implements MovieFinder { + @Repository + public class JdbcMovieFinder implements MovieFinder { - private JdbcTemplate jdbcTemplate; + private JdbcTemplate jdbcTemplate; - @Autowired - public void init(DataSource dataSource) { - this.jdbcTemplate = new JdbcTemplate(dataSource); - } + @Autowired + public void init(DataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } - // ... + // ... -} + } ---- [NOTE] @@ -22377,15 +22678,8 @@ 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. -[NOTE] -==== -Spring 3.0 updates all of the following approaches with Java 5 support such as generics -and varargs. -==== - * __JdbcTemplate__ is the classic Spring JDBC approach and the most popular. This - "lowest level" approach and all others use a JdbcTemplate under the covers, and all - are updated with Java 5 support such as generics and varargs. + "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. @@ -22446,7 +22740,7 @@ exceptions to be propagated to the caller. See <>. [[jdbc-JdbcTemplate]] -==== 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 @@ -22491,64 +22785,64 @@ the attendant Javadocs for that. ====== Querying (SELECT) Here is a simple query for getting the number of rows in a relation: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -int rowCount = this.jdbcTemplate.queryForObject("select count(*) from t_actor", Integer.class); + int rowCount = this.jdbcTemplate.queryForObject("select count(*) from t_actor", Integer.class); ---- A simple query using a bind variable: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -int countOfActorsNamedJoe = this.jdbcTemplate.queryForObject( - "select count(*) from t_actor where first_name = ?", Integer.class, "Joe"); + int countOfActorsNamedJoe = this.jdbcTemplate.queryForObject( + "select count(*) from t_actor where first_name = ?", Integer.class, "Joe"); ---- Querying for a `String`: -[source,java] +[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); + 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] +[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() { - 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; - } - }); + Actor actor = this.jdbcTemplate.queryForObject( + "select first_name, last_name from t_actor where id = ?", + new Object[]{1212L}, + new RowMapper() { + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -List actors = this.jdbcTemplate.query( - "select first_name, last_name from t_actor", - new RowMapper() { - 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; - } - }); + List actors = this.jdbcTemplate.query( + "select first_name, last_name from t_actor", + new RowMapper() { + 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 @@ -22557,22 +22851,22 @@ and extract them out into a single class (typically a `static` inner class) that then be referenced by DAO methods as needed. For example, it may be better to write the last code snippet as follows: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public List findAllActors() { - return this.jdbcTemplate.query( "select first_name, last_name from t_actor", new ActorMapper()); -} + public List findAllActors() { + return this.jdbcTemplate.query( "select first_name, last_name from t_actor", new ActorMapper()); + } -private static final class ActorMapper implements RowMapper { + private static final class ActorMapper implements RowMapper { - 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; - } -} + 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]] @@ -22580,28 +22874,28 @@ private static final class ActorMapper implements RowMapper { 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -this.jdbcTemplate.update( - "insert into t_actor (first_name, last_name) values (?, ?)", - "Leonor", "Watling"); + this.jdbcTemplate.update( + "insert into t_actor (first_name, last_name) values (?, ?)", + "Leonor", "Watling"); ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -this.jdbcTemplate.update( - "update t_actor set last_name = ? where id = ?", - "Banjo", 5276L); + this.jdbcTemplate.update( + "update t_actor set last_name = ? where id = ?", + "Banjo", 5276L); ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -this.jdbcTemplate.update( - "delete from actor where id = ?", - Long.valueOf(actorId)); + this.jdbcTemplate.update( + "delete from actor where id = ?", + Long.valueOf(actorId)); ---- [[jdbc-JdbcTemplate-examples-other]] @@ -22610,26 +22904,26 @@ You can use the `execute(..)` method to execute any arbitrary SQL, and as such t method is often used for DDL statements. It is heavily overloaded with variants taking callback interfaces, binding variable arrays, and so on. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))"); + 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 <>. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -this.jdbcTemplate.update( - "call SUPPORT.REFRESH_ACTORS_SUMMARY(?)", - Long.valueOf(unionId)); + this.jdbcTemplate.update( + "call SUPPORT.REFRESH_ACTORS_SUMMARY(?)", + Long.valueOf(unionId)); ---- [[jdbc-JdbcTemplate-idioms]] -===== JdbcTemplate best practices +===== 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` @@ -22643,50 +22937,50 @@ configure a `DataSource` in your Spring configuration file, and then dependency- 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class JdbcCorporateEventDao implements CorporateEventDao { + public class JdbcCorporateEventDao implements CorporateEventDao { - private JdbcTemplate jdbcTemplate; + private JdbcTemplate jdbcTemplate; - public void setDataSource(DataSource dataSource) { - **this.jdbcTemplate = new JdbcTemplate(dataSource);** - } + public void setDataSource(DataSource dataSource) { + **this.jdbcTemplate = new JdbcTemplate(dataSource);** + } - // JDBC-backed implementations of the methods on the CorporateEventDao follow... -} + // JDBC-backed implementations of the methods on the CorporateEventDao follow... + } ---- The corresponding configuration might look like this. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - - + + + - - - - - - + + + + + + - + - + ---- An alternative to explicit configuration is to use component-scanning and annotation @@ -22694,56 +22988,57 @@ support for dependency injection. In this case you annotate the class with `@Rep (which makes it a candidate for component-scanning) and annotate the `DataSource` setter method with `@Autowired`. -[source,java] +[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);** - } + **@Repository** + public class JdbcCorporateEventDao implements CorporateEventDao { - // JDBC-backed implementations of the methods on the CorporateEventDao follow... -} + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - + + - - - - - - + + + + + + - + - + ---- -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. +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 @@ -22755,7 +23050,7 @@ configured `JdbcTemplates`. [[jdbc-NamedParameterJdbcTemplate]] -==== NamedParameterJdbcTemplate +==== NamedParameterJdbcTemplate The `NamedParameterJdbcTemplate` class adds support for programming JDBC statements using named parameters, as opposed to programming JDBC statements using only classic @@ -22765,24 +23060,24 @@ section describes only those areas of the `NamedParameterJdbcTemplate` class tha from the `JdbcTemplate` itself; namely, programming JDBC statements using named parameters. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// some JDBC-backed DAO class... -private NamedParameterJdbcTemplate namedParameterJdbcTemplate; + // some JDBC-backed DAO class... + private NamedParameterJdbcTemplate namedParameterJdbcTemplate; -public void setDataSource(DataSource dataSource) { - this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); -} + public void setDataSource(DataSource dataSource) { + this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); + } -public int countOfActorsByFirstName(String firstName) { + public int countOfActorsByFirstName(String firstName) { - String sql = "select count(*) from T_ACTOR where first_name = :first_name"; + String sql = "select count(*) from T_ACTOR where first_name = :first_name"; - SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName); + SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName); - return this.namedParameterJdbcTemplate.queryForObject(sql, Integer.class, namedParameters); -} + return this.namedParameterJdbcTemplate.queryForObject(sql, Integer.class, namedParameters); + } ---- Notice the use of the named parameter notation in the value assigned to the `sql` @@ -22796,30 +23091,30 @@ methods exposed by the `NamedParameterJdbcOperations` and implemented by the The following example shows the use of the `Map`-based style. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// some JDBC-backed DAO class... -private NamedParameterJdbcTemplate namedParameterJdbcTemplate; + // some JDBC-backed DAO class... + private NamedParameterJdbcTemplate namedParameterJdbcTemplate; -public void setDataSource(DataSource dataSource) { - this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); -} + public void setDataSource(DataSource dataSource) { + this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); + } -public int countOfActorsByFirstName(String firstName) { + public int countOfActorsByFirstName(String firstName) { - String sql = "select count(*) from T_ACTOR where first_name = :first_name"; + String sql = "select count(*) from T_ACTOR where first_name = :first_name"; - Map namedParameters = Collections.singletonMap("first_name", firstName); + Map namedParameters = Collections.singletonMap("first_name", firstName); - return this.namedParameterJdbcTemplate.queryForObject(sql, Integer.class, namedParameters); -} + return this.namedParameterJdbcTemplate.queryForObject(sql, Integer.class, namedParameters); + } ---- 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 +`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. @@ -22830,52 +23125,51 @@ adheres to http://java.sun.com/products/javabeans/docs/spec.html[the JavaBean conventions]), and uses the properties of the wrapped JavaBean as the source of named parameter values. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class Actor { + public class Actor { - private Long id; - private String firstName; - private String lastName; + private Long id; + private String firstName; + private String lastName; - public String getFirstName() { - return this.firstName; - } + public String getFirstName() { + return this.firstName; + } - public String getLastName() { - return this.lastName; - } + public String getLastName() { + return this.lastName; + } - public Long getId() { - return this.id; - } + public Long getId() { + return this.id; + } - // setters omitted... + // setters omitted... -} + } ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// some JDBC-backed DAO class... -private NamedParameterJdbcTemplate namedParameterJdbcTemplate; + // some JDBC-backed DAO class... + private NamedParameterJdbcTemplate namedParameterJdbcTemplate; -public void setDataSource(DataSource dataSource) { - this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); -} + public void setDataSource(DataSource dataSource) { + this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); + } -public int countOfActors(Actor exampleActor) { + 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"; + // 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); + SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(exampleActor); - return this.namedParameterJdbcTemplate.queryForObject(sql, Integer.class, namedParameters); -} + return this.namedParameterJdbcTemplate.queryForObject(sql, Integer.class, namedParameters); + } ---- Remember that the `NamedParameterJdbcTemplate` class __wraps__ a classic `JdbcTemplate` @@ -22890,7 +23184,7 @@ See also <> for guidelines on using the [[jdbc-SQLExceptionTranslator]] -==== SQLExceptionTranslator +==== SQLExceptionTranslator `SQLExceptionTranslator` is an interface to be implemented by classes that can translate between `SQLExceptions` and Spring's own `org.springframework.dao.DataAccessException`, @@ -22931,18 +23225,18 @@ name from the database metadata of the database in use. You can extend `SQLErrorCodeSQLExceptionTranslator:` -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class CustomSQLErrorCodesTranslator extends SQLErrorCodeSQLExceptionTranslator { + 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; - } -} + 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 @@ -22952,29 +23246,30 @@ translator, it is necessary to pass it to the `JdbcTemplate` through the method processing where this translator is needed. Here is an example of how this custom translator can be used: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -private JdbcTemplate jdbcTemplate; + 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 setDataSource(DataSource dataSource) { -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); -} + // 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 @@ -22989,24 +23284,24 @@ Executing an SQL statement requires very little code. You need a `DataSource` an `JdbcTemplate`. The following example shows what you need to include for a minimal but fully functional class that creates a new table: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import javax.sql.DataSource; -import org.springframework.jdbc.core.JdbcTemplate; + import javax.sql.DataSource; + import org.springframework.jdbc.core.JdbcTemplate; -public class ExecuteAStatement { + public class ExecuteAStatement { - private JdbcTemplate jdbcTemplate; + private JdbcTemplate jdbcTemplate; - public void setDataSource(DataSource dataSource) { - this.jdbcTemplate = new JdbcTemplate(dataSource); - } + public void setDataSource(DataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } - public void doExecute() { - this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))"); - } -} + public void doExecute() { + this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))"); + } + } ---- @@ -23019,32 +23314,32 @@ Java class that is passed in as an argument. If the type conversion is invalid, `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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import javax.sql.DataSource; -import org.springframework.jdbc.core.JdbcTemplate; + import javax.sql.DataSource; + import org.springframework.jdbc.core.JdbcTemplate; -public class RunAQuery { + public class RunAQuery { - private JdbcTemplate jdbcTemplate; + private JdbcTemplate jdbcTemplate; - public void setDataSource(DataSource dataSource) { - this.jdbcTemplate = new JdbcTemplate(dataSource); - } + public void setDataSource(DataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } - public int getCount() { - return this.jdbcTemplate.queryForObject("select count(*) from mytable", Integer.class); - } + 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 String getName() { + return this.jdbcTemplate.queryForObject("select name from mytable", String.class); + } - public void setDataSource(DataSource dataSource) { - this.dataSource = dataSource; - } -} + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + } + } ---- In addition to the single result query methods, several methods return a list with an @@ -23053,23 +23348,23 @@ entry for each row that the query returned. The most generic method is 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -private JdbcTemplate jdbcTemplate; + private JdbcTemplate jdbcTemplate; -public void setDataSource(DataSource dataSource) { - this.jdbcTemplate = new JdbcTemplate(dataSource); -} + public void setDataSource(DataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } -public List> getList() { - return this.jdbcTemplate.queryForList("select * from mytable"); -} + public List> getList() { + return this.jdbcTemplate.queryForList("select * from mytable"); + } ---- The list returned would look something like this: -[source] +[literal] [subs="verbatim,quotes"] ---- [{name=Bob, id=1}, {name=Mary, id=2}] @@ -23084,27 +23379,25 @@ an SQL statement has placeholders for row parameters. The parameter values can b 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import javax.sql.DataSource; + import javax.sql.DataSource; -import org.springframework.jdbc.core.JdbcTemplate; + import org.springframework.jdbc.core.JdbcTemplate; -public class ExecuteAnUpdate { + public class ExecuteAnUpdate { - private JdbcTemplate jdbcTemplate; + private JdbcTemplate jdbcTemplate; - public void setDataSource(DataSource dataSource) { - this.jdbcTemplate = new JdbcTemplate(dataSource); - } + 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); - } -} + public void setName(int id, String name) { + this.jdbcTemplate.update("update mytable set name = ? where id = ?", name, id); + } + } ---- @@ -23120,25 +23413,24 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -final String INSERT_SQL = "insert into my_test (name) values(?)"; -final String name = "Rob"; + 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 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 + // keyHolder.getKey() now contains the generated key ---- @@ -23150,7 +23442,7 @@ jdbcTemplate.update( [[jdbc-datasource]] -==== 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 @@ -23181,29 +23473,29 @@ drivers. (Consult the documentation for your driver for the correct value.) Then 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] +[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(""); + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- - - - - - - + + + + + + - + ---- The following examples show the basic connectivity and configuration for DBCP and C3P0. @@ -23212,40 +23504,38 @@ documentation for the respective connection pooling implementations. DBCP configuration: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- - - - - - - + + + + + + - + ---- C3P0 configuration: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- - - - - - - + + + + + + - + ---- [[jdbc-DataSourceUtils]] -==== 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 @@ -23254,7 +23544,7 @@ supports thread-bound connections with, for example, `DataSourceTransactionManag [[jdbc-SmartDataSource]] -==== 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 @@ -23264,7 +23554,7 @@ operation. This usage is efficient when you know that you will reuse a connectio [[jdbc-AbstractDataSource]] -==== AbstractDataSource +==== AbstractDataSource `AbstractDataSource` is an `abstract` base class for Spring's `DataSource` implementations that implements code that is common to all `DataSource` implementations. @@ -23274,7 +23564,7 @@ implementation. [[jdbc-SingleConnectionDataSource]] -==== SingleConnectionDataSource +==== SingleConnectionDataSource The `SingleConnectionDataSource` class is an implementation of the `SmartDataSource` interface that wraps a __single__ `Connection` that is __not__ closed after each use. @@ -23293,7 +23583,7 @@ excessive creation of physical connections. [[jdbc-DriverManagerDataSource]] -==== 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 @@ -23310,7 +23600,7 @@ environment, that it is almost always preferable to use such a connection pool o [[jdbc-TransactionAwareDataSourceProxy]] -==== 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 @@ -23331,7 +23621,7 @@ __(See the `TransactionAwareDataSourceProxy` Javadocs for more details.)__ [[jdbc-DataSourceTransactionManager]] -==== DataSourceTransactionManager +==== DataSourceTransactionManager The `DataSourceTransactionManager` class is a `PlatformTransactionManager` implementation for single JDBC datasources. It binds a JDBC connection from the @@ -23400,42 +23690,41 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class JdbcActorDao implements ActorDao { - private JdbcTemplate jdbcTemplate; + public class JdbcActorDao implements ActorDao { + private JdbcTemplate jdbcTemplate; - public void setDataSource(DataSource dataSource) { - this.jdbcTemplate = new JdbcTemplate(dataSource); - } + public void setDataSource(DataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } - public int[] batchUpdate(final List 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[] batchUpdate(final List 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; - } + public int getBatchSize() { + return actors.size(); + } + }); + return updateCounts; + } - // ... additional methods -} + // ... 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 +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. @@ -23453,69 +23742,69 @@ of JavaBeans or an array of Maps containing the parameter values. This example shows a batch update using named parameters: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class JdbcActorDao implements ActorDao { - private NamedParameterTemplate namedParameterJdbcTemplate; + public class JdbcActorDao implements ActorDao { + private NamedParameterTemplate namedParameterJdbcTemplate; - public void setDataSource(DataSource dataSource) { - this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); - } + public void setDataSource(DataSource dataSource) { + this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); + } - public int[] batchUpdate(final List 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; - } + public int[] batchUpdate(final List 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 -} + // ... 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. +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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class JdbcActorDao implements ActorDao { - private JdbcTemplate jdbcTemplate; + public class JdbcActorDao implements ActorDao { - public void setDataSource(DataSource dataSource) { - this.jdbcTemplate = new JdbcTemplate(dataSource); - } + private JdbcTemplate jdbcTemplate; - public int[] batchUpdate(final List actors) { - List batch = new ArrayList(); - 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; - } + public void setDataSource(DataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } - // ... additional methods -} + public int[] batchUpdate(final List actors) { + List batch = new ArrayList(); + 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. +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. @@ -23532,46 +23821,45 @@ update calls into batches of the size specified. This example shows a batch update using a batch size of 100: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class JdbcActorDao implements ActorDao { - private JdbcTemplate jdbcTemplate; + public class JdbcActorDao implements ActorDao { - public void setDataSource(DataSource dataSource) { - this.jdbcTemplate = new JdbcTemplate(dataSource); - } + private JdbcTemplate jdbcTemplate; - public int[][] batchUpdate(final Collection actors) { - int[][] updateCounts = jdbcTemplate.batchUpdate( - "update t_actor set first_name = ?, last_name = ? where id = ?", - actors, - 100, - new ParameterizedPreparedStatementSetter() { - 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()); + public void setDataSource(DataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } - } - } ); - return updateCounts; - } + public int[][] batchUpdate(final Collection actors) { + int[][] updateCounts = jdbcTemplate.batchUpdate( + "update t_actor set first_name = ?, last_name = ? where id = ?", + actors, + 100, + new ParameterizedPreparedStatementSetter() { + 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 -} + // ... 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. +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. @@ -23596,29 +23884,29 @@ Configuration methods for this class follow the "fluid" style that returns the i 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class JdbcActorDao implements ActorDao { - private JdbcTemplate jdbcTemplate; - private SimpleJdbcInsert insertActor; + public class JdbcActorDao implements ActorDao { - public void setDataSource(DataSource dataSource) { - this.jdbcTemplate = new JdbcTemplate(dataSource); - this.insertActor = - new SimpleJdbcInsert(dataSource).withTableName("t_actor"); - } + private JdbcTemplate jdbcTemplate; + private SimpleJdbcInsert insertActor; - public void add(Actor actor) { - Map parameters = new HashMap(3); - parameters.put("id", actor.getId()); - parameters.put("first_name", actor.getFirstName()); - parameters.put("last_name", actor.getLastName()); - insertActor.execute(parameters); - } + public void setDataSource(DataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.insertActor = new SimpleJdbcInsert(dataSource).withTableName("t_actor"); + } - // ... additional methods -} + public void add(Actor actor) { + Map parameters = new HashMap(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 @@ -23635,41 +23923,40 @@ retrieves the auto-generated key and sets it on the new Actor object. When you c the `SimpleJdbcInsert`, in addition to specifying the table name, you specify the name of the generated key column with the `usingGeneratedKeyColumns` method. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class JdbcActorDao implements ActorDao { - private JdbcTemplate jdbcTemplate; - private SimpleJdbcInsert insertActor; + public class JdbcActorDao implements ActorDao { - public void setDataSource(DataSource dataSource) { - this.jdbcTemplate = new JdbcTemplate(dataSource); - this.insertActor = - new SimpleJdbcInsert(dataSource) - .withTableName("t_actor") - .usingGeneratedKeyColumns("id"); - } + private JdbcTemplate jdbcTemplate; + private SimpleJdbcInsert insertActor; - public void add(Actor actor) { - Map parameters = new HashMap(2); - parameters.put("first_name", actor.getFirstName()); - parameters.put("last_name", actor.getLastName()); - Number newId = insertActor.executeAndReturnKey(parameters); - actor.setId(newId.longValue()); - } + public void setDataSource(DataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.insertActor = new SimpleJdbcInsert(dataSource) + .withTableName("t_actor") + .usingGeneratedKeyColumns("id"); + } - // ... additional methods -} + public void add(Actor actor) { + Map parameters = new HashMap(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 - `executeReturningKey` 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 `executeReturningKeyHolder` method. +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 `executeReturningKey` 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 `executeReturningKeyHolder` method. @@ -23678,36 +23965,37 @@ the `executeReturningKeyHolder` method. You can limit the columns for an insert by specifying a list of column names with the `usingColumns` method: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class JdbcActorDao implements ActorDao { - private JdbcTemplate jdbcTemplate; - private SimpleJdbcInsert insertActor; + public class JdbcActorDao implements ActorDao { - 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"); - } + private JdbcTemplate jdbcTemplate; + private SimpleJdbcInsert insertActor; - public void add(Actor actor) { - Map parameters = new HashMap(2); - parameters.put("first_name", actor.getFirstName()); - parameters.put("last_name", actor.getLastName()); - Number newId = insertActor.executeAndReturnKey(parameters); - actor.setId(newId.longValue()); - } + 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"); + } - // ... additional methods -} + public void add(Actor actor) { + Map parameters = new HashMap(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. +The execution of the insert is the same as if you had relied on the metadata to determine +which columns to use. @@ -23720,65 +24008,65 @@ which is a very convenient class if you have a JavaBean-compliant class that con your values. It will use the corresponding getter method to extract the parameter values. Here is an example: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class JdbcActorDao implements ActorDao { - private JdbcTemplate jdbcTemplate; - private SimpleJdbcInsert insertActor; + public class JdbcActorDao implements ActorDao { - public void setDataSource(DataSource dataSource) { - this.jdbcTemplate = new JdbcTemplate(dataSource); - this.insertActor = - new SimpleJdbcInsert(dataSource) - .withTableName("t_actor") - .usingGeneratedKeyColumns("id"); - } + private JdbcTemplate jdbcTemplate; + private SimpleJdbcInsert insertActor; - public void add(Actor actor) { - SqlParameterSource parameters = new BeanPropertySqlParameterSource(actor); - Number newId = insertActor.executeAndReturnKey(parameters); - actor.setId(newId.longValue()); - } + public void setDataSource(DataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.insertActor = new SimpleJdbcInsert(dataSource) + .withTableName("t_actor") + .usingGeneratedKeyColumns("id"); + } - // ... additional methods -} + 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. +Another option is the `MapSqlParameterSource` that resembles a Map but provides a more +convenient `addValue` method that can be chained. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class JdbcActorDao implements ActorDao { - private JdbcTemplate jdbcTemplate; - private SimpleJdbcInsert insertActor; + public class JdbcActorDao implements ActorDao { - public void setDataSource(DataSource dataSource) { - this.jdbcTemplate = new JdbcTemplate(dataSource); - this.insertActor = - new SimpleJdbcInsert(dataSource) - .withTableName("t_actor") - .usingGeneratedKeyColumns("id"); - } + private JdbcTemplate jdbcTemplate; + private SimpleJdbcInsert insertActor; - 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()); - } + public void setDataSource(DataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.insertActor = new SimpleJdbcInsert(dataSource) + .withTableName("t_actor") + .usingGeneratedKeyColumns("id"); + } - // ... additional methods -} + 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. +As you can see, the configuration is the same; only the executing code has to change to +use these alternative input classes. @@ -23792,19 +24080,19 @@ shows a simple procedure that returns only scalar values in `VARCHAR` and `DATE` 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] +[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; + 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` @@ -23818,44 +24106,44 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class JdbcActorDao implements ActorDao { - private JdbcTemplate jdbcTemplate; - private SimpleJdbcCall procReadActor; + public class JdbcActorDao implements ActorDao { - public void setDataSource(DataSource dataSource) { - this.jdbcTemplate = new JdbcTemplate(dataSource); - this.procReadActor = - new SimpleJdbcCall(dataSource) - .withProcedureName("read_actor"); - } + private JdbcTemplate jdbcTemplate; + private SimpleJdbcCall procReadActor; - 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; - } + public void setDataSource(DataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.procReadActor = new SimpleJdbcCall(dataSource) + .withProcedureName("read_actor"); + } - // ... additional methods -} + 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 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 @@ -23873,26 +24161,27 @@ the `setResultsMapCaseInsensitive` property to `true`. Then you pass this custom the `commons-collections.jar` in your classpath for this to work. Here is an example of this configuration: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class JdbcActorDao implements ActorDao { - private SimpleJdbcCall procReadActor; + public class JdbcActorDao implements ActorDao { - public void setDataSource(DataSource dataSource) { - JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); - jdbcTemplate.setResultsMapCaseInsensitive(true); - this.procReadActor = - new SimpleJdbcCall(jdbcTemplate) - .withProcedureName("read_actor"); - } + private SimpleJdbcCall procReadActor; - // ... additional methods -} + 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. +By taking this action, you avoid conflicts in the case used for the names of your +returned `out` parameters. @@ -23923,35 +24212,34 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class JdbcActorDao implements ActorDao { - private SimpleJdbcCall procReadActor; + public class JdbcActorDao implements ActorDao { - 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) - ); - } + private SimpleJdbcCall procReadActor; - // ... additional methods -} + 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. +The execution and end results of the two examples are the same; this one specifies all +details explicitly rather than relying on metadata. @@ -23963,11 +24251,11 @@ You typically specify the parameter name and SQL type in the constructor. The SQ is specified using the `java.sql.Types` constants. We have already seen declarations like: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -new SqlParameter("in_id", Types.NUMERIC), - new SqlOutParameter("out_first_name", Types.VARCHAR), + 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 @@ -24007,51 +24295,52 @@ named `executeObject` is also available for stored procedures that only have one 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] +[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; + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class JdbcActorDao implements ActorDao { - private JdbcTemplate jdbcTemplate; - private SimpleJdbcCall funcGetActorName; + public class JdbcActorDao implements ActorDao { - 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"); - } + private JdbcTemplate jdbcTemplate; + private SimpleJdbcCall funcGetActorName; - public String getActorName(Long id) { - SqlParameterSource in = new MapSqlParameterSource() - .addValue("in_id", id); - String name = funcGetActorName.executeFunction(String.class, in); - return name; - } + 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"); + } - // ... additional methods -} + 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. +The execute method used returns a `String` containing the return value from the function +call. @@ -24071,48 +24360,47 @@ 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] +[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; + 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. +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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class JdbcActorDao implements ActorDao { - private SimpleJdbcCall procReadAllActors; + public class JdbcActorDao implements ActorDao { - 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)); - } + private SimpleJdbcCall procReadAllActors; - public List getActorsList() { - Map m = procReadAllActors.execute(new HashMap(0)); - return (List) m.get("actors"); - } + 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)); + } - // ... additional methods -} + public List getActorsList() { + Map m = procReadAllActors.execute(new HashMap(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. +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. @@ -24140,7 +24428,7 @@ continue using these classes. [[jdbc-SqlQuery]] -==== 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 @@ -24153,34 +24441,34 @@ mapping rows to Java classes. Other implementations that extend `SqlQuery` are [[jdbc-MappingSqlQuery]] -==== 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class ActorMappingQuery extends MappingSqlQuery { + public class ActorMappingQuery extends MappingSqlQuery { - 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(); - } + 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; - } + @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 @@ -24195,19 +24483,19 @@ as defined in `java.sql.Types`. After you define all parameters, you call the 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -private ActorMappingQuery actorMappingQuery; + private ActorMappingQuery actorMappingQuery; -@Autowired -public void setDataSource(DataSource dataSource) { - this.actorMappingQuery = new ActorMappingQuery(dataSource); -} + @Autowired + public void setDataSource(DataSource dataSource) { + this.actorMappingQuery = new ActorMappingQuery(dataSource); + } -public Customer getCustomer(Long id) { - return actorMappingQuery.findObject(id); -} + 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 @@ -24216,19 +24504,19 @@ method `findObject` with the id as parameter. If we had instead a query that ret 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public List searchForActors(int age, String namePattern) { - List actors = actorSearchMappingQuery.execute(age, namePattern); - return actors; -} + public List searchForActors(int age, String namePattern) { + List actors = actorSearchMappingQuery.execute(age, namePattern); + return actors; + } ---- [[jdbc-SqlUpdate]] -==== 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 @@ -24238,41 +24526,41 @@ subclassed, for example, to add a custom update method, as in the following snip 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import java.sql.Types; + import java.sql.Types; -import javax.sql.DataSource; + import javax.sql.DataSource; -import org.springframework.jdbc.core.SqlParameter; -import org.springframework.jdbc.object.SqlUpdate; + import org.springframework.jdbc.core.SqlParameter; + import org.springframework.jdbc.object.SqlUpdate; -public class UpdateCreditRating extends 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(); - } + 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); - } -} + /** + * @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 +==== StoredProcedure The `StoredProcedure` class is a superclass for object abstractions of RDBMS stored procedures. This class is `abstract`, and its various `execute(..)` methods have @@ -24286,11 +24574,11 @@ of its subclasses. You must specify the parameter name and SQL type in the const like in the following code snippet. The SQL type is specified using the `java.sql.Types` constants. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -new SqlParameter("in_id", Types.NUMERIC), - new SqlOutParameter("out_first_name", Types.VARCHAR), + 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 @@ -24317,86 +24605,86 @@ parameters, but an output parameter is declared as a date type using the class 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import java.sql.Types; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; + import java.sql.Types; + import java.util.Date; + import java.util.HashMap; + import java.util.Map; -import javax.sql.DataSource; + import javax.sql.DataSource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.jdbc.core.SqlOutParameter; -import org.springframework.jdbc.object.StoredProcedure; + import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.jdbc.core.SqlOutParameter; + import org.springframework.jdbc.object.StoredProcedure; -public class StoredProcedureDao { + public class StoredProcedureDao { - private GetSysdateProcedure getSysdate; + private GetSysdateProcedure getSysdate; - @Autowired - public void init(DataSource dataSource) { - this.getSysdate = new GetSysdateProcedure(dataSource); - } + @Autowired + public void init(DataSource dataSource) { + this.getSysdate = new GetSysdateProcedure(dataSource); + } - public Date getSysdate() { - return getSysdate.execute(); - } + public Date getSysdate() { + return getSysdate.execute(); + } - private class GetSysdateProcedure extends StoredProcedure { + private class GetSysdateProcedure extends StoredProcedure { - private static final String SQL = "sysdate"; + private static final String SQL = "sysdate"; - public GetSysdateProcedure(DataSource dataSource) { - setDataSource(dataSource); - setFunction(true); - setSql(SQL); - declareParameter(new SqlOutParameter("date", Types.DATE)); - compile(); - } + 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 results = execute(new HashMap()); - Date sysdate = (Date) results.get("date"); - return sysdate; - } - } + public Date execute() { + // the 'sysdate' sproc has no input parameters, so an empty Map is supplied... + Map results = execute(new HashMap()); + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import oracle.jdbc.OracleTypes; -import org.springframework.jdbc.core.SqlOutParameter; -import org.springframework.jdbc.object.StoredProcedure; + 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; + import javax.sql.DataSource; + import java.util.HashMap; + import java.util.Map; -public class TitlesAndGenresStoredProcedure extends StoredProcedure { + public class TitlesAndGenresStoredProcedure extends StoredProcedure { - private static final String SPROC_NAME = "AllTitlesAndGenres"; + 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 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 execute() { - // again, this sproc has no input parameters, so an empty Map is supplied - return super.execute(new HashMap()); - } -} + public Map execute() { + // again, this sproc has no input parameters, so an empty Map is supplied + return super.execute(new HashMap()); + } + } ---- Notice how the overloaded variants of the `declareParameter(..)` method that have been @@ -24407,46 +24695,46 @@ functionality. The code for the two `RowMapper` implementations is provided belo The `TitleMapper` class maps a `ResultSet` to a `Title` domain object for each row in the supplied `ResultSet`: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import org.springframework.jdbc.core.RowMapper; + import org.springframework.jdbc.core.RowMapper; -import java.sql.ResultSet; -import java.sql.SQLException; + import java.sql.ResultSet; + import java.sql.SQLException; -import com.foo.domain.Title; + import com.foo.domain.Title; -public final class TitleMapper implements RowMapper { + 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; - } -} + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import org.springframework.jdbc.core.RowMapper; + import org.springframework.jdbc.core.RowMapper; -import java.sql.ResultSet; -import java.sql.SQLException; + import java.sql.ResultSet; + import java.sql.SQLException; -import com.foo.domain.Genre; + import com.foo.domain.Genre; -public final class GenreMapper implements RowMapper<Genre> { + public final class GenreMapper implements RowMapper<Genre> { - public Genre mapRow(ResultSet rs, int rowNum) throws SQLException { - return new Genre(rs.getString("name")); - } -} + 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 @@ -24454,39 +24742,39 @@ definition in the RDBMS, you can code a strongly typed `execute(..)` method that delegate to the superclass' untyped `execute(Map parameters)` method (which has `protected` access); for example: -[source,java] +[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 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 javax.sql.DataSource; -import java.sql.Types; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; + import java.sql.Types; + import java.util.Date; + import java.util.HashMap; + import java.util.Map; -public class TitlesAfterDateStoredProcedure extends StoredProcedure { + public class TitlesAfterDateStoredProcedure extends StoredProcedure { - private static final String SPROC_NAME = "TitlesAfterDate"; - private static final String CUTOFF_DATE_PARAM = "cutoffDate"; + 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 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); - } -} + 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); + } + } ---- @@ -24553,23 +24841,23 @@ For this example we assume that there is a variable, `lobHandle` `r`, that alrea set to an instance of a `DefaultLobHandler`. You typically set this value through dependency injection. -[source,java] +[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(); + 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>> @@ -24584,16 +24872,16 @@ 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] +[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; } }); + 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>> @@ -24644,25 +24932,24 @@ declared type `ITEM_TYPE`. The `SqlReturnType` interface has a single method nam `getTypeValue` that must be implemented. This interface is used as part of the declaration of an `SqlOutParameter`. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -final TestItem = new TestItem(123L, "A test item", - new SimpleDateFormat("yyyy-M-d").parse("2010-12-31")); + 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; - } - })); + 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 @@ -24671,24 +24958,24 @@ stored procedure. The `SqlTypeValue` interface has a single method named can use it to create database-specific objects such as `StructDescriptor` s, as shown in the following example, or `ArrayDescriptor` s. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -final TestItem = new TestItem(123L, "A test item", - new SimpleDateFormat("yyyy-M-d").parse("2010-12-31")); + 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; - } -}; + 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 @@ -24699,18 +24986,18 @@ procedure. Oracle has its own internal `ARRAY` class that must be used in this c you can use the `SqlTypeValue` to create an instance of the Oracle `ARRAY` and populate it with values from the Java `ARRAY`. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -final Long[] ids = new Long[] {1L, 2L}; + 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; - } -}; + 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; + } + }; ---- @@ -24739,13 +25026,13 @@ testability, and the ability to rapidly evolve SQL during development. 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] +[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> + <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 @@ -24761,13 +25048,13 @@ The `EmbeddedDatabaseBuilder` class provides a fluent API for constructing an em 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] +[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() + 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() ---- @@ -24816,32 +25103,32 @@ attribute of the `embedded-database` tag to `Derby`. If using the builder API, c 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class DataAccessUnitTestTemplate { + public class DataAccessUnitTestTemplate { - private EmbeddedDatabase db; + 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(); - } + @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(...); - } + @Test + public void testDataAccess() { + JdbcTemplate template = new JdbcTemplate(db); + template.query(...); + } - @After - public void tearDown() { - db.shutdown(); - } + @After + public void tearDown() { + db.shutdown(); + } -} + } ---- @@ -24861,13 +25148,13 @@ an instance running on a server somewhere. 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] +[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> + <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 @@ -24888,25 +25175,25 @@ namespace provides a couple more options. The first is flag to switch the initia 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<jdbc:initialize-database data-source="dataSource" - **enabled="#{systemProperties.INITIALIZE_DATABASE}"**> - <jdbc:script location="..."/> -</jdbc:initialize-database> + <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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<jdbc:initialize-database data-source="dataSource" **ignore-failures="DROPS"**> - <jdbc:script location="..."/> -</jdbc:initialize-database> + <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 @@ -24981,8 +25268,8 @@ The second option can also be easy. Some suggestions on how to implement this ar [[orm-introduction]] === Introduction to ORM with Spring -The Spring Framework supports integration with Hibernate, Java Persistence API (JPA), -Java Data Objects (JDO) and iBATIS SQL Maps for resource management, data access object +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 @@ -25018,14 +25305,14 @@ Benefits of using the Spring Framework to create your ORM DAOs include: 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, iBATIS SQL Maps configuration objects, 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. + 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 @@ -25039,8 +25326,14 @@ Benefits of using the Spring Framework to create your ORM DAOs include: 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. -__TODO: provide links to current samples__ - +[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 http://spring.io provides a great introduction. +==== @@ -25105,28 +25398,28 @@ be acceptable to applications that are strongly ORM-based and/or do not need any exception treatment. However, Spring enables exception translation to be applied transparently through the `@Repository` annotation: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Repository -public class ProductDaoImpl implements ProductDao { + @Repository + public class ProductDaoImpl implements ProductDao { - // class body here... + // class body here... -} + } ---- -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<beans> + <beans> - <!-- Exception translation bean post processor --> - <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/> + <!-- Exception translation bean post processor --> + <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/> - <bean id="myProductDao" class="product.ProductDaoImpl"/> + <bean id="myProductDao" class="product.ProductDaoImpl"/> -</beans> + </beans> ---- The postprocessor automatically looks for all exception translators (implementations of @@ -25153,13 +25446,13 @@ chapter will then cover the other ORM technologies, showing briefer examples the [NOTE] ==== -As of Spring 3.0, Spring requires Hibernate 3.2 or later. +As of Spring 4.0, Spring requires Hibernate 3.6 or later. ==== [[orm-session-factory-setup]] -==== SessionFactory setup in a Spring container +==== 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 @@ -25170,47 +25463,45 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<beans> + <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="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> + <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> + </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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<beans> - - <jee:jndi-lookup id="myDataSource" jndi-name="java:comp/env/jdbc/myds"/> - -</beans> + <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 @@ -25226,24 +25517,24 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class ProductDaoImpl implements ProductDao { + public class ProductDaoImpl implements ProductDao { - private SessionFactory sessionFactory; + private SessionFactory sessionFactory; - public void setSessionFactory(SessionFactory sessionFactory) { - this.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(); - } -} + 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, @@ -25258,16 +25549,16 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<beans> + <beans> - <bean id="myProductDao" class="product.ProductDaoImpl"> - <property name="sessionFactory" ref="mySessionFactory"/> - </bean> + <bean id="myProductDao" class="product.ProductDaoImpl"> + <property name="sessionFactory" ref="mySessionFactory"/> + </bean> -</beans> + </beans> ---- The main advantage of this DAO style is that it depends on Hibernate API only; no import @@ -25316,71 +25607,71 @@ changed in a configuration file and do not affect the business service implement The following example shows how you can configure an AOP transaction interceptor, using XML, for a simple service class: -[source,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: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"> + <?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 --> + <!-- SessionFactory, DataSource, etc. omitted --> - <bean id="transactionManager" - class="org.springframework.orm.hibernate3.HibernateTransactionManager"> - <property name="sessionFactory" ref="sessionFactory"/> - </bean> + <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> + <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> + <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> + <bean id="myProductService" class="product.SimpleProductService"> + <property name="productDao" ref="myProductDao"/> + </bean> -</beans> + </beans> ---- This is the service class that is advised: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class ProductServiceImpl implements ProductService { + public class ProductServiceImpl implements ProductService { - private ProductDao productDao; + private ProductDao productDao; - public void setProductDao(ProductDao productDao) { - this.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); - // ... - } -} + // 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 @@ -25388,29 +25679,29 @@ annotate the service layer with @Transactional annotations and instruct the Spri container to find these annotations and provide transactional semantics for these annotated methods. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class ProductServiceImpl implements ProductService { + public class ProductServiceImpl implements ProductService { - private ProductDao productDao; + private ProductDao productDao; - public void setProductDao(ProductDao productDao) { - this.productDao = productDao; - } + public void setProductDao(ProductDao productDao) { + this.productDao = productDao; + } - @Transactional - public void increasePriceOfAllProductsInCategory(final String category) { - List productsToChange = this.productDao.loadProductsByCategory(category); - // ... - } + @Transactional + public void increasePriceOfAllProductsInCategory(final String category) { + List productsToChange = this.productDao.loadProductsByCategory(category); + // ... + } - @Transactional(readOnly = true) - public List<Product> findAllProducts() { - return this.productDao.findAllProducts(); - } + @Transactional(readOnly = true) + public List<Product> findAllProducts() { + return this.productDao.findAllProducts(); + } -} + } ---- As you can see from the following configuration example, the configuration is much @@ -25418,36 +25709,36 @@ 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] +[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"> + <?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 --> + <!-- SessionFactory, DataSource, etc. omitted --> - <bean id="transactionManager" - class="org.springframework.orm.hibernate3.HibernateTransactionManager"> - <property name="sessionFactory" ref="sessionFactory"/> - </bean> + <bean id="transactionManager" + class="org.springframework.orm.hibernate3.HibernateTransactionManager"> + <property name="sessionFactory" ref="sessionFactory"/> + </bean> - <tx:annotation-driven/> + <tx:annotation-driven/> - <bean id="myProductService" class="product.SimpleProductService"> - <property name="productDao" ref="myProductDao"/> - </bean> + <bean id="myProductService" class="product.SimpleProductService"> + <property name="productDao" ref="myProductDao"/> + </bean> -</beans> + </beans> ---- @@ -25463,50 +25754,48 @@ as a bean reference through a `setTransactionManager(..)` method, just as the a transaction manager and a business service definition in a Spring application context, and an example for a business method implementation: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<beans> + <beans> - <bean id="myTxManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> - <property name="sessionFactory" ref="mySessionFactory"/> - </bean> + <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> + <bean id="myProductService" class="product.ProductServiceImpl"> + <property name="transactionManager" ref="myTxManager"/> + <property name="productDao" ref="myProductDao"/> + </bean> -</beans> + </beans> ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class ProductServiceImpl implements ProductService { + public class ProductServiceImpl implements ProductService { - private TransactionTemplate transactionTemplate; - private ProductDao productDao; + private TransactionTemplate transactionTemplate; + private ProductDao productDao; - public void setTransactionManager(PlatformTransactionManager transactionManager) { - this.transactionTemplate = new TransactionTemplate(transactionManager); - } + public void setTransactionManager(PlatformTransactionManager transactionManager) { + this.transactionTemplate = new TransactionTemplate(transactionManager); + } - public void setProductDao(ProductDao productDao) { - this.productDao = productDao; - } + 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... - } - } - ); - } -} + 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 @@ -25527,7 +25816,7 @@ handling to a `PlatformTransactionManager` instance, which can be a 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 +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. @@ -25540,76 +25829,76 @@ sources are transactional container ones, a business service can demarcate trans 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<beans> + <beans> - <jee:jndi-lookup id="dataSource1" jndi-name="java:comp/env/jdbc/myds1"/> + <jee:jndi-lookup id="dataSource1" jndi-name="java:comp/env/jdbc/myds1"/> - <jee:jndi-lookup id="dataSource2" jndi-name="java:comp/env/jdbc/myds2"/> + <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="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="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="myTxManager" class="org.springframework.transaction.jta.JtaTransactionManager"/> - <bean id="myProductDao" class="product.ProductDaoImpl"> - <property name="sessionFactory" ref="mySessionFactory1"/> - </bean> + <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="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> + <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> + <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> + <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> + </beans> ---- Both `HibernateTransactionManager` and `JtaTransactionManager` allow for proper @@ -25684,11 +25973,11 @@ These warnings or exceptions indicate that the connection being accessed is no l 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: -[source] +[literal] [subs="verbatim,quotes"] ---- -java.sql.SQLException: The transaction is no longer active - status: 'Committed'. - No further JDBC access is allowed within this transaction. +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 @@ -25716,7 +26005,7 @@ When Hibernate is not configured with any awareness of the JTA * 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. + 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, @@ -25737,7 +26026,7 @@ following events occur when a JTA transaction commits: 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 + through an __afterCompletion__ callback by the JTA transaction manager, and can properly clear its cache. @@ -25752,21 +26041,21 @@ same style as the Hibernate support. The corresponding integration classes resid [[orm-jdo-setup]] -==== PersistenceManagerFactory setup +==== PersistenceManagerFactory setup Spring provides a `LocalPersistenceManagerFactoryBean` class that allows you to define a local JDO `PersistenceManagerFactory` within a Spring application context: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<beans> + <beans> - <bean id="myPmf" class="org.springframework.orm.jdo.LocalPersistenceManagerFactoryBean"> - <property name="configLocation" value="classpath:kodo.properties"/> - </bean> + <bean id="myPmf" class="org.springframework.orm.jdo.LocalPersistenceManagerFactoryBean"> + <property name="configLocation" value="classpath:kodo.properties"/> + </bean> -</beans> + </beans> ---- Alternatively, you can set up a `PersistenceManagerFactory` through direct instantiation @@ -25778,24 +26067,24 @@ setup style usually supports a Spring-defined JDBC `DataSource`, passed into the DataNucleus (formerly JPOX) ( http://www.datanucleus.org/[http://www.datanucleus.org/]), this is the XML configuration of the `PersistenceManagerFactory` implementation: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<beans> + <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="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> + <bean id="myPmf" class="org.datanucleus.jdo.JDOPersistenceManagerFactory" destroy-method="close"> + <property name="connectionFactory" ref="dataSource"/> + <property name="nontransactionalRead" value="true"/> + </bean> -</beans> + </beans> ---- You can also set up JDO `PersistenceManagerFactory` in the JNDI environment of a Java EE @@ -25814,44 +26103,44 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class ProductDaoImpl implements ProductDao { + public class ProductDaoImpl implements ProductDao { - private PersistenceManagerFactory persistenceManagerFactory; + private PersistenceManagerFactory persistenceManagerFactory; - public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) { - this.persistenceManagerFactory = pmf; - } + 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(); - } - } -} + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<beans> + <beans> - <bean id="myProductDao" class="product.ProductDaoImpl"> - <property name="persistenceManagerFactory" ref="myPmf"/> - </bean> + <bean id="myProductDao" class="product.ProductDaoImpl"> + <property name="persistenceManagerFactory" ref="myPmf"/> + </bean> -</beans> + </beans> ---- The main problem with such DAOs is that they always get a new `PersistenceManager` from @@ -25860,21 +26149,21 @@ the factory. To access a Spring-managed transactional `PersistenceManager`, defi your target `PersistenceManagerFactory`, then passing a reference to that proxy into your DAOs as in the following example: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<beans> + <beans> - <bean id="myPmfProxy" - class="org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy"> - <property name="targetPersistenceManagerFactory" ref="myPmf"/> - </bean> + <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> + <bean id="myProductDao" class="product.ProductDaoImpl"> + <property name="persistenceManagerFactory" ref="myPmfProxy"/> + </bean> -</beans> + </beans> ---- Your data access code will receive a transactional `PersistenceManager` (if any) from @@ -25888,46 +26177,46 @@ active transaction synchronization), it is safe to omit the `PersistenceManager. call and thus the entire `finally` block, which you might do to keep your DAO implementations concise: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class ProductDaoImpl implements ProductDao { + public class ProductDaoImpl implements ProductDao { - private PersistenceManagerFactory persistenceManagerFactory; + private PersistenceManagerFactory persistenceManagerFactory; - public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) { - this.persistenceManagerFactory = pmf; - } + 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); - } -} + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<beans> + <beans> - <bean id="myPmfProxy" - class="org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy"> - <property name="targetPersistenceManagerFactory" ref="myPmf"/> - <property name="allowCreate" value="false"/> - </bean> + <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> + <bean id="myProductDao" class="product.ProductDaoImpl"> + <property name="persistenceManagerFactory" ref="myPmfProxy"/> + </bean> -</beans> + </beans> ---- The main advantage of this DAO style is that it depends on JDO API only; no import of @@ -25959,46 +26248,45 @@ 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] +[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"> + <?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="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> + <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> + <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> + <aop:config> + <aop:pointcut id="productServiceMethods" + expression="execution(* product.ProductService.*(..))"/> + <aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/> + </aop:config> -</beans> + </beans> ---- JDO requires an active transaction to modify a persistent object. The non-transactional @@ -26018,7 +26306,7 @@ JDO 2.0 implementations by default. [[orm-jdo-dialect]] -==== JdoDialect +==== 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, @@ -26059,7 +26347,7 @@ that will be used by the application to obtain an entity manager. [[orm-jpa-setup-lemfb]] -===== LocalEntityManagerFactoryBean +===== LocalEntityManagerFactoryBean [NOTE] ==== @@ -26073,16 +26361,14 @@ factory bean uses the JPA `PersistenceProvider` autodetection mechanism (accordi JPA's Java SE bootstrapping) and, in most cases, requires you to specify only the persistence unit name: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<beans> - - <bean id="myEmf" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean"> - <property name="persistenceUnitName" value="myPersistenceUnit"/> - </bean> - -</beans> + <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 @@ -26106,14 +26392,12 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<beans> - - <jee:jndi-lookup id="myEmf" jndi-name="persistence/myPersistenceUnit"/> - -</beans> + <beans> + <jee:jndi-lookup id="myEmf" jndi-name="persistence/myPersistenceUnit"/> + </beans> ---- This action assumes standard Java EE 5 bootstrapping: the Java EE server autodetects @@ -26136,7 +26420,7 @@ application uses to refer to them, for example, in `@PersistenceUnit` and [[orm-jpa-setup-lcemfb]] -===== When is load-time weaving required? LocalContainerEntityManagerFactoryBean +===== LocalContainerEntityManagerFactoryBean [NOTE] ==== @@ -26154,34 +26438,30 @@ possible to work with custom data sources outside of JNDI and to control the wea process. The following example shows a typical bean definition for a `LocalContainerEntityManagerFactoryBean`: -[source,xml] +[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> + <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] +[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> + <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] @@ -26212,6 +26492,7 @@ application jar files. Because the Java EE 5 server only looks for default 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 @@ -26221,9 +26502,9 @@ 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 a Java 5 -http://java.sun.com/j2se/1.5.0/docs/api/java/lang/instrument/package-summary.html[agent] +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. @@ -26244,26 +26525,26 @@ setting up a load-time weaver, delivering autodetection of the platform (WebLogi GlassFish, Tomcat, Resin, JBoss or VM agent) and automatic propagation of the weaver to all weaver-aware beans: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<context:load-time-weaver/> -<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> - ... -</bean> + <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] +[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> + <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 @@ -26282,31 +26563,31 @@ expensive. The default implementation allows multiple locations to be specified parsed and later retrieved through the persistence unit name. (By default, the classpath is searched for `META-INF/persistence.xml` files.) -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<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="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> + <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, @@ -26335,49 +26616,49 @@ using an injected `EntityManagerFactory` or `EntityManager`. Spring can understa if a `PersistenceAnnotationBeanPostProcessor` is enabled. A plain JPA DAO implementation using the `@PersistenceUnit` annotation might look like this: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class ProductDaoImpl implements ProductDao { + public class ProductDaoImpl implements ProductDao { - private EntityManagerFactory emf; + private EntityManagerFactory emf; - @PersistenceUnit - public void setEntityManagerFactory(EntityManagerFactory emf) { - this.emf = 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(); - } - } - } -} + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<beans> + <beans> - <!-- bean post-processor for JPA annotations --> - <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/> + <!-- bean post-processor for JPA annotations --> + <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/> - <bean id="myProductDao" class="product.ProductDaoImpl"/> + <bean id="myProductDao" class="product.ProductDaoImpl"/> -</beans> + </beans> ---- As an alternative to defining a `PersistenceAnnotationBeanPostProcessor` explicitly, @@ -26386,17 +26667,17 @@ context configuration. Doing so automatically registers all Spring standard post-processors for annotation-based configuration, including `CommonAnnotationBeanPostProcessor` and so on. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<beans> + <beans> - <!-- post-processors for all standard config annotations --> - <context:annotation-config/> + <!-- post-processors for all standard config annotations --> + <context:annotation-config/> - <bean id="myProductDao" class="product.ProductDaoImpl"/> + <bean id="myProductDao" class="product.ProductDaoImpl"/> -</beans> + </beans> ---- The main problem with such a DAO is that it always creates a new `EntityManager` through @@ -26404,20 +26685,20 @@ the factory. You can avoid this by requesting a transactional `EntityManager` (a 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class ProductDaoImpl implements ProductDao { + public class ProductDaoImpl implements ProductDao { - @PersistenceContext - private EntityManager em; + @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(); - } -} + 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 @@ -26468,44 +26749,44 @@ 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] +[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"> + <?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="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> + <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> + <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> + <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> + </beans> ---- Spring JPA allows a configured `JpaTransactionManager` to expose a JPA transaction to @@ -26608,17 +26889,17 @@ Spring abstracts all marshalling operations behind the `org.springframework.oxm.Marshaller` interface, the main methods of which is listed below. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface Marshaller { + public interface Marshaller { - /** - * Marshals the object graph with the given root into the provided Result. - */ - void marshal(Object graph, Result result) - throws XmlMappingException, IOException; -} + /** + * 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 @@ -26628,7 +26909,7 @@ representations, as indicated in the table below. [[oxm-marshller-tbl]] |=== -| Result implementation| Wraps XML representation +| Result implementation| Wraps XML representation | `DOMResult` | `org.w3c.dom.Node` @@ -26656,28 +26937,26 @@ to determine how your O/X technology of choice manages this. Similar to the `Marshaller`, there is the `org.springframework.oxm.Unmarshaller` interface. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface Unmarshaller { + public interface Unmarshaller { - /** - * Unmarshals the given provided Source into an object graph. - */ - Object unmarshal(Source source) - throws XmlMappingException, IOException; -} + /** + * 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. +`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 +| Source implementation| Wraps XML representation | `DOMSource` | `org.w3c.dom.Node` @@ -26707,7 +26986,7 @@ provide a distinction between marshalling and unmarshalling operations, even tho 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[] +image::images/oxm-exceptions.png[width=400] O/X Mapping exception hierarchy @@ -26720,20 +26999,21 @@ Spring's OXM can be used for a wide variety of situations. In the following exam 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class Settings { - private boolean fooEnabled; + public class Settings { - public boolean isFooEnabled() { - return fooEnabled; - } + private boolean fooEnabled; - public void setFooEnabled(boolean fooEnabled) { - this.fooEnabled = 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 @@ -26741,99 +27021,98 @@ 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] +[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 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; + 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 class Application { - public void setMarshaller(Marshaller marshaller) { - this.marshaller = marshaller; - } + private static final String FILE_NAME = "settings.xml"; + private Settings settings = new Settings(); + private Marshaller marshaller; + private Unmarshaller unmarshaller; - public void setUnmarshaller(Unmarshaller unmarshaller) { - this.unmarshaller = unmarshaller; - } + public void setMarshaller(Marshaller marshaller) { + this.marshaller = marshaller; + } - 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 setUnmarshaller(Unmarshaller unmarshaller) { + this.unmarshaller = unmarshaller; + } - 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 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 static void main(String[] args) throws IOException { - ApplicationContext appContext = - new ClassPathXmlApplicationContext("applicationContext.xml"); - Application application = (Application) appContext.getBean("application"); - application.saveSettings(); - application.loadSettings(); - } -} + 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] +[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> + <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 +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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<?xml version="1.0" encoding="UTF-8"?> -<settings foo-enabled="false"/> + <?xml version="1.0" encoding="UTF-8"?> + <settings foo-enabled="false"/> ---- @@ -26845,13 +27124,13 @@ Marshallers could be configured more concisely using tags from the OXM namespace 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] +[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"**> + <?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: @@ -26864,10 +27143,10 @@ Currently, the following tags are available: 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<oxm:jaxb2-marshaller id="marshaller" contextPath="org.springframework.ws.samples.airline.schema"/> + <oxm:jaxb2-marshaller id="marshaller" contextPath="org.springframework.ws.samples.airline.schema"/> ---- @@ -26895,23 +27174,23 @@ names that contain schema derived classes. It also offers a `classesToBeBound` p 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<beans> + <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> - <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> + </beans> ---- @@ -26920,23 +27199,23 @@ validation is performed by specifying one or more schema resource to the bean, l The `jaxb2-marshaller` tag configures a `org.springframework.oxm.jaxb.Jaxb2Marshaller`. Here is an example: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<oxm:jaxb2-marshaller id="marshaller" contextPath="org.springframework.ws.samples.airline.schema"/> + <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] +[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> + <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: @@ -26974,15 +27253,13 @@ integration classes reside in the `org.springframework.oxm.castor` package. As with JAXB, the `CastorMarshaller` implements both the `Marshaller` and `Unmarshaller` interface. It can be wired up as follows: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<beans> - - <bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller" /> - ... - -</beans> + <beans> + <bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller" /> + ... + </beans> ---- @@ -26997,14 +27274,14 @@ XML Mapping]. The mapping can be set using the `mappingLocation` resource property, indicated below with a classpath resource. -[source,xml] +[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> + <beans> + <bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller" > + <property name="mappingLocation" value="classpath:mapping.xml" /> + </bean> + </beans> ---- @@ -27013,10 +27290,10 @@ with a classpath resource. The `castor-marshaller` tag configures a `org.springframework.oxm.castor.CastorMarshaller`. Here is an example: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<oxm:castor-marshaller id="marshaller" mapping-location="classpath:org/springframework/oxm/castor/mapping.xml"/> + <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 @@ -27075,15 +27352,15 @@ web site __]. The Spring-WS integration classes reside in the The `XmlBeansMarshaller` implements both the `Marshaller` and `Unmarshaller` interfaces. It can be configured as follows: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<beans> + <beans> - <bean id="xmlBeansMarshaller" class="org.springframework.oxm.xmlbeans.XmlBeansMarshaller" /> - ... + <bean id="xmlBeansMarshaller" class="org.springframework.oxm.xmlbeans.XmlBeansMarshaller" /> + ... -</beans> + </beans> ---- [NOTE] @@ -27098,10 +27375,10 @@ every `java.lang.Object`. The `xmlbeans-marshaller` tag configures a `org.springframework.oxm.xmlbeans.XmlBeansMarshaller`. Here is an example: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<oxm:xmlbeans-marshaller id="marshaller"/> + <oxm:xmlbeans-marshaller id="marshaller"/> ---- Available attributes are: @@ -27143,16 +27420,15 @@ interface. To operate, it requires the name of the class to marshal in, which yo 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] +[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> + <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 @@ -27165,10 +27441,10 @@ property values. The `jibx-marshaller` tag configures a `org.springframework.oxm.jibx.JibxMarshaller`. Here is an example: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<oxm:jibx-marshaller id="marshaller" target-class="org.springframework.ws.samples.airline.schema.Flight"/> + <oxm:jibx-marshaller id="marshaller" target-class="org.springframework.ws.samples.airline.schema.Flight"/> ---- Available attributes are: @@ -27209,21 +27485,19 @@ The `XStreamMarshaller` does not require any configuration, and can be configure application context directly. To further customize the XML, you can set an__alias map__, which consists of string aliases mapped to classes: -[source,xml] +[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> + <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] @@ -27236,19 +27510,19 @@ 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] +[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> + <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 -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/oxm/xstream/XStreamMarshaller.html#setConverters(com.thoughtworks.xstream.converters.ConverterMatcher...)[custom +{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 @@ -27273,11 +27547,11 @@ This part of the reference documentation covers the Spring Framework's support f presentation tier (and specifically web-based presentation tiers). The Spring Framework's own web framework, <<mvc,Spring Web MVC>>, is covered in the -first couple of chapters. A number of the remaining chapters in this part of the -reference documentation are concerned with the Spring Framework's integration with other -web technologies, such as <<struts,Struts>> and <<jsf,JSF>> (to name but two). +first couple of chapters. Subsequent chapters are concerned with the Spring Framework's +integration with other web technologies, such as <<struts,Struts>> <<jsf,JSF>> and +others. -This section concludes with coverage of Spring's MVC <<portlet,portlet framework>>. +Following that is coverage of Spring's MVC <<portlet,portlet framework>>. * <<mvc>> * <<view>> @@ -27298,8 +27572,8 @@ This section concludes with coverage of Spring's MVC <<portlet,portlet framework === 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 and theme resolution as well as support for uploading -files. The default handler is based on the `@Controller` and `@RequestMapping` +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. @@ -27364,7 +27638,8 @@ SWF allows you to capture logical page flows as self-contained modules that are 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. +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: @@ -27378,7 +27653,7 @@ Spring's web module includes many unique web support features: * __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 +* __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 @@ -27391,8 +27666,8 @@ Spring's web module includes many unique web support features: that mandate a particular technique. * __Flexible model transfer__. Model transfer with a name/value `Map` supports easy integration with any view technology. -* __Customizable locale and theme resolution, support for JSPs with or without Spring - tag library, support for JSTL, support for Velocity without the need for extra +* __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 @@ -27400,7 +27675,7 @@ Spring's web module includes many unique web support features: 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>> + 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 @@ -27413,20 +27688,20 @@ Spring's web module includes many unique web support features: Non-Spring MVC implementations are preferable for some projects. Many teams expect to leverage their existing investment in skills and tools. A large body of knowledge and experience exist for the Struts framework. If you can abide Struts' architectural flaws, -it can be a viable choice for the web layer; the same applies to WebWork and other web +it can be a viable choice for the web layer; the same applies to JSF and other web MVC frameworks. 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 a Struts or WebWork action. No "plug-ins" +Spring's respective helper method) from within a Struts action. 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 Struts or WebWork in this scenario. It +Spring's Web MVC. Spring does not compete with Struts 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 @@ -27450,7 +27725,7 @@ 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[] +image::images/mvc.png[width=400] The request processing workflow in Spring Web MVC (high level) @@ -27460,23 +27735,22 @@ map requests that you want the `DispatcherServlet` to handle, by using a URL map the same `web.xml` file. This is standard Java EE Servlet configuration; the following example shows such a `DispatcherServlet` declaration and mapping: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<web-app> + <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> - <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> - <servlet-mapping> - <servlet-name>example</servlet-name> - <url-pattern>/example/*</url-pattern> - </servlet-mapping> - -</web-app> + </web-app> ---- In the preceding example, all requests starting with `/example` will be handled by the @@ -27484,19 +27758,19 @@ In the preceding example, all requests starting with `/example` will be handled have the option of configuring the Servlet container programmatically. Below is the code based equivalent of the above `web.xml` example: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MyWebApplicationInitializer implements WebApplicationInitializer { + 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/*"); - } + @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 @@ -27504,7 +27778,7 @@ code-based configuration is detected and automatically used to initialize any Se 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. +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 @@ -27517,7 +27791,7 @@ scoped. In the Web MVC framework, each `DispatcherServlet` has its own 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[] +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 @@ -27526,23 +27800,20 @@ the same name in the global scope. Consider the following `DispatcherServlet` Servlet configuration (in the `web.xml` file): -[source,xml] +[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> + <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 @@ -27575,7 +27846,7 @@ see the table below listing the special bean types the `DispatcherServlet` relie |=== | Bean type| Explanation -a| <<mvc-handlermapping,HandlerMapping>> +| <<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 @@ -27587,24 +27858,24 @@ a| <<mvc-handlermapping,HandlerMapping>> requires resolving various annotations. Thus the main purpose of a `HandlerAdapter` is to shield the `DispatcherServlet` from such details. -a| <<mvc-exceptionhandlers,HandlerExceptionResolver>> +| <<mvc-exceptionhandlers,HandlerExceptionResolver>> | Maps exceptions to views also allowing for more complex exception handling code. -a| <<mvc-viewresolver,ViewResolver>> +| <<mvc-viewresolver,ViewResolver>> | Resolves logical String-based view names to actual `View` types. -a| <<mvc-localeresolver,LocaleResolver>> -| Resolves the locale a client is using, in order to be able to offer internationalized - views +| <<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 -a| <<mvc-themeresolver,ThemeResolver>> +| <<mvc-themeresolver,ThemeResolver>> | Resolves themes your web application can use, for example, to offer personalized layouts -a| <<mvc-multipart,MultipartResolver>> +| <<mvc-multipart,MultipartResolver>> | Parses multi-part requests for example to support processing file uploads from HTML forms. -a| <<mvc-flash-attributes,FlashMapManager>> +| <<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. |=== @@ -27617,7 +27888,7 @@ As mentioned in the previous section for each special bean the `DispatcherServle 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 +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. @@ -27718,23 +27989,24 @@ Portlet facilities. [TIP] ==== -Available in the <<new-in-3.0-samples,samples repository>>, a number of web applications -leverage the annotation support described in this section including__MvcShowcase__, -__MvcAjax__, __MvcBasic__, __PetClinic__, __PetCare__, and others. +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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Controller -public class HelloWorldController { + @Controller + public class HelloWorldController { - @RequestMapping("/helloWorld") - public String helloWorld(Model model) { - model.addAttribute("message", "Hello World!"); - return "helloWorld"; - } -} + @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 @@ -27751,7 +28023,7 @@ environment. ==== 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 +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. @@ -27765,28 +28037,28 @@ allows for autodetection, aligned with Spring general support for detecting comp 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 +your configuration. Use the __spring-context__ schema as shown in the following XML snippet: -[source,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: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"> + <?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"/> + <context:component-scan base-package="org.springframework.samples.petclinic.web"/> - <!-- ... --> + <!-- ... --> -</beans> + </beans> ---- @@ -27803,44 +28075,44 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Controller -**@RequestMapping("/appointments")** -public class AppointmentsController { + @Controller + **@RequestMapping("/appointments")** + public class AppointmentsController { - private final AppointmentBook appointmentBook; + private final AppointmentBook appointmentBook; - @Autowired - public AppointmentsController(AppointmentBook appointmentBook) { - this.appointmentBook = appointmentBook; - } + @Autowired + public AppointmentsController(AppointmentBook appointmentBook) { + this.appointmentBook = appointmentBook; + } - **@RequestMapping(method = RequestMethod.GET)** - public Map<String, Appointment> get() { - return appointmentBook.getAppointmentsForToday(); - } + **@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="/{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(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"; - } -} + **@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 @@ -27858,29 +28130,29 @@ A `@RequestMapping` on the class level is not required. Without it, all paths ar absolute, and not relative. The following example from the __PetClinic__ sample application shows a multi-action controller using `@RequestMapping`: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Controller -public class ClinicController { + @Controller + public class ClinicController { - private final Clinic clinic; + private final Clinic clinic; - @Autowired - public ClinicController(Clinic clinic) { - this.clinic = clinic; - } + @Autowired + public ClinicController(Clinic clinic) { + this.clinic = clinic; + } - **@RequestMapping("/")** - public void welcomeHandler() { - } + **@RequestMapping("/")** + public void welcomeHandler() { + } - **@RequestMapping("/vets")** - public ModelMap vetsHandler() { - return new ModelMap(this.clinic.getVets()); - } + **@RequestMapping("/vets")** + public ModelMap vetsHandler() { + return new ModelMap(this.clinic.getVets()); + } -} + } ---- .@RequestMapping On Interface Methods @@ -27962,15 +28234,15 @@ 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] +[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"; -} + @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 @@ -27984,41 +28256,41 @@ the value of `ownerId` is `fred`. 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] +[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 -} + @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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET) -public String findOwner(**@PathVariable** String ownerId, Model model) { - // implementation omitted -} + @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] +[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"; -} + @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 @@ -28028,18 +28300,19 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Controller -@RequestMapping(**"/owners/{ownerId}"**) -public class RelativePathUriTemplateController { + @Controller + @RequestMapping(**"/owners/{ownerId}"**) + public class RelativePathUriTemplateController { - @RequestMapping(**"/pets/{petId}"**) - public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { - // implementation omitted - } -} + @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. @@ -28057,14 +28330,14 @@ The `@RequestMapping` annotation supports the use of regular expressions in URI variables. The syntax is `{varName:regex}` where the first part defines the variable name and the second - the regular expression.For example: -[source,java] +[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) { - // ... - } -} + @RequestMapping("/spring-web/{symbolicName:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{extension:\\.[a-z]+}") + public void handle(@PathVariable String version, @PathVariable String extension) { + // ... + } + } ---- @@ -28105,70 +28378,70 @@ provided. Below is an example of extracting the matrix variable "q": -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// GET /pets/42;q=11;r=22 + // GET /pets/42;q=11;r=22 -@RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET) -public void findPet(@PathVariable String petId, @MatrixVariable int q) { + @RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET) + public void findPet(@PathVariable String petId, @MatrixVariable int q) { - // petId == 42 - // q == 11 + // 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// GET /owners/42;q=11/pets/21;q=22 + // 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) { + @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 + // q1 == 11 + // q2 == 22 -} + } ---- A matrix variable may be defined as optional and a default value specified: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// GET /pets/42 + // GET /pets/42 -@RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET) - public void findPet(@MatrixVariable(required=true, defaultValue="1") int q) { + @RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET) + public void findPet(@MatrixVariable(required=true, defaultValue="1") int q) { - // q == 1 + // q == 1 - } + } ---- All matrix variables may be obtained in a Map: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// GET /owners/42;q=11;r=12/pets/21;q=22;s=23 + // 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) { + @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] + // 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 @@ -28179,17 +28452,17 @@ default it is set to `false`. [[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 +request will be matched only if the __Content-Type__ request header matches the specified media type. For example: -[source,java] +[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 -} + @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 @@ -28207,20 +28480,20 @@ 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 +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__ +used to generate the response respects the media types specified in the __produces__ condition. For example: -[source,java] +[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 -} + @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 @@ -28242,35 +28515,37 @@ You can narrow request matching through request parameter conditions such as parameter presence/absence and the third for a specific parameter value. Here is an example with a request parameter value condition: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Controller -@RequestMapping("/owners/{ownerId}") -public class RelativePathUriTemplateController { + @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 - } -} + @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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Controller -@RequestMapping("/owners/{ownerId}") -public class RelativePathUriTemplateController { + @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 - } -} + @RequestMapping(value = "/pets", method = RequestMethod.GET, **headers="myHeader=myValue"**) + public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { + // implementation omitted + } + + } ---- [TIP] @@ -28381,21 +28656,21 @@ Spring will create a separate `BindingResult` instance for each of them so the f sample won't work: .Invalid ordering of BindingResult and @ModelAttribute -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RequestMapping(method = RequestMethod.POST) -public String processSubmit(**@ModelAttribute("pet") Pet pet**, Model model, **BindingResult result**) { ... } + @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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RequestMapping(method = RequestMethod.POST) -public String processSubmit(**@ModelAttribute("pet") Pet pet**, **BindingResult result**, Model model) { ... } + @RequestMapping(method = RequestMethod.POST) + public String processSubmit(**@ModelAttribute("pet") Pet pet**, **BindingResult result**, Model model) { ... } ---- @@ -28448,23 +28723,26 @@ your controller. The following code snippet shows the usage: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Controller -@RequestMapping("/pets") -@SessionAttributes("pet") -public class EditPetForm { + @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"; - } - // ... + @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 @@ -28480,13 +28758,13 @@ Type conversion is applied automatically if the target method parameter type is 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] +[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); -} + @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`. @@ -28502,38 +28780,36 @@ default `HttpMessageConverters`: 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,Enabling -the MVC Java Config or the MVC XML Namespace>> for more information. +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,Enabling the MVC Java -Config or the MVC XML Namespace>> instead. +the MVC namespace or the MVC Java config see <<mvc-config-enable>> instead. -[source,xml] +[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 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="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="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"/> + <bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller"/> ---- An `@RequestBody` method parameter can be annotated with `@Valid`, in which case it will @@ -28548,7 +28824,7 @@ sends a `400` error back to the client. [NOTE] ==== -Also see <<mvc-config-enable,Enabling the MVC Java Config or the MVC XML Namespace>> for +Also see <<mvc-config-enable>> for information on configuring message converters and a validator through the MVC namespace or the MVC Java config. ==== @@ -28561,14 +28837,14 @@ The `@ResponseBody` annotation is similar to `@RequestBody`. This annotation can 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RequestMapping(value = "/something", method = RequestMethod.PUT) -@ResponseBody -public String helloWorld() { - return "Hello World"; -} + @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 @@ -28580,26 +28856,27 @@ section and <<rest-message-conversion,Message Converters>>. [[mvc-ann-httpentity]] -===== Using 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] +[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 + @RequestMapping("/something") + public ResponseEntity<String> handle(HttpEntity<byte[]> requestEntity) throws UnsupportedEncodingException { + String requestHeader = requestEntity.getHeaders().getFirst("MyRequestHeader")); + byte[] requestBody = requestEntity.getBody(); - HttpHeaders responseHeaders = new HttpHeaders(); - responseHeaders.set("MyResponseHeader", "MyValue"); - return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED); -} + // 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 @@ -28624,25 +28901,25 @@ methods but cannot be mapped directly to requests. Instead `@ModelAttribute` met a controller are invoked before `@RequestMapping` methods, within the same controller. A couple of examples: -[source,java] +[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") + // 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); -} + @ModelAttribute + public Account addAccount(@RequestParam String number) { + return accountManager.findAccount(number); + } -// Add multiple attributes + // Add multiple attributes -@ModelAttribute -public void populateModel(@RequestParam String number, Model model) { - model.addAttribute(accountManager.findAccount(number)); - // add more ... -} + @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 @@ -28659,8 +28936,9 @@ A controller can have any number of `@ModelAttribute` methods. All such methods 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. +and such methods apply to all controllers. The `@ControllerAdvice` annotation is a +component annotation allowing implementation classes to be autodetected through +classpath scanning. [TIP] ==== @@ -28692,11 +28970,11 @@ populated from all request parameters that have matching names. This is known as binding in Spring MVC, a very useful mechanism that saves you from having to parse each form field individually. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST) -public String processSubmit(**@ModelAttribute Pet pet**) { } + @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: @@ -28714,13 +28992,13 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RequestMapping(value="/accounts/{account}", method = RequestMethod.PUT) -public String save(@ModelAttribute("account") Account account) { + @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 @@ -28739,19 +29017,19 @@ As a result of data binding there may be errors such as missing required fields conversion errors. To check for such errors add a `BindingResult` argument immediately following the `@ModelAttribute` argument: -[source,java] +[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) { + @RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST) + public String processSubmit(**@ModelAttribute("pet") Pet pet**, BindingResult result) { - if (result.hasErrors()) { - return "petForm"; - } + if (result.hasErrors()) { + return "petForm"; + } - // ... + // ... -} + } ---- With a `BindingResult` you can check if errors were found in which case it's common to @@ -28763,36 +29041,38 @@ validator passing the same `BindingResult` that was used to record data binding That allows for data binding and validation errors to be accumulated in one place and subsequently reported back to the user: -[source,java] +[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) { + @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"; - } + 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] +[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) { + @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"; - } + if (result.hasErrors()) { + return "petForm"; + } - // ... -} + // ... + + } ---- See <<validation-beanvalidation>> and <<validation>> for details on how to configure and @@ -28810,15 +29090,15 @@ conversational storage, serving as form-backing beans between subsequent request The following code snippet shows the usage of this annotation, specifying the model attribute name: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Controller -@RequestMapping("/editPet.do") -**@SessionAttributes("pet")** -public class EditPetForm { - // ... -} + @Controller + @RequestMapping("/editPet.do") + **@SessionAttributes("pet")** + public class EditPetForm { + // ... + } ---- [NOTE] @@ -28872,23 +29152,23 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<filter> - <filter-name>httpPutFormFilter</filter-name> - <filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class> -</filter> + <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> + <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> + <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 @@ -28912,7 +29192,7 @@ HTTP cookie. Let us consider that the following cookie has been received with an http request: -[source] +[literal] [subs="verbatim,quotes"] ---- JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84 @@ -28920,13 +29200,13 @@ JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84 The following code sample demonstrates how to get the value of the `JSESSIONID` cookie: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RequestMapping("/displayHeaderInfo.do") -public void displayHeaderInfo(**@CookieValue("JSESSIONID")** String cookie) { - //... -} + @RequestMapping("/displayHeaderInfo.do") + public void displayHeaderInfo(**@CookieValue("JSESSIONID")** String cookie) { + //... + } ---- Type conversion is applied automatically if the target method parameter type is not @@ -28942,7 +29222,7 @@ The `@RequestHeader` annotation allows a method parameter to be bound to a reque Here is a sample request header: -[source] +[literal] [subs="verbatim,quotes"] ---- Host localhost:8080 @@ -28956,14 +29236,14 @@ Keep-Alive 300 The following code sample demonstrates how to get the value of the `Accept-Encoding` and `Keep-Alive` headers: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RequestMapping("/displayHeaderInfo.do") -public void displayHeaderInfo(**@RequestHeader("Accept-Encoding")** String encoding, - **@RequestHeader("Keep-Alive")** long keepAlive) { - //... -} + @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 @@ -28998,7 +29278,7 @@ the `FormattingConversionService` (see <<format>>). 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. +`WebBindingInitializer`. [[mvc-ann-initbinder]] ====== Customizing data binding with @InitBinder @@ -29016,21 +29296,22 @@ arguments include `WebDataBinder` in combination with `WebRequest` or The following example demonstrates the use of `@InitBinder` to configure a `CustomDateEditor` for all `java.util.Date` form properties. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Controller -public class MyFormController { + @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)); - } + **@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]] @@ -29046,20 +29327,26 @@ custom implementation of the `WebBindingInitializer` interface, `org.springframework.samples.petclinic.web.ClinicBindingInitializer`, which configures PropertyEditors required by several of the PetClinic controllers. -[source,xml] +[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> + <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> ---- +[[mvc-ann-initbinder-advice]] +====== Customizing data binding with externalized @InitBinder methods + `@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. +which case they apply to all controllers. This provides an alternative to using a +`WebBindingInitializer`. + +The `@ControllerAdvice` annotation is a component annotation allowing implementation +classes to be autodetected through classpath scanning. [[mvc-ann-lastmodified]] @@ -29071,23 +29358,23 @@ request, comparing it against the `'If-Modified-Since'` request header value, an potentially returning a response with status code 304 (Not Modified). An annotated controller method can achieve that as follows: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RequestMapping -public String myHandleMethod(WebRequest webRequest, Model model) { + @RequestMapping + public String myHandleMethod(WebRequest webRequest, Model model) { - long lastModified = // 1. application-specific calculation + long lastModified = // 1. application-specific calculation - if (request.checkNotModified(lastModified)) { - // 2. shortcut exit - no further processing necessary - return null; - } + 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"; -} + // 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 @@ -29095,39 +29382,6 @@ returning `null`. The former sets the response status to 304 before it returns ` The latter, in combination with the former, causes Spring MVC to do no further processing of the request. -[[mvc-ann-controller-advice]] -===== Assisting Controllers with the @ControllerAdvice annotation -The `@ControllerAdvice` annotation is a component annotation allowing implementation -classes to be autodetected 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 those will apply to -`@RequestMapping` methods across 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] -[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 -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/ControllerAdvice.html[@ControllerAdvice -documentation] for more details. [[mvc-ann-async]] ==== Asynchronous Request Processing @@ -29140,19 +29394,20 @@ requests. Spring MVC invokes the `Callable` in a separate thread with the help o Servlet container to resume processing with the value returned by the `Callable`. Here is an example controller method: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RequestMapping(method=RequestMethod.POST) -public Callable<String> processUpload(final MultipartFile file) { + @RequestMapping(method=RequestMethod.POST) + public Callable<String> processUpload(final MultipartFile file) { - return new Callable<String>() { - public String call() throws Exception { - // ... - return "someView"; - } - }; -} + 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 @@ -29161,19 +29416,19 @@ is not known to Spring MVC. For example the result may be produced in response t external event such as a JMS message, a scheduled task, etc. Here is an example controller method: -[source,java] +[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; -} + @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); + // In some other thread... + deferredResult.setResult(data); ---- This may be difficult to understand without any knowledge of the Servlet 3 async @@ -29273,18 +29528,17 @@ namespace. Those interceptors provide a full set of callbacks and apply every ti ====== Servlet 3 Async Config To use Servlet 3 async request processing, you need to update `web.xml` to version 3.0: -[source,xml] +[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"> + <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"> - ... - - </web-app> + ... + </web-app> ---- The `DispatcherServlet` and any `Filter` configuration need to have the @@ -29361,17 +29615,16 @@ properties that you can use to customize their behavior: The following example shows how to configure an interceptor: -[source,xml] +[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> + <beans> + <bean id="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"> + <property name="interceptors"> + <bean class="example.MyInterceptor"/> + </property> + </bean> + <beans> ---- @@ -29401,60 +29654,56 @@ Interceptors can be configured using the `interceptors` property, which is prese all `HandlerMapping` classes extending from `AbstractHandlerMapping`. This is shown in the example below: -[source,xml] +[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> + <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> + <bean id="officeHoursInterceptor" + class="samples.TimeBasedAccessInterceptor"> + <property name="openingTime" value="9"/> + <property name="closingTime" value="18"/> + </bean> + <beans> ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package samples; + package samples; -public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter { + public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter { - private int openingTime; - private int closingTime; + private int openingTime; + private int closingTime; - public void setOpeningTime(int openingTime) { - this.openingTime = openingTime; - } + public void setOpeningTime(int openingTime) { + this.openingTime = openingTime; + } - public void setClosingTime(int closingTime) { - this.closingTime = closingTime; - } + 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; - } else { - response.sendRedirect("http://host.com/outsideOfficeHours.html"); - return false; - } - } -} + 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`. @@ -29476,8 +29725,7 @@ extend the `HandlerInterceptor` interface. 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,Enabling -the MVC Java Config or the MVC XML Namespace>>. +bean instances of type `MappedInterceptor` to do that. See <<mvc-config-enable>>. ==== @@ -29511,7 +29759,7 @@ examples follow. [[mvc-view-resolvers-tbl]] .View resolvers |=== -| ViewResolver| Description +| ViewResolver| Description | `AbstractCachingViewResolver` | Abstract view resolver that caches views. Often views need preparation before they can @@ -29552,15 +29800,15 @@ As an example, with JSP as a view technology, you can use the `UrlBasedViewResol This view resolver translates a view name to a URL and hands the request over to the RequestDispatcher to render the view. -[source,xml] +[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> + <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 @@ -29569,14 +29817,14 @@ 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] +[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> + <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 @@ -29610,25 +29858,25 @@ In the following example, the chain of view resolvers consists of two resolvers, resolver in the chain, and an `XmlViewResolver` for specifying Excel views. Excel views are not supported by the `InternalResourceViewResolver`. -[source,xml] +[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="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> + <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 --> + <!-- in views.xml --> -<beans> - <bean name="report" class="org.springframework.example.ReportExcelView"/> -</beans> + <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 @@ -29704,14 +29952,14 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@RequestMapping(value = "/files/{path}", method = RequestMethod.POST) -public String upload(...) { - // ... - return "redirect:files/{path}"; -} + @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 @@ -29785,7 +30033,7 @@ server: 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: -[source] +[literal] [subs="verbatim,quotes"] ---- Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 @@ -29819,34 +30067,34 @@ request media type, refer to the API documentation for `ContentNegotiatingViewRe Here is an example configuration of a `ContentNegotiatingViewResolver:` -[source,xml] +[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 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.springsource.samples.rest.SampleContentAtomView"/> + <bean id="content" class="com.springsource.samples.rest.SampleContentAtomView"/> ---- The `InternalResourceViewResolver` handles the translation of view names and JSP pages, @@ -29879,23 +30127,23 @@ The corresponding controller code that returns an Atom RSS feed for a URI of the `http://localhost/content.atom` or `http://localhost/content` with an `Accept` header of application/atom+xml is shown below. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Controller -public class ContentController { + @Controller + public class ContentController { - private List<SampleContent> contentList = new ArrayList<SampleContent>(); + 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; - } + @RequestMapping(value="/content", method=RequestMethod.GET) + public ModelAndView getContent() { + ModelAndView mav = new ModelAndView(); + mav.setViewName("content"); + mav.addObject("sampleContentList", contentList); + return mav; + } -} + } ---- @@ -29956,13 +30204,13 @@ Spring MVC provides a mechanism for building and encoding a URI using For example you can expand and encode a URI template string: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -UriComponents uriComponents = - UriComponentsBuilder.fromUriString("http://example.com/hotels/{hotel}/bookings/{booking}").build(); + UriComponents uriComponents = UriComponentsBuilder.fromUriString( + "http://example.com/hotels/{hotel}/bookings/{booking}").build(); -URI uri = uriComponents.expand("42", "21").encode().toUri(); + URI uri = uriComponents.expand("42", "21").encode().toUri(); ---- Note that `UriComponents` is immutable and the `expand()` and `encode()` operations @@ -29970,58 +30218,57 @@ return new instances if necessary. You can also expand and encode using individual URI components: -[source,java] +[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(); + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -HttpServletRequest request = ... + HttpServletRequest request = ... -// Re-use host, scheme, port, path and query string -// Replace the "accountId" query param + // 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(); + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// Re-use host, port and context path -// Append "/accounts" to the path + // Re-use host, port and context path + // Append "/accounts" to the path -ServletUriComponentsBuilder ucb = - ServletUriComponentsBuilder.fromContextPath(request).path("/accounts").build() + 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] +[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 + // 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() + ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromServletMapping(request) + .path("/accounts").build() ---- @@ -30049,12 +30296,25 @@ 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. +the client's operating system. __Note that this resolver does not support time zone +information.__ @@ -30062,21 +30322,21 @@ the client's operating system. ==== CookieLocaleResolver This locale resolver inspects a `Cookie` that might exist on the client to see if a -locale is specified. If so, it uses the specified locale. 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`. +`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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"> + <bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"> - <property name="cookieName" value="clientlanguage"/> + <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"> + <!-- in seconds. If set to -1, the cookie is not persisted (deleted when browser shuts down) --> + <property name="cookieMaxAge" value="100000"> -</bean> + </bean> ---- [[mvc-cookie-locale-resolver-props-tbl]] @@ -30093,7 +30353,7 @@ below an example of defining a `CookieLocaleResolver`. | 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 - his or her browser. + their browser. | cookiePath | / @@ -30106,8 +30366,8 @@ below an example of defining a `CookieLocaleResolver`. [[mvc-localeresolver-session]] ==== SessionLocaleResolver -The `SessionLocaleResolver` allows you to retrieve locales from the session that might -be associated with the user's request. +The `SessionLocaleResolver` allows you to retrieve `Locale` and `TimeZone` from the +session that might be associated with the user's request. @@ -30122,28 +30382,28 @@ containing a parameter named `siteLanguage` will now change the locale. So, for a request for the following URL, `http://www.sf.net/home.view?siteLanguage=nl` will change the site language to Dutch. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<bean id="localeChangeInterceptor" - class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"> - <property name="paramName" value="siteLanguage"/> -</bean> + <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="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> + <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> ---- @@ -30178,7 +30438,7 @@ The web application context automatically detects a bean with that name and uses 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: -[source] +[literal] [subs="verbatim,quotes"] ---- styleSheet=/themes/cool/style.css @@ -30190,18 +30450,18 @@ code. For a JSP, you typically do this using the `spring:theme` custom tag, whic 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] +[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> + <%@ 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, @@ -30273,15 +30533,16 @@ like any other attribute. The following example shows how to use the `CommonsMultipartResolver`: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<bean id="multipartResolver" - class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> + <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> + <!-- 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 @@ -30311,12 +30572,12 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<bean id="multipartResolver" - class="org.springframework.web.multipart.support.StandardServletMultipartResolver"> -</bean> + <bean id="multipartResolver" + class="org.springframework.web.multipart.support.StandardServletMultipartResolver"> + </bean> ---- @@ -30328,48 +30589,48 @@ other. First, create a form with a file input that will allow the user to upload The encoding attribute ( `enctype="multipart/form-data"`) lets the browser know how to encode the form as multipart request: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<html> - <head> - <title>Upload a file please - - -

Please upload a file

-
- - - -
- - + + + Upload a file please + + +

Please upload a file

+
+ + + +
+ + ---- The next step is to create a controller that handles the file upload. This controller is very similar to a <>, except that we use `MultipartHttpServletRequest` or `MultipartFile` in the method parameters: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Controller -public class FileUploadController { + @Controller + public class FileUploadController { - @RequestMapping(value = "/form", method = RequestMethod.POST) - public String handleFormUpload(@RequestParam("name") String name, - @RequestParam("file") MultipartFile file) { + @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"; - } else { - return "redirect:uploadFailure"; - } - } + 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 @@ -30379,23 +30640,23 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Controller -public class FileUploadController { + @Controller + public class FileUploadController { - @RequestMapping(value = "/form", method = RequestMethod.POST) - public String handleFormUpload(@RequestParam("name") String name, - @RequestParam("file") Part file) { + @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 + InputStream inputStream = file.getInputStream(); + // store bytes from uploaded file somewhere - return "redirect:uploadSuccess"; - } + return "redirect:uploadSuccess"; + } -} + } ---- @@ -30408,7 +30669,7 @@ unlike browsers that typically submit files and simple form fields, a programmat 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: -[source] +[literal] [subs="verbatim,quotes"] ---- POST /someUrl @@ -30420,7 +30681,7 @@ Content-Type: application/json; charset=UTF-8 Content-Transfer-Encoding: 8bit { - "name": "value" + "name": "value" } --edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp Content-Disposition: form-data; name="file-data"; filename="file.properties" @@ -30440,15 +30701,16 @@ this purpose. It allows you to have the content of a specific multipart passed t an `HttpMessageConverter` taking into consideration the `'Content-Type'` header of the multipart: -[source,java] +[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**) { - // ... + @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 @@ -30465,7 +30727,7 @@ converted with the help of the `MappingJackson2HttpMessageConverter`. [[mvc-exceptionhandlers-resolver]] -==== HandlerExceptionResolver +==== HandlerExceptionResolver Spring `HandlerExceptionResolver` implementations deal with unexpected exceptions that occur during controller execution. A `HandlerExceptionResolver` somewhat resembles the @@ -30485,13 +30747,14 @@ functionally equivalent to the exception mapping feature from the Servlet API, b 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. +within an `@Controller` or may apply globally to all `@RequestMapping` methods when +defined within an `@ControllerAdvice` class. The following sections explain this in more +detail. [[mvc-ann-exceptionhandler]] -==== @ExceptionHandler +==== @ExceptionHandler The `HandlerExceptionResolver` interface and the `SimpleMappingExceptionResolver` implementations allow you to map Exceptions to specific views declaratively along with @@ -30504,26 +30767,27 @@ You can do that with `@ExceptionHandler` methods. When declared within a control 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: +methods from any controller. The `@ControllerAdvice` annotation is a component +annotation, which can be used with classpath scanning. It is automatically enabled when +using the MVC namespace and the MVC Java config, or otherwise depending on whether the +`ExceptionHandlerExceptionResolver` is configured or not. Below is an example of a +controller-local `@ExceptionHandler` method: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Controller -public class SimpleController { + @Controller + public class SimpleController { - // @RequestMapping methods omitted ... + // @RequestMapping methods omitted ... - @ExceptionHandler(IOException.class) - public ResponseEntity handleIOException(IOException ex) { + @ExceptionHandler(IOException.class) + public ResponseEntity handleIOException(IOException ex) { + // prepare responseEntity + return responseEntity; + } - // prepare responseEntity - - return responseEntity; - } - -} + } ---- The `@ExceptionHandler` value can be set to an array of Exception types. If an exception @@ -30616,7 +30880,7 @@ response and write error content with message converters. See the Javadoc of [[mvc-ann-annotated-exceptions]] -==== Annotating Business Exceptions With @ResponseStatus +==== 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 @@ -30635,12 +30899,12 @@ status code or exception type. Starting with Servlet 3 an error page does not ne mapped, which effectively means the specified location customizes the default Servlet container error page. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - /error - + + /error + ---- Note that the actual location for the error page can be a JSP page or some other URL @@ -30649,36 +30913,36 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Controller -public class ErrorController { + @Controller + public class ErrorController { - @RequestMapping(value="/error", produces="application/json") - @ResponseBody - public Map handle(HttpServletRequest request) { + @RequestMapping(value="/error", produces="application/json") + @ResponseBody + public Map handle(HttpServletRequest request) { - Map map = new HashMap(); - map.put("status", request.getAttribute("javax.servlet.error.status_code")); - map.put("reason", request.getAttribute("javax.servlet.error.message")); + Map map = new HashMap(); + map.put("status", request.getAttribute("javax.servlet.error.status_code")); + map.put("reason", request.getAttribute("javax.servlet.error.message")); - return map; - } + return map; + } -} + } ---- or in a JSP: -[source,xml] +[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") %> -} + <%@ page contentType="application/json" pageEncoding="UTF-8"%> + { + status:<%=request.getAttribute("javax.servlet.error.status_code") %>, + reason:<%=request.getAttribute("javax.servlet.error.message") %> + } ---- @@ -30689,7 +30953,7 @@ or in a JSP: 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 +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 @@ -30701,7 +30965,7 @@ views, and controllers. [[mvc-coc-ccnhm]] -==== The Controller ControllerClassNameHandlerMapping +==== The Controller ControllerClassNameHandlerMapping The `ControllerClassNameHandlerMapping` class is a `HandlerMapping` implementation that uses a convention to determine the mapping between request URLs and the `Controller` @@ -30710,27 +30974,28 @@ instances that are to handle those requests. Consider the following simple `Controller` implementation. Take special notice of the __name__ of the class. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class **ViewShoppingCartController** implements Controller { + public class **ViewShoppingCartController** implements Controller { - public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) { - // the implementation is not hugely important for this example... - } -} + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - + + + ---- The `ControllerClassNameHandlerMapping` finds all of the various handler (or @@ -30772,24 +31037,24 @@ are to be displayed in (or on) a `View` adhere to a common naming convention. Co the following `Controller` implementation; notice that objects are added to the `ModelAndView` without any associated name specified. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class DisplayShoppingCartController implements Controller { + public class DisplayShoppingCartController implements Controller { - public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) { + public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) { - List cartItems = // get a List of CartItem objects - User user = // get the User doing the shopping + 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 + 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! + mav.addObject(cartItems); <-- look ma, no name, just the object + mav.addObject(user); <-- and again ma! - return mav; - } -} + return mav; + } + } ---- The `ModelAndView` class uses a `ModelMap` class that is a custom `Map` implementation @@ -30838,7 +31103,7 @@ semantics of name generation for collections clearer: [[mvc-coc-r2vnt]] -==== The View - RequestToViewNameTranslator +==== 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 @@ -30847,48 +31112,49 @@ such logical view name is explicitly supplied. It has just one implementation, t The `DefaultRequestToViewNameTranslator` maps request URLs to logical view names, as with this example: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class RegistrationController implements Controller { + 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 - } -} + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - + + - - - + + + - - + + - - - - + + + + - + ---- Notice how in the implementation of the `handleRequest(..)` method no `View` or logical @@ -30940,18 +31206,18 @@ bandwidth, as the rendered response is not sent back over the wire. You configure the `ShallowEtagHeaderFilter` in `web.xml`: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - etagFilter - org.springframework.web.filter.ShallowEtagHeaderFilter - + + etagFilter + org.springframework.web.filter.ShallowEtagHeaderFilter + - - etagFilter - petclinic - + + etagFilter + petclinic + ---- @@ -30959,28 +31225,28 @@ You configure the `ShallowEtagHeaderFilter` in `web.xml`: [[mvc-container-config]] === Code-based Servlet container initialization -In a Servlet 3.0+ environment, you have the option of configuring the Servlet container +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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import org.springframework.web.WebApplicationInitializer; + import org.springframework.web.WebApplicationInitializer; -public class MyWebApplicationInitializer implements WebApplicationInitializer { + public class MyWebApplicationInitializer implements WebApplicationInitializer { - @Override - public void onStartup(ServletContext container) { - XmlWebApplicationContext appContext = new XmlWebApplicationContext(); - appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml"); + @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("/"); - } + 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 @@ -30990,74 +31256,74 @@ An abstract base class implementation of `WebApplicationInitializer` named `DispatcherServlet` by simply overriding methods to specify the servlet mapping and the location of the `DispatcherServlet` configuration: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { + public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { - @Override - protected Class[] getRootConfigClasses() { - return null; - } + @Override + protected Class[] getRootConfigClasses() { + return null; + } - @Override - protected Class[] getServletConfigClasses() { - return new Class[] { MyWebConfig.class }; - } + @Override + protected Class[] getServletConfigClasses() { + return new Class[] { MyWebConfig.class }; + } - @Override - protected String[] getServletMappings() { - return new String[] { "/" }; - } + @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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MyWebAppInitializer extends AbstractDispatcherServletInitializer { + public class MyWebAppInitializer extends AbstractDispatcherServletInitializer { - @Override - protected WebApplicationContext createRootApplicationContext() { - return null; - } + @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 WebApplicationContext createServletApplicationContext() { + XmlWebApplicationContext cxt = new XmlWebApplicationContext(); + cxt.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml"); + return cxt; + } - @Override - protected String[] getServletMappings() { - return new String[] { "/" }; - } + @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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MyWebAppInitializer extends AbstractDispatcherServletInitializer { + public class MyWebAppInitializer extends AbstractDispatcherServletInitializer { - // ... + // ... - @Override - protected Filter[] getServletFilters() { - return new Filter[] { new HiddenHttpMethodFilter(), new CharacterEncodingFilter() }; - } + @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 @@ -31095,34 +31361,34 @@ to the created Spring MVC beans. But let's start from the beginning. To enable MVC Java config add the annotation `@EnableWebMvc` to one of your `@Configuration` classes: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -@EnableWebMvc -public class WebConfig { + @Configuration + @EnableWebMvc + public class WebConfig { -} + } ---- To achieve the same in XML use the `mvc:annotation-driven` element: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + - + ---- The above registers a `RequestMappingHandlerMapping`, a `RequestMappingHandlerAdapter`, @@ -31175,24 +31441,24 @@ and override the methods you need. Below is an example of some of the available to override. See `WebMvcConifgurer` for a list of all methods and the Javadoc for further details: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -@EnableWebMvc -public class WebConfig extends WebMvcConfigurerAdapter { + @Configuration + @EnableWebMvc + public class WebConfig extends WebMvcConfigurerAdapter { - @Override - protected void addFormatters(FormatterRegistry registry) { - // Add formatters and/or converters - } + @Override + protected void addFormatters(FormatterRegistry registry) { + // Add formatters and/or converters + } - @Override - public void configureMessageConverters(List> converters) { - // Configure the list of HttpMessageConverters to use - } + @Override + public void configureMessageConverters(List> converters) { + // Configure the list of HttpMessageConverters to use + } -} + } ---- To customize the default configuration of `` check what @@ -31201,24 +31467,24 @@ http://schema.spring.io/mvc/spring-mvc.xsd[Spring MVC XML schema] or use the cod 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - + + + + + + - - - - - - - - + + + + + + + + ---- @@ -31230,96 +31496,102 @@ incoming requests or restricted to specific URL path patterns. An example of registering interceptors in Java: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -@EnableWebMvc -public class WebConfig extends WebMvcConfigurerAdapter { + @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/*"); - } + @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 `` element: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - - - - + + + + + + + + + + + + ---- [[mvc-config-content-negotiation]] ==== Configuring Content Negotiation -Staring with Spring Framework 3.2, 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. +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`). +(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 `ContentNegotiationManger` also enables selective suffix pattern +matching for incoming requests. For more details, see the Javadoc of + + Below is an example of customizing content negotiation options through the MVC Java config: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -@EnableWebMvc -public class WebConfig extends WebMvcConfigurerAdapter { + @Configuration + @EnableWebMvc + public class WebConfig extends WebMvcConfigurerAdapter { - @Override - public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { - configurer.favorPathExtension(false).favorParameter(true); - } -} + @Override + public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { + configurer.favorPathExtension(false).favorParameter(true); + } + } ---- In the MVC namespace, the `` element has a `content-negotiation-manager` attribute, which expects a `ContentNegotiationManager` that in turn can be created with a `ContentNegotiationManagerFactoryBean`: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - - - - json=application/json - xml=application/xml - - - + + + + + + json=application/json + xml=application/xml + + + ---- If not using the MVC Java config or the MVC namespace, you'll need to create an instance @@ -31348,27 +31620,27 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -@EnableWebMvc -public class WebConfig extends WebMvcConfigurerAdapter { + @Configuration + @EnableWebMvc + public class WebConfig extends WebMvcConfigurerAdapter { - @Override - public void addViewControllers(ViewControllerRegistry registry) { - registry.addViewController("/").setViewName("home"); - } + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("/").setViewName("home"); + } -} + } ---- And the same in XML use the `` element: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- @@ -31387,53 +31659,53 @@ unnecessary overhead for resources that are already cached by the client. For ex 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -@EnableWebMvc -public class WebConfig extends WebMvcConfigurerAdapter { + @Configuration + @EnableWebMvc + public class WebConfig extends WebMvcConfigurerAdapter { - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.addResourceHandler("/resources/**").addResourceLocations("/public-resources/"); - } + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/resources/**").addResourceLocations("/public-resources/"); + } -} + } ---- And the same in XML: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -@EnableWebMvc -public class WebConfig extends WebMvcConfigurerAdapter { + @Configuration + @EnableWebMvc + public class WebConfig extends WebMvcConfigurerAdapter { - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.addResourceHandler("/resources/**").addResourceLocations("/public-resources/").setCachePeriod(31556926); - } + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/resources/**").addResourceLocations("/public-resources/").setCachePeriod(31556926); + } -} + } ---- And in XML: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- The `mapping` attribute must be an Ant pattern that can be used by @@ -31444,28 +31716,28 @@ order for the presence of the resource for any given request. For example, to en 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@EnableWebMvc -@Configuration -public class WebConfig extends WebMvcConfigurerAdapter { + @EnableWebMvc + @Configuration + public class WebConfig extends WebMvcConfigurerAdapter { - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.addResourceHandler("/resources/**") - .addResourceLocations("/", "classpath:/META-INF/public-web-resources/"); - } + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/resources/**") + .addResourceLocations("/", "classpath:/META-INF/public-web-resources/"); + } -} + } ---- And in XML: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- When serving resources that may change when a new version of the application is @@ -31484,7 +31756,7 @@ to be forced to re-download that custom-built `dojo.js` resource any time a new of the application is deployed. A simple way to achieve this would be to manage the version of the application in a properties file, such as: -[source] +[literal] [subs="verbatim,quotes"] ---- application.version=1.0.0 @@ -31493,56 +31765,57 @@ application.version=1.0.0 and then to make the properties file's values accessible to SpEL as a bean using the `util:properties` tag: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- With the application version now accessible via SpEL, we can incorporate this into the use of the `resources` tag: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- In Java, you can use the `@PropertySouce` annotation and then inject the `Environment` abstraction for access to all defined properties: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -@EnableWebMvc -@PropertySource("/WEB-INF/spring/application.properties") -public class WebConfig extends WebMvcConfigurerAdapter { + @Configuration + @EnableWebMvc + @PropertySource("/WEB-INF/spring/application.properties") + public class WebConfig extends WebMvcConfigurerAdapter { - @Inject Environment env; + @Inject Environment env; - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.addResourceHandler("/resources-" + env.getProperty("application.version") + "/**") - .addResourceLocations("/public-resources/"); - } + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler( + "/resources-" + env.getProperty("application.version") + "/**") + .addResourceLocations("/public-resources/"); + } -} + } ---- and finally, to request the resource with the proper URL, we can take advantage of the Spring JSP tags: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - + + + - + ---- @@ -31563,27 +31836,27 @@ lower than that of the `DefaultServletHttpRequestHandler`, which is `Integer.MAX To enable the feature using the default setup use: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -@EnableWebMvc -public class WebConfig extends WebMvcConfigurerAdapter { + @Configuration + @EnableWebMvc + public class WebConfig extends WebMvcConfigurerAdapter { - @Override - public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { - configurer.enable(); - } + @Override + public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { + configurer.enable(); + } -} + } ---- Or in XML: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- The caveat to overriding the "/" Servlet mapping is that the `RequestDispatcher` for the @@ -31595,27 +31868,27 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -@EnableWebMvc -public class WebConfig extends WebMvcConfigurerAdapter { + @Configuration + @EnableWebMvc + public class WebConfig extends WebMvcConfigurerAdapter { - @Override - public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { - configurer.enable("myCustomDefaultServlet"); - } + @Override + public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { + configurer.enable("myCustomDefaultServlet"); + } -} + } ---- Or in XML: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- @@ -31652,26 +31925,25 @@ This requires two things -- remove the `@EnableWebMvc` annotation in order to pr the import and then extend directly from `WebMvcConfigurationSupport`. Here is an example: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -public class WebConfig extends WebMvcConfigurationSupport { + @Configuration + public class WebConfig extends WebMvcConfigurationSupport { - @Override - public void addInterceptors(InterceptorRegistry registry){ - // ... - } + @Override + public void addInterceptors(InterceptorRegistry registry){ + // ... + } - @Override - @Bean - public RequestMappingHandlerAdapter requestMappingHandlerAdapter() { + @Override + @Bean + public RequestMappingHandlerAdapter requestMappingHandlerAdapter() { + // Create or let "super" create the adapter + // Then customize one of its properties + } - // Create or let "super" create the adapter - // Then customize one of its properties - } - -} + } ---- Note that modifying beans in this way does not prevent you from using any of the @@ -31688,19 +31960,19 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Component -public class MyPostProcessor implements BeanPostProcessor { + @Component + public class MyPostProcessor implements BeanPostProcessor { - public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException { - if (bean instanceof RequestMappingHandlerAdapter) { - // Modify properties of the adapter - } - } + 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 `` in order for @@ -31756,34 +32028,34 @@ need a view resolver that will resolve your views. The most commonly used view r when developing with JSPs are the `InternalResourceViewResolver` and the `ResourceBundleViewResolver`. Both are declared in the `WebApplicationContext`: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + -# 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 + # 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 + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - + + + + + ---- The `InternalResourceBundleViewResolver` can be configured for using JSPs as described @@ -31837,10 +32109,10 @@ called `spring-form.tld`. To use the tags from this library, add the following directive to the top of your JSP page: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> + <%@ 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. @@ -31859,26 +32131,26 @@ such as `firstName` and `lastName`. We will use it as the form backing object of form controller which returns `form.jsp`. Below is an example of what `form.jsp` would look like: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - - - - - -
First Name:
Last Name:
- -
-
+ + + + + + + + + + + + + +
First Name:
Last Name:
+ +
+
---- The `firstName` and `lastName` values are retrieved from the command object placed in @@ -31887,52 +32159,52 @@ how inner tags are used with the `form` tag. The generated HTML looks like a standard form: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -
- - - - - - - - - - - - -
First Name:
Last Name:
- -
-
+
+ + + + + + + + + + + + +
First Name:
Last Name:
+ +
+
---- 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - - - - - -
First Name:
Last Name:
- -
-
+ + + + + + + + + + + + + +
First Name:
Last Name:
+ +
+
---- @@ -31953,75 +32225,73 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class Preferences { + public class Preferences { - private boolean receiveNewsletter; + private boolean receiveNewsletter; + private String[] interests; + private String favouriteWord; - private String[] interests; + public boolean isReceiveNewsletter() { + return receiveNewsletter; + } - private String favouriteWord; + public void setReceiveNewsletter(boolean receiveNewsletter) { + this.receiveNewsletter = receiveNewsletter; + } - public boolean isReceiveNewsletter() { - return receiveNewsletter; - } + public String[] getInterests() { + return interests; + } - public void setReceiveNewsletter(boolean receiveNewsletter) { - this.receiveNewsletter = receiveNewsletter; - } + public void setInterests(String[] interests) { + this.interests = interests; + } - public String[] getInterests() { - return interests; - } + public String getFavouriteWord() { + return favouriteWord; + } - public void setInterests(String[] interests) { - this.interests = interests; - } - - public String getFavouriteWord() { - return favouriteWord; - } - - public void setFavouriteWord(String favouriteWord) { - this.favouriteWord = favouriteWord; - } - } + public void setFavouriteWord(String favouriteWord) { + this.favouriteWord = favouriteWord; + } + } ---- The `form.jsp` would look like: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - <%-- Approach 1: Property is of type java.lang.Boolean --%> - - + +
Subscribe to newsletter?:
+ + + <%-- Approach 1: Property is of type java.lang.Boolean --%> + + - - - - - - - - -
Subscribe to newsletter?:
Interests: - <%-- Approach 2: Property is of an array or of type java.util.Collection --%> - Quidditch: - Herbology: - Defence Against the Dark Arts: -
Favourite Word: - <%-- Approach 3: Property is of type java.lang.Object --%> - Magic: -
-
+ + Interests: + <%-- Approach 2: Property is of an array or of type java.util.Collection --%> + + Quidditch: + Herbology: + Defence Against the Dark Arts: + + + + + Favourite Word: + <%-- Approach 3: Property is of type java.lang.Object --%> + + Magic: + + + + ---- There are 3 approaches to the `checkbox` tag which should meet all your checkbox needs. @@ -32038,25 +32308,24 @@ There are 3 approaches to the `checkbox` tag which should meet all your checkbox Note that regardless of the approach, the same HTML structure is generated. Below is an HTML snippet of some checkboxes: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - Interests: - - Quidditch: - - Herbology: - - Defence Against the Dark Arts: - - - + + Interests: + + Quidditch: + + Herbology: + + Defence Against the Dark Arts: + + + ---- 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 +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 @@ -32078,20 +32347,20 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - -
Interests: - <%-- Property is of an array or of type java.util.Collection --%> - -
-
+ + + + + + +
Interests: + <%-- Property is of an array or of type java.util.Collection --%> + +
+
---- This example assumes that the "interestList" is a `List` available as a model attribute @@ -32109,14 +32378,16 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - Sex: - Male:
- Female: - + + Sex: + + Male:
+ Female: + + ---- @@ -32133,13 +32404,13 @@ entry's value will be used as the label to be displayed. You can also use a cust object where you can provide the property names for the value using "itemValue" and the label using "itemLabel". -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - Sex: - - + + Sex: + + ---- @@ -32148,30 +32419,30 @@ label using "itemLabel". This tag renders an HTML 'input' tag with type 'password' using the bound value. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - Password: - - - - + + Password: + + + + ---- 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - Password: - - - - + + Password: + + + + ---- @@ -32183,29 +32454,31 @@ option as well as the use of nested `option` and `options` tags. Let's assume a `User` has a list of skills. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - Skills: - - + + Skills: + + ---- If the `User's` skill were in Herbology, the HTML source of the 'Skills' row would look like: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - Skills: - - - + + Skills: + + + + ---- @@ -32215,39 +32488,39 @@ like: This tag renders an HTML 'option'. It sets 'selected' as appropriate based on the bound value. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - House: - - - - - - - - - + + House: + + + + + + + + + ---- If the `User's` house was in Gryffindor, the HTML source of the 'House' row would look like: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - House: - - - - + + House: + + + + ---- @@ -32257,36 +32530,36 @@ like: This tag renders a list of HTML 'option' tags. It sets the 'selected' attribute as appropriate based on the bound value. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - Country: - - - - - - - + + Country: + + + + + + + ---- If the `User` lived in the UK, the HTML source of the 'Country' row would look like: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - Country: - - - - + + Country: + + + + ---- As the example shows, the combined usage of an `option` tag with the `options` tag @@ -32308,14 +32581,14 @@ the item label property will apply to the map value. This tag renders an HTML 'textarea'. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - Notes: - - - + + Notes: + + + ---- @@ -32325,19 +32598,19 @@ This tag renders an HTML 'textarea'. 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- If we choose to submit the 'house' value as a hidden one, the HTML would look like: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- @@ -32353,79 +32626,79 @@ Let's assume we want to display all error messages for the `firstName` and `last fields once we submit the form. We have a validator for instances of the `User` class called `UserValidator`. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class UserValidator implements Validator { + public class UserValidator implements Validator { - public boolean supports(Class candidate) { - return User.class.isAssignableFrom(candidate); - } + 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."); - } - } + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - <%-- Show errors for firstName field --%> - - + +
First Name:
+ + + + <%-- Show errors for firstName field --%> + + - - - - <%-- Show errors for lastName field --%> - - - - - -
First Name:
Last Name:
- -
-
+ + Last Name: + + <%-- Show errors for lastName field --%> + + + + + + + + + ---- If we submit a form with empty values in the `firstName` and `lastName` fields, this is what the HTML would look like: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -
- - - - - <%-- Associated errors to firstName field displayed --%> - - + +
First Name:Field is required.
+ + + + <%-- Associated errors to firstName field displayed --%> + + - - - - <%-- Associated errors to lastName field displayed --%> - - - - - -
First Name:Field is required.
Last Name:Field is required.
- -
-
+ + Last Name: + + <%-- Associated errors to lastName field displayed --%> + Field is required. + + + + + + + + ---- What if we want to display the entire list of errors for a given page? The example below @@ -32438,56 +32711,56 @@ shows that the `errors` tag also supports some basic wildcarding functionality. 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - - - - - - - - -
First Name:
Last Name:
- -
-
+ + + + + + + + + + + + + + + + +
First Name:
Last Name:
+ +
+
---- The HTML would look like: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -
- Field is required.
Field is required.
- - - - - - + + Field is required.
Field is required.
+
First Name:Field is required.
+ + + + + - - - - - - - - - + + + + + + + + + ---- @@ -32512,42 +32785,42 @@ To support HTTP method conversion the Spring MVC form tag was updated to support the HTTP method. For example, the following snippet taken from the updated Petclinic sample -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - -

-
+ +

+
---- 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- - - httpMethodFilter - org.springframework.web.filter.HiddenHttpMethodFilter - + + httpMethodFilter + org.springframework.web.filter.HiddenHttpMethodFilter + - - httpMethodFilter - petclinic - + + httpMethodFilter + petclinic + ---- The corresponding @Controller method is shown below: -[source,java] +[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; -} + @RequestMapping(method = RequestMethod.DELETE) + public String deletePet(@PathVariable int ownerId, @PathVariable int petId) { + this.clinic.deletePet(petId); + return "redirect:/owners/" + ownerId; + } ---- @@ -32569,12 +32842,14 @@ is the default type. 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 2 (the standalone version -of Tiles, requiring Java 5+) in the `org.springframework.web.servlet.view.tiles2` -package as as well as Tiles 3 in the `org.springframework.web.servlet.view.tiles3` -package. Spring also continues to support Tiles 1.x (a.k.a. "Struts Tiles", as shipped -with Struts 1.1+; compatible with Java 1.4) in the original -`org.springframework.web.servlet.view.tiles` package. + +[NOTE] +==== +This section focuses on Spring's support for Tiles v3 in the +`org.springframework.web.servlet.view.tiles3` package as well as Tiles v2 in the +`org.springframework.web.servlet.view.tiles2` package. Tiles v1 (a.k.a. "Struts Tiles") +is no longer supported by Spring. +==== @@ -32597,20 +32872,20 @@ To be able to use Tiles, you have to configure it using files containing definit 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - /WEB-INF/defs/general.xml - /WEB-INF/defs/widgets.xml - /WEB-INF/defs/administrator.xml - /WEB-INF/defs/customer.xml - /WEB-INF/defs/templates.xml - - - + + + + /WEB-INF/defs/general.xml + /WEB-INF/defs/widgets.xml + /WEB-INF/defs/administrator.xml + /WEB-INF/defs/customer.xml + /WEB-INF/defs/templates.xml + + + ---- As you can see, there are five files containing definitions, which are all located in @@ -32623,61 +32898,60 @@ possibilities, the `UrlBasedViewResolver` and the `ResourceBundleViewResolver`. [[view-tiles-url]] -===== UrlBasedViewResolver +===== UrlBasedViewResolver The `UrlBasedViewResolver` instantiates the given `viewClass` for each view it has to resolve. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- [[view-tiles-resource]] -===== ResourceBundleViewResolver +===== ResourceBundleViewResolver The `ResourceBundleViewResolver` has to be provided with a property file containing viewnames and viewclasses the resolver can use: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -... -welcomeView.(class)=org.springframework.web.servlet.view.tiles2.TilesView -welcomeView.url=welcome (this is the name of a Tiles definition) + ... + welcomeView.(class)=org.springframework.web.servlet.view.tiles2.TilesView + welcomeView.url=welcome (this is the name of a Tiles definition) -vetsView.(class)=org.springframework.web.servlet.view.tiles2.TilesView -vetsView.url=vetsView (again, this is the name of a Tiles definition) + vetsView.(class)=org.springframework.web.servlet.view.tiles2.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 -... + 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 for Tiles 2 supports JSTL (the JSP Standard Tag Library) -out of the box, whereas there is a separate `TilesJstlView` subclass in the Tiles 1.x -support. +Note that the `TilesView` class supports JSTL (the JSP Standard Tag Library) out of the +box. [[view-tiles-preparer]] -===== SimpleSpringPreparerFactory and SpringBeanPreparerFactory +===== SimpleSpringPreparerFactory and SpringBeanPreparerFactory -As an advanced feature, Spring also supports two special Tiles 2 `PreparerFactory` +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. @@ -32685,7 +32959,7 @@ Specify `SimpleSpringPreparerFactory` to autowire ViewPreparer instances based o 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 +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 @@ -32695,25 +32969,25 @@ application context in this case, allowing for the use of explicit dependency in 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - /WEB-INF/defs/general.xml - /WEB-INF/defs/widgets.xml - /WEB-INF/defs/administrator.xml - /WEB-INF/defs/customer.xml - /WEB-INF/defs/templates.xml - - + + + + /WEB-INF/defs/general.xml + /WEB-INF/defs/widgets.xml + /WEB-INF/defs/administrator.xml + /WEB-INF/defs/customer.xml + /WEB-INF/defs/templates.xml + + - - + + - + ---- @@ -32747,50 +33021,46 @@ your `'WEB-INF/lib'` directory too! If you make use of Spring's 'dateToolAttribu A suitable configuration is initialized by adding the relevant configurer bean definition to your `'*-servlet.xml'` as shown below: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + - - - - - - + + + + + + ---- -[source] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + - - - - - - + + + + + + ---- [NOTE] @@ -32828,37 +33098,37 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - file - - org.apache.velocity.runtime.resource.loader.FileResourceLoader - - ${webapp.root}/WEB-INF/velocity - false - - - + + + + file + + org.apache.velocity.runtime.resource.loader.FileResourceLoader + + ${webapp.root}/WEB-INF/velocity + false + + + ---- Refer to the -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/ui/velocity/VelocityEngineFactory.html[API +{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. @@ -32871,19 +33141,19 @@ the `FreeMarkerConfigurer` bean. The `freemarkerSettings` property requires a `java.util.Properties` object and the `freemarkerVariables` property requires a `java.util.Map`. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - + + + + + + + + - + ---- See the FreeMarker documentation for details of settings and variables as they apply to @@ -32925,48 +33195,48 @@ in your MVC configuration by setting the 'commandName' bean property on your for controller. Example code is shown below for the `personFormV` and `personFormF` views configured earlier; -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - -... -
- Name: - #springBind( "command.name" ) -
- #foreach($error in $status.errorMessages) $error
#end -
- ... - - -... - + + + ... +
+ Name: + #springBind( "command.name" ) +
+ #foreach($error in $status.errorMessages) $error
#end +
+ ... + + + ... + ---- -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - -<#import "/spring.ftl" as spring /> - -... -
- Name: - <@spring.bind "command.name" /> -
- <#list spring.status.errorMessages as error> ${error}
-
- ... - - -... - + + <#import "/spring.ftl" as spring /> + + ... +
+ Name: + <@spring.bind "command.name" /> +
+ <#list spring.status.errorMessages as error> ${error}
+
+ ... + + + ... + ---- `#springBind` / `<@spring.bind>` requires a 'path' argument which consists of the name @@ -33088,14 +33358,14 @@ differences exist between the two languages, they are explained in the notes. [[views-form-macros-input]] ====== Input Fields -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - -... - Name: - #springFormInput("command.name" "")
- #springShowErrors("
" "")
+ + ... + Name: + #springFormInput("command.name" "")
+ #springShowErrors("
" "")
---- The formInput macro takes the path parameter (command.name) and an additional attributes @@ -33110,11 +33380,11 @@ time a class name or style attribute. Note that FreeMarker is able to specify de values for the attributes parameter, unlike Velocity, and the two macro calls above could be expressed as follows in FTL: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<@spring.formInput "command.name"/> -<@spring.showErrors "
"/> + <@spring.formInput "command.name"/> + <@spring.showErrors "
"/> ---- Output is shown below of the form fragment generating the name field, and displaying a @@ -33123,16 +33393,15 @@ occurs through Spring's Validation framework. The generated HTML looks like this: -[source] +[source,jsp,indent=0] [subs="verbatim,quotes"] ---- -Name: - -
- required -
-
+ Name: + +
+ required +
+
---- The formTextarea macro works the same way as the formInput macro and accepts the same @@ -33158,12 +33427,12 @@ value of 'London' for this field and so no validation is necessary. When the for rendered, the entire list of cities to choose from is supplied as reference data in the model under the name 'cityMap'. -[source] +[source,jsp,indent=0] [subs="verbatim,quotes"] ---- -... - Town: - <@spring.formRadioButtons "command.address.town", cityMap, "" />

+ ... + Town: + <@spring.formRadioButtons "command.address.town", cityMap, "" />

---- This renders a line of radio buttons, one for each value in `cityMap` using the @@ -33173,61 +33442,43 @@ keys are what the form actually submits as POSTed request parameters, map values 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] +[source,jsp,indent=0] [subs="verbatim,quotes"] ---- -Town: - -London - -Paris - -New York + Town: + London + Paris + New York ---- 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] +[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"); + 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; -} + 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] +[source,jsp,indent=0] [subs="verbatim,quotes"] ---- -Town: - -London - -Paris - -New York + Town: + London + Paris + New York ---- @@ -33244,14 +33495,14 @@ template processing to provide different behavior for different fields in your f To switch to XHTML compliance for your tags, specify a value of 'true' for a model/context variable named xhtmlCompliant: -[source] +[source,jsp,indent=0] [subs="verbatim,quotes"] ---- -## for Velocity.. -#set($springXhtmlCompliant = true) + ## for Velocity.. + #set($springXhtmlCompliant = true) -<#-- for FreeMarker --> -<#assign xhtmlCompliant = true in spring> + <#-- for FreeMarker --> + <#assign xhtmlCompliant = true in spring> ---- Any tags generated by the Spring macros will now be XHTML compliant after processing @@ -33259,17 +33510,17 @@ this directive. In similar fashion, HTML escaping can be specified per field: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<#-- until this point, default HTML escaping is used --> + <#-- 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 = 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 --> + <#assign htmlEscape = false in spring> + <#-- all future fields will be bound with HTML escaping off --> ---- @@ -33300,10 +33551,10 @@ Configuration is standard for a simple Spring application. The dispatcher servle file contains a reference to a `ViewResolver`, URL mappings and a single controller bean... -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- ... that encapsulates our word generation logic. @@ -33314,23 +33565,22 @@ bean... The controller logic is encapsulated in a subclass of `AbstractController`, with the handler method being defined like so... -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -protected ModelAndView handleRequestInternal( - HttpServletRequest request, - HttpServletResponse response) throws Exception { + protected ModelAndView handleRequestInternal(HttpServletRequest request, + HttpServletResponse response) throws Exception { - Map map = new HashMap(); - List wordList = new ArrayList(); + Map map = new HashMap(); + List wordList = new ArrayList(); - wordList.add("hello"); - wordList.add("world"); + wordList.add("hello"); + wordList.add("world"); - map.put("wordList", wordList); + map.put("wordList", wordList); - return new ModelAndView("home", map); -} + return new ModelAndView("home", map); + } ---- So far we've done nothing that's XSLT specific. The model data has been created in the @@ -33354,33 +33604,33 @@ also typically implement the abstract method `createXsltSource(..)` method. The 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package xslt; + package xslt; -// imports omitted for brevity + // imports omitted for brevity -public class HomePage extends AbstractXsltView { + public class HomePage extends AbstractXsltView { - protected Source createXsltSource(Map model, String rootName, HttpServletRequest - request, HttpServletResponse response) throws Exception { + protected Source createXsltSource(Map model, String rootName, + HttpServletRequest request, HttpServletResponse response) throws Exception { - Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); - Element root = document.createElement(rootName); + 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); - } + 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 @@ -33398,7 +33648,7 @@ The views.properties file (or equivalent xml definition if you're using an XML b view resolver as we did in the Velocity examples above) looks like this for the one-view application that is 'My First Words': -[source] +[literal] [subs="verbatim,quotes"] ---- home.(class)=xslt.HomePage @@ -33420,29 +33670,29 @@ Finally, we have the XSLT code used for transforming the above document. As show 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + - - - Hello! - -

My First Words

- - - -
+ + + Hello! + +

My First Words

+ + + +
- -
-
+ +
+
-
+
---- @@ -33452,7 +33702,7 @@ the war file in the `'WEB-INF/xsl'` directory. A summary of the files discussed and their location in the WAR file is shown in the simplified WAR structure below. -[source] +[literal] [subs="verbatim,quotes"] ---- ProjectRoot @@ -33520,7 +33770,7 @@ First, let's amend the views.properties file (or xml equivalent) and add a simpl definition for both document types. The entire file now looks like this with the XSLT view shown from earlier: -[source] +[literal] [subs="verbatim,quotes"] ---- home.(class)=xslt.HomePage @@ -33556,73 +33806,66 @@ generated by POI) or `org.springframework.web.servlet.view.document.AbstractJExc 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package excel; + package excel; -// imports omitted for brevity + // imports omitted for brevity -public class HomePage extends AbstractExcelView { + public class HomePage extends AbstractExcelView { - protected void buildExcelDocument( - Map model, - HSSFWorkbook wb, - HttpServletRequest req, - HttpServletResponse resp) - throws Exception { + protected void buildExcelDocument(Map model, HSSFWorkbook wb, HttpServletRequest req, + HttpServletResponse resp) throws Exception { - HSSFSheet sheet; - HSSFRow sheetRow; - HSSFCell cell; + 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); + // 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"); + // 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)); + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package excel; + package excel; -// imports omitted for brevity + // imports omitted for brevity -public class HomePage extends AbstractJExcelView { + public class HomePage extends AbstractJExcelView { - protected void buildExcelDocument(Map model, - WritableWorkbook wb, - HttpServletRequest request, - HttpServletResponse response) - throws Exception { + protected void buildExcelDocument(Map model, WritableWorkbook wb, + HttpServletRequest request, HttpServletResponse response) throws Exception { - WritableSheet sheet = wb.createSheet("Spring", 0); + WritableSheet sheet = wb.createSheet("Spring", 0); - sheet.addCell(new Label(0, 0, "Spring-Excel test")); + 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))); - } - } -} + 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 @@ -33641,30 +33884,24 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package pdf; + package pdf; -// imports omitted for brevity + // imports omitted for brevity -public class PDFPage extends AbstractPdfView { + public class PDFPage extends AbstractPdfView { - protected void buildPdfDocument( - Map model, - Document doc, - PdfWriter writer, - HttpServletRequest req, - HttpServletResponse resp) - throws Exception { + 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 - -
+ + + ---- Here we've configured an instance of the `ResourceBundleViewResolver` class that will @@ -33759,7 +33996,7 @@ Mapping one of these classes to a view name and a report file is a matter of add appropriate entries in the resource bundle configured in the previous section as shown here: -[source] +[literal] [subs="verbatim,quotes"] ---- simpleReport.(class)=org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView @@ -33786,7 +34023,7 @@ application. [[view-jasper-reports-configuration-multiformat-view]] -===== Using JasperReportsMultiFormatView +===== 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 @@ -33799,20 +34036,21 @@ up the actual view implementation class, and it uses the format key to lookup up 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public ModelAndView handleSimpleReportMulti(HttpServletRequest request, -HttpServletResponse response) throws Exception { + public ModelAndView handleSimpleReportMulti(HttpServletRequest request, + HttpServletResponse response) throws Exception { - String uri = request.getRequestURI(); - String format = uri.substring(uri.lastIndexOf(".") + 1); + String uri = request.getRequestURI(); + String format = uri.substring(uri.lastIndexOf(".") + 1); - Map model = getModel(); - model.put("format", format); + Map model = getModel(); + model.put("format", format); - return new ModelAndView("simpleReportMulti", model); -} + return new ModelAndView("simpleReportMulti", model); + + } ---- In this example, the mapping key is determined from the extension of the request URI and @@ -33861,15 +34099,15 @@ first approach is to add an instance of `JRDataSource` or a `Collection` type to 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -private Map getModel() { - Map model = new HashMap(); - Collection beanData = getBeanData(); - model.put("myBeanData", beanData); - return model; -} + 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 @@ -33877,23 +34115,23 @@ specific key and then configure this key using the `reportDataKey` property of t class. In both cases Spring will wrap instances of `Collection` in a `JRBeanCollectionDataSource` instance. For example: -[source,java] +[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; -} + 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: -[source] +[literal] [subs="verbatim,quotes"] ---- simpleReport.(class)=org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView @@ -33925,27 +34163,27 @@ To control which sub-report files are included in a master report using Spring, 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- Then, you define your sub-report to use this sub-report parameter: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - + + + + + + + + + ---- This defines a master report file that expects the sub-report to be passed in as an @@ -33954,14 +34192,14 @@ instance of `net.sf.jasperreports.engine.JasperReports` under the parameter load a report file and pass it into the JasperReports engine as a sub-report using the `subReportUrls` property: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - + + + + + ---- Here, the key of the `Map` corresponds to the name of the sub-report parameter in the @@ -33979,10 +34217,10 @@ instances of `JRDataSource` then you need to specify which of the parameters in `ModelAndView` Spring should convert. To do this, configure the list of parameter names using the `subReportDataKeys` property of your chosen view class: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- Here, the key you supply __must__ correspond to both the key used in your `ModelAndView` @@ -34000,22 +34238,22 @@ configuration the key of an entry should be the fully-qualified name of a static 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - Footer by Spring! - </td><td width="50%">&nbsp; </td></tr> - </table></body></html> - - - - - + + + + + + Footer by Spring! + </td><td width="50%">&nbsp; </td></tr> + </table></body></html> + + + + + ---- Here you can see that the `JasperReportsHtmlView` is configured with an exporter @@ -34029,55 +34267,53 @@ which will output a footer in the resulting HTML. === 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 +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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class SampleContentAtomView extends AbstractAtomFeedView { + public class SampleContentAtomView extends AbstractAtomFeedView { - @Override - protected void buildFeedMetadata(Map model, Feed feed, - HttpServletRequest request) { - // implementation omitted - } + @Override + protected void buildFeedMetadata(Map model, + Feed feed, HttpServletRequest request) { + // implementation omitted + } - @Override - protected List buildFeedEntries(Map model, - HttpServletRequest request, HttpServletResponse response) - throws Exception { + @Override + protected List buildFeedEntries(Map model, + HttpServletRequest request, HttpServletResponse response) throws Exception { + // implementation omitted + } - // implementation omitted - } -} + } ---- Similar requirements apply for implementing `AbstractRssFeedView`, as shown below. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class SampleContentAtomView extends AbstractRssFeedView { + public class SampleContentAtomView extends AbstractRssFeedView { - @Override - protected void buildFeedMetadata(Map model, Channel feed, - HttpServletRequest request) { - // implementation omitted - } + @Override + protected void buildFeedMetadata(Map model, + Channel feed, HttpServletRequest request) { + // implementation omitted + } - @Override - protected List buildFeedItems(Map model, - HttpServletRequest request, HttpServletResponse response) - throws Exception { - // implementation omitted - } + @Override + protected List buildFeedItems(Map model, + HttpServletRequest request, HttpServletResponse response) throws Exception { + // implementation omitted + } -} + } ---- The `buildFeedItems()` and `buildFeedEntires()` methods pass in the HTTP request in case @@ -34113,7 +34349,7 @@ content as JSON. By default, the entire contents of the model map (with the exce 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 +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 @@ -34152,15 +34388,14 @@ For more information about SWF, consult the Spring Web Flow website. **** This chapter details Spring's integration with third party web frameworks such as -http://java.sun.com/javaee/javaserverfaces/[JSF], http://struts.apache.org/[Struts], -http://en.wikipedia.org/wiki/WebWork[WebWork], and +http://java.sun.com/javaee/javaserverfaces/[JSF], http://struts.apache.org/[Struts], and http://tapestry.apache.org/[Tapestry]. 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 his or her development team is +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 (<>), 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 @@ -34195,7 +34430,7 @@ themselves, please do consult <> at the end of this c [[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 +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.) @@ -34212,36 +34447,36 @@ 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 -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/context/ContextLoaderListener.html[`ContextLoaderListener`] +{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` section (in the same file) that defines which set of Spring XML configuration files to load. Find below the configuration: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - org.springframework.web.context.ContextLoaderListener - + + org.springframework.web.context.ContextLoaderListener + ---- Find below the configuration: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - contextConfigLocation - /WEB-INF/applicationContext*.xml - + + contextConfigLocation + /WEB-INF/applicationContext*.xml + ---- 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 -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/context/WebApplicationContext.html[`WebApplicationContext`] +{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. @@ -34249,14 +34484,14 @@ All Java web frameworks are built on top of the Servlet API, and so one can use following code snippet to get access to this 'business context' `ApplicationContext` created by the `ContextLoaderListener`. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext); + WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext); ---- The -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/context/support/WebApplicationContextUtils.html[`WebApplicationContextUtils`] +{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` @@ -34304,27 +34539,27 @@ JSF EL integration. ==== DelegatingVariableResolver (JSF 1.1/1.2) The easiest way to integrate one's Spring middle-tier with one's JSF web layer is to use the -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/jsf/DelegatingVariableResolver.html[`DelegatingVariableResolver`] +{javadoc-baseurl}/org/springframework/web/jsf/DelegatingVariableResolver.html[`DelegatingVariableResolver`] class. To configure this variable resolver in one's application, one will need to edit one's __faces-context.xml__ file. After the opening `` element, add an `` element and a `` element within it. The value of the variable resolver should reference Spring's `DelegatingVariableResolver`; for example: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - org.springframework.web.jsf.DelegatingVariableResolver - - en - en - es - - messages - - + + + org.springframework.web.jsf.DelegatingVariableResolver + + en + en + es + + messages + + ---- The `DelegatingVariableResolver` will first delegate value lookups to the default @@ -34335,18 +34570,18 @@ JSF-managed beans. Managed beans are defined in one's `faces-config.xml` file. Find below an example where `#{userManager}` is a bean that is retrieved from the Spring 'business context'. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - userList - com.whatever.jsf.UserList - request - - userManager - #{userManager} - - + + userList + com.whatever.jsf.UserList + request + + userManager + #{userManager} + + ---- @@ -34362,15 +34597,15 @@ using request/session-scoped beans with special Spring resolution rules, e.g. Sp Configuration-wise, simply define `SpringBeanVariableResolver` in your __faces-context.xml__ file: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - org.springframework.web.jsf.SpringBeanVariableResolver - ... - - + + + org.springframework.web.jsf.SpringBeanVariableResolver + ... + + ---- @@ -34386,15 +34621,15 @@ implementation. Configuration-wise, simply define `SpringBeanFacesELResolver` in your JSF 1.2 __faces-context.xml__ file: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - org.springframework.web.jsf.el.SpringBeanFacesELResolver - ... - - + + + org.springframework.web.jsf.el.SpringBeanFacesELResolver + ... + + ---- @@ -34402,15 +34637,15 @@ __faces-context.xml__ file: [[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 -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/jsf/FacesContextUtils.html[`FacesContextUtils`] +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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -ApplicationContext ctx = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance()); + ApplicationContext ctx = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance()); ---- @@ -34432,11 +34667,9 @@ web developers. ==== __The following section discusses Struts 1 a.k.a. "Struts Classic".__ -Struts 2 is effectively a different product - a successor of WebWork 2.2 (as discussed -in <>), carrying the Struts brand now. Check out the Struts 2 -http://struts.apache.org/2.x/docs/spring-plugin.html[Spring Plugin] for the built-in -Spring integration shipped with Struts 2. In general, Struts 2 is closer to WebWork 2.2 -than to Struts 1 in terms of its Spring integration implications. +Struts 2 is effectively a different product, carrying the Struts brand now. Check out the +Struts 2 http://struts.apache.org/2.x/docs/spring-plugin.html[Spring Plugin] for the +built-in Spring integration shipped with Struts 2. ==== To integrate your Struts 1.x application with Spring, you have two options: @@ -34444,39 +34677,39 @@ To integrate your Struts 1.x application with Spring, you have two options: * Configure Spring to manage your Actions as beans, using the `ContextLoaderPlugin`, and set their dependencies in a Spring context file. * Subclass Spring's `ActionSupport` classes and grab your Spring-managed beans - explicitly using a__getWebApplicationContext()__ method. + explicitly using a __getWebApplicationContext()__ method. [[struts-contextloaderplugin]] ==== ContextLoaderPlugin The -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/struts/ContextLoaderPlugIn.html[`ContextLoaderPlugin`] +{javadoc-baseurl}/org/springframework/web/struts/ContextLoaderPlugIn.html[`ContextLoaderPlugin`] is a Struts 1.1+ plug-in that loads a Spring context file for the Struts `ActionServlet`. This context refers to the root `WebApplicationContext` (loaded by the `ContextLoaderListener`) as its parent. The default name of the context file is the name -of the mapped servlet, plus__-servlet.xml__. If `ActionServlet` is defined in web.xml as +of the mapped servlet, plus __-servlet.xml__. If `ActionServlet` is defined in web.xml as `action`, the default is __/WEB-INF/action-servlet.xml__. To configure this plug-in, add the following XML to the plug-ins section near the bottom of your __struts-config.xml__ file: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- The location of the context configuration files can be customized using the ' `contextConfigLocation`' property. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- It is possible to use this plugin to load all your context files, which can be useful @@ -34498,46 +34731,46 @@ __action-servlet.xml__ file. The bridge between the Action in __struts-config.xm __action-servlet.xml__ is built with the action-mapping's "path" and the bean's "name". If you have the following in your __struts-config.xml__ file: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- You must define that Action's bean with the "/users" name in __action-servlet.xml__: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- [[struts-delegatingrequestprocessor]] ===== DelegatingRequestProcessor To configure the -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/struts/DelegatingRequestProcessor.html[`DelegatingRequestProcessor`] +{javadoc-baseurl}/org/springframework/web/struts/DelegatingRequestProcessor.html[`DelegatingRequestProcessor`] in your __struts-config.xml__ file, override the "processorClass" property in the element. These lines follow the element. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- After adding this setting, your Action will automatically be looked up in Spring's context file, no matter what the type. In fact, you don't even need to specify a type. Both of the following snippets will work: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + ---- If you're using Struts' __modules__ feature, your bean names must contain the module @@ -34548,7 +34781,7 @@ prefix. For example, an action defined as `` with module p ==== If you are using Tiles in your Struts application, you must configure your with the -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/struts/DelegatingTilesRequestProcessor.html[`DelegatingTilesRequestProcessor`] +{javadoc-baseurl}/org/springframework/web/struts/DelegatingTilesRequestProcessor.html[`DelegatingTilesRequestProcessor`] instead. ==== @@ -34557,17 +34790,17 @@ instead. ===== DelegatingActionProxy If you have a custom `RequestProcessor` and can't use the `DelegatingRequestProcessor` or `DelegatingTilesRequestProcessor` approaches, you can use the -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/struts/DelegatingActionProxy.html[`DelegatingActionProxy`] +{javadoc-baseurl}/org/springframework/web/struts/DelegatingActionProxy.html[`DelegatingActionProxy`] as the type in your action-mapping. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + ---- The bean definition in __action-servlet.xml__ remains the same, whether you use a custom @@ -34576,13 +34809,13 @@ The bean definition in __action-servlet.xml__ remains the same, whether you use If you define your `Action` in a context file, the full feature set of Spring's bean container will be available for it: dependency injection as well as the option to instantiate a new `Action` instance for each request. To activate the latter, -add__scope="prototype"__ to your Action's bean definition. +add __scope="prototype"__ to your Action's bean definition. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- @@ -34593,41 +34826,39 @@ As previously mentioned, you can retrieve the `WebApplicationContext` from the `ServletContext` using the `WebApplicationContextUtils` class. An easier way is to extend Spring's `Action` classes for Struts. For example, instead of subclassing Struts' `Action` class, you can subclass Spring's -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/struts/ActionSupport.html[`ActionSupport`] +{javadoc-baseurl}/org/springframework/web/struts/ActionSupport.html[`ActionSupport`] class. The `ActionSupport` class provides additional convenience methods, -like__getWebApplicationContext()__. Below is an example of how you might use this in an +like __getWebApplicationContext()__. Below is an example of how you might use this in an Action: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class UserAction extends DispatchActionSupport { + public class UserAction extends DispatchActionSupport { - public ActionForward execute(ActionMapping mapping, - ActionForm form, - HttpServletRequest request, - HttpServletResponse response) throws Exception { - if (log.isDebugEnabled()) { - log.debug("entering 'delete' method..."); - } - WebApplicationContext ctx = getWebApplicationContext(); - UserManager mgr = (UserManager) ctx.getBean("userManager"); - // talk to manager for business logic - return mapping.findForward("success"); - } -} + public ActionForward execute(ActionMapping mapping, ActionForm form, + HttpServletRequest request, HttpServletResponse response) throws Exception { + if (log.isDebugEnabled()) { + log.debug("entering 'delete' method..."); + } + WebApplicationContext ctx = getWebApplicationContext(); + UserManager mgr = (UserManager) ctx.getBean("userManager"); + // talk to manager for business logic + return mapping.findForward("success"); + } + } ---- Spring includes subclasses for all of the standard Struts Actions - the Spring versions merely have __Support__ appended to the name: -* http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/struts/ActionSupport.html[`ActionSupport`], -* http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/struts/DispatchActionSupport.html[`DispatchActionSupport`], -* http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/struts/LookupDispatchActionSupport.html[`LookupDispatchActionSupport`] +* {javadoc-baseurl}/org/springframework/web/struts/ActionSupport.html[`ActionSupport`], +* {javadoc-baseurl}/org/springframework/web/struts/DispatchActionSupport.html[`DispatchActionSupport`], +* {javadoc-baseurl}/org/springframework/web/struts/LookupDispatchActionSupport.html[`LookupDispatchActionSupport`] and -* http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/struts/MappingDispatchActionSupport.html[`MappingDispatchActionSupport`]. +* {javadoc-baseurl}/org/springframework/web/struts/MappingDispatchActionSupport.html[`MappingDispatchActionSupport`]. The recommended strategy is to use the approach that best suits your project. Subclassing makes your code more readable, and you know exactly how your dependencies @@ -34638,45 +34869,6 @@ integrating with Struts. -[[webwork]] -=== WebWork 2.x -From the http://www.opensymphony.com/webwork/[WebWork homepage]: - -"__WebWork is a Java web-application development framework. It is built specifically -with developer productivity and code simplicity in mind, providing robust support for -building reusable UI templates, such as form controls, UI themes, internationalization, -dynamic form parameter mapping to JavaBeans, robust client and server side validation, -and much more.__" - -Web work's architecture and concepts are easy to understand, and the framework also has -an extensive tag library as well as nicely decoupled validation. - -One of the key enablers in WebWork's technology stack is -http://www.opensymphony.com/webwork/wikidocs/IoC%20Overview.html[an IoC container] to -manage Webwork Actions, handle the "wiring" of business objects, etc. Prior to WebWork -version 2.2, WebWork used its own proprietary IoC container (and provided integration -points so that one could integrate an IoC container such as Spring's into the mix). -However, as of WebWork version 2.2, the default IoC container that is used within -WebWork __is__ Spring. This is obviously great news if one is a Spring developer, -because it means that one is immediately familiar with the basics of IoC configuration, -idioms, and suchlike within WebWork. - -Now in the interests of adhering to the DRY (Don't Repeat Yourself) principle, it would -be foolish to document the Spring-WebWork integration in light of the fact that the -WebWork team have already written such a writeup. Please consult the -http://www.opensymphony.com/webwork/wikidocs/Spring.html[Spring-WebWork integration -page] on the http://wiki.opensymphony.com/display/WW/WebWork[WebWork wiki] for the full -lowdown. - -Note that the Spring-WebWork integration code was developed (and continues to be -maintained and improved) by the WebWork developers themselves. So please refer first to -the WebWork site and forums if you are having issues with the integration. But feel free -to post comments and queries regarding the Spring-WebWork integration on the -http://forum.springframework.org/forumdisplay.php?f=25[Spring support forums], too. - - - - [[tapestry]] === Tapestry 3.x and 4.x From the http://tapestry.apache.org/[Tapestry homepage]: @@ -34720,53 +34912,57 @@ him for what is really some silky smooth integration). Assume we have the following simple Spring container definition (in the ubiquitous XML format): -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - - - - - - - - + + + + + + + + + - - - - - - - - - - - *=PROPAGATION_REQUIRED - - + + + + + + + + + + + *=PROPAGATION_REQUIRED + + - - - - - - - - - - - *=PROPAGATION_REQUIRED - - - + + + + + + + + + + + *=PROPAGATION_REQUIRED + + + ---- Inside the Tapestry application, the above bean definitions need to be @@ -34782,13 +34978,13 @@ is the standard `ServletContext` from the Java EE Servlet specification. As such simple mechanism for a page to get an instance of the `UserService`, for example, would be with code such as: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -WebApplicationContext appContext = WebApplicationContextUtils.getApplicationContext( - getRequestCycle().getRequestContext().getServlet().getServletContext()); -UserService userService = (UserService) appContext.getBean("userService"); -// ... some code which uses UserService + WebApplicationContext appContext = WebApplicationContextUtils.getApplicationContext( + getRequestCycle().getRequestContext().getServlet().getServletContext()); + UserService userService = (UserService) appContext.getBean("userService"); + // ... some code which uses UserService ---- This mechanism does work. Having said that, it can be made a lot less verbose by @@ -34819,34 +35015,34 @@ the page's/component's lifecycle when we need to access the `ApplicationContext` `WebApplicationContextUtils.getApplicationContext(servletContext)` directly. One way is by defining a custom version of the Tapestry `IEngine` which exposes this for us: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.whatever.web.xportal; + package com.whatever.web.xportal; -// import ... + // import ... -public class MyEngine extends org.apache.tapestry.engine.BaseEngine { + public class MyEngine extends org.apache.tapestry.engine.BaseEngine { - public static final String APPLICATION_CONTEXT_KEY = "appContext"; + public static final String APPLICATION_CONTEXT_KEY = "appContext"; - /** - * @see org.apache.tapestry.engine.AbstractEngine#setupForRequest(org.apache.tapestry.request.RequestContext) - */ - protected void setupForRequest(RequestContext context) { - super.setupForRequest(context); + /** + * @see org.apache.tapestry.engine.AbstractEngine#setupForRequest(org.apache.tapestry.request.RequestContext) + */ + protected void setupForRequest(RequestContext context) { - // insert ApplicationContext in global, if not there - Map global = (Map) getGlobal(); - ApplicationContext ac = (ApplicationContext) global.get(APPLICATION_CONTEXT_KEY); - if (ac == null) { - ac = WebApplicationContextUtils.getWebApplicationContext( - context.getServlet().getServletContext() - ); - global.put(APPLICATION_CONTEXT_KEY, ac); - } - } -} + super.setupForRequest(context); + + // insert ApplicationContext in global, if not there + Map global = (Map) getGlobal(); + ApplicationContext ac = (ApplicationContext) global.get(APPLICATION_CONTEXT_KEY); + if (ac == null) { + ac = WebApplicationContextUtils.getWebApplicationContext( + context.getServlet().getServletContext()); + global.put(APPLICATION_CONTEXT_KEY, ac); + } + } + } ---- This engine class places the Spring Application Context as an attribute called @@ -34854,18 +35050,18 @@ This engine class places the Spring Application Context as an attribute called this special IEngine instance should be used for this Tapestry application, with an entry in the Tapestry application definition file. For example: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -file: xportal.application: - - - - + file: xportal.application: + + + + ---- @@ -34875,65 +35071,63 @@ Now in our page or component definition file (*.page or *.jwc), we simply add property-specification elements to grab the beans we need out of the `ApplicationContext`, and create page or component properties for them. For example: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - global.appContext.getBean("userService") - - - global.appContext.getBean("authenticationService") - + + global.appContext.getBean("userService") + + + global.appContext.getBean("authenticationService") + ---- The OGNL expression inside the property-specification specifies the initial value for the property, as a bean obtained from the context. The entire page definition might look like this: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - -**** + + **** - + - - - - - - global.appContext.getBean("userService") - - - global.appContext.getBean("authenticationService") - + + + + + + global.appContext.getBean("userService") + + + global.appContext.getBean("authenticationService") + - + - - - - + + + + - - - - - + + + + + - - - - - - + + + + + + - + ---- @@ -34943,133 +35137,133 @@ Now in the Java class definition for the page or component itself, all we need t add an abstract getter method for the properties we have defined (in order to be able to access the properties). -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// our UserService implementation; will come from page definition -public abstract UserService getUserService(); -// our AuthenticationService implementation; will come from page definition -public abstract AuthenticationService getAuthenticationService(); + // our UserService implementation; will come from page definition + public abstract UserService getUserService(); + // our AuthenticationService implementation; will come from page definition + public abstract AuthenticationService getAuthenticationService(); ---- For the sake of completeness, the entire Java class, for a login page in this example, might look like this: -[source,java] +[source,java,indent=0] ---- -package com.whatever.web.xportal.pages; + package com.whatever.web.xportal.pages; -/** - * Allows the user to login, by providing username and password. - * After successfully logging in, a cookie is placed on the client browser - * that provides the default username for future logins (the cookie - * persists for a week). - */ -public abstract class Login extends BasePage implements ErrorProperty, PageRenderListener { + /** + * Allows the user to login, by providing username and password. + * After successfully logging in, a cookie is placed on the client browser + * that provides the default username for future logins (the cookie + * persists for a week). + */ + public abstract class Login extends BasePage implements ErrorProperty, PageRenderListener { - /** the key under which the authenticated user object is stored in the visit as */ - public static final String USER_KEY = "user"; + /** the key under which the authenticated user object is stored in the visit as */ + public static final String USER_KEY = "user"; - /** The name of the cookie that identifies a user **/ - private static final String COOKIE_NAME = Login.class.getName() + ".username"; - private final static int ONE_WEEK = 7 * 24 * 60 * 60; + /** The name of the cookie that identifies a user **/ + private static final String COOKIE_NAME = Login.class.getName() + ".username"; + private final static int ONE_WEEK = 7 * 24 * 60 * 60; - public abstract String getUsername(); - public abstract void setUsername(String username); + public abstract String getUsername(); + public abstract void setUsername(String username); - public abstract String getPassword(); - public abstract void setPassword(String password); + public abstract String getPassword(); + public abstract void setPassword(String password); - public abstract ICallback getCallback(); - public abstract void setCallback(ICallback value); + public abstract ICallback getCallback(); + public abstract void setCallback(ICallback value); - public abstract UserService getUserService(); - public abstract AuthenticationService getAuthenticationService(); + public abstract UserService getUserService(); + public abstract AuthenticationService getAuthenticationService(); - protected IValidationDelegate getValidationDelegate() { - return (IValidationDelegate) getBeans().getBean("delegate"); - } + protected IValidationDelegate getValidationDelegate() { + return (IValidationDelegate) getBeans().getBean("delegate"); + } - protected void setErrorField(String componentId, String message) { - IFormComponent field = (IFormComponent) getComponent(componentId); - IValidationDelegate delegate = getValidationDelegate(); - delegate.setFormComponent(field); - delegate.record(new ValidatorException(message)); - } + protected void setErrorField(String componentId, String message) { + IFormComponent field = (IFormComponent) getComponent(componentId); + IValidationDelegate delegate = getValidationDelegate(); + delegate.setFormComponent(field); + delegate.record(new ValidatorException(message)); + } - /** - * Attempts to login. - *

- * If the user name is not known, or the password is invalid, then an error - * message is displayed. - **/ - public void attemptLogin(IRequestCycle cycle) { + /** + * Attempts to login. + *

+ * If the user name is not known, or the password is invalid, then an error + * message is displayed. + */ + public void attemptLogin(IRequestCycle cycle) { - String password = getPassword(); + String password = getPassword(); - // Do a little extra work to clear out the password. - setPassword(null); - IValidationDelegate delegate = getValidationDelegate(); + // Do a little extra work to clear out the password. + setPassword(null); + IValidationDelegate delegate = getValidationDelegate(); - delegate.setFormComponent((IFormComponent) getComponent("inputPassword")); - delegate.recordFieldInputValue(null); + delegate.setFormComponent((IFormComponent) getComponent("inputPassword")); + delegate.recordFieldInputValue(null); - // An error, from a validation field, may already have occurred. - if (delegate.getHasErrors()) { - return; - } + // An error, from a validation field, may already have occurred. + if (delegate.getHasErrors()) { + return; + } - try { - User user = getAuthenticationService().login(getUsername(), getPassword()); - loginUser(user, cycle); - } - catch (FailedLoginException ex) { - this.setError("Login failed: " + ex.getMessage()); - return; - } - } + try { + User user = getAuthenticationService().login(getUsername(), getPassword()); + loginUser(user, cycle); + } + catch (FailedLoginException ex) { + this.setError("Login failed: " + ex.getMessage()); + return; + } + } - /** - * Sets up the {@link User} as the logged in user, creates - * a cookie for their username (for subsequent logins), - * and redirects to the appropriate page, or - * a specified page). - **/ - public void loginUser(User user, IRequestCycle cycle) { + /** + * Sets up the {@link User} as the logged in user, creates + * a cookie for their username (for subsequent logins), + * and redirects to the appropriate page, or + * a specified page). + */ + public void loginUser(User user, IRequestCycle cycle) { - String username = user.getUsername(); + String username = user.getUsername(); - // Get the visit object; this will likely force the - // creation of the visit object and an HttpSession - Map visit = (Map) getVisit(); - visit.put(USER_KEY, user); + // Get the visit object; this will likely force the + // creation of the visit object and an HttpSession + Map visit = (Map) getVisit(); + visit.put(USER_KEY, user); - // After logging in, go to the MyLibrary page, unless otherwise specified - ICallback callback = getCallback(); + // After logging in, go to the MyLibrary page, unless otherwise specified + ICallback callback = getCallback(); - if (callback == null) { - cycle.activate("Home"); - } - else { - callback.performCallback(cycle); - } + if (callback == null) { + cycle.activate("Home"); + } + else { + callback.performCallback(cycle); + } - IEngine engine = getEngine(); - Cookie cookie = new Cookie(COOKIE_NAME, username); - cookie.setPath(engine.getServletPath()); - cookie.setMaxAge(ONE_WEEK); + IEngine engine = getEngine(); + Cookie cookie = new Cookie(COOKIE_NAME, username); + cookie.setPath(engine.getServletPath()); + cookie.setMaxAge(ONE_WEEK); - // Record the user's username in a cookie - cycle.getRequestContext().addCookie(cookie); - engine.forgetPage(getPageName()); - } + // Record the user's username in a cookie + cycle.getRequestContext().addCookie(cookie); + engine.forgetPage(getPageName()); + } - public void pageBeginRender(PageEvent event) { - if (getUsername() == null) { - setUsername(getRequestCycle().getRequestContext().getCookieValue(COOKIE_NAME)); - } - } -} + public void pageBeginRender(PageEvent event) { + if (getUsername() == null) { + setUsername(getRequestCycle().getRequestContext().getCookieValue(COOKIE_NAME)); + } + } + } ---- @@ -35089,50 +35283,48 @@ Spring-managed beans into Tapestry very easily; if we are using Java 5, consider order to dependency inject the Spring-managed `userService` and `authenticationService` objects (lots of the class definition has been elided for clarity). -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.whatever.web.xportal.pages; + package com.whatever.web.xportal.pages; -public abstract class Login extends BasePage implements ErrorProperty, PageRenderListener { + public abstract class Login extends BasePage implements ErrorProperty, PageRenderListener { - @InjectObject("spring:userService") - public abstract UserService getUserService(); + @InjectObject("spring:userService") + public abstract UserService getUserService(); - @InjectObject("spring:authenticationService") - public abstract AuthenticationService getAuthenticationService(); + @InjectObject("spring:authenticationService") + public abstract AuthenticationService getAuthenticationService(); -} + } ---- We are almost done. All that remains is the HiveMind configuration that exposes the Spring container stored in the `ServletContext` as a HiveMind service; for example: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - - - - - - + + + + + + + - - - - + + + + - + ---- If you are using Java 5 (and thus have access to annotations), then that really is it. @@ -35142,11 +35334,11 @@ classes with annotations; instead, one simply uses good old fashioned XML to dec dependency injection; for example, inside the `.page` or `.jwc` file for the `Login` page (or component): -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + ---- In this example, we've managed to allow service beans defined in a Spring container to @@ -35166,7 +35358,6 @@ chapter. * The http://java.sun.com/javaee/javaserverfaces/[JSF] homepage * The http://struts.apache.org/[Struts] homepage -* The http://www.opensymphony.com/webwork/[WebWork] homepage * The http://tapestry.apache.org/[Tapestry] homepage @@ -35304,20 +35495,20 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - sample - org.springframework.web.portlet.DispatcherPortlet - - text/html - view - - - Sample Portlet - - + + sample + org.springframework.web.portlet.DispatcherPortlet + + text/html + view + + + Sample Portlet + + ---- The `DispatcherPortlet` now needs to be configured. @@ -35437,18 +35628,18 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - ViewRendererServlet - org.springframework.web.servlet.ViewRendererServlet - + + ViewRendererServlet + org.springframework.web.servlet.ViewRendererServlet + - - ViewRendererServlet - /WEB-INF/servlet/view - + + ViewRendererServlet + /WEB-INF/servlet/view + ---- To perform the actual rendering, `DispatcherPortlet` does the following: @@ -35477,23 +35668,24 @@ 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] +[source,java,indent=0] ---- -public interface Controller { + 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 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; -} + /** + * 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 @@ -35563,33 +35755,38 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package samples; + package samples; -import javax.portlet.RenderRequest; -import javax.portlet.RenderResponse; + import javax.portlet.RenderRequest; + import javax.portlet.RenderResponse; -import org.springframework.web.portlet.mvc.AbstractController; -import org.springframework.web.portlet.ModelAndView; + import org.springframework.web.portlet.mvc.AbstractController; + import org.springframework.web.portlet.ModelAndView; -public class SampleController extends AbstractController { + public class SampleController extends AbstractController { - public ModelAndView handleRenderRequestInternal(RenderRequest request, RenderResponse response) { - ModelAndView mav = new ModelAndView("foo"); - mav.addObject("message", "Hello World!"); - return mav; - } -} + 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"] +---- + + + ---- The class above and the declaration in the web application context is all you need -besides setting up a handler mapping (see<>) to get this very +besides setting up a handler mapping (see <>) to get this very simple controller working. @@ -35651,22 +35848,22 @@ you start using them. [[portlet-controller-wrapping]] -==== PortletWrappingController +==== 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - config=/WEB-INF/my-portlet-config.xml - - + + + + + config=/WEB-INF/my-portlet-config.xml + + ---- This can be very valuable since you can then use interceptors to pre-process and @@ -35730,29 +35927,29 @@ properties: [[portlet-handlermapping-portletmode]] -==== PortletModeHandlerMapping +==== 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - + + + + + + + + + ---- [[portlet-handlermapping-parameter]] -==== ParameterHandlerMapping +==== 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 @@ -35764,24 +35961,24 @@ mapping. The default name of the parameter is `'action'`, but can be changed usi The bean configuration for this mapping will look something like this: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - + + + + + + + + + ---- [[portlet-handlermapping-portletmodeparameter]] -==== PortletModeParameterHandlerMapping +==== PortletModeParameterHandlerMapping The most powerful built-in handler mapping, `PortletModeParameterHandlerMapping` combines the capabilities of the two previous ones to allow different navigation within @@ -35797,28 +35994,28 @@ longer be valid in the mapping. This behavior can be changed by setting the The bean configuration for this mapping will look something like this: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + ---- This mapping can be chained ahead of a `PortletModeHandlerMapping`, which can then @@ -35827,7 +36024,7 @@ provide defaults for each mode and an overall default as well. [[portlet-handlermapping-interceptor]] -==== Adding HandlerInterceptors +==== 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 @@ -35857,7 +36054,7 @@ check what kind of request it is before processing it. [[portlet-handlermapping-interceptoradapter]] -==== HandlerInterceptorAdapter +==== HandlerInterceptorAdapter As with the servlet package, the portlet package has a concrete implementation of `HandlerInterceptor` called `HandlerInterceptorAdapter`. This class has empty versions @@ -35867,7 +36064,7 @@ methods when that is all you need. [[portlet-handlermapping-parameterinterceptor]] -==== ParameterMappingInterceptor +==== ParameterMappingInterceptor The portlet package also has a concrete interceptor named `ParameterMappingInterceptor` that is meant to be used directly with `ParameterHandlerMapping` and @@ -35893,7 +36090,7 @@ from Spring Web MVC. This includes not only the various `View` implementations themselves, but also the `ViewResolver` implementations. For more information, refer to <> and <> respectively. -A few items on using the existing `View` and `ViewResolver` implementations are worth +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, @@ -35949,15 +36146,14 @@ any other name, then the `DispatcherPortlet` will __not__ find your The following example shows how to use the `CommonsPortletMultipartResolver`: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - + + + + ---- Of course you also need to put the appropriate jars in your classpath for the multipart @@ -35985,14 +36181,14 @@ processed like any other. To use the `PortletMultipartResolver`, create a form w 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -

Please upload a file

-
- - - +

Please upload a file

+
+ + + ---- As you can see, we've created a field named "file" that matches the property of the bean @@ -36011,70 +36207,72 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - - - - - + + + + + + + - - - - - + + + + + ---- After that, create the controller and the actual class to hold the file property. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class FileUploadController extends SimpleFormController { + public class FileUploadController extends SimpleFormController { - public void onSubmitAction(ActionRequest request, ActionResponse response, - Object command, BindException errors) throws Exception { + public void onSubmitAction(ActionRequest request, ActionResponse response, + Object command, BindException errors) throws Exception { - // cast the bean - FileUploadBean bean = (FileUploadBean) command; + // 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 - } + // 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 - } + // 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 - } -} + 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 class FileUploadBean { - public void setFile(byte[] file) { - this.file = file; - } + private byte[] file; - public byte[] getFile() { - return 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 @@ -36087,49 +36285,48 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class FileUploadController extends SimpleFormController { + public class FileUploadController extends SimpleFormController { - public void onSubmitAction(ActionRequest request, ActionResponse response, - Object command, BindException errors) throws Exception { + public void onSubmitAction(ActionRequest request, ActionResponse response, + Object command, BindException errors) throws Exception { - // cast the bean - FileUploadBean bean = (FileUploadBean) command; + // 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 - } + // 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 - } + // do something with the file here + } - protected void initBinder( - PortletRequest request, PortletRequestDataBinder binder) throws Exception { + 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 - } -} + // 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 { + public class FileUploadBean { - private String file; + private String file; - public void setFile(String file) { - this.file = file; - } + public void setFile(String file) { + this.file = file; + } - public String getFile() { - return file; - } -} + public String getFile() { + return file; + } + } ---- Of course, this last example only makes (logical) sense in the context of uploading a @@ -36139,39 +36336,40 @@ The third (and final) option is where one binds directly to a `MultipartFile` pr 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class FileUploadController extends SimpleFormController { + public class FileUploadController extends SimpleFormController { - public void onSubmitAction(ActionRequest request, ActionResponse response, - Object command, BindException errors) throws Exception { + public void onSubmitAction(ActionRequest request, ActionResponse response, + Object command, BindException errors) throws Exception { - // cast the bean - FileUploadBean bean = (FileUploadBean) command; + // 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 - } + // 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 - } -} + // do something with the file here + } + } -public class FileUploadBean { + public class FileUploadBean { - private MultipartFile file; + private MultipartFile file; - public void setFile(MultipartFile file) { - this.file = file; - } + public void setFile(MultipartFile file) { + this.file = file; + } - public MultipartFile getFile() { - return file; - } -} + public MultipartFile getFile() { + return file; + } + + } ---- @@ -36215,22 +36413,22 @@ need to make sure that a corresponding custom `DefaultAnnotationHandlerMapping` `AnnotationMethodHandlerAdapter` is defined as well - provided that you intend to use `@RequestMapping`. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + - + - // ... (controller bean definitions) ... + // ... (controller bean definitions) ... - + ---- Defining a `DefaultAnnotationHandlerMapping` and/or `AnnotationMethodHandlerAdapter` @@ -36240,7 +36438,7 @@ specifying a custom `WebBindingInitializer` (see below). [[portlet-ann-controller]] -==== Defining a controller with @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 @@ -36260,31 +36458,31 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + - // ... + // ... - + ---- [[portlet-ann-requestmapping]] -==== Mapping requests with @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 @@ -36308,55 +36506,57 @@ handler methods. The following is an example of a form controller from the PetPortal sample application using this annotation: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Controller -@RequestMapping("EDIT") -@SessionAttributes("site") -public class PetSitesEditController { - private Properties petSites; + @Controller + @RequestMapping("EDIT") + @SessionAttributes("site") + public class PetSitesEditController { - public void setPetSites(Properties petSites) { - this.petSites = petSites; - } + private Properties petSites; - @ModelAttribute("petSites") - public Properties getPetSites() { - return this.petSites; - } + public void setPetSites(Properties petSites) { + this.petSites = petSites; + } - @RequestMapping // default (action=list) - public String showPetSites() { - return "petSitesEdit"; - } + @ModelAttribute("petSites") + public Properties getPetSites() { + return this.petSites; + } - @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()); - } + @RequestMapping // default (action=list) + public String showPetSites() { + return "petSitesEdit"; + } - return "petSitesAdd"; - } + @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()); + } - @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"); - } - } + return "petSitesAdd"; + } - @RequestMapping(params = "action=delete") - public void removeSite(@RequestParam("site") String site, ActionResponse response) { - this.petSites.remove(site); - response.setRenderParameter("action", "list"); - } -} + @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"); + } + } ---- @@ -36382,6 +36582,7 @@ command object, if desired): 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 @@ -36433,29 +36634,31 @@ The following return types are supported for handler methods: [[portlet-ann-requestparam]] -==== Binding request parameters to method parameters with @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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Controller -@RequestMapping("EDIT") -@SessionAttributes("site") -public class PetSitesEditController { + @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"); - } - // ... -} + 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 @@ -36465,7 +36668,7 @@ parameter is optional by setting `@RequestParam`'s `required` attribute to `fals [[portlet-ann-modelattrib]] -==== Providing a link to data from the model with @ModelAttribute +==== 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 @@ -36478,45 +36681,48 @@ a generic `java.lang.Object`, thus increasing type safety. 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 +[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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Controller -@RequestMapping("EDIT") -@SessionAttributes("site") -public class PetSitesEditController { + @Controller + @RequestMapping("EDIT") + @SessionAttributes("site") + public class PetSitesEditController { - // ... + // ... - @ModelAttribute("petSites") - public Properties getPetSites() { - return this.petSites; - } + @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"); - } - } -} + @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 +==== 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 @@ -36525,21 +36731,21 @@ conversational storage, serving as form-backing beans between subsequent request The following code snippet shows the usage of this annotation: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Controller -@RequestMapping("EDIT") -@SessionAttributes("site") -public class PetSitesEditController { - // ... -} + @Controller + @RequestMapping("EDIT") + @SessionAttributes("site") + public class PetSitesEditController { + // ... + } ---- [[portlet-ann-webdatabinder]] -==== Customizing WebDataBinder initialization +==== Customizing WebDataBinder initialization To customize request parameter binding with PropertyEditors, etc. via Spring's `WebDataBinder`, you can either use `@InitBinder`-annotated methods within your @@ -36548,7 +36754,7 @@ controller or externalize your configuration by providing a custom [[portlet-ann-initbinder]] -===== Customizing data binding with @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 @@ -36564,26 +36770,27 @@ arguments include `WebDataBinder` in combination with `WebRequest` or The following example demonstrates the use of `@InitBinder` for configuring a `CustomDateEditor` for all `java.util.Date` form properties. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Controller -public class MyFormController { + @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)); - } + @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 +===== 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 @@ -36632,7 +36839,6 @@ Some older portals have been known to corrupt the definition of the - [[spring-integration]] = Integration [partintro] @@ -36689,59 +36895,63 @@ usual (Spring) POJOs. Currently, Spring supports the following remoting technolo While discussing the remoting capabilities of Spring, we'll use the following domain model and corresponding services: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class Account implements Serializable{ + public class Account implements Serializable{ - private String name; + private String name; - public String getName(){ - return name; - } + public String getName(){ + return name; + } - public void setName(String name) { - this.name = name; - } -} + public void setName(String name) { + this.name = name; + } + + } ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface AccountService { + public interface AccountService { - public void insertAccount(Account account); + public void insertAccount(Account account); - public List getAccounts(String name); -} + public List getAccounts(String name); + + } ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface RemoteAccountService extends Remote { + public interface RemoteAccountService extends Remote { - public void insertAccount(Account account) throws RemoteException; + public void insertAccount(Account account) throws RemoteException; - public List getAccounts(String name) throws RemoteException; -} + public List getAccounts(String name) throws RemoteException; + + } ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// the implementation doing nothing at the moment -public class AccountServiceImpl implements AccountService { + // the implementation doing nothing at the moment + public class AccountServiceImpl implements AccountService { - public void insertAccount(Account acc) { - // do something... - } + public void insertAccount(Account acc) { + // do something... + } - public List getAccounts(String name) { - // do something... - } -} + public List getAccounts(String name) { + // do something... + } + + } ---- We will start exposing the service to a remote client by using RMI and talk a bit about @@ -36772,27 +36982,27 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- Next we'll have to expose our service using the `RmiServiceExporter`: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - + + + + + + + + ---- As you can see, we're overriding the port for the RMI registry. Often, your application @@ -36813,36 +37023,36 @@ anonymous port will be used to communicate with the service. ==== Linking in the service at the client Our client is a simple object using the `AccountService` to manage accounts: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class SimpleObject { + public class SimpleObject { - private AccountService accountService; + private AccountService accountService; - public void setAccountService(AccountService accountService) { - this.accountService = accountService; - } + public void setAccountService(AccountService accountService) { + this.accountService = accountService; + } - // additional methods using the 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + - - - - + + + + ---- That's all we need to do to support the remote account service on the client. Spring @@ -36867,19 +37077,19 @@ Hessian communicates via HTTP and does so using a custom servlet. Using Spring's 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - remoting - org.springframework.web.servlet.DispatcherServlet - 1 - + + remoting + org.springframework.web.servlet.DispatcherServlet + 1 + - - remoting - /remoting/* - + + remoting + /remoting/* + ---- You're probably familiar with Spring's `DispatcherServlet` principles and if so, you @@ -36901,17 +37111,17 @@ its target exporter in this case. In the newly created application context called `remoting-servlet.xml`, we'll create a `HessianServiceExporter` exporting your services: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + - - - - + + + + ---- Now we're ready to link in the service at the client. No explicit handler mapping is @@ -36923,13 +37133,13 @@ within the containing `DispatcherServlet`'s mapping (as defined above): Alternatively, create a `HessianServiceExporter` in your root application context (e.g. in `'WEB-INF/applicationContext.xml'`): -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + ---- In the latter case, define a corresponding servlet for this exporter in `'web.xml'`, @@ -36937,18 +37147,18 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - accountExporter - org.springframework.web.context.support.HttpRequestHandlerServlet - + + accountExporter + org.springframework.web.context.support.HttpRequestHandlerServlet + - - accountExporter - /remoting/AccountService - + + accountExporter + /remoting/AccountService + ---- @@ -36960,17 +37170,17 @@ principles apply as with the RMI example. We'll create a separate bean factory o application context and mention the following beans where the `SimpleObject` is using the `AccountService` to manage accounts: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + - - - - + + + + ---- @@ -36992,17 +37202,17 @@ example. Usually, you don't use per-user security credentials here, but rather s credentials defined at the `Hessian/BurlapProxyFactoryBean` level (similar to a JDBC `DataSource`). -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + - - - + + + ---- This is an example where we explicitly mention the `BeanNameUrlHandlerMapping` and set @@ -37047,13 +37257,13 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + ---- Such an exporter definition will be exposed through the `DispatcherServlet`'s standard @@ -37062,30 +37272,30 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + ---- 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - accountExporter - org.springframework.web.context.support.HttpRequestHandlerServlet - + + accountExporter + org.springframework.web.context.support.HttpRequestHandlerServlet + - - accountExporter - /remoting/AccountService - + + accountExporter + /remoting/AccountService + ---- If you are running outside of a servlet container and are using Sun's Java 6, then you @@ -37093,24 +37303,24 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + - - - - - - - - + + + + + + + + ---- @@ -37121,25 +37331,25 @@ Again, linking in the service from the client much resembles the way you would d 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + ---- As mentioned before, you can choose what HTTP client you want to use. By default, the `HttpInvokerProxy` uses the J2SE HTTP functionality, but you can also use the Commons `HttpClient` by setting the `httpInvokerRequestExecutor` property: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- @@ -37164,46 +37374,47 @@ future-proof web services. 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 2.5's `@Autowired` +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] +[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; + /** + * 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 { + @WebService(serviceName="AccountService") + public class AccountServiceEndpoint extends SpringBeanAutowiringSupport { - @Autowired - private AccountService biz; + @Autowired + private AccountService biz; - @WebMethod - public void insertAccount(Account acc) { - biz.insertAccount(acc); - } + @WebMethod + public void insertAccount(Account acc) { + biz.insertAccount(acc); + } - @WebMethod - public Account[] getAccounts(String name) { - return biz.getAccounts(name); - } -} + @WebMethod + public Account[] getAccounts(String name) { + return biz.getAccounts(name); + } + + } ---- Our `AccountServletEndpoint` needs to run in the same web application as the Spring @@ -37227,18 +37438,18 @@ up to the Spring application context. This means that Spring functionality like dependency injection may be applied to the endpoint instances. Of course, annotation-driven injection through `@Autowired` will work as well. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + - - ... - + + ... + -... + ... ---- The `AccountServiceEndpoint` may derive from Spring's `SpringBeanAutowiringSupport` but @@ -37246,25 +37457,26 @@ doesn't have to since the endpoint is a fully Spring-managed bean here. This mea the endpoint implementation may look like as follows, without any superclass declared - and Spring's `@Autowired` configuration annotation still being honored: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@WebService(serviceName="AccountService") -public class AccountServiceEndpoint { + @WebService(serviceName="AccountService") + public class AccountServiceEndpoint { - @Autowired - private AccountService biz; + @Autowired + private AccountService biz; - @WebMethod - public void insertAccount(Account acc) { - biz.insertAccount(acc); - } + @WebMethod + public void insertAccount(Account acc) { + biz.insertAccount(acc); + } - @WebMethod - public List getAccounts(String name) { - return biz.getAccounts(name); - } -} + @WebMethod + public List getAccounts(String name) { + return biz.getAccounts(name); + } + + } ---- @@ -37299,16 +37511,16 @@ return a JAX-WS service class for us to work with. The latter is the full-fledge 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - + + + + + + + ---- Where `serviceInterface` is our business interface the clients will use. @@ -37320,39 +37532,42 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - ... - - + + ... + + ---- From the client code we can access the web service just as if it was a normal class: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class AccountClientImpl { + public class AccountClientImpl { - private AccountService service; + private AccountService service; - public void setService(AccountService service) { - this.service = service; - } + public void setService(AccountService service) { + this.service = service; + } - public void foo() { - service.insertAccount(...); - } -} + public void foo() { + service.insertAccount(...); + } + } ---- -__NOTE:__ The above is slightly simplified in that JAX-WS requires endpoint interfaces +[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. +==== @@ -37369,53 +37584,55 @@ __messaging__. The following interface is used on both the server and the client side. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.foo; + package com.foo; -public interface CheckingAccountService { + public interface CheckingAccountService { - public void cancelAccount(Long accountId); -} + public void cancelAccount(Long accountId); + + } ---- The following simple implementation of the above interface is used on the server-side. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.foo; + package com.foo; -public class SimpleCheckingAccountService implements CheckingAccountService { + public class SimpleCheckingAccountService implements CheckingAccountService { - public void cancelAccount(Long accountId) { - System.out.println("Cancelling account [" + accountId + "]"); - } -} + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - - + + + - - - + + + - + ---- @@ -37425,46 +37642,47 @@ the client and server. On the server, you just need to expose the service object using the `JmsInvokerServiceExporter`. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - - - - - + + + + + + - - - - - - + + + + + + - + ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.foo; + package com.foo; -import org.springframework.context.support.ClassPathXmlApplicationContext; + import org.springframework.context.support.ClassPathXmlApplicationContext; -public class Server { + public class Server { - public static void main(String[] args) throws Exception { - new ClassPathXmlApplicationContext(new String[]{"com/foo/server.xml", "com/foo/jms.xml"}); - } -} + public static void main(String[] args) throws Exception { + new ClassPathXmlApplicationContext(new String[]{"com/foo/server.xml", "com/foo/jms.xml"}); + } + + } ---- @@ -37476,42 +37694,43 @@ upon interface ( `CheckingAccountService`). The resulting object created off the 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - - - - + + + + + - + ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.foo; + package com.foo; -import org.springframework.context.ApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; + import org.springframework.context.ApplicationContext; + import org.springframework.context.support.ClassPathXmlApplicationContext; -public class Client { + 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)); - } -} + 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 @@ -37541,7 +37760,7 @@ Offering a proxy with all interfaces implemented by the target usually does not 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 +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 @@ -37609,27 +37828,27 @@ This section describes how to use the `RestTemplate` and its associated [[rest-resttemplate]] ==== RestTemplate -Invoking RESTful services in Java is typically done using a helper class such as Jakarta -Commons `HttpClient`. For common REST operations this approach is too low level as shown -below. +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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -String uri = "http://example.com/hotels/1/bookings"; + String uri = "http://example.com/hotels/1/bookings"; -PostMethod post = new PostMethod(uri); -String request = // create booking request content -post.setRequestEntity(new StringRequestEntity(request)); + PostMethod post = new PostMethod(uri); + String request = // create booking request content + post.setRequestEntity(new StringRequestEntity(request)); -httpClient.executeMethod(post); + 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()); - } -} + 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 @@ -37643,29 +37862,33 @@ practices. | HTTP Method | RestTemplate Method | DELETE -| http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html#delete(String,%20Object...)[delete] +| {javadoc-baseurl}/org/springframework/web/client/RestTemplate.html#delete(String,%20Object...)[delete] | GET -| http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html#getForObject(String,%20Class,%20Object...)[getForObject] - http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html#getForEntity(String,%20Class,%20Object...)[getForEntity] +| {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 -| http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html#headForHeaders(String,%20Object...)[headForHeaders(String +| {javadoc-baseurl}/org/springframework/web/client/RestTemplate.html#headForHeaders(String,%20Object...)[headForHeaders(String url, String... urlVariables)] | OPTIONS -| http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html#optionsForAllow(String,%20Object...)[optionsForAllow(String +| {javadoc-baseurl}/org/springframework/web/client/RestTemplate.html#optionsForAllow(String,%20Object...)[optionsForAllow(String url, String... urlVariables)] | POST -| http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html#postForLocation(String,%20Object,%20Object...)[postForLocation(String +| {javadoc-baseurl}/org/springframework/web/client/RestTemplate.html#postForLocation(String,%20Object,%20Object...)[postForLocation(String url, Object request, String... urlVariables)] - http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html#postForObject(java.lang.String,%20java.lang.Object,%20java.lang.Class,%20java.lang.String...)[postForObject(String + {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 responseType, String... uriVariables)] | PUT -| http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html#put(String,%20Object,%20Object...)[put(String +| {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 @@ -37678,6 +37901,15 @@ exception processing the HTTP request, an exception of the type `RestClientExcep 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`), 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 @@ -37690,21 +37922,21 @@ defaults using the `messageConverters()` bean property as would be required if u Each method takes URI template arguments in two forms, either as a `String` variable length argument or a `Map`. For example, -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/bookings/{booking}", - String.class,"42", "21"); + String result = restTemplate.getForObject( + "http://example.com/hotels/{hotel}/bookings/{booking}", String.class,"42", "21"); ---- using variable length arguments and -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -Map vars = Collections.singletonMap("hotel", "42"); -String result = - restTemplate.getForObject("http://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars); + Map vars = Collections.singletonMap("hotel", "42"); + String result = restTemplate.getForObject( + "http://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars); ---- using a `Map`. @@ -37727,25 +37959,25 @@ issue, switch to `HttpComponentsClientHttpRequestFactory` instead. The previous example using Apache HttpComponents `HttpClient` directly rewritten to use the `RestTemplate` is shown below -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -uri = "http://example.com/hotels/{id}/bookings"; + uri = "http://example.com/hotels/{id}/bookings"; -RestTemplate template = new RestTemplate(); + RestTemplate template = new RestTemplate(); -Booking booking = // create booking object + Booking booking = // create booking object -URI location = template.postForLocation(uri, booking, "1"); + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory()); + RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory()); ---- [TIP] @@ -37753,35 +37985,34 @@ RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFact Apache HttpClient supports gzip encoding via the `DecompressingHttpClient`. To use it, construct a `HttpComponentsClientHttpRequestFactory` like so: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -HttpClient httpClient = new DecompressingHttpClient(new DefaultHttpClient()); -ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); -RestTemplate template = new RestTemplate(requestFactory); + HttpClient httpClient = new DecompressingHttpClient(new DefaultHttpClient()); + ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); + RestTemplate template = new RestTemplate(requestFactory); ---- ==== The general callback interface is `RequestCallback` and is called when the execute method is invoked. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public T execute(String url, HttpMethod method, RequestCallback requestCallback, - ResponseExtractor responseExtractor, - String... urlVariables) + public T execute(String url, HttpMethod method, RequestCallback requestCallback, + ResponseExtractor responseExtractor, String... urlVariables) -// also has an overload with urlVariables as a Map. + // also has an overload with urlVariables as a Map. ---- The `RequestCallback` interface is defined as -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface RequestCallback { - void doWithRequest(ClientHttpRequest request) throws IOException; -} + public interface RequestCallback { + void doWithRequest(ClientHttpRequest request) throws IOException; + } ---- and allows you to manipulate the request headers and write to the request body. When @@ -37800,10 +38031,10 @@ The String URI variants accept template arguments as a String variable length ar or as a `Map`. They also assume the URL String is not encoded and needs to be encoded. For example the following: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -restTemplate.getForObject("http://example.com/hotel list", String.class); + 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 @@ -37816,29 +38047,28 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -UriComponents uriComponents = - UriComponentsBuilder.fromUriString("http://example.com/hotels/{hotel}/bookings/{booking}").build() - .expand("42", "21") - .encode(); + UriComponents uriComponents = UriComponentsBuilder.fromUriString( + "http://example.com/hotels/{hotel}/bookings/{booking}").build() + .expand("42", "21") + .encode(); -URI uri = uriComponents.toUri(); + URI uri = uriComponents.toUri(); ---- Or specify each URI component individually: -[source,java] +[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(); + UriComponents uriComponents = UriComponentsBuilder.newInstance() + .scheme("http").host("example.com").path("/hotels/{hotel}/bookings/{booking}").build() + .expand("42", "21") + .encode(); -URI uri = uriComponents.toUri(); + URI uri = uriComponents.toUri(); ---- @@ -37851,18 +38081,19 @@ class. Perhaps most importantly, the `exchange()` method can be used to add request headers and read response headers. For example: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -HttpHeaders requestHeaders = new HttpHeaders(); -requestHeaders.set("MyRequestHeader", "MyValue"); -HttpEntity requestEntity = new HttpEntity(requestHeaders); + HttpHeaders requestHeaders = new HttpHeaders(); + requestHeaders.set("MyRequestHeader", "MyValue"); + HttpEntity requestEntity = new HttpEntity(requestHeaders); -HttpEntity response = template.exchange("http://example.com/hotels/{hotel}", - HttpMethod.GET, requestEntity, String.class, "42"); + HttpEntity response = template.exchange( + "http://example.com/hotels/{hotel}", + HttpMethod.GET, requestEntity, String.class, "42"); -String responseHeader = response.getHeaders().getFirst("MyResponseHeader"); -String body = response.getBody(); + String responseHeader = response.getHeaders().getFirst("MyResponseHeader"); + String body = response.getBody(); ---- In the above example, we first prepare a request entity that contains the @@ -37878,29 +38109,27 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface HttpMessageConverter { + public interface HttpMessageConverter { - // 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 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); + // 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 getSupportedMediaTypes(); + // Return the list of MediaType objects supported by this converter. + List getSupportedMediaTypes(); - // Read an object of the given type from the given input message, and returns it. - T read(Class clazz, HttpInputMessage inputMessage) throws IOException, - HttpMessageNotReadableException; + // Read an object of the given type from the given input message, and returns it. + T read(Class clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException; - // Write an given object to the given output message. - void write(T t, HttpOutputMessage outputMessage) throws IOException, - HttpMessageNotWritableException; + // 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 @@ -38016,13 +38245,13 @@ To avoid repeated low-level code, many EJB applications use the Service Locator 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, +* 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, +* 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 +* 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. @@ -38040,12 +38269,12 @@ practice and use the EJB Business Methods Interface pattern, so that the EJB's l interface extends a non EJB-specific business methods interface. Let's call this business methods interface `MyComponent`. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface MyComponent { - ... -} + public interface MyComponent { + ... + } ---- One of the main reasons to use the Business Methods Interface pattern is to ensure that @@ -38054,19 +38283,19 @@ class is automatic. Another reason is that it later makes it much easier for us 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 +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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -private MyComponent myComponent; + private MyComponent myComponent; -public void setMyComponent(MyComponent myComponent) { - this.myComponent = myComponent; -} + public void setMyComponent(MyComponent myComponent) { + this.myComponent = myComponent; + } ---- We can subsequently use this instance variable in any business method in the controller. @@ -38075,18 +38304,18 @@ Now assuming we are obtaining our controller object out of a Spring container, w 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + - - - + + + ---- There's a lot of work happening behind the scenes, courtesy of the Spring AOP framework, @@ -38102,15 +38331,15 @@ class to the EJB proxy. Alternatively (and preferably in case of many such proxy definitions), consider using the `` configuration element in Spring's "jee" namespace: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - + + + ---- This EJB access mechanism delivers huge simplification of application code: the web tier @@ -38195,28 +38424,30 @@ lookups simply provides consistent and more explicit EJB access configuration. [[ejb-implementation-ejb3]] ==== EJB 3 injection interceptor For EJB 3 Session Beans and Message-Driven Beans, Spring provides a convenient -interceptor that resolves Spring 2.5's `@Autowired` annotation in the EJB component +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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Stateless -@Interceptors(SpringBeanAutowiringInterceptor.class) -public class MyFacadeEJB implements MyFacadeLocal { + @Stateless + @Interceptors(SpringBeanAutowiringInterceptor.class) + public class MyFacadeEJB implements MyFacadeLocal { - // automatically injected with a matching Spring bean - @Autowired - private MyComponent myComp; + // 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(...); - } - ... -} + // for business method, delegate to POJO service impl. + public String myFacadeMethod(...) { + return myComp.myMethod(...); + } + + ... + + } ---- `SpringBeanAutowiringInterceptor` by default obtains target beans from a @@ -38290,7 +38521,7 @@ resource into Spring's transaction management mechanisms. [[jms-jmstemplate]] -==== 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 @@ -38351,7 +38582,7 @@ managed implementation of the `ConnectionFactory`. The standard API involves creating many intermediate objects. To send a message the following 'API' walk is performed -[source] +[literal] [subs="verbatim,quotes"] ---- ConnectionFactory->Connection->Session->MessageProducer->send @@ -38482,7 +38713,7 @@ with Java EE environments. ==== 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<>. The `JmsTransactionManager` performs +features of Spring as described in <>. 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. @@ -38514,7 +38745,7 @@ transactional JMS `Session`. [[jms-sending]] -=== Sending a Message +=== 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 @@ -38522,39 +38753,39 @@ that specify the destination using a string for use in a JNDI lookup. The send m that takes no destination argument uses the default destination. Here is an example that sends a message to a queue using the 1.0.2 implementation. -[source,java] +[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 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; + import org.springframework.jms.core.MessageCreator; + import org.springframework.jms.core.JmsTemplate; -public class JmsQueueSender { + public class JmsQueueSender { - private JmsTemplate jmsTemplate; - private Queue queue; + private JmsTemplate jmsTemplate; + private Queue queue; - public void setConnectionFactory(ConnectionFactory cf) { - this.jmsTemplate = new JmsTemplate(cf); - } + public void setConnectionFactory(ConnectionFactory cf) { + this.jmsTemplate = new JmsTemplate(cf); + } - public void setQueue(Queue queue) { - this.queue = queue; - } + 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"); - } - }); - } -} + 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 @@ -38599,40 +38830,40 @@ gives you access to the message after it has been converted, but before it is se example below demonstrates how to modify a message header and a property after a `java.util.Map` is converted to a message. -[source,java] +[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; - } - }); -} + 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: -[source] +[literal] [subs="verbatim,quotes"] ---- MapMessage={ - Header={ - ... standard headers ... - CorrelationID={123-00001} - } - Properties={ - AccountID={Integer:1234} - } - Fields={ - Name={String:Mark} - Age={Integer:47} - } + Header={ + ... standard headers ... + CorrelationID={123-00001} + } + Properties={ + AccountID={Integer:1234} + } + Fields={ + Name={String:Mark} + Age={Integer:47} + } } ---- @@ -38677,30 +38908,31 @@ ensure that your implementation is thread-safe. Below is a simple implementation of an MDP: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageListener; -import javax.jms.TextMessage; + import javax.jms.JMSException; + import javax.jms.Message; + import javax.jms.MessageListener; + import javax.jms.TextMessage; -public class ExampleListener implements MessageListener { + 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"); - } - } -} + 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 @@ -38709,18 +38941,18 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - - - - **** - + + + + + **** + ---- Please refer to the Spring Javadoc of the various message listener containers for a full @@ -38735,15 +38967,16 @@ The `SessionAwareMessageListener` interface is a Spring-specific interface that 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.springframework.jms.listener; + package org.springframework.jms.listener; -public interface SessionAwareMessageListener { + public interface SessionAwareMessageListener { - void onMessage(Message message, Session session) throws JMSException; -} + void onMessage(Message message, Session session) throws JMSException; + + } ---- You can choose to have your MDPs implement this interface (in preference to the standard @@ -38773,52 +39006,53 @@ messaging support: in a nutshell, it allows you to expose almost __any__ class a 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 handling methods are strongly typed according to the __contents__ of the various `Message` types that they can receive and handle. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface MessageDelegate { + public interface MessageDelegate { - void handleMessage(String message); + void handleMessage(String message); - void handleMessage(Map message); + void handleMessage(Map message); - void handleMessage(byte[] message); + void handleMessage(byte[] message); - void handleMessage(Serializable message); -} + void handleMessage(Serializable message); + + } ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class DefaultMessageDelegate implements MessageDelegate { - // implementation elided for clarity... -} + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - -** - - - -** + + ** + + + + ** - - - - - **** - + + + + + **** + ---- Below is an example of another MDP that can only handle the receiving of JMS @@ -38828,38 +39062,39 @@ defaults to `'handleMessage'`), but it is configurable (as you will see below). also how the `'receive(..)'` method is strongly typed to receive and respond only to JMS `TextMessage` messages. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface TextMessageDelegate { + public interface TextMessageDelegate { - void receive(TextMessage message); -} + void receive(TextMessage message); + + } ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class DefaultTextMessageDelegate implements TextMessageDelegate { - // implementation elided for clarity... -} + public class DefaultTextMessageDelegate implements TextMessageDelegate { + // implementation elided for clarity... + } ---- The configuration of the attendant `MessageListenerAdapter` would look like this: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - - + + + + + + + + + + ---- Please note that if the above `'messageListener'` receives a JMS `Message` of a type @@ -38868,22 +39103,23 @@ swallowed). Another of the capabilities of the `MessageListenerAdapter` class is ability to automatically send back a response `Message` if a handler method returns a non-void value. Consider the interface and class: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface ResponsiveTextMessageDelegate { + public interface ResponsiveTextMessageDelegate { - // notice the return type... - String receive(TextMessage message); -} + // notice the return type... + String receive(TextMessage message); + + } ---- -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class DefaultResponsiveTextMessageDelegate implements ResponsiveTextMessageDelegate { - // implementation elided for clarity... -} + public class DefaultResponsiveTextMessageDelegate implements ResponsiveTextMessageDelegate { + // implementation elided for clarity... + } ---- If the above `DefaultResponsiveTextMessageDelegate` is used in conjunction with a @@ -38912,15 +39148,15 @@ database access) will operate independently. This usually requires duplicate mes detection in the listener implementation, covering the case where database processing has committed but message processing failed to commit. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - **** - + + + + + **** + ---- For participating in an externally managed transaction, you will need to configure a @@ -38935,24 +39171,24 @@ Java EE server's configuration of JNDI resources.) This allows message reception 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- Then you just need to add it to our earlier container configuration. The container will take care of the rest. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - **** - + + + + + **** + ---- @@ -38966,55 +39202,55 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - + + + + + + + + + ---- Alternatively, you may set up a `JmsMessageEndpointManager` with a given `ActivationSpec` object. The `ActivationSpec` object may also come from a JNDI lookup (using ``). -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - - + + + + + + + + + + ---- Using Spring's `ResourceAdapterFactoryBean`, the target `ResourceAdapter` may be configured locally as depicted in the following example. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - - + + + + + + + + + + ---- The specified `WorkManager` may also point to an environment-specific thread pool - @@ -39055,34 +39291,36 @@ contract. Spring 2.5 introduces an XML namespace for simplifying JMS configuration. To use the JMS namespace elements you will need to reference the JMS schema: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - + - + ---- The namespace consists of two top-level elements: `` and `` both of which may contain one or more `` child elements. Here is an example of a basic configuration for two listeners. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - + - + ---- The example above is equivalent to creating two distinct listener container bean @@ -39132,20 +39370,20 @@ allows for customization of the various strategies (for example, `taskExecutor` these attributes, it is possible to define highly-customized listener containers while still benefiting from the convenience of the namespace. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - + - + ---- The following table describes all available attributes. Consult the class-level Javadoc @@ -39240,17 +39478,17 @@ choices and message redelivery scenarios. Configuring a JCA-based listener container with the "jms" schema support is very similar. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - + ---- The available configuration options for the JCA variant are described in the following @@ -39351,71 +39589,71 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.springframework.jmx; + package org.springframework.jmx; -public class JmxTestBean implements IJmxTestBean { + public class JmxTestBean implements IJmxTestBean { - private String name; - private int age; - private boolean isSuperman; + private String name; + private int age; + private boolean isSuperman; - public int getAge() { - return age; - } + public int getAge() { + return age; + } - public void setAge(int age) { - this.age = age; - } + public void setAge(int age) { + this.age = age; + } - public void setName(String name) { - this.name = name; - } + public void setName(String name) { + this.name = name; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public int add(int x, int y) { - return x + y; - } + public int add(int x, int y) { + return x + y; + } - public void dontExposeMe() { - throw new RuntimeException(); - } -} + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - - - - - - + + + + + + + + + + + + + + ---- 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<>. +corresponding entry value. This behavior can be changed as described in <>. 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 @@ -39425,7 +39663,7 @@ are exposed as attributes and all __public__ methods (bar those inherited from t [[jmx-exporting-mbeanserver]] -==== Creating an 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 @@ -39441,32 +39679,32 @@ You can also ensure that a specific `MBeanServer` is used by setting the value o `MBeanExporter`'s `server` property to the `MBeanServer` value returned by an `MBeanServerFactoryBean`; for example: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - - - - - - - - - + + + + + + + + + - - - - + + + + - + ---- Here an instance of `MBeanServer` is created by the `MBeanServerFactoryBean` and is @@ -39478,7 +39716,7 @@ correctly, you must (of course) have a JMX implementation on your classpath. [[jmx-mbean-server]] -==== Reusing an existing MBeanServer +==== 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 @@ -39486,41 +39724,41 @@ used, however when multiple instances exist, the exporter might pick the wrong s In such cases, one should use the `MBeanServer` `agentId` to indicate which instance to be used: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - - ... - - + + + + + + + + + + ... + + ---- For platforms/cases where the existing `MBeanServer` has a dynamic (or unknown) `agentId` which is retrieved through lookup methods, one should use <>: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - + + + + + + + - + - - + ---- @@ -39542,14 +39780,14 @@ registered as-is with the `MBeanServer` without further intervention from Spring can be automatically detected by the `MBeanExporter` by setting the `autodetect` property to `true`: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + - + ---- Here, the bean called `spring:mbean=true` is already a valid JMX MBean and will be @@ -39579,19 +39817,19 @@ these registration behaviors are summarized on the following table: | Registration behavior| Explanation | `REGISTRATION_FAIL_ON_EXISTING` -| This is the default registration behavior. If an `MBean` instance has already been +| 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 +| 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 +| 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). @@ -39606,26 +39844,26 @@ values. The following example illustrates how to effect a change from the default registration behavior to the `REGISTRATION_REPLACE_EXISTING` behavior: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - - - - - - + + + + + + + + - - - - + + + + - + ---- @@ -39678,57 +39916,65 @@ attribute respectively. The example below shows the annotated version of the `JmxTestBean` class that you saw earlier: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.springframework.jmx; + 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; + 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 { + @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; + private String name; + private int age; - @ManagedAttribute(description="The Age Attribute", currencyTimeLimit=15) - public int getAge() { - return age; - } + @ManagedAttribute(description="The Age Attribute", currencyTimeLimit=15) + public int getAge() { + return age; + } - public void setAge(int age) { - this.age = 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(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; - } + @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; - } + @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(); - } -} + public void dontExposeMe() { + throw new RuntimeException(); + } + + } ---- Here you can see that the `JmxTestBean` class is marked with the `ManagedResource` @@ -39750,36 +39996,36 @@ the management interface to contain only one operation, `add(int, int)`, when us The configuration below shows how you configure the `MBeanExporter` to use the `MetadataMBeanInfoAssembler`: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - + + + + + + - + - - - - + + + + - - - - + + + + - - - - - + + + + + ---- Here you can see that an `MetadataMBeanInfoAssembler` bean has been configured with an @@ -39889,29 +40135,29 @@ the `MetadataMBeanInfoAssembler` which will vote to include any bean which is ma 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - + ---- Notice that in this configuration no beans are passed to the `MBeanExporter`; however, @@ -39938,55 +40184,56 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface IJmxTestBean { + public interface IJmxTestBean { - public int add(int x, int y); + public int add(int x, int y); - public long myOperation(); + public long myOperation(); - public int getAge(); + public int getAge(); - public void setAge(int age); + public void setAge(int age); - public void setName(String name); + public void setName(String name); - public String getName(); -} + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - - - - - - - - org.springframework.jmx.IJmxTestBean - - - - + + + + + + + + + + org.springframework.jmx.IJmxTestBean + + + + - - - - + + + + - + ---- Here you can see that the `InterfaceBasedMBeanInfoAssembler` is configured to use the @@ -40010,29 +40257,29 @@ management interface. [[jmx-interface-methodnames]] -==== Using MethodNameBasedMBeanInfoAssembler +==== 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - - add,myOperation,getName,setName,getAge - - - - + + + + + + + + + + add,myOperation,getName,setName,getAge + + + + ---- Here you can see that the methods `add` and `myOperation` will be exposed as JMX @@ -40061,7 +40308,7 @@ uses source level metadata to obtain the `ObjectName`. [[jmx-naming-properties]] -==== Reading ObjectNames from 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 @@ -40071,37 +40318,37 @@ corresponding to the bean key. If no entry is found or if the `Properties` insta The code below shows a sample configuration for the `KeyNamingStrategy`: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - - - - - - + + + + + + + + - - - - + + + + - - - - bean:name=testBean1 - - - - names1.properties,names2.properties - - + + + bean:name=testBean1 + + + + names1.properties,names2.properties + + - + ---- Here an instance of `KeyNamingStrategy` is configured with a `Properties` instance that @@ -40123,33 +40370,33 @@ The `MetadataNamingStrategy` uses the `objectName` property of the `ManagedResou attribute on each bean to create the `ObjectName`. The code below shows the configuration for the `MetadataNamingStrategy`: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - - - - - - + + + + + + + + - - - - + + + + - - - + + + - + - + ---- If no `objectName` has been provided for the `ManagedResource` attribute, then an @@ -40158,10 +40405,10 @@ format:__[fully-qualified-package-name]:type=[short-classname],name=[bean-name]_ example, the generated `ObjectName` for the following bean would be: __com.foo:type=MyClass,name=myBean__. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- @@ -40176,23 +40423,23 @@ since it will always use standard Java annotation-based metadata (autodetection 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -@EnableMBeanExport -public class AppConfig { + @Configuration + @EnableMBeanExport + public class AppConfig { -} + } ---- If you prefer XML based configuration the ' `context:mbean-export'` element serves the same purpose. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- You can provide a reference to a particular MBean `server` if necessary, and the @@ -40201,20 +40448,20 @@ value for the generated MBean `ObjectNames`' domains. This would be used in plac fully qualified package name as described in the previous section on <>. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@EnableMBeanExport(server="myMBeanServer", defaultDomain="myDomain") -@Configuration -ContextConfiguration { + @EnableMBeanExport(server="myMBeanServer", defaultDomain="myDomain") + @Configuration + ContextConfiguration { -} + } ---- -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- [NOTE] @@ -40243,10 +40490,10 @@ connectors. To have Spring JMX create, start and expose a JSR-160 `JMXConnectorServer` use the following configuration: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- By default `ConnectorServerFactoryBean` creates a `JMXConnectorServer` bound to @@ -40254,20 +40501,20 @@ By default `ConnectorServerFactoryBean` creates a `JMXConnectorServer` bound to 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 J2SE 5.0 -do__not__ support JMXMP. +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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + ---- If the `ObjectName` property is set Spring will automatically register your connector @@ -40275,22 +40522,22 @@ with the `MBeanServer` under that `ObjectName`. The example below shows the full parameters which you can pass to the `ConnectorServerFactoryBean` when creating a JMXConnector: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - - - + + + + + + + + + + + ---- Note that when using a RMI-based connector you need the lookup service (tnameserv or @@ -40299,12 +40546,12 @@ using Spring to export remote services for you via RMI, then Spring will already constructed an RMI registry. If not, you can easily start a registry using the following snippet of configuration: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- @@ -40314,12 +40561,12 @@ snippet of configuration: To create an `MBeanServerConnection` to a remote JSR-160 enabled `MBeanServer` use the `MBeanServerConnectionFactoryBean` as shown below: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- @@ -40333,13 +40580,13 @@ other providers or JMX implementations (such as http://mx4j.sourceforge.net[MX4J can take advantage of protocols like SOAP, Hessian, Burlap over simple HTTP or SSL and others: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + ---- In the case of the above example, MX4J 3.0.0 was used; see the official MX4J @@ -40355,13 +40602,13 @@ local or remote `MBeanServer`. These proxies provide you with a standard Java in 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + ---- Here you can see that a proxy is created for the MBean registered under the @@ -40375,19 +40622,19 @@ The `MBeanProxyFactoryBean` can create a proxy to any MBean that is accessible v you can override this and provide an `MBeanServerConnection` pointing to a remote `MBeanServer` to cater for proxies pointing to remote MBeans: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + - - - - - + + + + + ---- Here you can see that we create an `MBeanServerConnection` pointing to a remote machine @@ -40413,56 +40660,57 @@ Spring's `MBeanExporter` and MBeans registered via some other mechanism). By way 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.example; + package com.example; -import javax.management.AttributeChangeNotification; -import javax.management.Notification; -import javax.management.NotificationFilter; -import javax.management.NotificationListener; + import javax.management.AttributeChangeNotification; + import javax.management.Notification; + import javax.management.NotificationFilter; + import javax.management.NotificationListener; -public class ConsoleLoggingNotificationListener - implements NotificationListener, NotificationFilter { + public class ConsoleLoggingNotificationListener + implements NotificationListener, NotificationFilter { - public void handleNotification(Notification notification, Object handback) { - System.out.println(notification); - System.out.println(handback); - } + 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()); - } -} + public boolean isNotificationEnabled(Notification notification) { + return AttributeChangeNotification.class.isAssignableFrom(notification.getClass()); + } + + } ---- -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - + + + + - + ---- With the above configuration in place, every time a JMX `Notification` is broadcast from @@ -40473,20 +40721,32 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + ---- If one wants to register a single `NotificationListener` instance for all of the beans @@ -40494,16 +40754,16 @@ that the enclosing `MBeanExporter` is exporting, one can use the special wildcar (sans quotes) as the key for an entry in the `notificationListenerMappings` property map; for example: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - + + + + + + + ---- If one needs to do the inverse (that is, register a number of distinct listeners against @@ -40519,39 +40779,39 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - - - - - - - - - - - - - bean:name=testBean1 - - - - - - + + + + + + + + + + + + + + + bean:name=testBean1 + + + + + + - - - - + + + + - + ---- The above example is equivalent to the first notification example. Lets assume then that @@ -40561,51 +40821,54 @@ additionally we want to filter out extraneous `Notifications` by supplying a indeed what a `NotificationFilter` is, please do consult that section of the JMX specification (1.2) entitled 'The JMX Notification Model'.) -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - - - - - - - - - - - - - bean:name=testBean1 - bean:name=testBean2 - - - - - - - - - - - - + + + + + + + + + + + + + + + bean:name=testBean1 + bean:name=testBean2 + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + ---- @@ -40650,38 +40913,39 @@ Find below a simple example... in this scenario, exported instances of the `JmxT are going to publish a `NotificationEvent` every time the `add(int, int)` operation is invoked. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.springframework.jmx; + package org.springframework.jmx; -import org.springframework.jmx.export.notification.NotificationPublisherAware; -import org.springframework.jmx.export.notification.NotificationPublisher; -import javax.management.Notification; + import org.springframework.jmx.export.notification.NotificationPublisherAware; + import org.springframework.jmx.export.notification.NotificationPublisher; + import javax.management.Notification; -public class JmxTestBean implements IJmxTestBean, NotificationPublisherAware { + public class JmxTestBean implements IJmxTestBean, NotificationPublisherAware { - private String name; - private int age; - private boolean isSuperman; - private NotificationPublisher publisher; + private String name; + private int age; + private boolean isSuperman; + private NotificationPublisher publisher; - // other getters and setters omitted for clarity + // 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 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 dontExposeMe() { + throw new RuntimeException(); + } - public void setNotificationPublisher(NotificationPublisher notificationPublisher) { - this.publisher = notificationPublisher; - } -} + public void setNotificationPublisher(NotificationPublisher notificationPublisher) { + this.publisher = notificationPublisher; + } + + } ---- The `NotificationPublisher` interface and the machinery to get it all working is one of @@ -40788,7 +41052,7 @@ might be running. [[cci-config-connectionfactory]] -==== ConnectionFactory configuration in Spring +==== 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 @@ -40797,10 +41061,10 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- In non-managed mode, you must configure the `ConnectionFactory` you want to use in the @@ -40808,18 +41072,18 @@ configuration of Spring as a JavaBean. The `LocalConnectionFactoryBean` class of this setup style, passing in the `ManagedConnectionFactory` implementation of your connector, exposing the application-level CCI `ConnectionFactory`. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - + + + + + - - - + + + ---- [NOTE] @@ -40844,46 +41108,46 @@ different methods to obtain a CCI connection. Some of the `ConnectionSpec` prope can often be configured in the application server (in managed mode) or on the corresponding local `ManagedConnectionFactory` implementation. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface ConnectionFactory implements Serializable, Referenceable { - ... - Connection getConnection() throws ResourceException; - Connection getConnection(ConnectionSpec connectionSpec) throws ResourceException; - ... -} + 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 +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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + - - - + + + - - - - - - - - - + + + + + + + + + ---- @@ -40896,25 +41160,25 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - + + + + + - - - + + + - - - + + + ---- [NOTE] @@ -40944,13 +41208,14 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface RecordCreator { + public interface RecordCreator { - Record createRecord(RecordFactory recordFactory) throws ResourceException, DataAccessException; -} + Record createRecord(RecordFactory recordFactory) throws ResourceException, DataAccessException; + + } ---- As you can see, the `createRecord(..)` method receives a `RecordFactory` instance as @@ -40959,47 +41224,50 @@ This reference can be used to create `IndexedRecord` or `MappedRecord` instances following sample shows how to use the `RecordCreator` interface and indexed/mapped records. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MyRecordCreator implements RecordCreator { + public class MyRecordCreator implements RecordCreator { - public Record createRecord(RecordFactory recordFactory) throws ResourceException { - IndexedRecord input = recordFactory.createIndexedRecord("input"); - input.add(new Integer(id)); - return input; - } -} + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface RecordExtractor { + public interface RecordExtractor { - Object extractData(Record record) throws ResourceException, SQLException, DataAccessException; -} + Object extractData(Record record) throws ResourceException, SQLException, DataAccessException; + + } ---- The following sample shows how to use the `RecordExtractor` interface. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MyRecordExtractor implements RecordExtractor { + 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); - } -} + 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); + } + + } ---- @@ -41017,16 +41285,20 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface javax.resource.cci.Interaction { - ... - boolean execute(InteractionSpec spec, Record input, Record output) throws ResourceException; + public interface javax.resource.cci.Interaction { - Record execute(InteractionSpec spec, Record input) throws ResourceException; - ... -} + ... + + 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 @@ -41043,39 +41315,45 @@ instance is mandatory. 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class CciTemplate implements CciOperations { + public class CciTemplate implements CciOperations { - public Record execute(InteractionSpec spec, Record inputRecord) - throws DataAccessException { ... } + public Record execute(InteractionSpec spec, Record inputRecord) + throws DataAccessException { ... } - public void execute(InteractionSpec spec, Record inputRecord, Record outputRecord) - 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class CciTemplate implements CciOperations { + public class CciTemplate implements CciOperations { - public Record execute(InteractionSpec spec, RecordCreator inputCreator) - throws DataAccessException { ... } + 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, Record inputRecord, + RecordExtractor outputExtractor) throws DataAccessException { + // ... + } - public Object execute(InteractionSpec spec, RecordCreator creator, RecordExtractor extractor) - 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 @@ -41088,16 +41366,16 @@ output `Record` as return value. `createMappedRecord(..)` methods. This can be used within DAO implementations to create `Record` instances to pass into corresponding `CciTemplate.execute(..)` methods. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class CciTemplate implements CciOperations { + public class CciTemplate implements CciOperations { - public IndexedRecord createIndexedRecord(String name) throws DataAccessException { ... } + public IndexedRecord createIndexedRecord(String name) throws DataAccessException { ... } - public MappedRecord createMappedRecord(String name) throws DataAccessException { ... } + public MappedRecord createMappedRecord(String name) throws DataAccessException { ... } -} + } ---- @@ -41110,18 +41388,28 @@ Spring's CCI support provides a abstract class for DAOs, supporting injection of Internally, this class will create a `CciTemplate` instance for a passed-in `ConnectionFactory`, exposing it to concrete data access implementations in subclasses. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public abstract class CciDaoSupport { + public abstract class CciDaoSupport { - public void setConnectionFactory(ConnectionFactory connectionFactory) { ... } - public ConnectionFactory getConnectionFactory() { ... } + public void setConnectionFactory(ConnectionFactory connectionFactory) { + // ... + } - public void setCciTemplate(CciTemplate cciTemplate) { ... } - public CciTemplate getCciTemplate() { ... } + public ConnectionFactory getConnectionFactory() { + // ... + } -} + public void setCciTemplate(CciTemplate cciTemplate) { + // ... + } + + public CciTemplate getCciTemplate() { + // ... + } + + } ---- @@ -41140,24 +41428,24 @@ that purpose. The `RecordCreator` interface has already been discussed in <>. The `outputRecordCreator` property must be directly specified on the `CciTemplate`. This could be done in the application code like so: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -cciTemplate.setOutputRecordCreator(new EciOutputRecordCreator()); + cciTemplate.setOutputRecordCreator(new EciOutputRecordCreator()); ---- Or (recommended) in the Spring configuration, if the `CciTemplate` is configured as a dedicated bean instance: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - - + + + + ---- [NOTE] @@ -41189,7 +41477,7 @@ corresponding methods called on the CCI `Interaction` interface: | void execute(InteractionSpec, Record, Record) | not set -| void execute(InteractionSpec, Record, Record) +| void execute(InteractionSpec, Record, Record) | void execute(InteractionSpec, Record, Record) | set @@ -41235,27 +41523,29 @@ perform custom operations on it, plus the CCI `ConnectionFactory` which the `Con 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface ConnectionCallback { + public interface ConnectionCallback { - Object doInConnection(Connection connection, ConnectionFactory connectionFactory) - throws ResourceException, SQLException, DataAccessException; -} + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface InteractionCallback { + public interface InteractionCallback { - Object doInInteraction(Interaction interaction, ConnectionFactory connectionFactory) - throws ResourceException, SQLException, DataAccessException; -} + Object doInInteraction(Interaction interaction, ConnectionFactory connectionFactory) + throws ResourceException, SQLException, DataAccessException; + + } ---- [NOTE] @@ -41267,7 +41557,7 @@ created inside every callback method. This is completely up to the DAO implement [[cci-template-example]] -==== Example for CciTemplate usage +==== 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. @@ -41275,67 +41565,69 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -ECIInteractionSpec interactionSpec = new ECIInteractionSpec(); -interactionSpec.setFunctionName("MYPROG"); -interactionSpec.setInteractionVerb(ECIInteractionSpec.SYNC_SEND_RECEIVE); + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MyDaoImpl extends CciDaoSupport implements MyDao { + public class MyDaoImpl extends CciDaoSupport implements MyDao { - public OutputObject getData(InputObject input) { - ECIInteractionSpec interactionSpec = ...; + 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); - } - }); + 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; - } -} + return output; + } + } ---- As discussed previously, callbacks can be used to work directly on CCI connections or interactions. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MyDaoImpl extends CciDaoSupport implements MyDao { + 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 { + 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; - } -} + // do something... + + } + }); + } + return output; + } + + } ---- [NOTE] @@ -41348,63 +41640,62 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MyDaoImpl extends CciDaoSupport implements MyDao { + public class MyDaoImpl extends CciDaoSupport implements MyDao { - public String getData(String input) { - ECIInteractionSpec interactionSpec = ...; + 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; + } - 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - + + + + + + - - - + + + - - - + + + ---- In managed mode (that is, in a Java EE environment), the configuration could look as follows: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - + + + ---- @@ -41419,14 +41710,17 @@ CCI API: an application-level input object will be passed to the operation objec 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 +[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 `MappingRecordOperation` essentially performs the same work as `CciTemplate`, but represents a specific, pre-configured operation as an object. It provides two template @@ -41439,32 +41733,44 @@ an output record to an output object (record mapping): Here are the signatures of these methods: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public abstract class MappingRecordOperation extends EisOperation { - ... - protected abstract Record createInputRecord(RecordFactory recordFactory, Object inputObject) - throws ResourceException, DataAccessException { ... } + public abstract class MappingRecordOperation extends EisOperation { - protected abstract Object extractOutputData(Record outputRecord) - throws ResourceException, SQLException, DataAccessException { ... } - ... -} + ... + + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public abstract class MappingRecordOperation extends EisOperation { - ... - public Object execute(Object inputObject) throws DataAccessException { - ... -} + 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 @@ -41472,18 +41778,18 @@ have an `InteractionSpec` as argument. Instead, the `InteractionSpec` is global operation. The following constructor must be used to instantiate an operation object with a specific `InteractionSpec`: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -InteractionSpec spec = ...; -MyMappingRecordOperation eisOperation = new MyMappingRecordOperation(getConnectionFactory(), spec); -... + InteractionSpec spec = ...; + MyMappingRecordOperation eisOperation = new MyMappingRecordOperation(getConnectionFactory(), spec); + ... ---- [[cci-object-mapping-comm-area]] -==== MappingCommAreaOperation +==== 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 @@ -41493,18 +41799,22 @@ such special COMMAREA support. It implicitly uses the `CommAreaRecord` class as 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public abstract class MappingCommAreaOperation extends MappingRecordOperation { - ... - protected abstract byte[] objectToBytes(Object inObject) - throws IOException, DataAccessException; + public abstract class MappingCommAreaOperation extends MappingRecordOperation { - protected abstract Object bytesToObject(byte[] bytes) - throws IOException, DataAccessException; - ... -} + ... + + protected abstract byte[] objectToBytes(Object inObject) + throws IOException, DataAccessException; + + protected abstract Object bytesToObject(byte[] bytes) + throws IOException, DataAccessException; + + ... + + } ---- @@ -41540,7 +41850,7 @@ The operation object approach uses records in the same manner as the `CciTemplat [[cci-objects-mappring-record-example]] -==== Example for MappingRecordOperation usage +==== Example for MappingRecordOperation usage In this section, the usage of the `MappingRecordOperation` will be shown to access a database with the Blackbox CCI connector. @@ -41556,118 +41866,118 @@ SQL request to execute. In this sample, we directly define the way to convert th 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class PersonMappingOperation extends MappingRecordOperation { + 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); - } + 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 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; - } -} + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MyDaoImpl extends CciDaoSupport implements MyDao { + 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; - } -} + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + - - - + + + - - - - - - - - - + + + + + + + + + - - - + + + ---- In managed mode (that is, in a Java EE environment), the configuration could look as follows: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - - - - - - - + + + + + + + + + - - - + + + ---- [[cci-objects-mapping-comm-area-example]] -==== Example for MappingCommAreaOperation usage +==== 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. @@ -41675,89 +41985,93 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public abstract class EciMappingOperation extends MappingCommAreaOperation { + 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()); - } + 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(); - } - } -} + 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class MyDaoImpl extends CciDaoSupport implements MyDao { + 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); - } - }); + public OutputObject getData(Integer id) { + EciMappingOperation query = new EciMappingOperation(getConnectionFactory(), "MYPROG") { - return (OutputObject) query.execute(new Integer(id)); - } -} + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - + + + + + + - - - + + + - - - + + + ---- In managed mode (that is, in a Java EE environment), the configuration could look as follows: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - + + + ---- @@ -41771,20 +42085,16 @@ There are essentially three options: none (for example with CICS EPI connector), transactions (for example with a CICS ECI connector), global transactions (for example with an IMS connector). -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - XATransaction - - - - + + + + + XATransaction + + ---- For global transactions, you can use Spring's generic transaction infrastructure to @@ -41798,15 +42108,15 @@ transaction demarcation methods. Spring's `CciLocalTransactionManager` executes local CCI transactions, fully compliant with Spring's generic `PlatformTransactionManager` abstraction. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - + + + ---- Both transaction strategies can be used with any of Spring's transaction demarcation @@ -41869,13 +42179,14 @@ of JavaMail MIME messages, called === Usage Let's assume there is a business interface called `OrderManager`: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface OrderManager { + public interface OrderManager { - void placeOrder(Order order); -} + void placeOrder(Order order); + + } ---- Let us also assume that there is a requirement stating that an email message with an @@ -41884,72 +42195,73 @@ order number needs to be generated and sent to a customer placing the relevant o [[mail-usage-simple]] -==== Basic MailSender and SimpleMailMessage usage +==== Basic MailSender and SimpleMailMessage usage -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import org.springframework.mail.MailException; -import org.springframework.mail.MailSender; -import org.springframework.mail.SimpleMailMessage; + import org.springframework.mail.MailException; + import org.springframework.mail.MailSender; + import org.springframework.mail.SimpleMailMessage; -public class SimpleOrderManager implements OrderManager { + public class SimpleOrderManager implements OrderManager { - private MailSender mailSender; - private SimpleMailMessage templateMessage; + private MailSender mailSender; + private SimpleMailMessage templateMessage; - public void setMailSender(MailSender mailSender) { - this.mailSender = mailSender; - } + public void setMailSender(MailSender mailSender) { + this.mailSender = mailSender; + } - public void setTemplateMessage(SimpleMailMessage templateMessage) { - this.templateMessage = templateMessage; - } + public void setTemplateMessage(SimpleMailMessage templateMessage) { + this.templateMessage = templateMessage; + } - public void placeOrder(Order order) { + public void placeOrder(Order order) { - // Do the business calculations... + // Do the business calculations... - // Call the collaborators to persist the order... + // 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()); - } - } -} + // 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + - - - - - + + + + + - - - - + + + + ---- @@ -41961,56 +42273,58 @@ Here is another implementation of `OrderManager` using the `MimeMessagePreparato 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] +[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.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; + 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 { + public class SimpleOrderManager implements OrderManager { - private JavaMailSender mailSender; + private JavaMailSender mailSender; - public void setMailSender(JavaMailSender mailSender) { - this.mailSender = mailSender; - } + public void setMailSender(JavaMailSender mailSender) { + this.mailSender = mailSender; + } - public void placeOrder(final Order order) { + public void placeOrder(final Order order) { - // Do the business calculations... + // Do the business calculations... - // Call the collaborators to persist the order... + // Call the collaborators to persist the order... - MimeMessagePreparator preparator = new MimeMessagePreparator() { + MimeMessagePreparator preparator = new MimeMessagePreparator() { - public void prepare(MimeMessage mimeMessage) throws Exception { + 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()); - } - } -} + 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] @@ -42027,26 +42341,26 @@ Please refer to the relevant JavaDocs for more information. [[mail-javamail-mime]] -=== Using the JavaMail MimeMessageHelper +=== 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] +[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"); + // 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!"); + MimeMessage message = sender.createMimeMessage(); + MimeMessageHelper helper = new MimeMessageHelper(message); + helper.setTo("test@host.com"); + helper.setText("Thank you for ordering!"); -sender.send(message); + sender.send(message); ---- @@ -42063,25 +42377,25 @@ that you don't want displayed as an attachment. The following example shows you how to use the `MimeMessageHelper` to send an email along with a single JPEG image attachment. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -JavaMailSenderImpl sender = new JavaMailSenderImpl(); -sender.setHost("mail.host.com"); + JavaMailSenderImpl sender = new JavaMailSenderImpl(); + sender.setHost("mail.host.com"); -MimeMessage message = sender.createMimeMessage(); + 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 you need a multipart message + MimeMessageHelper helper = new MimeMessageHelper(message, true); + helper.setTo("test@host.com"); -helper.setText("Check out this image!"); + 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); + // 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); + sender.send(message); ---- @@ -42090,26 +42404,26 @@ sender.send(message); The following example shows you how to use the `MimeMessageHelper` to send an email along with an inline image. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -JavaMailSenderImpl sender = new JavaMailSenderImpl(); -sender.setHost("mail.host.com"); + JavaMailSenderImpl sender = new JavaMailSenderImpl(); + sender.setHost("mail.host.com"); -MimeMessage message = sender.createMimeMessage(); + 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 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("", true); + // use the true flag to indicate the text included is HTML + helper.setText("", 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); + // 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); + sender.send(message); ---- [WARNING] @@ -42156,106 +42470,106 @@ Find below the Velocity template that this example will be using. As you can see HTML-based, and since it is plain text it can be created using your favorite HTML or text editor. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -# in the com/foo/package - - -

Hi ${user.userName}, welcome to the Chipping Sodbury On-the-Hill message boards!

+ # in the com/foo/package + + +

Hi ${user.userName}, welcome to the Chipping Sodbury On-the-Hill message boards!

-
- Your email address is ${user.emailAddress}. -
- - - +
+ Your email address is ${user.emailAddress}. +
+ + ---- 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package com.foo; + 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 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; + import javax.mail.internet.MimeMessage; + import java.util.HashMap; + import java.util.Map; -public class SimpleRegistrationService implements RegistrationService { + public class SimpleRegistrationService implements RegistrationService { - private JavaMailSender mailSender; - private VelocityEngine velocityEngine; + private JavaMailSender mailSender; + private VelocityEngine velocityEngine; - public void setMailSender(JavaMailSender mailSender) { - this.mailSender = mailSender; - } + public void setMailSender(JavaMailSender mailSender) { + this.mailSender = mailSender; + } - public void setVelocityEngine(VelocityEngine velocityEngine) { - this.velocityEngine = velocityEngine; - } + public void setVelocityEngine(VelocityEngine velocityEngine) { + this.velocityEngine = velocityEngine; + } - public void register(User user) { + public void register(User user) { - // Do the registration logic... + // Do the registration logic... - sendConfirmationEmail(user); - } + 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); - } -} + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - - + + + - - - - + + + + - - - - resource.loader=class - class.resource.loader.class=org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader - - - + + + + resource.loader=class + class.resource.loader.class=org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader + + + - + ---- @@ -42289,7 +42603,7 @@ operation). [[scheduling-task-executor]] -=== The Spring TaskExecutor abstraction +=== 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 @@ -42312,7 +42626,7 @@ behavior, it is possible to use this abstraction for your own needs. [[scheduling-task-executor-types]] -==== TaskExecutor 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. @@ -42336,16 +42650,6 @@ Spring distribution. In all likelihood, you shouldn't ever need to implement you non-Quartz components. * `ThreadPoolTaskExecutor` -+ - -**** -It is not possible to use any backport or alternate versions of the -`java.util.concurrent` package with this implementation. Both Doug Lea's and Dawid -Kurzyniec's implementations use different package structures which will prevent them -from working correctly. -**** - -+ This implementation can only be used in a Java 5 environment but is also the most commonly used one in that environment. It exposes bean properties for configuring a @@ -42378,45 +42682,46 @@ therefore can be used directly as a WorkManager as well. [[scheduling-task-executor-usage]] -==== Using a TaskExecutor +==== 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import org.springframework.core.task.TaskExecutor; + import org.springframework.core.task.TaskExecutor; -public class TaskExecutorExample { + public class TaskExecutorExample { - private class MessagePrinterTask implements Runnable { + private class MessagePrinterTask implements Runnable { - private String message; + private String message; - public MessagePrinterTask(String message) { - this.message = message; - } + public MessagePrinterTask(String message) { + this.message = message; + } - public void run() { - System.out.println(message); - } + public void run() { + System.out.println(message); + } - } + } - private TaskExecutor taskExecutor; + private TaskExecutor taskExecutor; - public TaskExecutorExample(TaskExecutor taskExecutor) { - this.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)); - } - } -} + 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, @@ -42426,47 +42731,47 @@ decide when the task gets executed. To configure the rules that the `TaskExecutor` will use, simple bean properties have been exposed. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - + + + + + - - - + + + ---- [[scheduling-task-scheduler]] -=== The Spring TaskScheduler abstraction +=== 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface TaskScheduler { + public interface TaskScheduler { - ScheduledFuture schedule(Runnable task, Trigger trigger); + ScheduledFuture schedule(Runnable task, Trigger trigger); - ScheduledFuture schedule(Runnable task, Date startTime); + ScheduledFuture schedule(Runnable task, Date startTime); - ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period); + ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period); - ScheduledFuture scheduleAtFixedRate(Runnable task, long period); + ScheduledFuture scheduleAtFixedRate(Runnable task, long period); - ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay); + ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay); - ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay); + ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay); -} + } ---- The simplest method is the one named 'schedule' that takes a `Runnable` and `Date` only. @@ -42487,14 +42792,14 @@ If these determinations do take into account the outcome of the preceding execut that information is available within a `TriggerContext`. The `Trigger` interface itself is quite simple: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface Trigger { + public interface Trigger { - Date nextExecutionTime(TriggerContext triggerContext); + Date nextExecutionTime(TriggerContext triggerContext); -} + } ---- As you can see, the `TriggerContext` is the most important part. It encapsulates all of @@ -42502,34 +42807,34 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface TriggerContext { + public interface TriggerContext { - Date lastScheduledExecutionTime(); + Date lastScheduledExecutionTime(); - Date lastActualExecutionTime(); + Date lastActualExecutionTime(); - Date lastCompletionTime(); + Date lastCompletionTime(); -} + } ---- [[scheduling-trigger-implementations]] -==== 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -scheduler.schedule(task, new CronTrigger("* 15 9-17 * * MON-FRI")); + scheduler.schedule(task, new CronTrigger("* 15 9-17 * * MON-FRI")); ---- The other out-of-the-box implementation is a `PeriodicTrigger` that accepts a fixed @@ -42546,7 +42851,7 @@ could be configured externally. [[scheduling-task-scheduler-implementations]] -==== TaskScheduler 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 @@ -42578,14 +42883,14 @@ execution. To enable support for `@Scheduled` and `@Async` annotations add `@EnableScheduling` and `@EnableAsync` to one of your `@Configuration` classes: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -@EnableAsync -@EnableScheduling -public class AppConfig { -} + @Configuration + @EnableAsync + @EnableScheduling + public class AppConfig { + } ---- You are free to pick and choose the relevant annotations for your application. For @@ -42595,12 +42900,12 @@ fine-grained control you can additionally implement the `SchedulingConfigurer` a If you prefer XML configuration use the `` element. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - -} + + + } ---- Notice with the above XML that an executor reference is provided for handling those @@ -42616,52 +42921,59 @@ example, the following method would be invoked every 5 seconds with a fixed dela meaning that the period will be measured from the completion time of each preceding invocation. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Scheduled(fixedDelay=5000) -public void doSomething() { - // something that should execute periodically -} + @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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Scheduled(fixedRate=5000) -public void doSomething() { - // something that should execute periodically -} + @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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Scheduled(initialDelay=1000, fixedRate=5000) -public void doSomething() { - // something that should execute periodically -} + @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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Scheduled(cron="*/5 * * * * MON-FRI") -public void doSomething() { - // something that should execute on weekdays only -} + @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. @@ -42687,13 +42999,13 @@ invocation and the actual execution of the method will occur in a task that has submitted to a Spring `TaskExecutor`. In the simplest case, the annotation may be applied to a `void`-returning method. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Async -void doSomething() { - // this will be executed asynchronously -} + @Async + void doSomething() { + // this will be executed asynchronously + } ---- Unlike the methods annotated with the `@Scheduled` annotation, these methods can expect @@ -42701,13 +43013,13 @@ arguments, because they will be invoked in the "normal" way by callers at runtim than from a scheduled task being managed by the container. For example, the following is a legitimate application of the `@Async` annotation. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Async -void doSomething(String s) { - // this will be executed asynchronously -} + @Async + void doSomething(String s) { + // this will be executed asynchronously + } ---- Even methods that return a value can be invoked asynchronously. However, such methods @@ -42715,13 +43027,13 @@ are required to have a `Future` typed return value. This still provides the bene asynchronous execution so that the caller can perform other tasks prior to calling `get()` on that Future. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Async -Future returnSomething(int i) { - // this will be executed asynchronously -} + @Async + Future returnSomething(int i) { + // this will be executed asynchronously + } ---- `@Async` can not be used in conjunction with lifecycle callbacks such as @@ -42729,28 +43041,32 @@ Future returnSomething(int i) { separate initializing Spring bean that invokes the `@Async` annotated method on the target then. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class SampleBeanImpl implements SampleBean { + public class SampleBeanImpl implements SampleBean { - @Async - void doSomething() { ... } -} + @Async + void doSomething() { + // ... + } -public class SampleBeanInititalizer { + } - private final SampleBean bean; + public class SampleBeanInititalizer { - public SampleBeanInitializer(SampleBean bean) { - this.bean = bean; - } + private final SampleBean bean; - @PostConstruct - public void initialize() { - bean.doSomething(); - } -} + public SampleBeanInitializer(SampleBean bean) { + this.bean = bean; + } + + @PostConstruct + public void initialize() { + bean.doSomething(); + } + + } ---- @@ -42762,13 +43078,13 @@ one supplied to the 'annotation-driven' element as described above. However, the 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Async("otherExecutor") -void doSomething(String s) { - // this will be executed asynchronously by "otherExecutor" -} + @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 @@ -42791,10 +43107,10 @@ scheduled with a trigger. The following element will create a `ThreadPoolTaskScheduler` instance with the specified thread pool size. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- The value provided for the 'id' attribute will be used as the prefix for thread names @@ -42808,10 +43124,10 @@ There are no other configuration options for the scheduler. ==== The 'executor' element The following will create a `ThreadPoolTaskExecutor` instance: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- As with the scheduler above, the value provided for the 'id' attribute will be used as @@ -42823,12 +43139,13 @@ the __core__ and the __max__ size. If a single value is provided then the execut 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- As you can see from that configuration, a 'queue-capacity' value has also been provided. @@ -42870,13 +43187,14 @@ capacity on the queue, in the pool, or both. Any of these options can be chosen enumeration of values available for the 'rejection-policy' attribute on the 'executor' element. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- @@ -42890,14 +43208,14 @@ for configuring Message-driven POJOs. Basically a "ref" attribute can point to a 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + - + ---- As you can see, the scheduler is referenced by the outer element, and each individual @@ -42910,16 +43228,16 @@ any previous execution takes. Additionally, for both fixed-delay and fixed-rate 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - + + + + + - + ---- @@ -42940,17 +43258,17 @@ classes that simplify the usage of Quartz within Spring-based applications. provides a `JobDetailBean` that makes the `JobDetail` more of an actual JavaBean with sensible defaults. Let's have a look at an example: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - + + + + + + + + ---- The job detail bean has all information it needs to run the job ( `ExampleJob`). The @@ -42960,34 +43278,38 @@ maps the properties from the job data map to properties of the actual job. So in case, if the `ExampleJob` contains a property named `timeout`, the `JobDetailBean` will automatically apply it: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package example; + package example; -public class ExampleJob extends QuartzJobBean { + public class ExampleJob extends QuartzJobBean { - private int timeout; + private int timeout; - /** - * Setter called after the ExampleJob is instantiated - * with the value from the JobDetailBean (5) - */ - public void setTimeout(int timeout) { - this.timeout = timeout; - } + /** + * Setter called after the ExampleJob is instantiated + * with the value from the JobDetailBean (5) + */ + public void setTimeout(int timeout) { + this.timeout = timeout; + } - protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException { - // do the actual work - } -} + protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException { + // do the actual work + } + + } ---- All additional settings from the job detail bean are of course available to you as well. -__Note: Using the `name` and `group` properties, you can modify the name and the group +[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 -job detail bean (in the example above, this is `exampleJob`).__ +job detail bean (in the example above, this is `exampleJob`). +==== @@ -42997,35 +43319,35 @@ job detail bean (in the example above, this is `exampleJob`).__ Often you just need to invoke a method on a specific object. Using the `MethodInvokingJobDetailFactoryBean` you can do exactly this: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + ---- The above example will result in the `doIt` method being called on the `exampleBusinessObject` method (see below): -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public class ExampleBusinessObject { + public class ExampleBusinessObject { - // properties and collaborators + // properties and collaborators - public void doIt() { - // do the actual work - } -} + public void doIt() { + // do the actual work + } + } ---- -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- Using the `MethodInvokingJobDetailFactoryBean`, you don't need to create one-line jobs @@ -43040,14 +43362,14 @@ job will not start before the first one has finished. To make jobs resulting fro `MethodInvokingJobDetailFactoryBean` non-concurrent, set the `concurrent` flag to `false`. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - + + + + + ---- [NOTE] @@ -43073,46 +43395,46 @@ those triggers. Find below a couple of examples: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - + + + + + + + + - - - - - + + + + + ---- 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - + + + + + + + + ---- 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 -http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/scheduling/quartz/SchedulerFactoryBean.html[SchedulerFactoryBean +{javadoc-baseurl}/org/springframework/scheduling/quartz/SchedulerFactoryBean.html[SchedulerFactoryBean Javadoc] for more information. @@ -43127,11 +43449,23 @@ Javadoc] for more information. [[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 within the -Spring 2.0 development timeframe, and c) the Spring developers were most familiar with +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. There is nothing stopping the inclusion of further languages though. If you want to see @@ -43141,25 +43475,9 @@ http://opensource.atlassian.com/projects/spring/secure/Dashboard.jspa[JIRA] page implement such support yourself). **** -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 0.9 / 1.0 -* Groovy 1.0 / 1.5 -* BeanShell 2.0 - Fully working examples of where this dynamic language support can be immediately useful are described in <>. -__Note:__ Only the specific versions as listed above are supported in Spring 2.5. In -particular, JRuby 1.1 (which introduced many incompatible API changes) is __not__ -supported at this point of time. - @@ -43177,54 +43495,57 @@ and note that this interface is defined in plain Java. Dependent objects that ar injected with a reference to the `Messenger` won't know that the underlying implementation is a Groovy script. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.springframework.scripting; + package org.springframework.scripting; -public interface Messenger { + public interface Messenger { - String getMessage(); -} + String getMessage(); + + } ---- Here is the definition of a class that has a dependency on the `Messenger` interface. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.springframework.scripting; + package org.springframework.scripting; -public class DefaultBookingService implements BookingService { + public class DefaultBookingService implements BookingService { - private Messenger messenger; + private Messenger messenger; - public void setMessenger(Messenger messenger) { - this.messenger = messenger; - } + public void setMessenger(Messenger messenger) { + this.messenger = messenger; + } - public void processBooking() { - // use the injected Messenger object... - } -} + public void processBooking() { + // use the injected Messenger object... + } + + } ---- Here is an implementation of the `Messenger` interface in Groovy. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// from the file 'Messenger.groovy' -package org.springframework.scripting.groovy; + // 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 + // 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 { + // define the implementation in Groovy + class GroovyMessenger implements Messenger { - String message -} + String message + + } ---- Finally, here are the bean definitions that will effect the injection of the @@ -43243,27 +43564,27 @@ to do so. For more information on schema-based configuration, see <>. ==== -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - - - + + + + - - - - + + + + - + ---- The `bookingService` bean (a `DefaultBookingService`) can now use its private @@ -43287,7 +43608,7 @@ Please note that this chapter does not attempt to explain the syntax and idioms 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<> at the end of this chapter. +consult <> at the end of this chapter. @@ -43297,7 +43618,7 @@ 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 `` +* Define your dynamic-language-backed beans using the appropriate `` 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 @@ -43315,19 +43636,6 @@ of your dynamic language source files. [[dynamic-language-beans-concepts-xml-language-element]] ===== The element - -.XML Schema -**** -All of the configuration examples in this chapter make use of the new XML Schema support -that was added in Spring 2.0. - -It is possible to forego the use of XML Schema and stick with the old-style DTD based -validation of your Spring XML files, but then you lose out on the convenience offered by -the `` element. See the Spring test suite for examples of the older -style configuration that doesn't require XML Schema-based validation (it is quite -verbose and doesn't hide any of the underlying Spring implementation from you). -**** - 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 @@ -43374,27 +43682,27 @@ 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 `` element of your bean definition. -So if we stick with<> from earlier in this +So if we stick with <> from earlier in this chapter, here's what we would change in the Spring XML configuration to effect refreshable beans: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - - script-source="classpath:Messenger.groovy"> - - + + + script-source="classpath:Messenger.groovy"> + + - - - + + + - + ---- That really is all you have to do. The `'refresh-check-delay'` attribute defined on the @@ -43411,25 +43719,24 @@ the program pauses while I (the author) go off and edit the underlying dynamic l source file so that the refresh will trigger on the dynamic-language-backed bean when the program resumes execution. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -import org.springframework.context.ApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; -import org.springframework.scripting.Messenger; + import org.springframework.context.ApplicationContext; + import org.springframework.context.support.ClassPathXmlApplicationContext; + import org.springframework.scripting.Messenger; -public final class Boot { + 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()); - } -} + 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 @@ -43437,24 +43744,24 @@ Let's assume then, for the purposes of this example, that all calls to 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.springframework.scripting + package org.springframework.scripting -class GroovyMessenger implements Messenger { + class GroovyMessenger implements Messenger { - private String message = "Bingo" + private String message = "Bingo" - public String getMessage() { - // change the implementation to surround the message in quotes - return "'" + this.message + "'" - } + public String getMessage() { + // change the implementation to surround the message in quotes + return "'" + this.message + "'" + } - public void setMessage(String message) { - this.message = 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 @@ -43465,7 +43772,7 @@ dynamic-language-backed `Messenger` implementation will be __'I Can Do The Frug' 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 +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 @@ -43488,22 +43795,23 @@ embedded directly in Spring bean definitions. More specifically, the inside a Spring configuration file. An example will perhaps make the inline script feature crystal clear: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - -package org.springframework.scripting.groovy; + + -import org.springframework.scripting.Messenger + package org.springframework.scripting.groovy; -class GroovyMessenger implements Messenger { + import org.springframework.scripting.Messenger - String message -} - - - + class GroovyMessenger implements Messenger { + String message + } + + + + ---- If we put to one side the issues surrounding whether it is good practice to define @@ -43518,29 +43826,31 @@ 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 `` region might be better.) -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - -require 'java' + + -include_class 'org.springframework.scripting.Messenger' + require 'java' -class RubyMessenger < Messenger + include_class 'org.springframework.scripting.Messenger' - def setMessage(message) - @@message = message - end + class RubyMessenger < Messenger - def getMessage - @@message - end + def setMessage(message) + @@message = message + end -end - - - + def getMessage + @@message + end + + end + + + + ---- @@ -43553,42 +43863,43 @@ dynamic-language-backed beans). In the interests of making this special handling constructors and properties 100% clear, the following mixture of code and configuration will __not__ work. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// from the file 'Messenger.groovy' -package org.springframework.scripting.groovy; + // from the file 'Messenger.groovy' + package org.springframework.scripting.groovy; -import org.springframework.scripting.Messenger + import org.springframework.scripting.Messenger -class GroovyMessenger implements Messenger { + class GroovyMessenger implements Messenger { - GroovyMessenger() {} + GroovyMessenger() {} - // this constructor is not available for Constructor Injection - GroovyMessenger(String message) { - this.message = message; - } + // this constructor is not available for Constructor Injection + GroovyMessenger(String message) { + this.message = message; + } - String message + String message - String anotherMessage -} + String anotherMessage + + } ---- -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - + + + + - - + + - + ---- In practice this limitation is not as significant as it first appears since setter @@ -43629,50 +43940,51 @@ Let us look at a fully working example of using a JRuby-based bean. Here is the implementation of the `Messenger` interface that was defined earlier in this chapter (for your convenience it is repeated below). -[source,ruby] +[source,ruby,indent=0] [subs="verbatim,quotes"] ---- -package org.springframework.scripting; + package org.springframework.scripting; -public interface Messenger { + public interface Messenger { - String getMessage(); -} + String getMessage(); + + } ---- -[source,ruby] +[source,ruby,indent=0] [subs="verbatim,quotes"] ---- -require 'java' + require 'java' -class RubyMessenger - include org.springframework.scripting.Messenger + class RubyMessenger + include org.springframework.scripting.Messenger - def setMessage(message) - @@message = message - end + def setMessage(message) + @@message = message + end - def getMessage - @@message - end -end + def getMessage + @@message + end + end -# this last line is not essential (but see below) -RubyMessenger.new + # 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - + - + ---- Take note of the last line of that JRuby source ( `'RubyMessenger.new'`). When using @@ -43682,17 +43994,17 @@ dynamic-language-backed bean as the result of the execution of your JRuby source 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] +[source,ruby,indent=0] [subs="verbatim,quotes"] ---- -require 'java' + require 'java' -include_class 'org.springframework.scripting.Messenger' + include_class 'org.springframework.scripting.Messenger' -# class definition same as above... + # class definition same as above... -# instantiate and return a new instance of the RubyMessenger class -RubyMessenger.new + # 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 @@ -43708,7 +44020,7 @@ 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): -[source] +[literal] [subs="verbatim,quotes"] ---- org.springframework.scripting.ScriptCompilationException: Compilation of JRuby script returned '' @@ -43734,9 +44046,9 @@ JRuby-based beans. The Groovy scripting support in Spring requires the following libraries to be on the classpath of your application. -* `groovy-1.5.5.jar` -* `asm-2.2.2.jar` -* `antlr-2.7.6.jar` +* `groovy-1.8.jar` +* `asm-3.2.jar` +* `antlr-2.7.7.jar` **** From the Groovy homepage... @@ -43749,65 +44061,67 @@ If you have read this chapter straight from the top, you will already have <> of a Groovy-dynamic-language-backed bean. Let's look at another example (again using an example from the Spring test suite). -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.springframework.scripting; + package org.springframework.scripting; -public interface Calculator { + public interface Calculator { - int add(int x, int y); -} + int add(int x, int y); + + } ---- Here is an implementation of the `Calculator` interface in Groovy. -[source,groovy] +[source,groovy,indent=0] [subs="verbatim,quotes"] ---- -// from the file 'calculator.groovy' -package org.springframework.scripting.groovy + // from the file 'calculator.groovy' + package org.springframework.scripting.groovy -class GroovyCalculator implements Calculator { + class GroovyCalculator implements Calculator { - int add(int x, int y) { - x + y - } -} + int add(int x, int y) { + x + y + } + + } ---- -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- -<-- from the file 'beans.xml' --> - - - + <-- from the file 'beans.xml' --> + + + ---- Lastly, here is a small application to exercise the above configuration. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.springframework.scripting; + package org.springframework.scripting; -import org.springframework.context.ApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; + import org.springframework.context.ApplicationContext; + import org.springframework.context.support.ClassPathXmlApplicationContext; -public class Main { + 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)); - } -} + 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 +consult the dynamic language showcase project for a more complex example, or indeed <> later in this chapter). It is important that you __do not__ define more than one class per Groovy source file. @@ -43823,13 +44137,14 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public interface GroovyObjectCustomizer { + public interface GroovyObjectCustomizer { - void customize(GroovyObject goo); -} + void customize(GroovyObject goo); + + } ---- The Spring Framework will instantiate an instance of your Groovy-backed bean, and will @@ -43838,57 +44153,58 @@ 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -public final class SimpleMethodTracingCustomizer implements GroovyObjectCustomizer { + public final class SimpleMethodTracingCustomizer implements GroovyObjectCustomizer { - public void customize(GroovyObject goo) { - DelegatingMetaClass metaClass = new DelegatingMetaClass(goo.getMetaClass()) { + 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); - } -} + 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 2.0 namespace support. +of a `GroovyObjectCustomizer` is easy if you are using the Spring namespace support. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - + + ---- -If you are not using the Spring 2.0 namespace support, you can still use the +If you are not using the Spring namespace support, you can still use the `GroovyObjectCustomizer` functionality. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - + + + + + + + - + ---- @@ -43925,45 +44241,46 @@ Let's look at a fully working example of using a BeanShell-based bean that imple the `Messenger` interface that was defined earlier in this chapter (repeated below for your convenience). -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -package org.springframework.scripting; + package org.springframework.scripting; -public interface Messenger { + public interface Messenger { - String getMessage(); -} + String getMessage(); + + } ---- Here is the BeanShell 'implementation' (the term is used loosely here) of the `Messenger` interface. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -String message; + String message; -String getMessage() { - return message; -} + String getMessage() { + return message; + } -void setMessage(String aMessage) { - message = aMessage; -} + 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - + + ---- See <> for some scenarios where you might want to use @@ -43987,12 +44304,12 @@ of Spring MVC controllers. In pure Spring MVC applications, the navigational flo 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 +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 +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 @@ -44009,40 +44326,40 @@ beans, you will have had to enable the 'refreshable beans' functionality. See Find below an example of an `org.springframework.web.servlet.mvc.Controller` implemented using the Groovy dynamic language. -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -// from the file '/WEB-INF/groovy/FortuneController.groovy' -package org.springframework.showcase.fortune.web + // 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 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 + import javax.servlet.http.HttpServletRequest + import javax.servlet.http.HttpServletResponse -class FortuneController implements Controller { + class FortuneController implements Controller { - @Property FortuneService fortuneService + @Property FortuneService fortuneService - ModelAndView handleRequest( - HttpServletRequest request, HttpServletResponse httpServletResponse) { + ModelAndView handleRequest(HttpServletRequest request, + HttpServletResponse httpServletResponse) { + return new ModelAndView("tell", "fortune", this.fortuneService.tellFortune()) + } - return new ModelAndView("tell", "fortune", this.fortuneService.tellFortune()) - } -} + } ---- -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - + + + ---- @@ -44071,26 +44388,27 @@ Find below an example of a Spring `org.springframework.validation.Validator` imp using the Groovy dynamic language. (See <> for a discussion of the `Validator` interface.) -[source,groovy] +[source,groovy,indent=0] [subs="verbatim,quotes"] ---- -import org.springframework.validation.Validator -import org.springframework.validation.Errors -import org.springframework.beans.TestBean + import org.springframework.validation.Validator + import org.springframework.validation.Errors + import org.springframework.beans.TestBean -class TestBeanValidator implements Validator { + class TestBeanValidator implements Validator { - boolean supports(Class clazz) { - return TestBean.class.isAssignableFrom(clazz) - } + 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.") - } -} + void validate(Object bean, Errors errors) { + if(bean.name?.trim()?.size() > 0) { + return + } + errors.reject("whitespace", "Cannot be composed wholly of whitespace.") + } + + } ---- @@ -44109,7 +44427,7 @@ framework actually is unaware that a bean that is being advised might be a scrip 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<>. +use <>. 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 @@ -44127,27 +44445,27 @@ bean. (The default scope is <>, just a with 'regular' beans.) Find below an example of using the `scope` attribute to define a Groovy bean scoped as -a<>. +a <>. -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - - + + + - - - + + + - + ---- See <> in <> for a fuller discussion of the scoping support @@ -44257,7 +44575,7 @@ take a closer look at each annotation: [[cache-annotations-cacheable]] -==== @Cacheable annotation +==== @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 @@ -44265,11 +44583,11 @@ is, methods for whom the result is stored into the cache so on subsequent invoca execute the method. In its simplest form, the annotation declaration requires the name of the cache associated with the annotated method: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Cacheable("books") -public Book findBook(ISBN isbn) {...} + @Cacheable("books") + public Book findBook(ISBN isbn) {...} ---- In the snippet above, the method `findBook` is associated with the cache named `books`. @@ -44286,11 +44604,11 @@ All the other caches that do not contain the method will be updated as well even the cached method was not actually executed. ==== -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Cacheable({ "books", "isbns" }) -public Book findBook(ISBN isbn) {...} + @Cacheable({ "books", "isbns" }) + public Book findBook(ISBN isbn) {...} ---- @@ -44300,15 +44618,13 @@ Since caches are essentially key-value stores, each invocation of a cached metho 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 0. +* 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 key computed from the hashes of all parameters. +* If more the one param is given, return a `SimpleKey` containing all parameters. -This approach works well for objects with __natural keys__ as long as the `hashCode()` -reflects that. If that is not the case then for distributed or persistent environments, -the strategy needs to be changed as the objects hashCode is not preserved. In fact, -depending on the JVM implementation or running conditions, the same hashCode can be -reused for different objects, in the same VM instance. +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 the the +strategy needs to be changed. To provide a different __default__ key generator, one needs to implement the `org.springframework.cache.KeyGenerator` interface. Once configured, the generator will @@ -44316,6 +44632,18 @@ be used for each declaration that does not specify its own key generation strate below). +[NOTE] +==== +The default key generation strategy changed with the release of Spring 4.0. Earlier +versions of Spring used a key generation strategy that only considered the `hashCode()` +of parameters and not `equals()`, this often caused unexpected key collisions (see +https://jira.springsource.org/browse/SPR-10237[SPR-10237] for background). + +If you want to use the previous key generator, you can use the +`org.springframework.cache.interceptor.DefaultKeyGenerator` class. +==== + + [[cache-annotations-cacheable-key]] ===== Custom Key Generation Declaration Since caching is generic, it is quite likely the target methods have various signatures @@ -44323,11 +44651,11 @@ that cannot be simply mapped on top of the cache structure. This tends to become 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Cacheable("books") -public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) + @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, @@ -44346,17 +44674,17 @@ 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 favour and read <>: -[source,java] +[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")** + 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="#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) + @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 @@ -44373,28 +44701,28 @@ method is not cached, that is executed every since time no matter what values ar cache or what arguments are used. A quick example - the following method will be cached, only if the argument `name` has a length shorter then 32: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Cacheable(value="book", condition="#name.length < 32") -public Book findBook(String name) + @Cacheable(value="book", condition="#name.length < 32") + public Book findBook(String name) ---- In addition the `conditional` parameter, the `unless` parameter can be used to veto the -adding of a value to the cache. Unlike `conditional`, `unless` `SpEL` expressions are -evalulated __after__ the method has been called. Expanding on the previous example - -perhaps we only want to cache paperback books: +adding of a value to the cache. Unlike `conditional`, `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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Cacheable(value="book", condition="#name.length < 32", unless="#result.hardback") -public Book findBook(String name) + @Cacheable(value="book", condition="#name.length < 32", unless="#result.hardback") + public Book findBook(String name) ---- [[cache-spel-context]] -===== Available caching SpEL evaluation context +===== Available caching SpEL evaluation context Each `SpEL` expression evaluates again a dedicated <>. In addition to the build in parameters, the @@ -44454,7 +44782,7 @@ conditional(see next section) computations: [[cache-annotations-put]] -==== @CachePut annotation +==== @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 @@ -44472,7 +44800,7 @@ other), such declarations should be avoided. [[cache-annotations-evict]] -==== @CacheEvict annotation +==== @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 @@ -44483,11 +44811,11 @@ that are affected by the action, allows a key or a condition to be specified but 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] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@CacheEvict(value = "books", allEntries=true) -public void loadBooks(InputStream batch) + @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 @@ -44513,7 +44841,7 @@ and thus requires a result. [[cache-annotations-caching]] -==== @Caching annotation +==== @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 @@ -44522,11 +44850,11 @@ such declarations however there is a workaround - using a __enclosing__ annotati this case, `@Caching`. `@Caching` allows multiple nested `@Cacheable`, `@CachePut` and `@CacheEvict` to be used on the same method: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Caching(evict = { @CacheEvict("primary"), @CacheEvict(value = "secondary", key = "#p0") }) -public Book importBooks(String deposit, Date date) + @Caching(evict = { @CacheEvict("primary"), @CacheEvict(value = "secondary", key = "#p0") }) + public Book importBooks(String deposit, Date date) ---- @@ -44542,27 +44870,30 @@ your code). To enable caching annotations add the annotation `@EnableCaching` to one of your `@Configuration` classes: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Configuration -@EnableCaching -public class AppConfig { -} + @Configuration + @EnableCaching + public class AppConfig { + } ---- Alternatively for XML configuration use the `cache:annotation-driven` element: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - + + + ---- Both the `cache:annotation-driven` element and `@EnableCaching` annotation allow various @@ -44621,7 +44952,7 @@ only checks for `@Cacheable/@CacheEvict` beans in your controllers, and not your services. See <> for more information. ==== -Method visibility and @Cacheable / @CachePut / @CacheEvict +.Method visibility and @Cacheable / @CachePut / @CacheEvict **** 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 @@ -44640,7 +44971,7 @@ The fact that Java annotations are __not inherited from interfaces__ means that 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__. +would be decidedly __bad__. ==== [NOTE] @@ -44656,43 +44987,43 @@ using the aspectj mode in this case. [[cache-annotation-stereotype]] ==== Using custom annotations -The caching abstraction allows one to use her own annotations to identify what method +The caching abstraction allows you to use your own annotations to identify what method trigger 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 +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 <> annotations, both `@Cacheable` and -`@CacheEvict` can be used as meta-annotations, that is annotations that can annotate -other annotations. To wit, let us replace a common `@Cacheable` declaration with our -own, custom annotation: +`@CacheEvict` can be used as <>, that is +annotations that can annotate other annotations. To wit, let us replace a common +`@Cacheable` declaration with our own, custom annotation: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD}) -@Cacheable(value="books", key="#isbn") -public @interface SlowService { -} + @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] +[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") + public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) ---- with: -[source,java] +[source,java,indent=0] [subs="verbatim,quotes"] ---- -@SlowService -public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) + @SlowService + public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) ---- Even though `@SlowService` is not a Spring annotation, the container automatically picks @@ -44710,26 +45041,26 @@ specifies the target method and the caching directives externally (similar to th declarative transaction management <>). The previous example can be translated into: -[source,xml] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + - - - - - - - + + + + + + + - - - - + + + + - + ---- In the configuration above, the `bookService` is made cacheable. The caching semantics @@ -44773,18 +45104,18 @@ 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - + + + + + + + + + ---- The snippet above uses the `SimpleCacheManager` to create a `CacheManager` for the two @@ -44799,18 +45130,18 @@ eviction contracts. [[cache-store-configuration-ehcache]] -==== EhCache-based Cache +==== 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - + - - + + ---- This setup bootstraps ehcache library inside Spring IoC (through bean `ehcache`) which @@ -44820,7 +45151,7 @@ ehcache-specific configuration is read from the resource `ehcache.xml`. [[cache-store-configuration-gemfire]] -==== GemFire-based Cache +==== GemFire-based Cache GemFire is a memory-oriented/disk-backed, elastically scalable, continuously available, active (with built-in pattern-based subscription notifications), globally replicated @@ -44840,18 +45171,18 @@ is unable to find a suitable store. In situations like this, rather then removin 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] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - + + + + + + + + + ---- The `CompositeCacheManager` above chains multiple `CacheManager` s and additionally,
First Name:Field is required.
Last Name:Field is required.
- -
Last Name:Field is required.
+ +