How can I inject #RequestBody object(value object) to Spring Service layer?
I want to Inject(Autowired)objects what come from request body values.
HelloController
#Autowired
UserService userService;
(….)
#GetMapping("/hello")
public String hello(
#RequestBody UserRequestBodyDto userDto,
HttpServletResponse response){
return null;
}
UserRequestBodyDto
#Data
public class UserRequestBodyDto{
private String name;
private String address;
}
UserServiceImpl
#Service
public class UserServiceImpl implements UserService{
#AutoWired
public UserServiceImpl(UserRequestBodyDto userDto){
(….)
}
}
In that case, how can I inject UserRequestBodyDto objects into service layer?
Add 'setUserDto' method to UserService is the best way?
or If
convert dto to entity is the best way to inject objets,
how can I manage many of same classes between dto class and entity class?
+a) In my opinion, make a RequestScopedBean is bad way.
ref: Spring: injecting #RequestBody into #Bean
Why you need to #AutoWired a request object? I seems completely unnecessary while doing operation with a request object cause, it will change on every new request.
So you can do operation with request object in the service layer method.
public UserServiceImpl(UserRequestBodyDto userDto){
(….)//do operation with userDto here.
}
Or, I you really need to #AutoWire the request object then declare UserRequestBodyDto userDto in service layer with #AutoWired annotation. And when the service layer method executes just set the values to this.userDto.
#Service
public class UserServiceImpl implements UserService{
#AutoWired
private UserRequestBodyDto userDto;
#AutoWired
public UserServiceImpl(UserRequestBodyDto userDto){
this.userDto = userDto;//Here, setting value of userDto to this.userDto
}
}
Related
I wonder whether it's possible to replace all the Autowired fields with final ones and #RequiredArgsConstructor below the class declaration?
For instance, replace the following code
public class Controller {
#Autowired
private Reposiroty repository;
#Autowired
private Service service;
...
}
with something like that:
#RequiredArgsConstructor
public class Controller {
private final Reposiroty repository;
private final Service service;
...
}
Thanks in advance!
As i consume a lot of data in httpservletrequest header and set a lot of values in request attribute in service class, I'm not sure if this would cause thread safety issues, I looked over the web if autowiring httpservlet request would cause threadsafety issues and i got mixed opinion
Following are the places where i autowire httpservletrequest
#RestController
Public class UserController {
#Autowire
HttpServletRequest httpServletRequest;
#Autowire
IAMService iamservice;
#PostMapping("/addUser")
public String addUser(#RequestBody UserDto userdto){
return iamservice.addUser(userdto);
}
}
#Service
Public Class IAMService {
#Autowire
HttpServletRequest httpServletRequest;
#Autowire
UserDao userDao;
public String addUser(UserDto userdto){
Long primaryKey = userDao.save(userdto,httpServletRequest.getHeader("loggedInUserId"));
httpServletRequest.setAttribute("userPrimaryKey",primaryKey);
return "User is added successfully";
}
}
We should not #Autowire HttpServletRequest. Consider modifying your code as below to have valid usage of request object and avoid thread-safety issues-
#RestController
Public class UserController {
#Autowire
IAMService iamservice;
#PostMapping("/addUser")
public String addUser(#RequestBody UserDto userdto, HttpServletRequest httpServletRequest){
return iamservice.addUser(userdto, httpServletRequest);
}
}
#Service
Public Class IAMService {
#Autowire
UserDao userDao;
public String addUser(UserDto userdto, HttpServletRequest httpServletRequest){
Long primaryKey = userDao.save(userdto,httpServletRequest.getHeader("loggedInUserId"));
httpServletRequest.setAttribute("userPrimaryKey",primaryKey);
return "User is added successfully";
}
}
I use Project Lombok for my Java projects.
My Controller looks like this:
#RestController
#AllArgsConstructor
public class UserController {
private RegisterService registerService;
private UserService userService;
private UserValidator userValidator;
private LoginService loginService;
/*
* rest of the controller
*/
}
so the controller must look like this:
#WebMvcTest(UserController.class)
public class UserControllerTest {
#MockBean
private UserRepository userRepository;
#MockBean
private RegisterService registerService;
#MockBean
private UserService userService;
#MockBean
private LoginService loginService;
#MockBean
private UserValidator UserValidator;
/*
* rest of the contoller test
*/
}
Just to make programming less code-monkey, is there anything like #AllMockArgConstructor?
Or any way, I don't always have to add all services?
If this is a stupid question, please explain me why.
Thanks in advance!
Unfortunately it cannot be done,
#MockBeans annotation target is applied only to fields and types, and not for methods, you can read more about it here.
If #MockBeans was able to support also methods then it was can be done that way:
#Getter(onMethod_={#MockBean} )
#WebMvcTest(UserController.class)
public class UserControllerTest {
...
}
I have problems with configuring tests for SpringJUnit4ClassRunner.class).
My problem is because my mapper from map struct returns null when reached.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
class UserServiceTestSuite {
#Spy
private UserDto userDto;
#Spy
private UserMapper userMapper;
#Mock
private UserRepository userRepository;
#InjectMocks
private UserService userService;
private User createUser() {
return User.builder()
.firstName("Steve")
.lastName("Jobs")
.login("SteveJobs")
.password("password")
.role(UserRole.ROLE_ADMIN)
.build();
}
#Test
public void testCreateUser() {
//Given
User user = createUser();
Mockito.when(userRepository.save(user)).thenReturn(user);
//When
UserDto userDto = userService.createUser(userMapper.mapToUserDto(user));
Long id = userDto.getId();
//Then
Assert.assertEquals("Steve", userDto.getFirstName());
Assert.assertEquals("Jobs", userDto.getLastName());
Assert.assertEquals("SteveJobs", userDto.getLogin());
Assert.assertEquals("ROLE_ADMIN", userDto.getRole());
}
In my opinion you have two options:
Inject the mapper via #SpringBootTest(classes = {UserMapperImpl.class}) and
#Autowired
private UserMapper userMapper;
Simply initialize the Mapper private UserMapper userMapper = new UserMapperImpl() (and remove #Spy)
When using the second approach you can even remove the #SpringBootTest because in the given snippet you do not need a Spring context (created by the annotation).
#RunWith(MockitoJUnitRunner.class) can be used to automatically inject objects annotated with #Mock into your UserService. Writing unit tests without creating a spring context helps to keep test suite execution time low.
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
UserDTO userMapper.mapToUserDto(Object value);
}
Using this mapper interface you can instantiate from the mock and use the methods.
For example:
#Mock
private UserMapper userMapper;
Init the mocks:
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
and you use the call INSTANCE
UserDto userDto = userService.createUser(userMapper.INSTANCE.mapToUserDto(user));
In a Spring MVC project I've a DAO class myproj.models.UserDAO:
#Repository
#Transactional
public class UserDAO {
// UserDAO methods ...
}
and I should use it inside a controller, say myproj.controllers.UserController:
#Controller
public class UserController {
// UserController methods ...
#RequestMapping(value="/{user}")
public String create(String user) {
// Here I want to use the UserDAO
// ...
}
}
How can I create an instance of the UserDAO object and use it inside a controller method?
You could try following
#Repository
#Transactional
public class UserDAO {
// UserDAO methods ...
}
Controller:
#Controller
public class UserController {
#Autowired //this will give you the reference to UserDAO
UserDAO userDao;
// UserController methods ...
#RequestMapping(value="/{user}")
public String create(String user) {
// Here I want to use the UserDAO
userDao.userDaoMethod();
// ...
}
}
For more information on #Autowired explore this
User Autowired annotation to inject a bean instance of your DAO:
#Controller
public class UserController {
#Autowired
UserDAO userDao;
#RequestMapping(value="/{user}")
public String create(String user) {
userDao.method();
}
}