My sample project is Maven based structure, all my application proeprties files under src/main/resources folder. Below is the complete sample code. I am not understanding why my code not able to find profiles properly unless I use #PropertySource annotation.
My actual doubt is: I've configured spring properties pretty well in application.properties file, but yet why it cannot find profile and their respective property files? Unless I am using #PropertySource annotation, iam not getting value for env.getProperty("mysql.url"). I mean Environment class not able to pick up values from profiles property files. WHY?
Iam getting error as below:
Jul 08, 2017 7:54:26 PM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext#300ffa5d: startup date [Sat Jul 08 19:54:26 IST 2017]; root of context hierarchy
helloBean
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'datasource' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:687)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1207)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:284)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1084)
at com.oreilly.datasource.Main2.main(Main2.java:15)
DatasourceConfig.java
package com.oreilly.datasource;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
import org.springframework.core.env.Environment;
#Configuration
/*#PropertySource("classpath:/application.properties")
#PropertySource("classpath:/dev/application-dev.properties")
#PropertySource("classpath:/prod/application-prod.properties")*/
public class DatasourceConfig {
#Autowired
private Environment env;
#Bean(name="helloBean")
public String helloWorld() {
System.out.println("helloBean");
return "helloWorld....";
}
#Bean(name="datasource")
#Profile("dev")
public DataSource datasourceForDev(){
BasicDataSource dataSource = new BasicDataSource();
System.out.println(env.getProperty("mysql.url"));
return dataSource;
}
#Bean(name="datasource")
#Profile("prod")
public DataSource datasourceForProd(){
BasicDataSource dataSource = new BasicDataSource();
System.out.println(env.getProperty("mysql.url"));
return dataSource;
}
}
Main2.java
package com.oreilly.datasource;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main2 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DatasourceConfig.class);
DataSource dataSource = context.getBean("datasource", DataSource.class);
String helloBean = context.getBean("helloBean", String.class);
}
}
application.properties
spring.profiles.active=prod
spring.config.name=application
spring.config.location=classpath:/application.properties,classpath:/dev/application-dev.properties,classpath:/prod/application-dev.properties
Below is the project folder structure:
Please tell me what went wrong?
Spring is smart, it chooses the application-x.properties (where x is the environment) depending of the value assigned to spring.profiles.active in the application.properties, so you don't have to worry about register all the files in different #PropertySource annotations.
You can get more info here: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-profile-specific-properties
I suggest you remove all the #Profile annotations and let just one datasource that will be variable (depending on the seleced environment from application.properties). You may understand this with the example that i put at the end of this post.
If you want to define a mysql.url for a particular profile (lets say dev), you need to add the "mysql.url" in the application-dev.properties file, and then set the spring.profiles.active value to dev in application.properties.
Then, in your DatasourceConfig.java, you can perform something like this:
#Autowired
private Environment env;
//Takes the mysqlUrl from application-x.properties (where x is the value of spring.profiles.active that comes from application.properties)
#Value("${mysql.url}")
private String mysqlUrl;
#Bean(name="helloBean")...
#Bean(name="datasource")
public DataSource datasource() {
BasicDataSource dataSource = new BasicDataSource();
System.out.println(mySqlUrl); //This value is variable depending of the profile that you're pointing on.
return dataSource;
}
Please let me know it this is useful for you.
I have resolved my issue just by modifying as below:
#PropertySource("classpath:/${spring.profiles.active}/application-${spring.profiles.active}.properties")
Now I am able to pickup application-dev.properties (or) application-prod.properties dynamically.
Note: Environment class requires #PropertySource annotation, otherwise we get null for env.get('someproperty').
#Configuration
#PropertySource("classpath:application.properties")
public class DatasourceConfig {
....
}
put property file in same location as application.property and follow the naming convention application-{profile}.properties like application-dev.properties, application-prod.properties.
'I want properties file to be picked up automatically based upon profile I declared in application.properties.'
:
Run you application with -Dspring.profiles.active=dev/prod . Spring load 1)application.property , 2)pplication-dev/prod.properties file with overides value from application.property
Related
I got class:
#WebServiceClient(name = "${service.name}", targetNamespace = "${service.namespace}", wsdlLocation = "${service.wsdlLocation}")
public class ExampleService extends Service {
#Value("${service.wsdlLocation}")
private static String wsdlLocation;
}
It is a part of the mvn project which I compile and use from my local maven repo as a dependency to my other spring-boot app, which have configuration yml:
service:
name: name
namespace: namespace
wsdlLocation: wsdlLocation
Is there a way that this ExampleService class will use configuration of the "parent" project?
EDIT:
Two answers appearred, but I felt I did not ask question clearly. What I want to do now is to use class ExampleService in my "parent" project and make it see configuration from that parent project. So far when for example I write in parent project code like:
ExampleService exampleService = new ExampleService();
System.out.println(exampleService.getWsdlLocation());
Null is printed. So I'm looking for some solution to that.
EDIT 2:
Here are my classes.
Parent project:
package com.example.kris.parentservice;
import com.example.kris.childservice.ExampleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/example")
public class ExampleController {
// #Autowired
private ExampleService exampleService;
#Value(value = "${service.wsdlLocation}")
private String wsdlLocation;
#GetMapping(value = "/example")
public void example() {
exampleService = new ExampleService();
System.out.println("ExampleService field: " + exampleService.getWsdlLocation());
System.out.println("#Value: " + wsdlLocation);
}
}
Child project:
package com.example.kris.childservice;
import org.springframework.beans.factory.annotation.Value;
public class ExampleService {
#Value("${service.wsdlLocation}")
private String wsdlLocation;
public String getWsdlLocation() {
return wsdlLocation;
}
}
And output after running parent controller:
ExampleService field: null
#Value:test.wsdl.location
YamlPropertySourceLoader can be helpful in spring boot app inject a bean like this
#Bean
public PropertySource<?> yamlPropertySourceLoader() throws IOException {
YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
PropertySource<?> applicationYamlPropertySource = loader.load("application.yml",
new ClassPathResource("application.yml"), "default");
return applicationYamlPropertySource;
}
Spring Boot uses a very particular PropertySource order that is
designed to allow sensible overriding of values. Properties are
considered in the following order: Source
base on same you can decide which properties are going to be in effect. If a property key is duplicated, the last declared file will ‘win’ and override.
Devtools global settings properties on your home directory (~/.spring-boot-devtools.properties when devtools is active).
#TestPropertySource annotations on your tests.
properties attribute on your tests. Available on #SpringBootTest and
the test annotations for testing a particular slice of your
application.
Command line arguments.
Properties from SPRING_APPLICATION_JSON (inline JSON embedded in
an environment variable or system property).
ServletConfig init parameters.
ServletContext init parameters.
JNDI attributes from java:comp/env.
Java System properties (System.getProperties()).
OS environment variables.
A RandomValuePropertySource that has properties only in
random.*.
Profile-specific application properties outside of your packaged
jar
(application-{profile}.properties and YAML variants).
Profile-specific application properties packaged inside your jar
(application-{profile}.properties and YAML variants).
Application properties outside of your packaged jar
(application.properties and YAML variants).
Application properties packaged inside your jar
(application.properties and YAML variants).
#PropertySource annotations on your #Configuration classes.
Default properties (specified by setting
SpringApplication.setDefaultProperties).
Edit:
As mentioned in your edit, you are creating new object using below code
ExampleService exampleService = new ExampleService();
System.out.println(exampleService.getWsdlLocation())
This will create object from spring, you should using autowiring
#Autowired
private ExampleService exampleService ;
System.out.println(exampleService.getWsdlLocation())
Everybody knows if we want to read the properties file, we can do as follows:
#Configuration
#PropertySource("classpath:/application.properties")
public class AppConfig {
#Value("${app.name}")
public String name;
#Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
public PostService postService() {
return new PostServiceImpl(name);
}
}
But, now I have a framework which is similar to SpringBoot. It can integrate Spring with Mybatis.
The problem is preceding code only can read my project classpath file but I need to read the properties file project using my framework. How I do it?
Update
I'm sorry for everybody. Maybe I don't say clearly, so here is the picture:
I don't use SpringBoot
I want to read the project(using my framework) classpath, not my framework classpath.
Thanks.
Spring provides external configuration. By this you can run your application in different environment.
refer link :
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
If you do not like application.properties as the configuration file name, you can switch to another file name by specifying a spring.config.name environment property.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
#Configuration
#PropertySource("classpath:db.properties")
#PropertySource("classpath:project.properties")
public class DBConfiguration {
#Autowired
Environment env;
#Bean
public DBConnection getDBConnection() {
System.out.println("Getting DBConnection Bean for
App:"+env.getProperty("APP_NAME"));
DBConnection dbConnection = new DBConnection(env.getProperty("DB_DRIVER_CLASS"),
env.getProperty("DB_URL"), env.getProperty("DB_USERNAME"),
env.getProperty("DB_PASSWORD").toCharArray());
return dbConnection;
}
}
DB.properties:
#Database configuration
DB_DRIVER_CLASS=com.mysql.jdbc.Driver
DB_URL=jdbc:mysql://localhost:3306/Test
DB_USERNAME=root
DB_PASSWORD=root
project.properties:
APP_NAME=TEST APP
Spring framework can read external configuration files from different locations.
It can read the configuration file from your project directory but you would need to remove this line:
#PropertySource("classpath:/application.properties")
that limits it to your application class path.
You can check here to see the different locations spring read configuration files from.
If you are just wanting to read properties yourself from the classpath, you can use
Properties prop = new Properties();
InputStream input = this.getClass().getResourceAsStream("/application.properties")
prop.load(input);
// get the property value and print it out
System.out.println(prop.getProperty("foo"));
For non boot users who want to scan properties external to application classpath:
#PropertySource("file:/path/to/application.properties")
The "file" can be replaced with "http" for webhosted remote properties
I using next option to load properties file from anywhere, and put it into environment to access it via Environment#getProperty or #Value("name"):
#Configuration
public class MVCConfig {
#Autowired
private ConfigurableEnvironment env;
#PostConstruct
public void setup() {
Properties config = new Properties();
try (InputStream stream = this.getClass().getResourceAsStream("/custom.properties")) {
config.load(stream);
}
env.getPropertySources().addLast(new PropertiesPropertySource("mvc", config));
}
}
I wanted to know if there is any way in Spring Boot to read property values from properties file by using Dynamic Keys. I know properties can be put in application.properties and can be read using #Value("propertyKey") But my keys are going to be dynamic.
I know about #PropertySource to read property values and I can construct my keys dynamically. So is there any way that is provided by Spring Boot?
you can use:
#Autowired
private Environment env;
and then load property from code:
env.getProperty("your.property")
1- Register a Properties File via Java Annotations.
#Configuration
#PropertySource("classpath:test.properties")
public class PropertiesJavaConfig {
}
2- Dynamically select the right file at runtime.
#PropertySource({
"classpath:persistence-${envTarget:DB}.properties"
})
In case you are reading from the application.properties, you just define the environment spring autowired variable as specified by freakman (org.springframework.core.env.Environment). But if you are using a new properties file specific for certain properties, you can use the following code:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
#Configuration
#PropertySource("classpath:filename.properties")
public class CustomConfig {
#Autowired
Environment env;
public String getProperty(String keyName) {
return env.getProperty(keyName);
}
}
In a Spring Boot application I'm trying to setup multiple database connections. I've started building the primary datasource, but I'm getting the following error on the mySqlEntityManagerFactory method.
Could not autowire. no beans of EntityManagerFactoryBuilder
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.PersistenceContext;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
#Configuration
#Transactional
#EnableTransactionManagement
#EnableJpaRepositories(
basePackages = "digital.sheppard.dao",
entityManagerFactoryRef = "entityManager",
transactionManagerRef = "transactionManager")
public class PrimaryDBConfig {
#Bean(name="dataSource")
#Primary
#ConfigurationProperties(prefix = "primary.datasource.mysql")
public DataSource mysqlDataSource() {
return DataSourceBuilder.create().build();
}
#PersistenceContext(unitName = "primary")
#Primary
#Bean(name = "entityManager")
public LocalContainerEntityManagerFactoryBean mySqlEntityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder.dataSource(mysqlDataSource()).persistenceUnit("primary").properties(jpaProperties())
.packages("digital.sheppard.model").build();
}
private Map<String, Object> jpaProperties() {
Map<String, Object> props = new HashMap<String, Object>();
props.put("hibernte.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
props.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
return props;
}
}
How would I autowire the EntityManagerFactoryBuilder?
I'm trying to follow the code on this blog https://raymondhlee.wordpress.com/2015/10/31/configuring-multiple-jpa-entity-managers-in-spring-boot/
Here's the main application class if it's helpful
#Configuration
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
#ComponentScan
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
For me the reason was a wrong import, so I advise everybody to look at the often overlooked import section of your configuration class. There is a Hibernate class named the same as the springframework one. Of course you should choose the spring one...
I think you should remove this code
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
The exception is due to public LocalContainerEntityManagerFactoryBean mySqlEntityManagerFactory(EntityManagerFactoryBuilder builder) {
which expects a bean of EntityManagerFactoryBuilder.
I checked the reference link, I am not sure if that code will work.
Typically, one creates an instance of LocalContainerEntityManagerFactoryBean and initializes it as per need. In your case you can do
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(mysqlDataSource());
em.setPersistenceUnitName("primary");
em.setPackagesToScan(new String[] { "digital.sheppard.model" });
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(jpaProperties());
The persistence unit name should be same as defined in persistence.xml, though the file is now optional when using Spring JPA.
For a non spring version check out https://stackoverflow.com/a/26814642/776548
Also
since you are initializing EntityManagerFactory by yourself, we will have to exclude DataSourceAutoConfiguration.class.
#Primary is only required if you want multiple datasources. If you have only one, consider removing the annotation, and add it when you need to have multiple data sources
Change parameter name builder to entityManagerFactoryBuilder to inject bean present in JpaBaseConfiguration.class
Have you tried to remove your exclusion of 'DataSourceAutoConfiguration' ?
Using '#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})' prevent a lot of beans from beeing created.
If you got a problem when using a datasource and adding this is your solution, maybe it's not the right one.
Know that spring boot detect the presence of certain classes in the classpath.
If you're using maven, it's reading all classes from all dependencies.
So consider let this DataSourceAutoConfiguration.class running;
cheers
It could be, notice that just could be, your main class is not at the top of the "class tree". Spring needs to scan all classes that are a child (according to package convention) starting from the main class.
Maybe you would read https://www.baeldung.com/spring-component-scanning
If your classes aren't been read by spring scan, they will never be into spring context.
Couple of possibilities :
You need to add the #EnableJpaRepositories(basePackages = {"your.pkg.here"}) to the Application . This tells Spring Data to look for your repository classes under the specified package.
For me , removing #Primary on multiple Data sources, which was added by mistake fixed the issue
it was caused by your ide software,set up these options
I am studying for Spring Core certification and I am finding some doubts related to how Spring handle transaction.
So I have an exercise in which I find something like this.
In a class named rewards.internal.RewardNetworkImpl I have this method:
#Transactional
public RewardConfirmation rewardAccountFor(Dining dining) {
Account account = accountRepository.findByCreditCard(dining.getCreditCardNumber());
Restaurant restaurant = restaurantRepository.findByMerchantNumber(dining.getMerchantNumber());
MonetaryAmount amount = restaurant.calculateBenefitFor(account, dining);
AccountContribution contribution = account.makeContribution(amount);
accountRepository.updateBeneficiaries(account);
return rewardRepository.confirmReward(contribution, dining);
}
So this method perform some operation on some services classes (that substantially execute some queries on the DB). I want that all these operations are executed into a single transaction (that guaranteed the ACID properties) so I annotate this method with the #Transactional annotation. Ok, I think that this is pretty clear for me.
My Spring project is splitted into 2 main folder (2 directory I think) that contains the same packages). These folder are named respectively src/test/java and src/main/java. The first one contains the classes related to the JUnit test and the second one contains classes related to the proper project that implements the application), this is a screenshot of how is organized my project:
Ok, now in the exercise it say tha I have to define a bean named transactionManager that configures a DataSourceTransactionManager adding this method to the Spring configuration class named rewards.SystemTestConfig that is situated into the src/test/java folder:
#Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
So it is pretty clear for me that this method configure a bean named transactionManager implemented by a DataSourceTransactionManager that use a DataSource object.
My doubt is related to the place where this configuration is putted. Infact it is putted inside the rewards.SystemTestConfig inside the src/test/java folder (the folder that contains the JUnit stuff.
So my doubts are: is this configuration related only for the test environment or is it global to my entire project? (the transactionManager works only for the test environment or is it used also into my concrete application?)
I have another configuration class into the src/main/java folder, this one:
package config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import rewards.RewardNetwork;
import rewards.internal.RewardNetworkImpl;
import rewards.internal.account.AccountRepository;
import rewards.internal.account.JdbcAccountRepository;
import rewards.internal.restaurant.JdbcRestaurantRepository;
import rewards.internal.restaurant.RestaurantRepository;
import rewards.internal.reward.JdbcRewardRepository;
import rewards.internal.reward.RewardRepository;
// TODO-03: Add an annotation to instruct the container to look for the
// #Transactional annotation. Save all work, run RewardNetworkTests. It should pass.
#Configuration
#EnableTransactionManagement
public class RewardsConfig {
#Autowired
DataSource dataSource;
#Bean
public RewardNetwork rewardNetwork(){
return new RewardNetworkImpl(
accountRepository(),
restaurantRepository(),
rewardRepository());
}
#Bean
public AccountRepository accountRepository(){
JdbcAccountRepository repository = new JdbcAccountRepository();
repository.setDataSource(dataSource);
return repository;
}
#Bean
public RestaurantRepository restaurantRepository(){
JdbcRestaurantRepository repository = new JdbcRestaurantRepository();
repository.setDataSource(dataSource);
return repository;
}
#Bean
public RewardRepository rewardRepository(){
JdbcRewardRepository repository = new JdbcRewardRepository();
repository.setDataSource(dataSource);
return repository;
}
}
As you can see it don't contains an explicit configuration of the transactionManager bean but it is annoted with #EnableTransactionManagement annotation. I know that this annotation defines a Bean Post-Processor that proxies #Transactional beans and so happen:
Target object wrapped in a proxy (using an an Around advice)
Proxy implements the following behavior: Transaction started before entering the method AND Commit at the end of the method AND Rollback if method throws a RuntimeException.
But I can't understand why in the rewards.SystemTestConfig configuration class of the test environment it is explicitly declared the transactionManager bean and in the RewardsConfig class (the class that configure the proper application) it is not declared the transactionManager (I think that this bean is absolutly needed to operate with transaction) but in its place there is the #EnableTransactionManagement annotation.
So my doubts are:
1) Declaring the transactionManager bean into rewards.SystemTestConfig configuration class of the test environment it is global and it work also for the proper application folder (but I don't think).
2) Noting with #EnableTransactionManagement annotation the rewards.SystemTestConfig (that configure the proper application) it is automatically created a transaction manager. But what kind? and especially how do you know which datasource should use?
Can you help me to understand how it work? What am I missing?