Spring XML config to JavaConfig: inheritance and bean dependency - spring

I have a list of services that extend an AbstractService class. The AbstractService holds the DAO (you can call it "repository") and has the getter/setter for the DAO:
/**
* #param <T> Entity type
* #param <K> Entity ID type
* #param <S> DAO type
*/
public abstract class AbstractService<T, K extends Serializable, S extends BaseDAO<T, K>> implements BaseService<T, K> {
private S dao;
public S getDAO() { return dao; }
public void setDAO(S dao) { this.dao = dao; }
// Then common methods to all my services, using the DAO, for instance
#Override
public Optional<T> findOne(K key) throws DataException {
return Optional.ofNullable(dao.findOne(key));
}
}
A service example:
#Service
public class EmployeeServiceImpl extends AbstractService<Employee, Integer, EmployeeDAO> implements EmployeeService {
// Some specific methods to that service
}
The related DAO (I use Spring Data JPA):
public interface EmployeeDAO extends BaseDAO<Employee, Integer> {
}
Extending
#NoRepositoryBean
public interface BaseDAO<T, K extends Serializable> extends JpaRepository<T, K> {
}
By the way I added the annotations #Service and #NoRepositoryBean while moving to JavaConfig.
My old XML config was:
<bean id="com.xxx.service._AbstractService" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="com.xxx.dao._TxManager" />
<property name="transactionAttributes">
<props>
<prop key="save*">PROPAGATION_REQUIRED,-com.xxx.DataException</prop>
<prop key="update*">PROPAGATION_REQUIRED,-com.xxx.DataException</prop>
<prop key="delete*">PROPAGATION_REQUIRED,-com.xxx.DataException</prop>
</props>
</property>
</bean>
<bean id="com.xxx.service.EmployeeService" parent="com.xxx.service._AbstractBO">
<property name="target">
<bean class="com.xxx.service.EmployeeServiceImpl">
<property name="DAO" ref="com.xxx.dao.EmployeeDAO"/>
</bean>
</property>
</bean>
First question, what is the proper way to inject the generic DAO and handle the service inheritance using JavaConfig?
Second question, how to translate the XML snippet about transactions (com.xxx.service._AbstractBO) to JavaConfig?
Here is what I have so far, 2 classes:
#Configuration
#ComponentScan("com.xxx.service")
public class SpringConfig {
}
And the persistence config
#Configuration
#EnableJpaRepositories("com.xxx.repository")
#EnableTransactionManagement
public class PersistenceConfig {
/* Here so far I defined the DataSource, the EntityManagerFactory,
the PlatformTransactionManager and the JpaVendorAdapter */
}
Thanks in advance for your time!

Here is what I ended up doing: instead of trying to translate my old XML config, I changed the classes design. Since DAOs are required anyway, I inject them in each concrete class constructor which calls a new abstract class constructor I added.
The abstract service:
final private S dao;
public AbstractService(S dao) {
super();
this.dao = dao;
}
// getter protected and setter removed
protected S getDAO() {
return dao;
}
And a concrete service example:
#Service
public class EmployeeServiceImpl extends AbstractService<Employee, Integer, EmployeeDAO> implements EmployeeService {
#Inject
public EmployeeServiceImpl(EmployeeDAO dao) {
super(dao);
}
}
The good thing is I didn't have to change the Java config I posted in my question, meaning #EnableJpaRepositories("com.xxx.repository") and #ComponentScan("com.xxx.service") are sufficient to generate and bind beans.

Related

Spring Boot #Transactional not working when on #Service class (instead of controller)

