Spring transaction configruation (bean vs inner class) - spring

In examples from book Spring in action I found that configuration of TransactionManager is achieved by nested class:
#Configuration
#ComponentScan
public class JpaConfig {
//EntityManagerFactory, JpaVendorAdapter, DataSource #Beans
#Configuration
#EnableTransactionManagement
public static class TransactionConfig implements TransactionManagementConfigurer {
#Inject
private EntityManagerFactory emf;
public PlatformTransactionManager annotationDrivenTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
}
}
Is it a good practice or something? Is there any difference in comparison to standard #Bean approach like:
#Configuration
#EnableTransactionManagement
public class DbConfig {
//EntityManagerFactory, JpaVendorAdapter, DataSource #Beans
#Bean
public JpaTransactionManager createTransactionManager(EntityManagerFactory emf) {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory(emf);
return jpaTransactionManager;
}
}
or it is just about separating responsibilities?

Is it a good practice or something? Is there any difference in comparison to standard #Bean
Both the approaches work as you might have already noticed.
The first approach is similar to inner bean concept.
If you know that the bean is not going to be used by any other bean except the outer bean then you can declare it as an inner bean. The advantage here is that by making the bean as inner bean you are ensuring that it is not exposed to the other beans except the outer bean and so won't be able to use/inject the inner bean in other beans.
Quoting from the above link
An inner bean definition does not require a defined id or name; if specified, the container does not use such a value as an identifier. The container also ignores the scope flag on creation: Inner beans are always anonymous and they are always created with the outer bean. It is not possible to inject inner beans into collaborating beans other than into the enclosing bean or to access them independently.

Related

Someone can explain to me for a use case of springboot injection

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.

Spring Scheduled task: Scope 'request' is not active for the current thread

