I am preparing for a Spring core exam and on one of the mock questions I am getting a very confusing answer.
#Configuration
public class ApplicationConfig {
private DataSource dataSource;
#Autowired
public ApplicationConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
#Bean(name="clientRepository")
ClientRepository jpaClientRepository() {
return new JpaClientRepository();
}
}
The answer states: Default or no-arg constructor is missing. Default or no-arg constructor is mandatory. Here, the provided constructor with a dataSource parameter is not taken into account.
I don't understand first why constructor is required and second why ApplicationConfig is no good.
#Configuration specifically is an odd beast. Spring needs to analyze it to build a dependency graph before it has the beans to supply, so you can't use constructor injection with a configuration class.
The reason will be clear if you try to register the above configuration with an application context, e.g.:
ApplicationContext applicationContext =
new AnnotationConfigApplicationContext(ApplicationConfig.class);
The framework will throw an exception complaining about the missing default constructor, because it will look for it while trying to instantiate the configuration using reflection.
A better way would be to have #Autowired dataSource field:
#Autowired DataSource dataSource;
and no constructor explicitely defined (i.e. having an implicit default no-arg constructor).
Related
I read these lines from a colleague's code:
#Bean(name = "mysql")
#ConfigurationProperties(prefix = "spring.mysql")
#Primary
public DataSource mysqlDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
public ClassA classA () {
return new ClassA (this.mysqlDataSource());
}
#Bean
public ClassB classB () {
return new ClassB (this.mysqlDataSource());
}
I thought this will create 2 DataSources for Bean classA and classB. for injecting the datasource, we need something like:
#Bean
public ClassA classA (DataSource ds) {
return new ClassA (ds);
}
But Spring just create one datasource, and this.mysqlDataSource() returns the same one everytime. how does it happen? If I do need another DataSource, i need create it on the fly?
Spring says #Component and #Configuration has different meanings.
If you use #Configuration instead of #Component, CGLIB proxying will be used.
"The #Bean methods in a regular Spring component are processed differently than their counterparts inside a Spring #Configuration class. The difference is that #Component classes are not enhanced with CGLIB to intercept the invocation of methods and fields. CGLIB proxying is the means by which invoking methods or fields within #Bean methods in #Configuration classes creates bean metadata references to collaborating objects; such methods are not invoked with normal Java semantics but rather go through the container in order to provide the usual lifecycle management and proxying of Spring beans even when referring to other beans via programmatic calls to #Bean methods. In contrast, invoking a method or field in an #Bean method within a plain #Component class has standard Java semantics, with no special CGLIB processing or other constraints applying."
https://docs.spring.io/spring/docs/5.0.4.RELEASE/spring-framework-reference/core.html#spring-core
Alternatively, you can keep #ConfigurationProperties in the class level and remove #Bean from DataSource so that mysqlDataSource() will be treated as a regular method..
The method this.mysqlDataSource() returns the bean because Spring create a proxy for configuration class. You can see details here
By default Spring container creates bean with scope "singleton".
So you have single DataSource instance in container and this instance will be injected to ClassA and ClassB objects. If you want to have different instances you should change scope to "prototype".
You can use annotation #Scope("prototype") to do it.
I am using #RefreshScope annotation to refresh my beans after a change in Spring Config Server. This works fine but it has side effects in some cases.
First case: I have a bean CustomPermissionEvaluator that implements PermissionEvaluator (spring-security) in order to override default implementation (DenyAllPermissionEvaluator).
import org.springframework.security.access.PermissionEvaluator;
#Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
#Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
If I annotate CustomPermissionEvaluator with #RefreshScope, Spring no longer take it into account and the default implementation is chosen over this custom implementation.
Second case: I have the following bean:
#Bean
#Profile("enable_mongo_ssl")
public MongoClientOptions mongoClientOptions() {
return getMongoClientOptionsBuilder()
.build();
}
If I annotate it with #RefreshScope, MongoAutoConfiguration is not taking it when building MongoClient and default mongoClientOptions with no certificate is instead used.
Last case:
I have an object mapper defined:
#Bean
#Primary
public ObjectMapper defaultObjectMapper() {
return ObjectMapperProvider.defaultObjectMapper();
}
If I annotate it with #RefreshScope, I get the following error:
more than one 'primary' bean found among candidates: [defaultObjectMapper, jacksonObjectMapper]
I guess those examples have to do with the fact the #RefreshScope annotation causes the bean to be proxied.
But is there a clean way to keep using this annotation and make those features work?
I have seen lots of examples about Spring configuration through #Configuration and #Bean annotations. But I relealized that it's a common practice to add #Bean annotation to methods that are called directly to populate other beans. For example:
#Bean
public Properties hibernateProperties() {
Properties hibernateProp = new Properties();
hibernateProp.put("hibernate.dialect",
"org.hibernate.dialect.H2Dialect");
hibernateProp.put("hibernate.hbm2ddl.auto", "create-drop");
hibernateProp.put("hibernate.format_sql", true);
hibernateProp.put("hibernate.use_sql_comments", true);
hibernateProp.put("hibernate.show_sql", true);
return hibernateProp;
}
#Bean public SessionFactory sessionFactory() {
return new LocalSessionFactoryBuilder(dataSource())
.scanPackages("com.ps.ents")
.addProperties(hibernateProperties())
.buildSessionFactory();}
So, I'm wondering if it's better just declaring the hibernateProperties() as private without the #Bean annotation.
I would like to know if this is a bad/unneeded common practice or there is a reason behind.
Thanks in advance!
According to Spring Documentation inter-bean dependency injection is one good approach in order to define the bean dependencies in a simple form. Of course if you define your hibernateProperties() as private it will work but it could not be injected to other components in your application through Spring Container.
Just decide depending on how many classes depends on your bean and also if you need to reuse it in order to call its methods or inject it to other classes.
Decorating a method in #Configuration class with #Bean means that the return value of that method will become a Spring bean.
By default those beans are singletons(only one instance for the lifespan of the application).
In your example Spring knows that hibernateProperties() is a singleton bean and will create it only ones. So here:
#Bean public SessionFactory sessionFactory() {
return new LocalSessionFactoryBuilder(dataSource())
.scanPackages("com.ps.ents")
.addProperties(hibernateProperties())
.buildSessionFactory();
}
hibernateProperties() method will not be executed again, but the bean will be taken from the application context. If you don't annotate hibernateProperties() with #Bean it will be a simple java method and it will be executed whenever it's called. It depends on what you want.
Just to mention, the other way to do dependency injection in #Configuration classes is add a parameter. Like this:
#Bean public SessionFactory sessionFactory(Properties props) {
return new LocalSessionFactoryBuilder(dataSource())
.scanPackages("com.ps.ents")
.addProperties(props)
.buildSessionFactory();
}
When Spring tries to create the sessionFactory() bean it will first look in the application context for a bean of type Properties.
My goal is to have a have integration tests that ensures that there isn't too many database queries happening during lookups. (This helps us catch n+1 queries due to incorrect JPA configuration)
I know that the database connection is correct because there is no configuration problems during the test run whenever MyDataSourceWrapperConfiguration is not included in the test. However, once it is added, the circular dependency happens. (see error below) I believe #Primary is necessary in order for the JPA/JDBC code to use the correct DataSource instance.
MyDataSourceWrapper is a custom class that tracks the number of queries that have happened for a given transaction, but it delegates the real database work to the DataSource passed in via constructor.
Error:
The dependencies of some of the beans in the application context form a cycle:
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
┌─────┐
| databaseQueryCounterProxyDataSource defined in me.testsupport.database.MyDataSourceWrapperConfiguration
↑ ↓
| dataSource defined in org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Tomcat
↑ ↓
| dataSourceInitializer
└─────┘
My Configuration:
#Configuration
public class MyDataSourceWrapperConfiguration {
#Primary
#Bean
DataSource databaseQueryCounterProxyDataSource(final DataSource delegate) {
return MyDataSourceWrapper(delegate);
}
}
My Test:
#ActiveProfiles({ "it" })
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration({ DatabaseConnectionConfiguration.class, DatabaseQueryCounterConfiguration.class })
#EnableAutoConfiguration
public class EngApplicationRepositoryIT {
#Rule
public MyDatabaseQueryCounter databaseQueryCounter = new MyDatabaseQueryCounter ();
#Rule
public ErrorCollector errorCollector = new ErrorCollector();
#Autowired
MyRepository repository;
#Test
public void test() {
this.repository.loadData();
this.errorCollector.checkThat(this.databaseQueryCounter.getSelectCounts(), is(lessThan(10)));
}
}
UPDATE: This original question was for springboot 1.5. The accepted answer reflects that, however, the answer from #rajadilipkolli works for springboot 2.x
In your case you will get 2 DataSource instances which is probably not what you want. Instead use BeanPostProcessor which is the component actually designed for this. See also the Spring Reference Guide.
Create and register a BeanPostProcessor which does the wrapping.
public class DataSourceWrapper implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if (bean instanceof DataSource) {
return new MyDataSourceWrapper((DataSource)bean);
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
Then just register that as a #Bean instead of your MyDataSourceWrapper.
Tip: Instead of rolling your own wrapping DataSource you might be interested in datasource-proxy combined with datasource-assert which has counter etc. support already (saves you maintaining your own components).
Starting from spring boot 2.0.0.M3 using BeanPostProcessor wont work.
As a work around create your own bean like below
#Bean
public DataSource customDataSource(DataSourceProperties properties) {
log.info("Inside Proxy Creation");
final HikariDataSource dataSource = (HikariDataSource) properties
.initializeDataSourceBuilder().type(HikariDataSource.class).build();
if (properties.getName() != null) {
dataSource.setPoolName(properties.getName());
}
return ProxyDataSourceBuilder.create(dataSource).countQuery().name("MyDS")
.logSlowQueryToSysOut(1, TimeUnit.MINUTES).build();
}
Another way is to use datasource-proxy version of datasource-decorator starter
Following solution works for me using Spring Boot 2.0.6.
It uses explicit binding instead of annotation #ConfigurationProperties(prefix = "spring.datasource.hikari").
#Configuration
public class DataSourceConfig {
private final Environment env;
#Autowired
public DataSourceConfig(Environment env) {
this.env = env;
}
#Primary
#Bean
public MyDataSourceWrapper primaryDataSource(DataSourceProperties properties) {
DataSource dataSource = properties.initializeDataSourceBuilder().build();
Binder binder = Binder.get(env);
binder.bind("spring.datasource.hikari", Bindable.ofInstance(dataSource).withExistingValue(dataSource));
return new MyDataSourceWrapper(dataSource);
}
}
You can actually still use BeanPostProcessor in Spring Boot 2, but it needs to return the correct type (the actual type of the declared Bean). To do this you need to create a proxy of the correct type which redirects DataSource methods to your interceptor and all the other methods to the original bean.
For example code see the Spring Boot issue and discussion at https://github.com/spring-projects/spring-boot/issues/12592.
I have the following components, in two different files:
#Component
public class Chauffeur {
Car car;
public Chauffeur(Car car){
this.car = car;
}
public void go(){
System.out.println("Chauffeur");
car.drive();
}
}
#Component
public class Car{
public void drive() {
System.out.println("Drive car");
}
}
the following configuration file:
#Configuration
#ComponentScan
public class DriveableConfiguration {
}
and the following test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=DriveableConfiguration.class)
public class DriveableTest {
#Autowired
Chauffeur chauffeur;
#Test
public void chauffeurTest(){
chauffeur.go();
}
}
All the classes above are in the same package and the test is passing.
In the test I annotated chauffer with #Autowired, which should mean that the Spring container looks after the creation of the instance of Chauffeur without the developer needing to explicitly instantiate it.
Now, the constructor for Chauffer needs an instance of Car, so there is no default constructor for that class. Nonetheless the container creates it, injecting the required instance in the constructor.
Is the #Autowired saying to the container to instantiate the element with whatever (Components, Beans) it can provide, included parameters in the constructor? If so, in what case is it needed to use #Autowired to annotate a constructor?
Only if you use Spring 4.3+. In such a case #Autowired on constructor is optional if you have one non default constructor.
You can check the example here.
So as of 4.3, you no longer need to specify an explicit injection annotation in such a single-constructor scenario. This is particularly elegant for classes which otherwise do not carry any container annotations at all, for example when programmatically registered
For versions lower than 4.3 you will an exception will be thrown:
the container will throw an exception looking for a default
constructor, unless you explicitly indicate autowire mode
‘constructor’ in your bean definition setup (e.g. in an XML )