As part of my university project I was asked to implement a simple spring-boot app(backend only) which can communicate with Postman through HTTP requests.
The project built in controller-service-repository architecture and conatins only 1 Entity(Post object with string content) and 2 end-points(create new post, get all posts).
I know there is a few ways to configure beans in spring-boot:
with an external XML file.
With #Configuration annotation & #Bean annotation
With #Component annotation(#RestController,#Service, #JpaRepository)
The 3rd way working great but i was asked to implement the 2nd way and I'm really struggling to get this working.
Im getting:
ServletException: Circular view path [post]: would dispatch back to the current handler URL [/post] again. Check your ViewResolver setup!
Tried to explore about this exception and i did manage to "solve" it by adding this maven dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>2.5.2</version>
</dependency>
which led to:
"org.thymeleaf.exceptions.TemplateInputException: Error resolving template [post], template might not exist or might not be accessible by any of the configured Template Resolvers"
what am i doing wrong ?
Configuration class:
#Configuration
#EnableJpaRepositories(basePackages = {
"com.example.microblog.post.domain.repository"
})
public class ApplicationBeans {
#Bean
public PostController postController(PostService postService){
return new PostController(postService);
}
#Bean
public PostService postService(){
return new PostService();
}
}
Controller class:
#AllArgsConstructor
#RequestMapping(path = "post")
public class PostController {
#Autowired
private PostService service;
#CrossOrigin(origins = "http://localhost:4200")
#PostMapping("")
public PostEntity create(#RequestBody PostDto dto) {
return service.create(dto);
}
#GetMapping("/all")
#CrossOrigin(origins = "http://localhost:4200")
public List<PostEntity> getAll() {
return service.getAll();
}
}
Service Class:
#Transactional
public class PostService {
#Autowired
private PostRepository PostRepository;
public PostEntity create(PostDto dto){
PostEntity newPost = new PostEntity(dto.getContent());
return PostRepository.save(newPost);
}
public List<PostEntity> getAll(){
return PostRepository.findAll();
}
Repository class:
public interface PostRepository extends JpaRepository<PostEntity,Long> {}
For second approach, when you create a Bean, try not to have #Component/#Controller ... on the class for which you create the bean
#Configuration
public class AppConfig {
#Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
You can continue to autowire them like in third approach, try not to keep beans of same name
Your error points in the direction, that your Controller has difficulties to resolve the answer of your Service to a valid JSON response.
Note that #RestController is just a convenient way to add #Controller and #ResponseBody annotation. When you just add a #Bean annotation you are not adding either #Controller or #ResponseBody.
If you want to use the Controller class without using these Annotations you need to implement the functionality that these classes provide.
However I really see no way, why option 2 would be used for a Controller class. If you want to use it for a #Service class (which is doing the same as #Component) you can use the approach that Ravi suggested.
Related
I am having the class like below
#Controller
#RequestMapping(value = "/test")
#AllArgsConstructor
public class TestController {
#Qualifier("userDAO")
private final Test testDAO;
}
Below is my bean registration
<bean id="userDAO" class="com.test.dao.TestImpl"
p:sqlSessionFactory-ref="testSqlSessionFactory" />
when I run my app got error like below
No default constructor found; nested exception is java.lang.NoSuchMethodException bean configuration
Also I tried to add the lombok.config file in root and copied the Qualifier annotation, that's also not helped.
spring version is 3.2.15. lombok version 1.18.16
It's working fine with below
#Autowired
public TestController(#Qualifier("userDAO") final Test testDAO) {
this.testDAO = testDAO;
}
How to resolve this?
Adding only an #AllArgsConstructor isn't enough, as it will add the constructor but it doesn't add #Autowired. Because that is missing Spring will look for the default constructor.
To fix you can do 1 of 3 things
Upgrade to Spring 4.3 or higher, as that will automatically use the single constructor and autowire it
Instruct lombok to add #Autowired to the constructor.
Ditch lombok and just provide the constructor yourself.
The first should be pretty easy (include a newer version of Spring in your dependencies). The second requires some additional code.
#Controller
#RequestMapping(value = "/test")
#AllArgsConstructor(onConstructor = #__(#Autowired))
public class TestController {
private final Test testDAO;
}
The #Qualifier won't work (and should be removed) as it should be on the constructor argument.
I would just ditch Lombok for this case and just add the constructor (option 3).
#Controller
#RequestMapping(value = "/test")
public class TestController {
private final Test testDAO;
#Autowired
public TestController(#Qualifier("userDAO") Test testDao) {
this.testDao=testDao;
}
}
I'm using Spring Boot AutoConfiguration for registering the beans. Need to find a way through which I can register the beans configured via Auto-Configuration as a Rest Controller
SampleController.java
public class SampleController
{
#GetMapping("/sample-path")
public String sampleMethod()
{
return "Sample String";
}
}
SampleControllerAutoConfiguration.java
#Configuration
#ConditionalOnProperty(value = "some.property", havingValue = "true") // using this property, the parent app may or may not chose to have the Controller Endpoint
public class SampleControllerAutoConfiguration
{
// Need to register this bean as a controller
#Bean
#ConditionalOnMissingBean
public SampleController sampleController()
{
return new SampleController();
}
}
I can't annotate SampleController with #RestController since it's in the same package as the parent project which imports this and hence get's auto-configured due to Component-Scan
I want to take the json data in the 'https://dovizkurlari-l6vtviaacq-uc.a.run.app/api/doviz/usd' and present it with a service. but I got an error I can't solve.
I added the below property in my application.properties
application.properties
tcmb.request.dolar=https://dovizkurlari-l6vtviaacq-uc.a.run.app/api/doviz/usd
Then I assign this link to the variable dolarUrl in tcmbCommon class.
tcmbCommon.java
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#ToString
public class TcmbCommon {
#Value("${tcmb.request.dolar}")
public String dolarUrl;
}
and in a service class, I got the values for this service with restTemplate
DollarService.java
#Service
#RequiredArgsConstructor
#PropertySource("classpath:application.properties")
public class DolarService {
#Autowired
TcmbCommon tcmbCommon;
public String getDolar(){
RestTemplate rest = new RestTemplate();
String data = rest.getForObject(tcmbCommon.getDolarUrl(), String.class);
return data;
}
}
finally I created an response service by calling the DolarService class in the DolarController class
DolarController.java
#RestController
#RequestMapping(value = "/api/dolar")
#RequiredArgsConstructor
#Log4j2
public class DolarController {
private #NonNull
final DolarService dolarService;
#GetMapping("/getDolar")
public ResponseEntity<?> sDolar(){
return new ResponseEntity<>(dolarService.getDolar(), HttpStatus.OK);
}
}
I get this error when starting the project.
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.tcmb.webservices.tcmbwebservices.controller.DolarController required a bean of type 'com.tcmb.webservices.tcmbwebservices.services.DolarService' that could not be found.
Action:
Consider defining a bean of type 'com.tcmb.webservices.tcmbwebservices.services.DolarService' in your configuration.
Disconnected from the target VM, address: '127.0.0.1:63388', transport: 'socket'
you missed to write #Autowired in controller class.
**#Autowired
final DolarService dolarService;**
then try to run.
Please modify DolarController.java with below code while injecting dependencies to DollarService bean. I guess having #Autowired and final on a field are contradictory.
#Autowired // this will inject the dependency of DollarService class (autowiring)
private DolarService dolarService;
If you want to use the final keyword autowired, you need to do constructor injection as mentioned below.
#Autowired
public DolarController(DolarService dolarService) {
this.dolarService = dolarService;
}
Please let me know if it is still not resolved.
I want to create two spring-boot projects which communicate via rest calls. The first one contains the UI part with loginForm. The second project should communicate with the DB and should fetch the information about the user and then send it back to the UI.
The service project contains two modules: data module and impl module. The data project should contain the common data shared between the UI and the service project. It should be only a jar which I will add as a dependency in the UI project and the impl module in the service project.
The impl module should contain entities, repositories, restcontrollers and a service layer containing the real back-end logic of the application. I have already created the UI project but I have problems with the service. In the data module I have created the classes with the user information in a package org.tu.userdata. In the service impl I have an userController like this:
package org.tu.userserviceimpl.controller;
#RestController
#RequestMapping("/user")
public class UserController {
private final UserAuthenticationService userService;
#Autowired
public UserController(UserAuthenticationService userService) {
this.userService = userService;
}
#PostMapping(value = { "/logUser" })
public UserDto logUser(#RequestBody AuthenticateUserDto authenticateUserDto) throws Exception {
return userService.logUser(authenticateUserDto);
}
#PostMapping(value = { "/register" })
public UserDto register(#RequestBody RegisterUserDto registerUserDto) throws Exception {
return userService.registerUser(registerUserDto);
}
}
It injects the UserAuthenticationService which is an interface implemented like this:
package org.tu.userserviceimpl.service.impl;
#Service
public class UserAuthenticationServiceImpl implements UserAuthenticationService {
private final UserRepository userRepository;
#Autowired
public UserAuthenticationServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Override
public UserDto registerUser(RegisterUserDto registerUserDto) throws AuthenticationException {
return new UserDto();
}
#Override
public UserDto logUser(AuthenticateUserDto authenticateUserDto)
throws UserPrincipalNotFoundException, AuthenticationException {
return new UserDto();
}
}
the UserRepository:
package org.tu.userserviceimpl.repository;
#Repository
public interface UserRepository extends JpaRepository<UserEntity, Long> {
UserEntity findByUsername(String username);
boolean existsByUsername(String username);
boolean existsByEmail(String email);
}
and an application class:
package org.tu.userserviceimpl;
#SpringBootApplication
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
When I run it I get:
*************************** APPLICATION FAILED TO START
Description:
Parameter 0 of constructor in
org.tu.userserviceimpl.service.impl.UserAuthenticationServiceImpl
required a bean of type
'org.tu.userserviceimpl.repository.UserRepository' that could not be
found.
I thought that's strange because the UserRepository should be visible there since its a directory below the application class. To solve this I added a ComponentScan annotation on the application class:
#ComponentScan("org.tu.userserviceimpl.repository")
After that the project builds and deploys fine but I cannot access it. I got an 404 error. I even added an method like this one in the UserController just to troubleshoot it:
#GetMapping("/hello")
public String hello() {
return "hello";
}
But still cannot access it. The application is deployed on port 8082 but when I try to access "http://localhost:8082/user/hello" I cannot. After that I tried to remove the code in the UserAuthenticationServiceImpl which injects the UserRepository and I removed the componentScan annotation as well. After this code removal I was able to reach "http://localhost:8082/user/hello" and I got the message "hello" there. This means that the problem is somewhere in the Repository. Then I tried to add:
#EnableJpaRepositories("org.tu.userserviceimpl.repository")
#EntityScan("org.tu.userserviceimpl.entity")
on top of the application class and I added the code which injects the repository in the UserAuthenticationServiceImpl again. This time the outcome was a different error:
Parameter 0 of constructor in org.tu.userserviceimpl.service.impl.UserAuthenticationServiceImpl required a bean named 'entityManagerFactory' that could not be found.
I deleted the EntityScan annotation but the result was the same. Any ideas? What I am doing wrong?
I uploaded almost the same project in github: https://github.com/lei-gustavson/user-service.git
You have already created an argument based constructor in UserAuthenticationServiceImpl .
as below:
public UserAuthenticationServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
So NO argument constructor can't be created by JVM automatically.
and the below line has also not autowired
private final UserRepository userRepository;
Please add no argument constructor or remove the existing to resolve this issue.
Can you try putting below statement ?
#EntityScan("org")
I also have faced same issue but when i have used above statement for my directory structure it works.
Note :
You are confused between #ComponentScan and #EntityScan.
Just do one thing both above annotation annote like,
#ComponentScan("org"),
#EntityScan("org") and
#EnableJPARepository("org")
Mostly it will work for you
Thank you all for trying to help me! I find a solution. The problem was that i didnt have JPAConfig file. This solved the problem:
#Configuration
#EnableJpaRepositories("org.tu.userserviceimpl.repository")
public class JPAConfig {
}
After that i deleted the ComponentScan annotation from the SpringBootApplication class and everything was up and running
One of my classes deals with HttpServletRequest and is a component like this:
#Component
public class AuthorizationService {
#Autowired
HttpServletRequest request;
public Boolean authorize(Integer fetId) {
...play with headers, db and other stuff...
}
and is used somewhere else like this
public class CarRestController {
#Autowired
CarService service;
#Autowired
AuthorizationService authorizer;
#RequestMapping(method = RequestMethod.GET)
public List<Car> get()throws Exception {
authorizer.authorize(666);
...
return cars;
}
My worry is that since AuthorizationService is a #component, it will be a singleton by default and therefore there can only be one request that will be swapped by newer ones coming as it is processed.
Should I do this to solve the problem?
#Component
#Scope("prototype")
public class AuthorizationService {
Many Thanks
Remove the #Scope on the service and don't worry, your controller is also a singleton (because it's managed by spring).
BTW: you are missing a #Controller on your controller
Some reading:
Spring framework reference: The IoC container
Must Spring MVC Classes be Thread-Safe
Spring takes care of request scoped object like HttpServletRequest automatically. Remove #Scope and you should be fine.