Table of Contents

Related to Spring

Questions answered

Is it possible to set a bean property to given resource in module classpath as InputStream / as URL?

Yes, this is possible due to build-in org.springframework.beans.propertyeditors.InputStreamEditor, which allows conversion from String → org.springframework.core.io.Resource → java.io.InputStream. If you have e.g. overloaded constructors, one of which has String argument, then you need to specify the target type explicitly:
<bean class="...">
    <constructor-arg value="classpath:/com/company/module/file.xml" type="java.io.InputStream" />
</bean>

The same conversion is also possible via build-in org.springframework.beans.propertyeditors.URLEditor, which allows conversion from String → org.springframework.core.io.Resource → java.net.URL:

<bean class="...">
    <constructor-arg value="classpath:/com/company/module/file.xml" type="java.net.URL" />
</bean>

How to programmatically enumerate all classpath resources using Ant mask?

import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
 
for (Resource resource : new PathMatchingResourcePatternResolver().getResources("classpath*:/path/*.txt")) {
    resource.getInputStream();
}

How to set ''null'' to a bean property?

Use SpEL: <bean ... p:fieldName="#{null}" />

Implementation of cache which is based on Apache LRUMap

See lru_map_cache.7z (1.98 KiB, 107 downloads). Usage:

spring-context.xml

<bean id="cacheManager" class="util.LRUMapCacheManager" />

Is it possible to set a bean property to given resource in module classpath as Property / as ResourceBundle?

One alternative is to use PropertiesFactoryBean which allows you to load properties from text or XML file(s) and optionally merge with fixed values:
<bean class="...">
    <constructor-arg>
        <bean class="org.springframework.beans.factory.config.PropertiesFactoryBean">
            <property name="location" value="classpath:/com/company/module/list_en.properties" />
        </bean>
    </constructor-arg>
</bean>

or if you need to inject java.util.ResourceBundle type you may use org.springframework.beans.propertyeditors.ResourceBundleEditor (which is not registered in Spring context by default, so one need to make it manually):

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="customEditors">
        <map>
            <entry key="java.util.ResourceBundle">
                <bean class="org.springframework.beans.propertyeditors.ResourceBundleEditor" />
            </entry>
        </map>
    </property>
</bean>
 
<bean class="...">
    <!-- Resource location is the same as above: -->
    <constructor-arg value="com.company.module.list" />
</bean>

More information is here.

How to override property declared in parent properties?

It is possible to pass an array of java.util.Properties objects to org.springframework.beans.factory.config.PropertiesFactoryBean which will be merged in given order. Note that under-the-hood below solution uses org.springframework.core.convert.support.StringToPropertiesConverter which converts from multi-line string to java.util.Properties.
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
                           http://www.springframework.org/schema/util  http://www.springframework.org/schema/util/spring-util-4.1.xsd">
 
    <util:properties id="configurationProperties">
        <prop key="location">/tmp</prop>
        <prop key="size">1000</prop>
    </util:properties>
 
    <bean id="testConfigurationProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
        <property name="propertiesArray">
            <list>
                <ref bean="configurationProperties" />
                <value>
                    size=10
                </value>
            </list>
        </property>
    </bean>
</beans>

How to initialize the property value from system property or use the default value if system property is not defined?

Use SpEL:
<bean ...>
    <property name="url" value="#{ systemProperties[url] ?: 'http://default/url' }" />
</bean>

How to collect and inject all beans of a given type?

From How to collect and inject all beans of a given type in Spring:
  • @Autowired List<ChildBean> list will work OK both for lists and arrays.
  • The analogue for XML configuration would be autowire="byType" or autowire="constructor" e.g. <bean class="ParentBean" autowire="byType" /> (see Autowiring collaborators).

What is the Spring bean lifecycle?

spring-bean-life-cycle-tutorial.jpg

How to create deamon threads?

<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"
    p:corePoolSize="5"
    p:maxPoolSize="30"
    p:daemon="true"
/>

How to call a method on specific bean?

  • Method using MethodInvokingFactoryBean:
    <bean id="registerListener1" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="targetObject">
            <ref local="listenerContainer" />
        </property>
        <property name="targetMethod" value="addListener" />
        <property name="arguments">
            <list>
                <ref bean="listener1" />
            </list>
        </property>
    </bean>
  • Method using SpEL: #{listenerContainer.addListener(listener1)}.

