Is #Autowired annotation mandatory on constructor in Spring boot - spring-boot

Is it mandatory to put #Autowired if I am using constructor DI?

Is #Autowired annotation mandatory for a constructor?
No.
As of Spring 4.3 if your class has only single constructor then there is no need to put #Autowired.
Before Spring 4.3:
package com.example.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class DatabaseAccountService implements AccountService {
private final RiskAssessor riskAssessor;
#Autowired
public DatabaseAccountService(RiskAssessor riskAssessor) {
this.riskAssessor = riskAssessor;
}
// ...
}
After:
#Service
public class DatabaseAccountService implements AccountService {
private final RiskAssessor riskAssessor;
public DatabaseAccountService(RiskAssessor riskAssessor) {
this.riskAssessor = riskAssessor;
}
// ...
}
Extra: With Lombok, after Spring 4.3
With Lombok you can use #RequiredArgsConstructor to make the code even cleaner:
#Service
#RequiredArgsConstructor
public class DatabaseAccountService implements AccountService {
private final RiskAssessor riskAssessor;
}

Related

Using transactional in spring boot with hibernate

I am getting the error while using hibernate in spring boot application No qualifying bean of type TransactionManager' available
I am using the following config class:
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#org.springframework.context.annotation.Configuration
#EnableTransactionManagement
public class Config {
#Bean
public SessionFactory sessionFactory() {
Configuration configuration = new Configuration();
configuration.configure();
configuration.addAnnotatedClass(Ct.class);
configuration.addAnnotatedClass(St.class);
SessionFactory sessionFactory = configuration.buildSessionFactory();
return sessionFactory;
}
}
#RestController
public class RestAPIController {
#Autowired
private SessionFactory sessionFactory;
#PutMapping("/addS")
#Transactional
public void addSt(#RequestParam("cc") String cc,#RequestParam("st") String st) {
CC cc1= new CC();
CC.setCode(cc);
State state = new State(cc,st);
sessionFactory.getCurrentSession().save(state);
}
}
}
The main reason I added the #Transactional in the addSt method is due to error: The transaction was still an active when an exception occurred during Database.
So I turned to use spring boot for managing transactions. I am not sure what to do here.
--------------------UPDATED CODE--------------------
#Repository
public interface StateRepository extends CrudRepository<State, String> {}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.ArrayList;
import java.util.List;
#Service
#Transactional
public class StateService {
#Autowired
private StateRepository stateRepository;
public void save(State state) {
stateRepository.save(state);
}
public List<State> findAll() {
List<State> states = new ArrayList<>();
stateRepository.findAll().forEach(states::add);
return states;
}
}
For starters use proper layers and write a service and use JPA instead of plain Hibernate. If you want a Session you can always use EntityManager.unwrap to obtain the underlying Session.
#Service
#Transactional
public StateService {
#PersistenceContext
private EntityManager em;
public void save(State state) {
em.persist(state);
}
Use this service in your controller instead of the SessionFactory.
#RestController
public class RestAPIController {
private final StateService stateService;
RestAPIController(StateService stateService) {
this.stateService=stateService;
}
#PutMapping("/addS")
public void addSt(#RequestParam("cc") String cc, #RequestParam("st") String st) {
CC cc1= new CC();
CC.setCode(cc);
State state = new State(cc,st);
stateService.save(state);
}
}
Now ditch your Config class and restart the application.
NOTE
When using Spring Data JPA it is even easier, define a repository extending CrudRepository and inject that into the service instead of an EntityManager. (I'm assuming that Long is the type of primary key you defined).
public interface StateRepository extends CrudRepository<State, Long> {}
#Service
#Transactional
public StateService {
private final StateRepository states;
public StateService(StateRepository states) {
this.states=states;
}
public void save(State state) {
states.save(state);
}
}

#ConfigurationProperties, #Value not Working YET Passing the Tests

I have a strange problem reading configuration, none of solutions I've seen seem to work. Here is my code:
#SpringBootApplication
#EnableConfigurationProperties
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Here is my properties class
#Component
#ConfigurationProperties(prefix = "my")
#Data
#ToString
#NoArgsConstructor
#AllArgsConstructor
public class MyProperties {
private String host;
private int port;
}
I then use MyProperties class in my class using #Autowired:
#Autowired
private MyProperties props;
However, I'm getting null for my props object.
Strangely, this is passing the tests just perfectly:
#SpringBootTest
class ApplicationTests {
#Autowired
private MyProperties props;
#Test
void test_configuration() {
Assertions.assertEquals(props.getHost(), "xx.xx.xx.xx");//pass!
Assertions.assertEquals(props.getPort(), xxxxxx);//pass!
}
}
It has totally refused to work, and so has #Value injection. What could I be missing?
EDIT
Here's complete code of how I'm using #Autowired on MyProperties (I've included #Value which is also not working)
#Slf4j
#Component //also tried #Configurable, #Service
public class MyService {
#Autowired
private MyProperties props;
#Value("localhost")
public String host;
public void post() {
log.info(host + props);// =null and null
}
}
EDIT2
However, I've noticed that on the controller, it works perfectly okay:
#Slf4j
#RestController
#Service
public class Main {
#Autowired
private MyProperties props;
#Value("localhost")
private String host;
#GetMapping("/post")
public void post() {
log.info(host + props);//=it's perfect!
new MyService().post();// calling MyService - where #Autowired or #Value is failing
}
}
The reason this isn't working is because the MyService you're using isn't a Spring bean, but an instance you created by yourself (using new MyService()).
To make this work, you should autowire MyService, in stead of creating your own instance:
#Slf4j
#RestController
public class Main {
#Autowired // Autowire MyService
private MyService myService;
#GetMapping("/post")
public void post() {
myService.post(); // Use the myService field
}
}
For more information, look at this Q&A: Why is my Spring #Autowired field null.
UPDATE:
new MyService() is not a "spring bean", thus can't be auto-wired with anything!;)
1. Lombok
Some people use Project Lombok to add getters and setters automatically. Make sure that Lombok does not generate any particular constructor for such a type, as it is used automatically by the container to instantiate the object.
With "such a type" ConfigurationProperties is referred in Externalized Configuration (one of my favorite chapters;) More Exact: 2.8.1. JavaBean properties binding, at the bottom of second "Note!" ;)
So this could be a reason (for strange behavior).

Spring Boot and/or Vaadin autowiring behaviors I do not understand

I am working on a Spring Boot application an am having issues where items are not being auto-wired as I expect. What is adding to this confusion is that I have another Spring Boot application that also happens to involve Vaadin. In this application, auto-wiring seems more "automated".
Here is what I mean. In the Vaadin app, a #Service object, is injected into a Vaadin "view" with little effort. Additionally, the service object itself is injected with a Repository object. I need to do almost nothing to make this all happen.
In my other non-Vaadin application, I set up the code to do similar things, but in this case, it does not work. The application fails to start and complains as below:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.mycompany.app.backend.service.TaskMasterService required a bean of type 'com.mycompany.app.backend.repository.TaskMasterRepository' that could not be found.
Action:
Consider defining a bean of type 'com.mycompany.app.backend.repository.TaskMasterRepository' in your configuration.
I assume that I'm missing some configuration somewhere, or that the Vaadin application has "extra" functionality added that makes this work.
Here are snippets of the Vaadin application:
TasksView.java:
#Route("main")
public class TasksView extends VerticalLayout {
private final TaskService taskService;
private final TaskNoteService noteService;
public TasksView(TaskService taskService, TaskNoteService noteService) {
this.taskService = taskService;
this.noteService = noteService;
}
...
}
TaskService.java:
#Service
#Transactional
public class TaskService {
private TaskRepository taskRepo;
public TaskService(TaskRepository taskRepo) {
this.taskRepo = taskRepo;
}
...
}
TaskRepository.java:
public interface TaskRepository extends JpaRepository<Task, Long>, TaskRepositoryCustom {
...
}
I don't need to #Autowired anything or provide a #Configuration class or define a #Bean anywhere. It just works.
Here are some snippets from the non-Vaadin application:
IngesterRunner.java:
#Service
public class IngesterRunner {
private TaskMasterService tmService;
private TaskRelationshipService trService;
private VariantDetailService vdService;
public IngesterRunner(TaskMasterService tmService, TaskRelationshipService trService,
VariantDetailService vdService) {
this.tmService = tmService;
this.trService = trService;
this.vdService = vdService;
}
...
}
TaskMasterService.java:
#Service
#Transactional
public class TaskMasterService {
private final TaskMasterRepository repo;
public TaskMasterService(TaskMasterRepository repo) {
this.repo = repo;
}
...
}
TaskMasterRepository.java:
package com.mycompany.app.backend.repository;
public interface TaskMasterRepository extends JpaRepository<TaskMaster, Long> {
}
JPAConfiguration.java:
#Configuration
#EnableJpaRepositories(basePackages = "com.mycompany.app.backend.repository", transactionManagerRef = "jpaTransactionManager")
#EnableTransactionManagement
public class JPAConfiguration {
private static final String[] ENTITY_PACKAGES_TO_SCAN = {
"com.mycompany.app.backend.entity" };
...
}
I don't understand what is different about the first application that makes auto-wiring work so much more smoothly. Is it something I have in my pom.xml? Is it some sort of extra support provided by Vaadin?
If requested, I can try to include the POM files if that will help.
Update:
I have tried a number of suggestions, but none seem to have any effect. I added #Autowired annotations, but it makes no difference.
#Service
public class IngesterRunner {
private TaskMasterService tmService;
private TaskRelationshipService trService;
private VariantDetailService vdService;
public IngesterRunner(#Autowired TaskMasterService tmService,
#Autowired TaskRelationshipService trService, #Autowired VariantDetailService vdService) {
this.tmService = tmService;
this.trService = trService;
this.vdService = vdService;
}
...
}
#Service
#Transactional
public class TaskMasterService {
private final TaskMasterRepository repo;
public TaskMasterService(#Autowired TaskMasterRepository repo) {
this.repo = repo;
}
...
}
#Repository annotation in TaskMasterRepository.java, like
#Repository
public interface TaskMasterRepository extends JpaRepository<TaskMaster, Long> {
}

#Autowired is not working for CrudRepository

I have problems injecting a repository, there is no problem injecting a Service. Im injecting the repo in a service:
#Service
public class AuthorService {
#Autowired
private AuthorRepository repository;
public String getAll(){return "XXXXX";}
}
and the repository is:
public interface AuthorRepository extends CrudRepository<Author, Integer> {
}
And my code structure is the following:
with the main class:
#SpringBootApplication
public class AuthorBookGraphqlApplication {
public static void main(String[] args) {
SpringApplication.run(AuthorBookGraphqlApplication.class, args);
}
}
the error is thrown on start:
Field repository in com.author.book.graphql.demo.service.AuthorService required a bean of type 'com.author.book.graphql.demo.repository.AuthorRepository' that could not be found.
Update code as below
Spring will automatically import the beans into the container and inject to dependencies with these annotations.
#Component, #Controller, #Service and #Repository - Helps to define the beans so the container is aware of them and can inject them for you with #Autowired.
#Autowired - Handles only wiring part here.
#Service
public class AuthorService {
#Autowired
private AuthorRepository repository;
public String getAll(){return "XXXXX";}
}
#Repository
public interface AuthorRepository extends CrudRepository<Author, Integer> {}
Before class AuthorRepository, Let's put more annotation #Repository.

Spring Boot Annotation #Autowired of Service fails

I'm trying to use #Autowired annotation for a Service class in Spring Boot application, but it keeps throwing No qualifying bean of type exception. However, if I change the service class to a bean, then it works fine. This is my code:
package com.mypkg.domain;
#Service
public class GlobalPropertiesLoader {
#Autowired
private SampleService sampleService;
}
package com.mypkg.service;
#Service
public class SampleService{
}
And this is my SpringBoot class:
package com.mypkg;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
#SpringBootApplication
#EnableJpaRepositories
#Import(RepositoryRestMvcConfiguration.class)
#EnableTransactionManagement
public class TrackingService {
private static final Logger LOGGER = LoggerFactory.getLogger(TrackingService.class);
static AnnotationConfigApplicationContext context;
public static void main(String[] args) throws Exception {
SpringApplication.run(TrackingService.class, args);
context = new AnnotationConfigApplicationContext();
context.refresh();
context.close();
}
}
When I try to run this, I get the following exception:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.mypkg.service.SampleService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
But when I remove the #Service annotation from the SampleService class, and add it as a bean in my AppConfig class as below, it works fine:
#Configuration
public class AppServiceConfig {
public AppServiceConfig() {
}
#Bean(name="sampleService")
public SampleService sampleService(){
return new SampleService();
}
}
The classes are in different packages. I am not using #ComponentScan. Instead, I'm using #SpringBootApplication which does that automatically. However, I tried with ComponentScan as well but that didn't help.
What am I doing wrong here?
You are using two ways to build a Spring's bean. You just need to use one of them.
#Service over the POJO
#Service
public class SampleService
#Bean in the configuration class which must be annotated with #Configuration
#Bean
public SampleService sampleService(){
return new SampleService();
}
#Autowired is resolved by class type then #Bean(name="sampleService") is not needed is you have only one bean with that class type.
EDIT 01
package com.example
#SpringBootApplication
public class Application implements CommandLineRunner {
public static void main(String... args) {
SpringApplication.run(Application.class);
}
#Autowired
private UserRepository userRepository;
#Autowired
private UserService userService;
#Override
public void run(String... strings) throws Exception {
System.out.println("repo " + userRepository);
System.out.println("serv " + userService);
}
}
package com.example.config
#Configuration
public class AppConfig {
#Bean
public UserRepository userRepository() {
System.out.println("repo from bean");
return new UserRepository();
}
#Bean
public UserService userService() {
System.out.println("ser from bean");
return new UserService();
}
}
package com.example.repository
#Service
public class UserRepository {
#PostConstruct
public void init() {
System.out.println("repo from #service");
}
}
package com.example.service
#Service
public class UserService {
#PostConstruct
public void init() {
System.out.println("service from #service");
}
}
Using this code you can comment the AppConfig class and then you will see how UserRepository and UserService are autowired. After that comment #Service in each class and un-comment AppConfig and classes will be autowired too.

Resources