Spring 5.3.20 - have 2 Demos.
A: The classical Java based JPA hello world.
#Configuration
#EnableTransactionManagement
#PropertySource()
public class JpaAppConfig {
#Value() user;
#Value() password;
#Bean public static PropertySourcesPlaceholderConfigurer...
#Bean public PlatformTransactionManager...
#Bean public LocalContainerEntityManagerFactoryBean...
#Bean public DataSource ... { dataSource.setUsername(user); }
#Bean public PersistenceExceptionTranslationPostProcessor...
}
B: A combined Java/Annotations based properties demo.
#Configuration
#ComponentScan() /* is needed for some other bean used in main */
public class AppConfig {
#Autowired Environment env
#Bean MyObject1 ... { foo = env.getProperty("foo"); }
#Bean MyObject2 ... { /* has #Value as field of MyObject2 */ }
}
A breaks if I omit PropertySourcesPlaceholderConfigurer.
B is doing just fine without.
Why?
What exactly is either:
causing PSPC to be required in A, or
adding PSPC in B without me realizing?
Related
I am trying to create Spring beans using only annotations. I am not able to load values for my #Bean class from properties file.
Here is my code:
This is my main class
public class AnnotationDI {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(ConfigurationProvider.class);
ApplicationProperties properties = (ApplicationProperties)context.getBean(ApplicationProperties.class);
System.out.println(properties);
}}
Configuration class
#Configuration
public class ConfigurationProvider {
private ApplicationProperties m_applicationProperties;
#Bean
public ApplicationProperties getApplicationProperties() {
return new ApplicationProperties();
}
}
Bean class
#PropertySource(value = { "classpath:application.properties" })
public class ApplicationProperties {
#Value("${longThreadCount}")
private String m_longProcessThread;
#Value("${routeTimeout}")
private String m_routeTimeout;
#Value("${updateDirectoryPath}")
private String m_updateDirectoryPath;
public String getLongProcessThread() {
return m_longProcessThread;
}
#Override
public String toString() {
return "ApplicationProperties [m_longProcessThread=" +m_longProcessThread"]";
}
}
when i run this program, I get following output
m_longProcessThread=${longThreadCount}
Any idea what am i doing wrong?
To be able to have #Value with placeholders resolved you need to register a PropertySourcesPlaceholderConfigurer. As this is a BeanFactoryPostProcessor it needs to be registered as a static bean so that it can be detected early on in the process.
#Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#PropertySource annotation has to be used in conjunction with #Configuration annotation like so,
#Configuration
#PropertySource(value = { "classpath:application.properties" })
public class ApplicationProperties {
...
}
Adding #Configuration annotation would solve the issue in this case.
I have two projects one with DAO classes and Model and another with Rest Controller
Project A : DAO Classes + Model
Project B : Rest Controller
Project A
application.properties:
spring.abcDatasource.url=
spring.abcDatasource.username=
spring.abcDatasource.password=
spring.abcDatasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.xyzDatasource.url=
spring.xyzDatasource.username=
spring.xyzDatasource.password=
spring.xyzDatasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.initialize=false
DBConfiguration.java
#Configuration
public class DBConfiguration {
#Primary
#Bean(name = "abcDS")
#ConfigurationProperties(prefix = "spring.abcDatasource")
public DataSource abcDS() {
return DataSourceBuilder.create().build();
}
#Bean(name = "abcJdbc")
public JdbcTemplate abcJdbcTemplate(#Qualifier("abcDS") DataSource abcDS) {
return new JdbcTemplate(abcDS);
}
#Bean(name = "xyzDS")
#ConfigurationProperties(prefix = "spring.xyzDatasource")
public DataSource xyzDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "xyzJdbc")
public JdbcTemplate ebsJdbcTemplate(#Qualifier("xyzDS") DataSource xyzDatasource) {
return new JdbcTemplate(xyzDatasource);
}
}
AlphaDAO.Java
#Repository
public class AlphaDAO{
#Autowired
#Qualifier("abcJdbc")
private JdbcTemplate abcJdbc;
#Autowired
#Qualifier("xyzJdbc")
private JdbcTemplate xyzJdbc;
SqlParameterSource namedParameters;
public Collection<Alpha> findAll(String owner){
String sql = "SELECT * from alpha where OWNER in (:owner)" ;
NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(abcJdbc.getDataSource());
namedParameters = new MapSqlParameterSource("owner", owner);
List<Alpha> list = namedParameterJdbcTemplate.query(sql,namedParameters,
new BeanPropertyRowMapper(Alpha.class));
return list;
}
Project B Rest Controller :
AlphaServiceApplication.java
#SpringBootApplication
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class AlphaServiceApplication extends SpringBootServletInitializer implements WebApplicationInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(AlphaServiceApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(AlphaServiceApplication.class, args);
}
}
AlphaServiceController.java
#RestController
public class AlphaServiceController {
private static final Logger logger = LoggerFactory.getLogger(AlphaServiceController.class);
#Autowired
AlphaDAO dao;
#CrossOrigin(origins = "http://localhost:4200")
#RequestMapping("/alpha")
public Collection<Alpha> index(#RequestBody String owner) {
return dao.findAll(owner);
}
If I try to run the rest controller I am getting the error saying
APPLICATION FAILED TO START
Description:
Field dao in com.xyz.web.wip.AlphaService.AlphaServiceController required a bean of type 'com.xyz.comp.wip.alphaComp.dao.AlphaDAO' that could not be found.
Action:
Consider defining a bean of type 'com.xyz.comp.wip.alphaComp.dao.AlphaDAO' in your configuration.
Your AlphaDao class doesnt make much sense, you are trying to autowire two fields but you still have a constructor.
Spring cant build the object because there is no qualifier on the constructor.
You can either do constructor injection or field injection but you shouldn’t use both.
I would recommend using constructor injection.
#Repository
public class AlphaDAO{
private final JdbcTemplate abcJdbc;
private final JdbcTemplate xyzJdbc;
#Autowired
public AlphaDAO(
#Qualifier("abcJdbc") JdbcTemplate abcJdbc,
#Qualifier("xyzJdbc") JdbcTemplate xyzJdbc){
this.abcJdbc = abcJdbc;
this.xyzJdbc = xyzJdbc;
}
Also remove your #Bean method from the controller.
Since the DAO classes and Rest Controller is in different packages. Added scanBasePackages to #SpringBootApplication annotation with one level up worked fine.
AlphaServiceApplication.java
#SpringBootApplication(scanBasePackages = { "com.xyz" })
On a project I'm currently working on, we have the need for multiple profiles, i.e. "default" and "cloud".
both DefaultContext and CloudContext contains the same bean definitions
We are using PCF(Pivotal Cloud Foundry)
we have created an interface
public interface Config {
public DataSource getDataSource();
public SomeService getService();
}
Then implement each profile with this interface
#Primary
#Configuration
#Profile("default")
public class DevConfig implements Config
{
public DataSource getDataSource() {
// create and return production datasource
}
public SomeService getService() {
// Create and return production service
}
}
And then do the same for cloud.
#Configuratio
#Profile("cloud")
public class CloudConfig extends AbstractCloudConfig implements Config
{
public DataSource getDataSource() {
// create and return dummy datasource
}
public SomeService getService() {
// Create and return dummy service
}
}
And we are Autowiring in the service call, in processor file.
#Service("processor")
public class Processor {
#Autowired Config dsConfig;
public object get(int Number)
{
return dao.get(Number,dsConfig.getDataSource());
}
}
If we deploy in PCF, its working fine, as the profile is cloud. If we are running in local, it should get the default profile, but dsConfig is null.
Could you please help on this.
#Configuration classes aren't availalbe for autowiring.
As #spencergibb pointed out in the comment you need to tell the container to make this classes available for autowiring.
For that annotate them with #Component.
Something like this:
#Component
#Profile("default")
public class DevConfig implements Config
{
public DataSource getDataSource() {
// create and return production datasource
}
public SomeService getService() {
// Create and return production service
}
}
In case it still doesn't work, check the following two points:
Do you have the configs (DevConfig and Cloudconfig) in different packages so the ContextScan doesn't find it?
Are you running in another profile locally? (like Dev).
You can put this snipped to your code (its from JHipster) to log the active profiles.
#Autowired
private Environment env;
/**
* Initializes Application.
* <p/>
* Spring profiles can be configured with a program arguments --spring.profiles.active=your-active-profile
* <p/>
*/
#PostConstruct
public void initApplication() throws IOException {
if (env.getActiveProfiles().length == 0) {
log.warn("No Spring profile configured, running with default configuration");
}
else {
log.info("Running with Spring profile(s) : {}", Arrays.toString(env.getActiveProfiles()));
}
}
I'd rather autowire datasource and service classes instead of configuration class.
In this way you wouldn't need any instance of configuration and directly autowire whatever class you want.
So the classes will look like below.
Default Config:
#Primary
#Configuration
#Profile("default")
public class DevConfig implements Config
{
#Bean
public DataSource getDataSource() {
// create and return production datasource
}
#Bean
public SomeService getService() {
// Create and return production service
}
}
Cloud Config:
#Configuration
#Profile("cloud")
public class CloudConfig extends AbstractCloudConfig implements Config
{
#Bean
public DataSource getDataSource() {
// create and return dummy datasource
}
#Bean
public SomeService getService() {
// Create and return dummy service
}
}
Processor Class:
#Service("processor")
public class Processor {
#Autowired
private DataSource dataSource;
public object get(int Number)
{
return dao.get(Number,datasource);
}
}
I'm unable to get #Profile to work on a #Bean, but it works fine on the #Configuration class. In my module tests I correctly get the MyServiceStub autowired in the first example, but not in the second.
Does anyone know why?
This works:
MyConfig.java
#Configuration
#Import({StubConfig.class, RealConfig.class}
public class MyConfig {}
StubConfig.java
#Profile({"featureTest", "moduleTest"})
#Configuration
public class StubConfig {
#Bean
public MyService myService() {
return new MyServiceStub();
}
}
RealConfig.java
#Profile({"!featureTest", "!moduleTest"})
#Configuration
public class RealConfig {
#Bean
public MyService myService() {
return new MyService();
}
}
But this doesn't work:
MyConfig.java
#Configuration
public class MyConfig {
#Bean
#Profile({"featureTest", "moduleTest"})
public MyService myServiceStub() {
return new MyServiceStub();
}
#Bean
#Profile({"!featureTest", "!moduleTest"})
public MyService myService() {
return new MyService();
}
}
Instead of using #Profile approach, you could try using the #Conditional to have conditional bean creation.
Or have an explicit if-stmt to check what is the activeProfile. And you can create the Bean instance depending on the activeProfile. This approach I think would mirror what could be done in Spring XML using their expression language. But the #Conditional seems better fit for you.
Let's assume we have an application that can be customized for some customers. The application is using Java-based spring configuration (a.k.a. Java config) for dependency injection. The application consists of modules and their submodules. Each module and submodule has its own #Configuration class which is imported by parent configuration using #Import. This creates the following hierarchy:
MainConfig
----------+---------------- ....
| |
ModuleAConfig ModuleBConfig
|--------------------|
| |
SubModuleA1Config SubModuleA2Config
For example ModuleAConfig looks like this:
#Configuration
#Import({SubModuleA1Config.class, SubModuleA2Config.class})
public class ModuleAConfig {
// some module level beans
}
Let's say that SubModuleA1Config defines bean someBean of type SomeBean:
#Configuration
public class SubModuleA1Config {
#Bean
public SomeBean someBean() { return new SomeBean(); }
}
Now I want to customize the application for Customer1 (C1) - I want to use C1SomeBean (extending SomeBean) instead of SomeBean as someBean.
How can I achieve this with minimum duplication?
One of my ideas was to prepare alternative hierarchy with C1Config inheriting from MainConfig, C1ModuleAConfig from ModuleAConfig and C1SubModuleA1Config from SubModuleA1Config. C1SubModuleA1Config would override someBean() method returning C1SomeBean. Unfortunately with Spring 4.0.6 I get something like:
Overriding bean definition for bean 'someBean': replacing [someBean defined in class C1SubmoduleA1Config] with [someBean defined in class SubModuleA1Config]
and indeed SomeBean class is returned from context instead of C1SomeBean. This is clearly not what I want.
Note that you cannot override #Import extending configuration classes.
If you want to select which imports to use at runtime, you could use a #ImportSelector instead.
However, #Configuration classes are not more that spring (scoped) managed factories so as you already have a factory method for someBean you don't need to go even further:
#Configuration
public class SubModuleA1Config {
#Autowired
private Environment env;
#Bean
public SomeBean someBean() {
String customerProperty = env.getProperty("customer");
if ("C1".equals(customerProperty))
return new C1SomeBean();
return new SomeBean();
}
}
Update
Using a ImportSelector:
class CustomerImportSelector implements ImportSelector, EnvironmentAware {
private static final String PACKAGE = "org.example.config";
private static final String CONFIG_CLASS = "SubModuleConfig";
private Environment env;
#Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
String customer = env.getProperty("customer");
return new String[] { PACKAGE + "." + customer + "." + CONFIG_CLASS };
}
#Override
public void setEnvironment(Environment environment) {
this.env = environment;
}
}
#Configuration
#Import(CustomerImportSelector.class)
public class ModuleAConfig {
// some module level beans
}
However, as every customer has a a separate package, consider also using #ComponentScan. This will pick the configuration class present and don't need a extra configuration property.
#Configuration
#ComponentScan(basePackages="org.example.customer")
public class SubModuleA1Config {
#Autowired
private CustomerFactory customerFactory;
#Bean
public SomeBean someBean() {
return customerFactory.someBean();
}
}
public interface CustomerFactory {
SomeBean someBean();
}
#Component
public class C1CustomerFactory implements CustomerFactory {
#Override
public SomeBean someBean() {
return new C1SomeBean();
}
}