How to inject correctly prototype-scoped bean into singleton-scoped bean?

Doing this via @Autowired (or normal declarative bean injection via <property>) will not result the desired effect, as in our singleton bean we want to get new version of prototype bean each time on request. The solutions are:
  • Wire the Spring context and retrieve the bean using the context (@Autowired ApplicationContext context; ... context.getBean("prototypeBean");). Drawback: explicit dependency on Spring classes.
  • Use Lookup method injection. Mind that method to be overridden does not necessarily need to be abstract (normal protected method is fairly OK) as it will some difficulties to unit test the bean. Unfortunately, there is no way to configure this functionality using annotations prior to Spring v4.1 (see SPR-5192).
  • Use <aop:scoped-proxy/> (see Scoped beans as dependencies). Drawback: does not make sense with prototype scope as each method invocation will operate with new bean instance which makes it impossible to use this approach with stateful beans.

See also:

How to configure Spring application to use aspect load-time weaving (LTW)?

  1. Create META-INF/aop.xml (should be included into WAR or JAR) and e.g. aspect MultiThreadedHttpConnectionManagerAdvice.java as below:

    META-INF/aop.xml

    <!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
    <aspectj>
     
        <!-- Add options="-verbose -debug" for more logging -->
        <weaver>
            <!-- Only weave classes in these packages: -->
            <include within="org.apache.commons.httpclient..*" />
            <!-- Also include the aspect itself (otherwise "java.lang.NoSuchMethodError: MultiThreadedHttpConnectionManagerAdvice.aspectOf()" will be thrown): -->
            <include within="aspects.MultiThreadedHttpConnectionManagerAdvice" />
        </weaver>
     
        <aspects>
            <!-- weave in just this aspect -->
            <aspect name="aspects.MultiThreadedHttpConnectionManagerAdvice" />
        </aspects>
     
    </aspectj>

    MultiThreadedHttpConnectionManagerAdvice.java

    package aspects;
     
    import java.lang.reflect.Field;
    import java.util.IdentityHashMap;
    import java.util.LinkedList;
    import java.util.Map;
     
    import org.apache.commons.logging.LogFactory;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.util.ReflectionUtils;
     
    @Aspect
    public class MultiThreadedHttpConnectionManagerAdvice {
     
        private final Map<Object, ConnectionPoolInfo> infos = new IdentityHashMap<Object, ConnectionPoolInfo>();
     
        /**
         * Statistics is reset after this amount of invocations.
         */
        private static final int NUMBER_OF_INVOCATIONS_PER_MEASUREMENT    = 100;
     
        static class ConnectionPoolInfo {
            public int    invocationsNumber;
            public int    maxWaitingThreadsNumber;
     
            @Override
            public String toString() {
                return "maxWaitingThreadsNumber:" + maxWaitingThreadsNumber;
            }
        }
     
        /**
         * @param pool instance of {@link MultiThreadedHttpConnectionManager$ConnectionPool}.
         */
        private void updateStatistics(Object pool) {
            LinkedList<?> waitingThreads = getField(pool, "waitingThreads");
     
            synchronized (infos) {
                ConnectionPoolInfo info = infos.get(pool);
     
                if (info == null) {
                    info = new ConnectionPoolInfo();
                    infos.put(pool, info);
                }
     
                if (info.maxWaitingThreadsNumber < waitingThreads.size()) {
                    info.maxWaitingThreadsNumber = waitingThreads.size();
                }
     
                if (++info.invocationsNumber % NUMBER_OF_INVOCATIONS_PER_MEASUREMENT == 0) {
                    LogFactory.getLog(getClass()).error(
                            "After " + info.invocationsNumber + " invocations " + info + " poolsNumber:" + infos.size());
     
                    info.maxWaitingThreadsNumber = 0;
                }
            }
        }
     
        @After("execution(* org.apache.commons.httpclient.MultiThreadedHttpConnectionManager.ConnectionPool.createConnection*(..))")
        public void createConnection(JoinPoint pjp) {
            updateStatistics(pjp.getThis());
        }
     
        @Before("execution(* org.apache.commons.httpclient.MultiThreadedHttpConnectionManager.ConnectionPool.getFreeConnection*(..))")
        public void getFreeConnection(JoinPoint pjp) {
            updateStatistics(pjp.getThis());
        }
     
        /**
         * Get the value of private field {@code fieldName} of given object {@code target}.
         */
        @SuppressWarnings("unchecked")
        public static <T> T getField(Object target, String fieldName) {
            Field field = ReflectionUtils.findField(target.getClass(), fieldName);
            ReflectionUtils.makeAccessible(field);
            return (T) ReflectionUtils.getField(field, target);
        }
    }
  2. Configure Spring to use LWP:

    spring-aop-context.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
                            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
     
        <context:load-time-weaver />
        <aop:aspectj-autoproxy />
     
    </beans>
    </xml>
  3. Add LWP agent to JVM startup: java ... -javaagent:/path/to/org.springframework.instrument-3.1.1.RELEASE.jar ...

