I'm new to Spring and I'm building an application where some entities (JPA/Hibernate) need access to a property from application.properties. I do have a configuration class in which this is trivial:
#Configuration
public class FactoryBeanAppConfig {
#Value("${aws.accessKeyId}")
private String awsAccessKeyId;
#Value("${aws.secretKey}")
private String awsSecretKey;
}
but since entities do not have and I think they should not have the annotations such as #Configuration or #Component, what's the Spring way for them to access the property?
Now, I know I can create my own class, my own bean, and make it as a simple wrapper around the properties; but is that the Spring way to do it or is there another way?
specify Property file location using #PropertySource
Something like below
#PropertySource("classpath:/application.proerties")
You also need to add below bean in your config
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigIn() {
return new PropertySourcesPlaceholderConfigurer();
}
There is no "Spring way", since JPA entities and Spring have nothing to do with each other. Most importantly, JPA entities are not Spring beans, so Spring doesn't know or care about them as they're not managed by Spring.
You can try to hack around, trying in vain to access Spring's configuration from code that should not be trying to access it, or you can accept the truth that your design is broken and you're trying to do something that's not meant to be done.
As was proposed several times, use a service class for this. It's managed by Spring, so it can access the Spring config, and it can handle entities, so there's no crossing boundaries.
First create a public static variable in some bean managed by Spring, then make the following use of the #Value annotation.
public static String variable;
#Value("${variable}")
private void setVariable(String value) {
variable = value;
}
It will be set at runtime on startup, now you can access it from entities and everywhere else because it is just a public static var.
You can use #PropertySource to load the properties file as follows
#Configuration
#PropertySource("classpath:/com/organization/config/application.proerties")
public class FactoryBeanAppConfig {
...
}
Entities should not acces environment properties. If you are using your entity through a service, then the service can access the properties to act on the entity.
Related
I need to assure data migration using mongock.
The #ChangeUnit class holds the logic for migration. It has a field annotated with #Value which is always null, even though I properly initialized in application.properties:
mongock.migration-scan-package=my.package
login-secret=test
Then the MigrationConfiguration looks as follows:
#ChangeUnit(id = "test", order = "001", author = "test")
#RequiredArgsConstructor
#Configuration
public class InitUsersChangeLog {
private final MyService service;
private final MongoTemplate template;
#Value("${login-secret}")
private String LOGIN;
#Execution
public void initUser() {
service.create(User.builder().login(LOGIN).build());
}
}
Main class:
#EnableMongock
#SpringBootApplication
public class MailServiceApplication {...}
My assumption is that this value is not injected properly into the MongockConfiguration bean. I tried to configure the bean manually (without using mongock.migration-scan-package=my.package) in the properties, but with no success.
As Mongock currently doesn't support #Value annotation you can try to use getProperty method from Environment bean. Environment bean can be injected same as other beans using constructor or Lombok annotations.
You want to change this:
#Value("your.key.property")
to that:
private final Environment env;
public void method(){
env.getProperty("your.key.property")
}
Mongock currently no supports #value injection via field o method parameter. We will provide that in a future minor release within version 5, but we can't give you dates, yet.
Extending MichalJ's answer, which is absolutely valid. I would like to add that the changeUnits are not retrieved by Mongock via Springboot, they are processed by Mongock independently. So the annotation #Configuration, #Component, etc. won't be taken into account and they could even be damaging.
Related to that, this code won't work, at least not in a near future:
#Value("${login-secret}")
private String LOGIN;
First, as said, Mongock doesn't support value currently, but the first approach will require the constructor parameter to have that #Value("${login-secret}"), not at the field level.
Is it possible to use #ConstructorBinding bound immutable #ConfigurationProperties beans in an implementation of EnvironmentPostProcessor?
I have an EnvironmentPostProcessor implementation:
#Order(LOWEST_PRECEDENCE)
#EnableConfigurationProperties({KeyVaultCertificatesEnvironmentProperties.class, KeyVaultCertificatesEnvironmentServerSslProperties.class})
public class KeyVaultCertificatesEnvironmentPostProcessor implements EnvironmentPostProcessor {
private KeyVaultCertificatesEnvironmentProperties keyVaultProperites;
private KeyVaultCertificatesEnvironmentServerSslProperties sslProperties;
This implementation must use some #ConfigurationProperties beans. I would like these bean to be immutable. Here is a sketch of the beans
#ConstructorBinding
#ConfigurationProperties(prefix=KeyVaultCertificatesEnvironmentProperties.PREFIX)
public class KeyVaultCertificatesEnvironmentProperties {
//...
}
#ConstructorBinding
#ConfigurationProperties(prefix=KeyVaultCertificatesEnvironmentServerSslProperties.PREFIX)
public class KeyVaultCertificatesEnvironmentServerSslProperties {
//...
}
I don't know how to make it so the two #ConfigurationProperties beans are available to my EnvironmentPostProcessor.postProcessEnvironment implementation. I suspect there may be some timing issues.
Any ideas how I can do this?
Thanks,
Ed
It's not possible to directly use any #ConfigurationProperties beans with an EnvironmentPostProcessor because the ApplicationContext that holds the beans is always created after the Environment. It is, however, possible to use the org.springframework.boot.context.properties.bind.Binder class directly if you want to create a an object from the properties that have been loaded into the Environment so far.
For example, you can have the following class:
public class Certs {
// ... fields
Certs(String first, String second) {
// ...
}
}
and bind it with the following code:
Binder binder = Binder.get(environment);
Certs certs = binder.bind("my.certs", Certs.class).get();
Note that the Certs properties class doesn't use any #ConfigurationProperties or #ConstructorBinding annotations and it has a single constructor. For more complex setups you might need to use the Binder constructor that accepts a BindConstructorProvider.
If you're using Spring Boot 2.4 or above you might want to look into the ConfigDataLocationResolver and ConfigDataLoaders classes. These allow you to support custom imports from an application.properties or application.yaml file. The resolver provides a ConfigDataLocationResolverContext object with a getBinder() method that you can use for reading additional properties. You can read a bit about it in this blog post.
You can also look at the new org.springframework.boot.Bootstrapper interface that was added in Spring Boot 2.4 to help Spring Cloud solve a similar chicken-and-egg problem. There's also a test application in the Spring Boot codebase that might help.
Anyone can explain how Spring defines the bean creation mechanism when create a bean which depends on a list of other beans? It would be good to show the part of Spring specification on how it's defined.
Code like:
public interface Test {
}
#Service
public class TestImpl1 implements Test{
}
#Service
public class TestImpl2 implements Test{
}
public class TestContainer {
List<Test> testList;
TestContainer() {
testList = new ArrayList<>();
}
public void addTest(Test test) {
testList.add(test);
}
}
then
#Bean
public TestContainer testContainer(List<Test> testList) {
TestContainer testContainer = new TestContainer();
for (Test test : testList) {
testContainer.addTest(test);
}
return testContainer;
}
Question is really: when creating bean for TestContainer, how does Spring figure out what should be in List testList?
This looks like what you are looking for:
https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-factory-class
You haven't specified what exactly do you want to know about bean creation but here is the minimum you should know. By default all beans are singleton scoped (only created once during the container life-cycle and for all the subsequent request the same instance is returned). All singleton scoped beans are created eagerly. If the singleton bean is dependent on some other beans (needs them for instantiation) then those other beans will be instantiated with it/right before it, doesn't matter whether they are singletons or not, marked as lazy or not.
This is part of the spring documentation I think on their website. Look for it. The whole spring documentation is worth to read, even if it is a long read (https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#spring-core)
But mostly, spring would first read all the XML, anotations and other source of config data and get a list of beans to init. Then it would get a list of dependencies between beans constructing kind of a tree. As the dependencies need to be initialized first, there an obvious orderining for initialization.
On top, you can set your own priorities for beans so that you can get some beans initialized first or last for example.
For your specific case, spring will inspect the code source as well as use the Java reflection API to figure out you want a collection of interface Test implementations. So spring would look for ALL the defined bean that match and return them that not more complex than that.
It's mostly using the concepts of graph data structure where the beans become the nodes of the graph and they are resolved using topological sort.
I'm working on Spring Boot Rest API, and I did end up using the new keyword here and there.
I'm wondering, did I do something wrong when I used the new keyword for my program. And if it is absolutely forbidden to use new keyword on a real project.
If the answer is yes should i annotate each class i wrote with #component annotation so i can instantiate an object using #autowired.
If the answer is no when can we break that rule ?
You can create objects using the new keyword in a spring application.
But these objects would be outside the scope of the Spring Application Context and hence are not spring managed.
Since these are not spring managed, any nested levels of dependency (such as your Service class having a reference to your Repository class etc)
will not be resolved.
So if you try to invoke a method in your service class, you might end up getting a NullPointer for the repository.
#Service
public class GreetingService {
#Autowired
private GreetingRepository greetingRepository;
public String greet(String userid) {
return greetingRepository.greet(userid);
}
}
#RestController
public class GreetingController {
#Autowired
private GreetingService greetingService;
#RequestMapping("/greeting")
public String greeting(#RequestParam(value = "name", defaultValue = "World") String name) {
return String.format("Hello %s", greetingService.greet(name));
}
#RequestMapping("/greeting2")
public String greeting2(#RequestParam(value = "name", defaultValue = "World") String name) {
GreetingService newGreetingService = new GreetingService();
return String.format("Hello %s", newGreetingService.greet(name));
}
}
In the above example /greeting will work but /greeting2 will fail because the nested dependencies are not resolved.
So if you want your object to be spring managed, then you have to Autowire them.
Generally speaking, for view layer pojos and custom bean configurations, you will use the new keyword.
There is no rule for using or not using new.
It's up to you if you want Spring to manage your objects or want to take care of them on your own.
Spring eases object creation, dependency management, and auto wiring; however, you can instantiate it using new if you don't want that.
I think its fine to use new keyword, but you should learn the difference between different stereotype (Controller, Service, Repository)
You can follow this question to get some clarity:
What's the difference between #Component, #Repository & #Service annotations in Spring?
Using appropriate annotation will allow you to correctly use DI (dependency injection), that will help in writing sliced tests for your spring boot application. Also the Service,Controller and Repository components are created as Singleton, so lesser GC overhead. Moreover components that you create using new keyword are not managed by Spring, and by default Spring will never inject dependencies in a object created using new.
Spring official documentation:
https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-spring-beans-and-dependency-injection.html
You will need new on Spring mock tests when you will have to create an object as service and inject mock object as dao.
Look at the following code; here as you see, based on a condition it's necessary to dynamically load advertisements on demand. so here you can not #autowire this group of items because all the information are loaded from DB or an external system, so you just need to fill you model accordingly.
if (customer.getType() == CustomerType.INTERNET) {
List < Advertisement > adList = new ArrayList < Advertisement > ();
for (Product product: internetProductList) {
Advertisement advertisement = new Advertisement();
advertisement.setProduct(product);
adList.add(advertisement);
}
}
Note it's appropriate to use Spring for managing external dependencies
like plugging a JDBC connection into a DAO or configurations like
specifying which database type to use.
I am building a spring 4 + Hibernate5 application. I wonder whether is there any difference in defining the data base connection properties like url ,username etc via DataSource object and via hibernate properties like "hibernate.connection.url", "hibernate.connection.username" etc. Offcourse ultimately the datasource object will be tied to the session factory. Just want to make sure to do the things in right way.
I want to define a separate datesource object via dataSource property, so that I can use the AbstractTransactionalTestNGSpringContextTests for test cases. This classs is always expecting a data source object. I want to use the #Rollback feature and this feature is working with AbstractTransactionalTestNGSpringContextTests. AbstractTestNGSpringContextTests is not supporting the roll back feature but still persisting working perfectly.
Need inputs to implements in the right way.
Adding example code to provide more information.
#ContextConfiguration(locations = { "classpath:spring/fpda_persistence_config.xml" })
#Rollback
#Transactional
public class BankTransactionDAOTest extends AbstractTestNGSpringContextTests {
#Autowired
private BankTransactionDAO bankTransactionDao;
#Test
public void createBankTransactionTest(){
BankTransaction bt = new BankTransaction();
bt.setAuthoritativeTableId(new BigDecimal(1234));
bt.setBankTransactionTypeCode(new Character('C'));
bt.setInstanceId(new BigDecimal(1234));
bt.setRowCreatedEpochTime(new BigDecimal(1234));
bt.setTransactionId(new BigDecimal(1234));
bt.setTransactionKey(new BigDecimal(System.currentTimeMillis()));
bankTransactionDao.createBankTransaction(bt);
}
}
here to make transaction roll back happen sucessfully, I came to know that we should extend AbstractTransactionalTestNGSpringContextTests instead of AbstractTestNGSpringContextTests. Then I should have declared the datasource property instead of defining all properties in hibernate properties.
so overall is it a right approach to declare some properties in data source and some properties in hibernate?. will it make any difference.
Thanks in Advance.
In our project we are using class-level annotation for test classes
#TestPropertySource(locations = "classpath:application-test.properties")