Spring PropertySourcesPlaceholderConfigurer in library - spring

Background: I am writing a library that will be compiled into a JAR file. That library will be used as a dependency in a number of web applications. Both the library and the web apps are using Spring. There is an onus on the web application to run a ComponentScan on the library classes to pick up any Spring Beans / configuration.
Ask: Within the library I want to load properties from a property file using PropertySourcesPlaceholderConfigurer. Something like this:
package com.blah;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
#Configuration
#PropertySource("classpath:properties/blah.${environment}.properties")
public class AppConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
This is working fine.
Question: if the web-app that loads this library as a dependency also uses PropertySourcesPlaceholderConfigurer to load properties, will there be a conflict between the two? Will one override the other (even if the properties are different)? Or can they live in peaceful harmony side-by-side?
Using Spring 3.2.4
UPDATE
As per Bogdan Oros's answer below, it looks like this is OK i.e. they will not conflict and both sets of properties will be loaded. I created two Config files:
#Configuration
#PropertySource("classpath:properties/blah.stage1.properties")
public class BlahClientConfig1 {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
and
#Configuration
#PropertySource("classpath:properties/blah.stage2.properties")
public class BlahClientConfig2 {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
When I run my tests, I can successfully retrieve property values from both blah.stage1.properties and blah.stage2.properties

You can do an experiment a create same bean in two different configurations in same classpath and it will work
#Configuration
class AConfiguration {
#Bean
public static PropertySourcesPlaceholderConfigurer resolver() {
return new PropertySourcesPlaceholderConfigurer();
}
}
#Configuration
class B {|
#Bean
public static PropertySourcesPlaceholderConfigurer resolver() {
return new PropertySourcesPlaceholderConfigurer();
}
}
It will work correctly because spring resolves such situations.
You'll find that one bean is overridden by the other.
But if you will help spring and setup names explicitly
#Configuration
class A {
#Bean(name="resolver")
public static PropertySourcesPlaceholderConfigurer resolver() {
return new PropertySourcesPlaceholderConfigurer();
}
}
#Configuration
class B {
#Bean(name="resolver")
public static PropertySourcesPlaceholderConfigurer resolver() {
return new PropertySourcesPlaceholderConfigurer();
}
}
This situation will cause a injection failure, because it cannot decide which bean to inject.
It is explained here and also it can be configured with DefaultListableBeanFactory. Check this answer.

Related

Transitive inclusion of #Configuration classes from a .xml-based spring config

Suppose we start with an xml-based config, say main.xml, that imports a java config FullConfig.java via:
<context:annotation-config/>
<bean class="test.FullConfig"/>
This java config has the form:
#Configuration
#Import(value = {IncludeConfig.class})
public class FullConfig {
#Autowired
#Qualifier(value = "tmpBean")
private DataClazz autowired;
#Bean
public DataClazz someOtherBean() {
System.out.println("Using autowired tmpBean:" + autowired);
return new DataClazz();
}
}
so it imports a further java config, which contains a definition of the tmpBean of DataClazz type,
#Configuration
public class IncludeConfig {
#Bean
public DataClazz tmpBean() {
return new DataClazz();
}
}
Now two questions:
Is this "transitive inclusion" guaranteed to work in spring (i.e. is someOtherBean() guaranteed not to thrown a NPE)?
IntelliJ up to version 2017.2 does mark #Qualifier(value = "tmpBean") red with a message "Cannot find bean with qualifier 'tmpBean'". Should that be considered a bug?
Note: I have checked that an application using ClassPathXmlApplicationContext("main.xml") does work correctly, i.e. no NPE is thrown (and all relevant beans are visible).
You need to return DataClazz:
#Bean
public DataClazz someOtherBean() {
System.out.println("Using autowired tmpBean:" + autowired);
return autowired;
}
Probably yes but try to test it.
IDEA-82844 (Bug)

Does a Spring Boot Project that runs as a Jar needs a web.xml file?

As the title states.
I am migrating a Spring-MVC application that uses XML based Configuration.
I don't know where to move the filters located in the web.xml file to the new Spring Boot Project.
You can make use of the annotation : #ImportResource for this
Find more details here
You can define your filters using Java Configurations when using Spring Boot.
As mentioned in the documentation, you only need to declare that filter as a Bean in a configuration class.
#Configuration
public class WebConfig {
#Bean
public Filter someFilter() {
return new someFilter();
}
}
If for some reason "SomeFilter" is not a spring managed bean, or if you need to customize the filter behaviour, then you can register the filter using FilterRegistrationBean as follows
#Configuration
public class WebConfig {
#Bean
public Filter someFilter() {
return new someFilter();
}
#Bean
public FilterRegistrationBean someFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(someFilter());
return registration;
}
}
In case of multiple Filters you can specify the order using FilterRegistrationBean.setOrder() as mentioned in the doc
Finally I registered my Interceptors using Java Configuration (no xml) this way.
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Autowired
ControllerInterceptor controllerInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(this.controllerInterceptor).addPathPatterns(this.buildPaths());
}
private String[] buildPaths() {
String paths[] = { "/api/example1/**", "/api/example2/**" };
return paths;
}
}