Troubleshooting:

If during application startup java.lang.NoSuchMethodError: aspectOf() is thrown then make sure that aspects are listed in aop.xml.

See also Configuring AspectJ aspects using Spring IoC.

How to make a bean to implement an additional interface?

Sounds weird, but let's assume you have a class that you don't have sources for and you want to make this class implement an additional interface, PriorityOrdered for example and to route all calls to this interface to some bean, while all other calls should be routed to original bean.

The solution is based on JDK proxy mechanism, it means that a bean that needs to be tricked (service.MyServiceImpl in the example), has to implement some business interface (service.MyService) which is exposed to other beans in the context.

First write the implementation for the interface that needs to be introduced (if necessary):

FixedPriorityOrder.java

package util;
 
import org.springframework.core.PriorityOrdered;
 
/**
 * Simple implementation of {@link PriorityOrdered} interface that returns the pre-configured order.
 */
public class FixedPriorityOrder implements PriorityOrdered {
 
    private final int order;
 
    private FixedPriorityOrder(int order) {
        this.order = order;
    }
 
    @Override
    public int getOrder() {
        return order;
    }
}

Now bringing all together:

<!--
    This interceptor will route calls to interfaces that passed bean implements to that bean.
-->
<bean id="normalPriorityOrder" class="org.springframework.aop.support.DelegatingIntroductionInterceptor">
    <constructor-arg>
        <bean class="util.FixedPriorityOrder">
            <constructor-arg value="0" />
        </bean>
    </constructor-arg>
</bean>
 
<bean id="descriptorComponent" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="proxyInterfaces">
        <list>
            <value>org.springframework.beans.factory.InitializingBean</value>
            <value>org.springframework.core.PriorityOrdered</value>
            <!-- ... add all other interfaces that target bean implements... -->
            <value>service.MyService</value>
        </list>
    </property>
    <property name="target">
        <bean class="service.MyServiceImpl">
            ...
        </bean>
    </property>
    <property name="interceptorNames">
        <list>
            <value>normalPriorityOrder</value>
        </list>
    </property>
</bean>

JDBC

How @Transactional support is actually implemented?

What are the possibilities to add transactional support to my application?

  • Using programmatic transaction management facilities via TransactionTemplate (see Using the TransactionTemplate) or via PlatformTransactionManager:
    @Autowired
    private PlatformTransactionManager    transactionManager;
     
    void doInTransaction() {
        DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
        transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
     
        TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
     
        // Do the job
     
        transactionManager.commit(transactionStatus);
     
        // Error handling is omitted:
        //transactionManager.rollback(transactionStatus);
    }
  • Using @Transactional annotations and <tx:annotation-driven /> (see Using @Transactional)
  • Using Spring 2.0 transaction advice via <aop:advisor> and <tx:advice> (see <tx:advice/> settings)

And more exotic:

How to define custom mapping for SQL error codes to exception classes?

In this scenario we want custom DataTruncationException to be thrown in case of specific SQL error codes:
package org.mycompany.dao.exception;
 
import org.springframework.dao.DataIntegrityViolationException;
 
public class DataTruncationException extends DataIntegrityViolationException {
 