I am writing a project with Spring Boot 1.5.8.
I have some Entity classes which are generated and contain bean (for Example FooBean) which only exists in request scope. What is important: i am not able to change that part of code. So assume that i have an Entty:
#Enitty
public class FooEntity{
#Transient protected FooBean fooBean;
}
and FooBean implementation:
#Component
#Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class FooBean {
...
}
I also have some part of code where i have method which should be runned by Spring CRON:
#Scheduled(cron = "0 0/2 * * * ?")
#Transactional(value = Transactional.TxType.REQUIRES_NEW)
void scheduledTask() {
...
}
What's important: in that scheduledTask i am saving some instances of FooEntity to DB.
And of course: When i try to invoke repository.saveAll() Spring is throwing an exception:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.fooBean ': Scope 'request' is not active for the current thread;
Is there any possibility to resolve that problem? How can I override this scoped bean so it will be available in not-request scope?
I think I'm writing a little bit later :) but you can create your own instance of this bean.
In spring boot you can simply create your own singleton instance of the bean like this:
#Configuration
#EnableScheduling
public class ApiConfig {
#Bean
#Primary
public FooBean fooBean(){
return new FooBean();
}
}
In case your bean has some dependencies in constructor (constructor autowired) which has to be autowired you can simply put those beans in method params and spring will provide it for you like this:
#Bean
#Primary
public FooBean fooBean(BeanToAutowire myBean){
return new FooBean(myBean);
}
In case your bean has no constructor autowired dependencies but annotation autowired (using #Autowired) you can simple do it like this:
#Bean
#Primary
public FooBean fooBean(AutowireCapableBeanFactory beanFactory){
FooBean bean = new FooBean(myBean);
beanFactory.autowireBean(bean); // here spring will autowire all dependencies for you
return bean;
}
It is not the most elegant way to do it but it works. Side effect is that there will be two instances of FooBean in app context but spring will use your instance because you set this instance as primary using #Primary annotation. Only beans which use Autowired annotation together with qualifier annotation can choose which instance of FooBean will be autowired.

Why adding #Bean to a method I'll call directly

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.

why is spring boot's DataJpaTest scanning #Component

Confident this hasn't been asked but reading through the Spring docs and testing utilities I found this annotation and thought I'd start using it. Reading through the fine print I read:
Regular #Component beans will not be loaded into the ApplicationContext.
That sounded good and I even liked the idea of using H2 except from what I found the entity I wanted to use had catalog and schema modifiers to it and the default H2 I couldn't figure out how to support that. I made an H2 datasource for the test branch and use that and override the replace. I wound up with
#RunWith(SpringRunner.class)
#ContextConfiguration(classes=ABCH2Congfiguration.class)
#DataJpaTest
#AutoConfigureTestDatabase(replace= AutoConfigureTestDatabase.Replace.NONE)
public class StatusRepositoryTest {
}
However my tests fails fro Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type.
which leads to:
Error creating bean with name 'customerServiceImpl': Unsatisfied dependency.
However the customerServiceImpl is this bean:
#Component
public class CustomerServiceImpl implements CustomerService {
}
That says #Component. The fine print for DataJpaTest says it doesn't load #Components. Why is it doing that and thus failing the test?
As Kyle and Eugene asked below here's the rest:
package com.xxx.abc.triage;
#Component
public interface CustomerService {
}
Configuration
#ComponentScan("com.xxx.abc")
#EnableJpaRepositories("com.xxx.abc")
//#Profile("h2")
public class ABMH2Congfiguration {
#Primary
#Bean(name = "h2source")
public DataSource dataSource() {
EmbeddedDatabase build = new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).setName("ABC").addScript("init.sql").build();
return build;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter bean = new HibernateJpaVendorAdapter();
bean.setDatabase(Database.H2);
bean.setShowSql(true);
bean.setGenerateDdl(true);
return bean;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
DataSource dataSource, JpaVendorAdapter jpaVendorAdapter) {
LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();
bean.setDataSource(dataSource);
bean.setJpaVendorAdapter(jpaVendorAdapter);
bean.setPackagesToScan("com.xxx.abc");
return bean;
}
#Bean
public JpaTransactionManager transactionManager(EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
}
And just to clarify the question, why is #Component being loaded into the context within a #DataJpaTest?
#ComponentScan automatically inject all found #Component and #Service into context. You could override it by separate #Bean:
#Bean
CustomerService customerService{
return null;
}
Or remove #Component annotation from CustomerService and CustomerServiceImpl, but you should add #Bean at your production #Configuration
#DataJpaTest does not load #Component, #Service... by default, only #Repository and internal things needed to configure Spring data JPA.
In your test, you can load any #Configuration you need, and in your case, you load #ABMH2Congfiguration which performs a #ComponentScan that's why Spring try to load your CustomerService.
You should only scanning the #Repository in this configuration class, and scan others #Component, #Service... in another #Configuration like DomainConfiguration. It's always a good practice to separate different types of configurations.

CommandLineRunner and Beans (Spring)

code what my question is about:
#SpringBootApplication
public class Application {
private static final Logger log = LoggerFactory.getLogger(Application.class);
public static void main(String args[]) {
SpringApplication.run(Application.class);
}
#Bean
public Object test(RestTemplate restTemplate) {
Quote quote = restTemplate.getForObject(
"http://gturnquist-quoters.cfapps.io/api/random", Quote.class);
log.info(quote.toString());
return new Random();
}
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
#Bean
public CommandLineRunner run(RestTemplate restTemplate) throws Exception {
return args -> {
Quote quote = restTemplate.getForObject(
"http://gturnquist-quoters.cfapps.io/api/random", Quote.class);
log.info(quote.toString());
};
}
}
I'm very new to Spring. As far as I understood the #Bean annotation is responsible that an Object is saved in a IoC container, correct?
If so: Are first all Methods with #Bean collected and then executed?
In my example I added a method test() what does the same as run() but returns an Object (Random()) instead.
The result is the same so it is working with CommandLineRunner and Object.
Is there a Reason why it should return a CommandLineRunner i.e. use the syntax like run()?
Moreover: At that point I don't see so far the advantage to move methods to an container. Why not just execute it?
Thank you!
#Configuration classes (#SpringBootApplication extends #Configuration) are the place where the spring beans are registered.
#Bean is used to declare a spring bean. The method that is annotated with #Bean has to return an object(the bean). By default the spring beans are singletons, so once the method annotated with #Bean is executed and returns it's value this object lives til the end of the application.
In your case
#Bean
public Object test(RestTemplate restTemplate) {
Quote quote = restTemplate.getForObject(
"http://gturnquist-quoters.cfapps.io/api/random", Quote.class);
log.info(quote.toString());
return new Random();
}
this will produce s singleton bean of type Random with name 'test'. So if you try to inject (e.g. with #Autowire) a bean of that type or name in other spring bean you will get that value. So this is not a good use of #Bean annotation, unless you want exactly that.
CommandLineRunner on the other hand is a special bean that lets you execute some logic after the application context is loaded and started. So it makes sense to use the restTemplate here, call the url and print the returned value.
Not long ago the only way to register a Spring bean was with xml. So we had an xml files and bean declarations like this:
<bean id="myBean" class="org.company.MyClass">
<property name="someField" value="1"/>
</bean>
The #Configuration classes are the equivalent of the xml files and the #Bean methods are the equivalent of the <bean> xml element.
So it's best to avoid executing logic in bean methods and stick to creating objects and setting their properties.

Resources