Inject test beans into main method

I'm using JavaConfig to manage and wire Spring beans into my Java app. The Java application is a main method - and basically runs as a batch job, invoked via a bash file. Is there a way that I can use a different (test) config in my main method?
public static void main(String[] args) {
final ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
// do Stuff
}
I have used the following annotations successfully before in my test classes:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { TestConfig.class })
, but this does not work for "main" applications. Short of passing in the Spring context to use as an argument, not sure what I can do here. Thanks
You should be able to use profiles in your actual config class to do what you want as well.
By setting the desired Profile you can "inject" the different beans you want.
Your ApplicationConfig might look like:
#Configuration
#Import({
JndiDataConfig.class,
TestDataConfig.class,
)
public class ApplicationConfig {
...
where TestDataConfig looks (in part) like:
#Configuration
#Profile("test")
public class TestDataConfig {
#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();
}
}
and where JndiDataConfig looks like:
#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");
}
}

Can #PropertySources be chosen by Spring profile?

I have a Spring 3.1 #Configuration that needs a property foo to build a bean. The property is defined in defaults.properties but may be overridden by the property in overrides.properties if the application has an active override Spring profile.
Without the override, the code would look like this, and work...
#Configuration
#PropertySource("classpath:defaults.properties")
public class MyConfiguration {
#Autowired
private Environment environment;
#Bean
public Bean bean() {
...
// this.environment.getRequiredProperty("foo");
...
}
}
I would like a #PropertySource for classpath:overrides.properties contingent on #Profile("overrides"). Does anyone have any ideas on how this could be achieved? Some options I've considered are a duplicate #Configuration, but that would violate DRY, or programmatic manipulation of the ConfigurableEnvironment, but I'm not sure where the environment.getPropertySources.addFirst() call would go.
Placing the following in an XML configuration works if I inject the property directly with #Value, but not when I use Environment and the getRequiredProperty() method.
<context:property-placeholder ignore-unresolvable="true" location="classpath:defaults.properties"/>
<beans profile="overrides">
<context:property-placeholder ignore-unresolvable="true" order="0"
location="classpath:overrides.properties"/>
</beans>
Update
If you're trying to do this now, check out Spring Boot's YAML support, particularly the 'Using YAML instead of Properties' section. The profile support there would make this question moot, but there isn't #PropertySource support yet.
Add the overriding #PropertySource in a static inner class. Unfortunately, you must specify all property sources together which means creating a "default" profile as the alternative to "override".
#Configuration
public class MyConfiguration
{
#Configuration
#Profile("default")
#PropertySource("classpath:defaults.properties")
static class Defaults
{ }
#Configuration
#Profile("override")
#PropertySource({"classpath:defaults.properties", "classpath:overrides.properties"})
static class Overrides
{
// nothing needed here if you are only overriding property values
}
#Autowired
private Environment environment;
#Bean
public Bean bean() {
...
// this.environment.getRequiredProperty("foo");
...
}
}
I suggest, defining two files, where the second is optional with the profile as suffix:
#Configuration
#PropertySources({
#PropertySource("classpath:/myconfig.properties"),
#PropertySource(value = "classpath:/myconfig-${spring.profiles.active}.properties", ignoreResourceNotFound = true)
})
public class MyConfigurationFile {
#Value("${my.prop1}")
private String prop1;
#Value("${my.prop2}")
private String prop2;
}
You can do:
<context:property-placeholder location="classpath:${spring.profiles.active}.properties" />
Edit: if you need something more advanced, you can register your PropertySources on application startup.
web.xml
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>com.xxx.core.spring.properties.PropertySourcesApplicationContextInitializer</param-value>
</context-param>
file you create:
public class PropertySourcesApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final Logger LOGGER = LoggerFactory.getLogger(PropertySourcesApplicationContextInitializer.class);
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
LOGGER.info("Adding some additional property sources");
String[] profiles = applicationContext.getEnvironment().getActiveProfiles()
// ... Add property sources according to selected spring profile
// (note there already are some property sources registered, system properties etc)
applicationContext.getEnvironment().getPropertySources().addLast(myPropertySource);
}
}
Once you've done it you just need to add in your context:
<context:property-placeholder/>
I can't really answer to your question about multiple profiles but I guess you activate them on such an initializer, and you could register the appropriate PropertySource items during profile activations.
I can't think of any other way than one you have suggested Emerson, which is to define this bean in a separate #Configuration file with an #Profile annotation:
#Configuration
#Profile("override")
#PropertySource("classpath:override.properties")
public class OverriddenConfig {
#Autowired
private Environment environment;
#Bean
public Bean bean() {
//if..
}
}
In case you need to support multiple profiles you could do something like this:
#Configuration
public class Config {
#Configuration
#Profile("default")
#PropertySource("classpath:application.properties")
static class DefaultProperties {
}
#Configuration
#Profile("!default")
#PropertySource({"classpath:application.properties", "classpath:application-${spring.profiles.active}.properties"})
static class NonDefaultProperties {
}
}
That way you don't need to define a static configuration class for each profile.
Thanks David Harkness for putting me into the right direction.
Note: This answer provides an alternate solution to using properties files with #PropertySource. I went this route because it was too cumbersome trying to work with multiple properties files that may each have overrides while avoiding repetitive code.
Create a POJO interface for each related set of properties to define their names and types.
public interface DataSourceProperties
{
String driverClassName();
String url();
String user();
String password();
}
Implement to return the default values.
public class DefaultDataSourceProperties implements DataSourceProperties
{
public String driverClassName() { return "com.mysql.jdbc.Driver"; }
...
}
Subclass for each profile (e.g. development, production) and override any values that differ from the default. This requires a set of mutually-exclusive profiles, but you can easily add "default" as the alternative to "overrides".
#Profile("production")
#Configuration
public class ProductionDataSourceProperties extends DefaultDataSourceProperties
{
// nothing to override as defaults are for production
}
#Profile("development")
#Configuration
public class DevelopmentDataSourceProperties extends DefaultDataSourceProperties
{
public String user() { return "dev"; }
public String password() { return "dev"; }
}
Finally, autowire the properties configurations into the other configurations that need them. The advantage here is that you don't repeat any #Bean creation code.
#Configuration
public class DataSourceConfig
{
#Autowired
private DataSourceProperties properties;
#Bean
public DataSource dataSource() {
BoneCPDataSource source = new BoneCPDataSource();
source.setJdbcUrl(properties.url());
...
return source;
}
}
I am still not convinced I'll stick with this over manually configuring properties files based on the active profiles in a servlet context initializer. My thought was that doing manual configuration would not be as amenable to unit testing, but I'm not so sure now. I really prefer reading properties files to a list of property accessors.
All mentioned here solutions are a bit awkward, work only with one profile preset, and they won't work with more/other profiles. Currently a Spring team refuses to introduce this feature. But here's the working workaround I've found:
package com.example;
public class MyPropertySourceFactory implements PropertySourceFactory, SpringApplicationRunListener {
public static final Logger logger = LoggerFactory.getLogger(MyPropertySourceFactory.class);
#NonNull private static String[] activeProfiles = new String[0];
// this constructor is used for PropertySourceFactory
public MyPropertySourceFactory() {
}
// this constructor is used for SpringApplicationRunListener
public MyPropertySourceFactory(SpringApplication app, String[] params) {
}
#Override
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
activeProfiles = environment.getActiveProfiles();
}
#Override
public PropertySource<?> createPropertySource(String name, EncodedResource encodedResource) throws IOException {
logger.info("Loading: {} with profiles: {}", encodedResource.toString(), activeProfiles);
// here you know all profiles and have the source Resource with main
// properties, just try to load other resoures in the same path with different
// profile names and return them as a CompositePropertySource
}
}
To make it working you have to have src/main/resources/META-INF/spring.factories with the following content:
org.springframework.boot.SpringApplicationRunListener=com.example.MyPropertySourceFactory
Now you can put your custom properties file somewhere and load it with #PropertySources:
#Configuration
#PropertySource(value = "classpath:lib.yml", factory = MyPropertySourceFactory.class)
public class PropertyLoader {
}