I thought it was best practice to put the #Transactional annotation on the service layer classes and not on the controllers (see f.e. Why we shouldn't make a Spring MVC controller #Transactional?). But this not working on my Spring Boot application. Why is that?
The registerAction method in the controller (see code below) performs multiple service calls. When f.e. the mailService.sendActivationMail(...) fails, I want to rollback the inserted user from the userService.registerUser(...) call. Do I need to put the #Transactional annotation on the controller class or not?
My Spring Boot application correctly uses transactions when the #Transactional annotation is set on the controller class:
AuthController.java
#RestController
#RequestMapping("/api/auth")
#Transactional
public class AuthController {
#Autowired
private UserService userService;
#Autowired
private ProfileService profileService;
#Autowired
private MailService mailService;
#RequestMapping(path = "register", method = RequestMethod.POST)
public Profile registerAction(#Valid #RequestBody Registration registration) {
ApiUser user = userService.registerUser(registration);
Profile profile = profileService.createProfile(user, registration);
mailService.sendActivationMail(user);
return profile;
}
}
but transactions don't work when the #Transactional annotation is set on the Service classes instead (and not on the controller):
UserService.java
#Service
#Transactional
public class UserService {
#Autowired
private ApiUserRepository userRepository;
public ApiUser registerUser(Registration registration) {
...
userRepository.save(user);
...
}
}
My configuration classes:
SpringApiApplication.java
#SpringBootApplication
public class SpringApiApplication {
public static void main(String[] args) {
SpringApplication.run(SpringApiCommonApplication.class, args);
}
}
ApiConfiguration.java
#Configuration
#EnableJpaAuditing
#EnableTransactionManagement
public class ApiConfiguration {
#Autowired
private ApiProperties properties;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public UsernameCanonicalizer usernameCanonicalizer() {
return new UsernameCanonicalizer();
}
#Bean
public EmailCanonicalizer emailCanonicalizer() {
return new EmailCanonicalizer();
}
#Bean
public ApiTokenHandler activationTokenHandler() {
return new StandardApiTokenHandler(properties.getActivationTokenDuration());
}
}
#M.Deinum got me on the right track. Spring (boot) doesn't automatically wrap your controller calls in a transaction like other frameworks do. So you have to either add a #Transactional annotation to your controller, or move the code from the controller class to a service class.
Moving the code from the controller class to a service class is the better thing to do since (amongst other things) makes the code better testable. So that's what I did.
AuthenticationService.java
#Service
public class AuthenticationService {
#Autowired
private UserManager userManager;
#Autowired
private ProfileManager profileManager;
#Autowired
private MailManager mailManager;
#Transactional
public Profile registerUser(Registration registration) {
ApiUser user = userManager.registerUser(registration);
Profile profile = profileManager.createProfile(user, registration);
mailManager.sendActivationMail(user);
return profile;
}
...
}
AuthController.java
#RestController
#RequestMapping("/api/auth")
public class AuthController {
#Autowired
private AuthenticationService authenticationService;
#RequestMapping(path = "register", method = RequestMethod.POST)
#ApiOperation(value = "Registers a user")
public Profile register(#Valid #RequestBody Registration registration) {
return authenticationService.registerUser(registration);
}
...
}
You can place the #Transactional annotation before an interface definition, a method on an interface, a class definition, or a public method on a class. However, the mere presence of the #Transactional annotation is not enough to activate the transactional behavior.
The #Transactional annotation is simply metadata that can be consumed by some runtime infrastructure that is #Transactional-aware and that can use the metadata to configure the appropriate beans with transactional behavior.
In the preceding example, the <tx:annotation-driven/> element switches on the transactional behavior.
// Below is the service class that we want to make transactional
#Transactional
public class DefaultEmployeeService implements EmployeeService {
void insertEmployee(Employee Employee);
void updateEmployee(Employee Employee);
}
XML Configuration:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" ...>
<!-- this is the service object that we want to make transactional -->
<bean id="employeeService" class="service.DefaultEmployeeService"/>
<!-- enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="txManager"/>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- (this dependency is defined somewhere else) -->
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
Some points that you can try:
Do all above mentioned configurations.
Remove #Transactional configuration from Controller.
Reference the Service object along with Transaction manager bean.
More Details: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html

spring autowiring fails with #Transactional

I want to use #Transactional annotation in the save() method of UserService(concrete class) as follows:
#Service
public class UserService {
#Transactional
public Long save(User userCommand, BindingResult result) {
...
}
}
I will use this service in MyRealm by autowiring.
public class MyRealm extends AuthorizingRealm {
#Autowired
private UserService userService;
}
However, it fails with the following error:
java.lang.IllegalArgumentException: Can not set n.r.c.s.user.UserService field n.r.c.s.realm.MyRealm.userService to com.sun.proxy.$Proxy48
Of course, it works if I remove the #Transational annotation.
My transaction manager setting is as follows:
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
Please, let me know what's wrong with my code?
Do I need to set up something like proxy?
When proxying is enabled you need to use interfaces, not implementations.
#Service
public class UserService implements SomeInterface {
#Transactional
public Long save(User userCommand, BindingResult result) {
...
}
}
public class MyRealm extends AuthorizingRealm {
#Autowired
private SomeInterface userService;
}
If you do not want to do this, you can always check your AOP config. you are probably doing proxy for a proxy somewhere.

Spring autowiring not working, getting `applicationDao` as null