    public DataTruncationException(String msg, Throwable cause) {
        super(msg, cause);
    }
}
<bean id="exceptionTranslator" class="org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator">
    <property name="dataSource">
        <ref bean="myDataSource" />
    </property>
    <!--
        Important that this property is set after datasource was set.
        Relies on actual SQLErrorCodeSQLExceptionTranslator implementation.
    -->
    <property name="sqlErrorCodes.customTranslations">
        <array>
            <!-- Custom mapping of MSSQL (8152) and HSQL (-3401) error code to specific exception for data truncation. -->
            <bean class="org.springframework.jdbc.support.CustomSQLErrorCodesTranslation">
                <property name="errorCodes">
                    <array>
                        <value>8152</value>
                        <value>-3401</value>
                    </array>
                </property>
                <property name="exceptionClass" value="org.mycompany.dao.exception.DataTruncationException" />
            </bean>
        </array>
    </property>
</bean>
 
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
    p:dataSource-ref="myDataSource"
    p:exceptionTranslator-ref="exceptionTranslator"
/>

Later in your Web controller you can for example map given exception to corresponding HTTP code:

import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
 
...
 
@ExceptionHandler(DataTruncationException.class)
@ResponseStatus(value = HttpStatus.REQUEST_ENTITY_TOO_LARGE)
@ResponseBody
public String handleDataTruncationException(DataTruncationException ex) {
    return ExceptionUtils.getStackTrace(ex);
}

How to mock JNDI lookups in unit tests?

From here and here:
  • In your shared Spring context define:

    dao.xml

    <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/SampleDB"/>
     
    <bean id="myDao" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg ref="dataSource" />
    </bean>
  • In your test context define:

    dao-test.xml

    <import resource="classpath:/com/company/module/dao/dao.xml"/>
     
    <bean id="testDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="org.hsqldb.jdbcDriver" />
        <property name="url" value="jdbc:hsqldb:mem:test" />
        <property name="username" value="sa" />
        <property name="password" value="" />
    </bean>
  • In your unit test:
    @RunWith(SpringRunner.class)
    @ContextConfiguration({"classpath:/com/company/module/dao/dao-test.xml"})
    public class MyDaoImplTest {
     
        @Resource
        private JdbcTemplate myDao;
     
        @Resource
        private DataSource testDataSource;
     
        @Before
        public void setupJndi() throws NamingException {
            SimpleNamingContextBuilder.emptyActivatedContextBuilder();
            SimpleNamingContextBuilder.getCurrentContextBuilder().bind("java:comp/env/jdbc/SampleDB", testDataSource);
        }
    }

Why do I get CacheException in my unit test?