Spring #Configuration bean created in #Bean method not enhanced by CGLIB

I'm trying to create a MainConfig that imports another Config by using an #Bean method instead of #Import like this :
#Configuration
public class MainConfig {
#Bean
public Service service() {
return new Service(infrastructureConfig().database());
}
#Bean
public OtherService otherService() {
return new OtherService(infrastructureConfig().database());
}
#Bean
public InfrastructureConfig intrastructureConfig() {
return new InfrastructureConfig();
}
}
#Configuration
public class InfrastructureConfig {
#Bean
public Database database() {
return new Database();
}
...
}
When using this technique, the Database is created twice because Spring doesn't seem to consider the #Configuration annotation on InfrastructureConfig. When using #Import, it works fine.
I don't want to use #Import because I want to mock my InfrastructureConfig like this :
#Configuration
public class TestConfig extends MainConfig {
#Override
public InfrastructureConfig infrastructureConfig() {
return mock(InfrastructureConfig.class);
}
}
Am I missing something or it is not supported ?
Thanks
When I first tried out Spring Java configuration I think I made the same assumption and was surprised when it didn't work.
I'm not sure this is the neatest way of solving this but I have used the following approach successfully.
To include that #Configuration class you can add this annotation to your MainConfig:
#ComponentScan(basePackages = "org.foo", includeFilters = {#Filter(filterType = ANNOTATION, value = CONFIGURATION)}, excludeFilters = {#Filter(filterType = ASSIGNABLE_TYPE, value = MainConfig)})
Since #Configuration classes are also candidates for component scanning this allows you to scan for all classes annotated with #Configuration. Since you're putting this annotation on MainConfig you need to exclude that with the ASSIGNABLE_TYPE filter since you'll get a circular reference.
I opened a Spring ticket SpringSource JIRA and they said that it is a known limitation and it is working as designed.

Resources