Below are my classes and xml:
#Component
#Service("ApplicationService")
public class ApplicationServiceImpl implements ApplicationService{
public ApplicationDao getApplicationDao() {
return applicationDao;
}
public void setApplicationDao(ApplicationDao applicationDao) {
this.applicationDao = applicationDao;
}
#Autowired
private ApplicationDao applicationDao;
// some methods..
}
#Service
public interface ApplicationService {
// methods...
}
#Component
#Repository("ApplicationDao")
public class ApplicationDaoImpl implements ApplicationDao {
#Autowired
private SessionFactory sessionFactory;
public SessionFactory getSessionFactory() {
return sessionFactory;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
// other methods...
}
public interface ApplicationDao {
// methods...
}
xml file:
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven/>
<context:component-scan base-package="com" />
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<!-- <property name="dataSource" ref="dataSource" /> -->
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
</bean>
<bean id="ApplicationDao" class="com.dao.impl.ApplicationDaoImpl"/>
<bean id="ApplicationService" class="com.service.impl.ApplicationServiceImpl"/>
Here autowiring is not working.in ApplicationServiceImpl, I am getting applicationDao as null. Have not tested sessionFactory in ApplicationDaoImpl.
I know that if I am using #Component then bean declaration in xmnl is not required.
You should not instantiate service like that..
At the time of application loading, spring container will create all instances defined in spring.xml or annotated classes and it's dependencies..
So you have to access them with the following example code..
ApplicationContext applicationContext = ApplicationContextProvider.getApplicationContext();
ApplicationService applicationService = (ApplicationService) applicationContext.getBean("ApplicationService");
Since ApplicationService is having property that is ApplicationServiceDAOImpl, it's already been created by spring container and will return you..
But in case of directly instantiating manually by you, you are just creating instance of ApplicationService but not for ApplicationServiceDAOImpl.. so it obviously returns null
I'm currently using this approach only to access beans or services..
update for comment
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext arg0)
throws BeansException {
applicationContext = arg0;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
}
Hope it helps,
try a change like this:
import org.springframework.beans.factory.annotation.Qualifier;
#Autowired
#Qualifier("ApplicationDao")
private ApplicationDao applicationDao;
this give spring a hint.
If you are using annotations in your application, you should denote it using a tag :
<mvc:annotation-driven />
Add this line in application context xml above component scan tag.
Also, if you are using annotations, remove ApplicationDao and ApplicationService bean declarations from xml.
And don't mark you service and dao classes with both #Component and #Service or #Repository annotations. No need to mark them #Component there. Remove it.

an annotation to avoid setter method?

If I specify what should be injected into a property, like
<property name="xxx" ref="some_bean" />
or
<property name="xxx">
<bean .../>
</property>
then I have to write a setter method.
May I use some annotation to avoid this like #autowired?
You can do this with constructor injection. 3 main ways to do this:
XML:
<bean id="beanA" class="com.BeanA">
<constructor-arg ref="beanB"/>
</bean>
<bean id="beanB" class="com.BeanB"/>
JavaConfig:
#Configuration
public class MyConfig {
#Bean
public BeanA beanA() {
return new BeanA(beanB());
}
#Bean
public BeanB beanB() {
return new BeanB();
}
}
Autowiring:
#Component
public class BeanA {
private final BeanB beanb;
// This assumes that there is a BeanB in your application context already
#Autowired
public BeanA(final BeanB beanB) {
this.beanB = beanB;
}
}
You can take Autowiring even further, and wire directly to the field:
#Component
public class BeanA {
// This assumes that there is a BeanB in your application context already
#Autowired
private final BeanB beanb;
}

Spring dependency injection and generic class

Sorry for my English. I can not to do dependency injection for generic class in spring. Generic class:
abstract class BaseBO<Id, Entity, DAOClass extends DAO<Id, Entity>> implements BO<Id, Entity, DAOClass> {
DAOClass dao;
public DAOClass getDAO() {
return dao;
}
//...
}
Use generic class:
public class TaskBO extends BaseBO<Long, Task, TaskDAO> implements BO<Long, Task, TaskDAO> {
}
I want to do dependency injection in a class TaskBO for property TaskDAO.
But I can only to set dependency TaskDAO via a interface DAO for bean TaskBO:
<bean id="TaskBO" class="com.otv.model.bo.TaskBO">
<property name="DAO" ref="TaskDAO" />
</bean>
<bean id="TaskDAO" class="com.otv.model.dao.TaskDAO">
<property name="sessionFactory" ref="SessionFactory" />
</bean>
How to set dependency injection via class TaskDAO?
Attach stacktrace with error if you have one.
Before all use consistent case for property accessor:
abstract class BaseBO<Id, Entity, DAOClass extends DAO<Id, Entity>> implements BO<Id, Entity, DAOClass> {
DAOClass dao;
public DAOClass getDao() {
return dao;
}
//...
}
Most probably you need a setter for BaseBO.dao property
public void setDao(DAOClass dao) {
this.dao = dao;
}
or
public class TaskBO extends BaseBO<Long, Task, TaskDAO> implements BO<Long, Task, TaskDAO> {
public void setDao(TaskDAO dao) {
super.dao = dao;
}
}

Resources