Issue 1:
Eric Pederson opened SPR-7164 and commented

Using @Bean style bean definitions with placeholders introduces a lot of noise. For example, replacing

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

<property name="driverClassName" value="${dataSource.driverClassName:com.mysql.jdbc.Driver}"/>

<property name="url" value="${dataSource.url:jdbc:mysql://localhost:3306/auth}"/>

<property name="username" value="${dataSource.username:root}"/>

<property name="password" value="${dataSource.password:root}"/>

</bean>

with a @Bean and @Values you get

@Value("${dataSource.driverClassName:com.mysql.jdbc.Driver}")

var driverClassName: String = null

@Value("${dataSource.url:jdbc:mysql://localhost:3306/auth}")

var url: String = null

@Value("${dataSource.username:root}")

var username: String = null

@Value("${dataSource.password:root}")

var password: String = null

@Bean(destroyMethod = "close")

def dataSource: DataSource = {

var dataSource: BasicDataSource = new BasicDataSource

dataSource.setDriverClassName(driverClassName)

dataSource.setUrl(url)

dataSource.setUsername(username)

dataSource.setPassword(password)

return dataSource

}

Yikes!

The PropertyPlaceholderConfigurer knows

what the merged properties are

what the SystemPropertiesMode is

what the placeholder prefix, suffix and default delimiter are

about handling default values (new in 3.0 thanks!!)

What I want to be able to do is something like (where ppc is a reference to the PropertyPlaceholderConfigurer)

@Bean(destroyMethod = "close")

def dataSource: DataSource = {

var dataSource: BasicDataSource = new BasicDataSource

dataSource.setDriverClassName(ppc.resolve("${dataSource.driverClassName:com.mysql.jdbc.Driver}"))

dataSource.setUrl(ppc.resolve("${dataSource.url:jdbc:mysql://localhost:3306/auth}"))

dataSource.setUsername(ppc.resolve("${dataSource.username:root}"))

dataSource.setPassword(ppc.resolve("${dataSource.password:root}"))

return dataSource

}

There are workarounds but they involve subclassing 2 or 3 Spring classes to get access to protected or private data, and other unDRY unpleasantness. See example below.

Thanks,

// Example

@Configuration

class AppConfig {

var log: Logger = Logger.getLogger(this.getClass().getName())

@Bean

def propertyPlaceholderConfigurer: PropertyPlaceholderConfigurer = {

val resources = new ArrayBuffer[Resource]

if (AppConfig.environment != "development") resources.append(new ClassPathResource(AppConfig.environment + "-environment.properties"))

if (AppConfig.server != null) resources.append(new ClassPathResource(AppConfig.server + "-server.properties"))

val propertiesSupport = new PropertiesLoaderSupport {

def merge: Properties = super.mergeProperties

}

propertiesSupport.setIgnoreResourceNotFound(true)

propertiesSupport.setLocations(resources.toArray)

AppConfig.mergedProperties = propertiesSupport.merge

val ppc = AppConfig.propertyPlaceholderConfigurer

ppc.setProperties(AppConfig.mergedProperties)

ppc.setSystemPropertiesMode(PropertyPlaceholderConfigurer.SYSTEM_PROPERTIES_MODE_OVERRIDE)

return ppc

}

}

object AppConfig {

var propertyPlaceholderConfigurer = new PropertyPlaceholderConfigurer {

def resolve(placeholder: String) = super.resolvePlaceholder(placeholder, mergedProperties, PropertyPlaceholderConfigurer.SYSTEM_PROPERTIES_MODE_OVERRIDE)

}

var mergedProperties: Properties = null

var placeholderResolver = new PlaceholderResolver {

def resolvePlaceholder(placeholderName: String) = {

propertyPlaceholderConfigurer.resolve(placeholderName)

}

}

var placeholderHelper = new PropertyPlaceholderHelper("${", "}", ":", false)

def resolve(placeholder: String) = placeholderHelper.replacePlaceholders(placeholder, placeholderResolver)

var environmentName: String = null

def environment: String = {

if (environmentName == null) {

val systemEnvironmentName = System.getProperty("config.environment")

if (systemEnvironmentName != null) {

environmentName = systemEnvironmentName

} else {

environmentName = "development"

}

}

return environmentName

}

def server: String = System.getProperty("config.server")

}

@Configuration

class DataConfig {

@Bean(destroyMethod = "close")

def dataSource: DataSource = {

var dataSource: BasicDataSource = null

if (AppConfig.environment == "development") {

dataSource = new BasicDataSource

dataSource.setDriverClassName(AppConfig.resolve("${dataSource.driverClassName:com.mysql.jdbc.Driver}"))

dataSource.setUrl(AppConfig.resolve("${dataSource.url:jdbc:mysql://localhost:3306/auth}"))

dataSource.setUsername(AppConfig.resolve("${dataSource.username:root}"))

dataSource.setPassword(AppConfig.resolve("${dataSource.password:root}"))

} else {

// Lookup via JNDI

}

return dataSource

}

}

Affects: 3.0.2

Issue Links:

#8242 org.springframework.beans.factory.config.PropertyPlaceholderConfigurer should add a public method 'parseStringValue' ("is duplicated by")

Issue 2:
Doug Gschwind opened SPR-7585 and commented

The RequestMapping annotation does a great job of helping the reader of a Spring MVC Controller understand which method(s) on that Controller will be invoked given a URI such as "/account", the HTTP method (e.g. GET or POST), as well as given the presence/absence of certain identified HTTP request parameters.

I believe this implementation suffers from the following though, and hence could be improved to address these concerns :

These URIs embedded in a Spring MVC Controller are the source of Don't Repeat Yourself violations since that same URI has to be repeated elsewhere in say a JSP that has a hyperlink or a button that intends to invoke a given method on a given type of Controller when the hyperlink or button is clicked upon.

Spring MVC does not appear to have a facility to be able to generate a URL in say a JSP, that intends to invoke a given method on a given type of Controller. This is closely related to the first item.

By having these URIs sprinkled throughout your Spring MVC Controller classes, you have a little bit of research to do if you plan to introduce a new Controller that should have a distinct URI that it responds to. A text search through the source files would solve that problem, but that is slow and error prone. I understand the Spring MVC plugin for Eclipse has some support for this, but once again requires the human eye to review all the existing URIs to determine if there exists a collision or partial URI match of some sort.

I have tried to solve one and two by placing information in the Spring MVC descriptor, but that involves interrogating the ApplicationContext, and of course, my own custom code. I would like the framework to provide this support for me, so that I have to specify a URI for a Controller and Method, once and only once. If that were possible, I could make a URI change in one place, and the app would continue to run fine (assuming that change introduced no [partial] collisions) without any further effort.

Thank you,

Doug Gschwind

Affects: 3.0 GA

Issue Links:

#10449 Allow links to @Controller methods from views without hardcoding URLs ("duplicates")