Setup:
  • Hibenate session is managed by Spring and has second-level cache enabled:

    dao-context.xml

    <bean id="hibernateSessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean" lazy-init="true">
        <property name="dataSource" ref="myDataSource" />
        <property name="mappingResources">
            <list>
                <value>/.../hibernate-mapping.xml</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">...</prop>
                <prop key="hibernate.cache.use_query_cache">true</prop>
                <prop key="hibernate.cache.use_second_level_cache">true</prop>
                <prop key="hibernate.cache.provider_class">net.sf.ehcache.hibernate.EhCacheProvider</prop>
                <prop key="hibernate.cache.region.factory_class">net.sf.ehcache.hibernate.EhCacheRegionFactory</prop>
                <prop key="net.sf.ehcache.configurationResourceName">/.../ehcache.xml</prop>
            </props>
        </property>
    </bean>
  • Unit test classes refer this context using @ContextConfiguration:

    MyTest.java

    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringRunner;
     
    @ContextConfiguration("classpath:/.../dao-context.xml")
    @RunWith(SpringRunner.class)
    public class MyTest {
        ...
    }
  • The exception is:
    Caused by: net.sf.ehcache.CacheException: Another CacheManager with same name 'hibernate-cache' already exists in the same VM. Please provide unique names for each CacheManager in the config or do one of following:
    1. Use one of the CacheManager.create() static factory methods to reuse same CacheManager with same name or create one if necessary
    2. Shutdown the earlier cacheManager before creating new one with same name.
    The source of the existing CacheManager is: URLConfigurationSource [url=file:/C:/workspace/chepo/chepo-dataaccess-db/bin/org/epo/lifesciences/chepo/dao/ehcache.xml]
        at net.sf.ehcache.CacheManager.assertNoCacheManagerExistsWithSameName(CacheManager.java:457)
        at net.sf.ehcache.CacheManager.init(CacheManager.java:354)
        at net.sf.ehcache.CacheManager.<init>(CacheManager.java:242)
        at net.sf.ehcache.hibernate.EhCacheRegionFactory.start(EhCacheRegionFactory.java:79)
        ... 59 more
        at net.sf.ehcache.hibernate.EhCacheRegionFactory.start(EhCacheRegionFactory.java:89)
        at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:238)
        at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1872)
        at org.springframework.orm.hibernate3.LocalSessionFactoryBean.newSessionFactory(LocalSessionFactoryBean.java:860)
        at org.springframework.orm.hibernate3.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:779)
        at org.springframework.orm.hibernate3.AbstractSessionFactoryBean.afterPropertiesSet(AbstractSessionFactoryBean.java:188)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1514)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1452)
        ... 52 more
        at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:730)
        at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:196)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1035)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:939)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:485)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
        at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:585)
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:913)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:464)
        at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:103)
        at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:1)
        at org.springframework.test.context.support.DelegatingSmartContextLoader.loadContext(DelegatingSmartContextLoader.java:228)
        at org.springframework.test.context.TestContext.loadApplicationContext(TestContext.java:124)
        at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:148)
        ... 24 more

In this scenario net.sf.ehcache.CacheManager with name hibernate-cache is started by some unit test and as CacheManager shares cache instances via static variable, this cache instance looks like already being initialized for following unit test. In order to solve the issue make sure that:
  • You close the context after each unit test (see ''SpringRunner'' does not close the Application Context at the end of JUnit test case):

    MyTest.java

    import org.springframework.test.annotation.DirtiesContext;
    import org.springframework.test.annotation.DirtiesContext.ClassMode;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringRunner;
     
    @ContextConfiguration("classpath:/.../dao-context.xml")
    @RunWith(SpringRunner.class)
    @DirtiesContext(classMode = ClassMode.AFTER_CLASS)
    public class MyTest {
        ...
    }

    This will guarantee that Hibernate factory is correctly closed, which in its turn will shutdown cache manager.

  • You shutdown all other (not Hibernate managed) cache managers using destroy-method:

    another-context.xml

    <bean id="cacheManager" class="net.sf.ehcache.CacheManager" destroy-method="shutdown">
        <constructor-arg value="classpath:/.../negative-cache.xml" type="java.net.URL" />
    </bean>

MVC

How to call a static method in JSP/EL?

In general you need to make you method as normal getter, however with SpEL this is possible:
<%@taglib prefix="s" uri="http://www.springframework.org/tags" %>

<s:eval expression="T(org.company.Calculate).getAmount(row.balance)" />

Other examples of SpEL expressions:

  • #{T(Math).min(T(Runtime).getRuntime().maxMemory() / 3, 0L + T(Integer).MAX_VALUE)}
    Mind how 2nd argument was forced to long, otherwise “Method call of 'min' is ambiguous, supported type conversions allow multiple variants to match” exception will be thrown.
  • #{T(org.apache.commons.io.IOUtils).toString(new java.net.URL('file:///etc/HOSTNAME'))}
    This will stringify the given system file.

For non-JSP, non-EL approach check Invoke static method from spring config:

<bean id="test" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetClass" value="MyClass" />
    <property name="targetMethod" value="staticMethod" />
    <property name="arguments">
    <list>
        <value>anArgument</value>
    </list>
    </property>
</bean>

What is the lifecycle of Spring MVC Controller?

By default Spring MVC controllers are singletons. The request lifecycle is displayed on this diagram:

Spring MVC Request Lifecycle

What are the options for exception handling in Spring MVC?

From Spring MVC part V: Exception handling:
  • Return the error code with exception as a body:
    import org.apache.commons.lang.exception.ExceptionUtils;
     
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    @ResponseBody
    public String handleIllegalArgumentException(IllegalArgumentException ex) {
        return ExceptionUtils.getStackTrace(ex);
    }

    If you specify the custom reason for error response, then response body is ignored:
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "The passed argument is invalid")
    public void handleIllegalArgumentException(IllegalArgumentException ex) {
    }

  • Redirect to a dedicated error page:
    @ExceptionHandler(IndexOutOfBoundsException.class)
    public String handleException(IndexOutOfBoundsException ex) {
        return "pages/errorPage";
    }

How to override returned media type for response body in case of exception handling?

It may happen that during processing of request the exception occurs. If mapped method defines the produced media type, this media type is remembered and used for response body of the exception handler, which in most cases is not appropriate, because the browser expects text/plain MIME type for error HTTP codes.

In below scenario let's assume that myService.getInfo() throws IllegalArgumentException. In this case Spring will convert returned value from handleIllegalArgumentException() into application/json, however more appropriate will be text/plain.

To overcome this, exception hander should reset the remembered produced types before returning the response body:

import java.security.Principal;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.HandlerMapping;
 
...
 
@RequestMapping(value = "/", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Info getInfo(Principal principal) {
    return myService.getInfo(principal);
}
 
@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
@ResponseBody
public String handleIllegalArgumentException(HttpServletRequest request, IllegalArgumentException ex) {
    resetProducibleMediaTypes(request);
 
    return ExceptionUtils.getStackTrace(ex);
}
 
/**
 * Media types to negotiate for error conditions.
 */
private static final Set<MediaType>    DEFAULT_MEDIA_TYPES;
 
static {
    Set<MediaType> mediaTypes = new HashSet<MediaType>();
 
    Collections.addAll(mediaTypes, MediaType.TEXT_PLAIN, MediaType.ALL);
 
    DEFAULT_MEDIA_TYPES = Collections.unmodifiableSet(mediaTypes);
}
 
/**
 * Content types, which have been remembered to handle the return type of handler method, need to be reset to allow
 * the return type of exception handler to have different content type.
 */
private static void resetProducibleMediaTypes(HttpServletRequest request) {
    request.setAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, DEFAULT_MEDIA_TYPES);
}

How to add HTTP request post-processing handler in Spring Web?

Add the following to your Web context (see also Bind Spring ''HandlerInterceptor'' only to one controller):
<mvc:interceptors>
    <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />
</mvc:interceptors>

How to use a request-scoped bean inside a spawned thread?

Spring infrastructure (e.g. DispatcherServlet) binds the HTTP request to current thread, which makes it inaccessible for spawned threads. To overcome the problem the following conditions should be met:

Is it possible to return several model objects?

One should use ModelAndView for that (from Can I return two models in ModelAndView in Spring MVC):
@RequestMapping(value = {"/registrationForm"}, method = RequestMethod.GET)
public ModelAndView newForm() {
    final ModelAndView modelAndView = new ModelAndView("registrationForm");
 
    modelAndView.addObject("registrationForm", new RegistrationForm());
    modelAndView.addObject("sharedData", sharedData);
 
    return modelAndView;
}

How to enable validation in Spring MVC?

As from Spring Validation and Validation in Spring MVC you need to:
  • Include javax.validation:validation-api into you dependencies list.
  • Annotate the model with @Valid:
    @RequestMapping(method = RequestMethod.POST)
    public String onFormSubmitted(@ModelAttribute("myForm") @Valid MyForm myForm, BindingResult result) {
        if (result.hasErrors()) {
            return "myForm";
        }
     
        return "formSubmitted";
    }
  • Initialize validator for binder:
    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.setValidator(new MyFormValidator());
    }
  • Implement custom validator:
    import org.springframework.validation.Errors;
    import org.springframework.validation.ValidationUtils;
    import org.springframework.validation.Validator;
     
    public class MyFormValidator implements Validator {
     
        /**
         * @see org.springframework.validation.Validator#supports(java.lang.Class)
         */
        public boolean supports(Class<?> clazz) {
            return MyForm.class.isAssignableFrom(clazz);
        }
     
        /**
         * @see org.springframework.validation.Validator#validate(java.lang.Object, org.springframework.validation.Errors)
         */
        public void validate(Object target, Errors errors) {
            ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email", "required", "Field is required.");
        }
    }

Additional info:

How to integrate ReCaptcha into Spring MVC?

See here and here:

pom.xml

<dependency>
    <groupId>net.tanesha.recaptcha4j</groupId>
    <artifactId>recaptcha4j</artifactId>
    <version>0.0.7</version>
</dependency>

Use of a “*” for the field name is important, otherwise MVC will try to resolve the field value:

registrationForm.jsp

<%@ page import="net.tanesha.recaptcha.ReCaptcha" %>
<%@ page import="net.tanesha.recaptcha.ReCaptchaFactory" %>
<%@ page import="com.company.module.web.SharedData" %>
...
<form:form method="post" commandName="registrationForm">

<form:errors path="captcha*" /><br/>
<%
    SharedData sharedData = (SharedData) request.getAttribute("sharedData");
    ReCaptcha captcha = ReCaptchaFactory.newReCaptcha(sharedData.getRecaptchaPublicKey(), sharedData.getRecaptchaPrivateKey(), false);
    out.print(captcha.createRecaptchaHtml(null, null));
%>

</form:form>

The controller code:

import net.tanesha.recaptcha.ReCaptchaImpl;
import net.tanesha.recaptcha.ReCaptchaResponse;
 
@Controller
@SessionAttributes("sharedData")
public class RegistrationFormController {
 
    @Autowired
    private SharedData sharedData;
 
    @RequestMapping(value = {"/registrationForm"}, method = RequestMethod.GET)
    public ModelAndView newForm() {
        final ModelAndView modelAndView = new ModelAndView("registrationForm");
 
        modelAndView.addObject("registrationForm", new RegistrationForm());
        modelAndView.addObject("sharedData", sharedData);
 
        return modelAndView;
    }
 
    @RequestMapping(value = {"/registrationForm"}, method = RequestMethod.POST)
    public String onRegistrationFormSubmitted(HttpServletRequest request,
                @RequestParam("recaptcha_challenge_field") String challenge,
                @RequestParam("recaptcha_response_field") String response,
                @ModelAttribute("registrationForm") @Valid RegistrationForm registrationForm, BindingResult result) {
 
        final ReCaptchaImpl recaptcha = new ReCaptchaImpl();
 
        recaptcha.setPrivateKey(sharedData.getRecaptchaPrivateKey());
 
        final ReCaptchaResponse captchaResponse = recaptcha.checkAnswer(request.getRemoteAddr(), challenge, response);
 
        if (!captchaResponse.isValid()) {
            // 2nd argument should match <form:errors path="..."> in JSP:
            result.addError(new FieldError("comment", "captcha", "Captcha is wrong"));
        }
 
        if (result.hasErrors()) {
            return "registrationForm";
        }
 
        // Perform business logic, e.g. persist registration data
 
        return "formSubmitted";
    }
}

And shared data is initialized in Spring context:

web-context.xml

<bean class="com.company.module.web.SharedData">
    <!-- ReCaptcha private key -->
    <constructor-arg value="5LemXMassAAsAJ9itNONm2hYjWmvAsaRBxyIWYfk" />
    <!-- ReCaptcha public key -->
    <constructor-arg value="5LemcnmaAAssaBJcF6gYH-zcBTeJ_ArWakbg3wsX" />
</bean>

Check also Integrating Captcha with Spring Security, CAPTCHA in java.

How do you make the “welcome file” (index.html) be handled via Spring and not your web container?

From here:

web.xml

<servlet-mapping>
    <servlet-name>spring</servlet-name>
    <url-pattern>*.html</url-pattern>
    <url-pattern>/index.html</url-pattern>
</servlet-mapping>
 
<welcome-file-list>
    <welcome-file>index.html</welcome-file>
</welcome-file-list>

To map certain resources back to default servlet, use the following:

web.xml

<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.jpg</url-pattern>
</servlet-mapping>

:WARN: Mind the priority of URL-matching rules1):

  1. URL patterns (/*)
  2. extension patterns (*.html)
  3. default servlet (/)

software/spring.txt · Last modified: 2011/06/14 19:46 by dmitry
 
 
Recent changes RSS feed Driven by DokuWiki