Methods in service layer are recognized as static - java

I built a Spring Boot application and added several methods in the service layer. Then I autowired their class into the Controller. IDEA shows an error that
Non-static method 'findAll()' cannot be referenced from a static
context.
#Autowired
public UserMapper Usermanager;
public List<UserEntity> findAll() {
List<UserEntity> list = Usermanager.findALL();
return list;
}
public List<UserEntity> findByName() {
List<UserEntity> list = Usermanager.findByName();
return list;
}
Static value is belong to the class instead of the object. For this reason if we use a static value NPE(NonePointerException) will happen.

The reason of this problem is that I invoked the class instead of instance object. All methods in class is static, they are absolutely not able to be invoke. There are codes in my controller.
#Autowired
UserService userService;
#Autowired
UserMapper userMapper;
//查找
#GetMapping("/findall")
public List<UserEntity> findAll() {
//used to be:return UserService.findAll();
return userService.findAll();
}
#GetMapping("/find/{name}")
public List<UserEntity> findByName(#PathVariable String name) {
//used to be:return UserService.findByName();
return userService.findByName();
}

Related

Should I instantiate my Service in order to share infornation between different methods?

Currently I have an endpoint in my controller that calls different methods from my service. Each of these methods recieve a parameter called executionId that is different for every http request.
#RestController
public class ProcedureController {
#Autowired
MyService service;
#GetMapping
public void execute(){
String executionId = ...; //randomly generated
service.executeMethodA(executionId);
service.executeMethodB(executionId);
}
}
#Service
public class MyService {
public void executeMethodA(String executionId){
...
}
public void executeMethodB(String executionId){
...
}
}
It works fine but it seems very repetitive to pass executionId as a parameter to each method. So one solution that I thought was to instantiate my service with the executionId so it can hold this information and every method can access it.
#RestController
public class ProcedureController {
#GetMapping
public void execute(){
String executionId = ...; //randomly generated
MyService service = new MyService(executionId);
service.executeMethodA();
service.executeMethodB();
}
}
#Service
public class MyService {
String executionId;
public MyService(String executionId){
this.executionId = executionId;
}
public void executeMethodA(){
// Use executionId here
...
}
public void executeMethodB(){
// Use executionId here
...
}
}
Is this solution a bad practice? If so, how can I achieve something similar?
Spring allows you to inject a managed object (bean) as a dependency into another object via the #Autowired annotation.
For example, if I have a UserService that has a dependency on UserRepository, I can have the UserRepository injected using #Autowired annotation like this:
UserRepository Class
class UserRepository {
UserRepository () {}
}
UserService Class
class UserService {
#Autowired
private UserRepository userRepository;
UserService () {}
}
This is done using Field Injection. The same thing can be accomplice using Setter Injection:
class UserService {
private UserRepository userRepository;
UserService () {}
#Autowired // Using setter injection
public void setUserRepository(
UserRepository userRepository) {
this.userRepository = userRepository
}
}
or via Constructor Injection:
class UserService {
private UserRepository userRepository;
#Autowired // Using constructor Injection
UserService (UserRepository userRepository) {
this.userRepository = userRepository
}
}

Custom method in single repositories returns null

I followed the reference guide for creating and customizing Repositories and came up with the following:
public interface UserRepository extends JpaRepository<User, Long>, UserRepositoryCustom {
}
#Transactional(readOnly = true)
public class UserRepositoryCustomImpl implements UserRepositoryCustom {
#Override
public User findByToken(UUID token) {
return new User();
}
}
public interface UserRepositoryCustom {
User findByToken(UUID token);
}
In my case userRepository.findByToken(token);returns null.
#Edit
The test below fails
#RunWith(SpringRunner.class)
#DataJpaTest
#AutoConfigureTestDatabase(replace = NONE)
public class UserRepositoryTest {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
#Autowired
private TestEntityManager entityManager;
#Autowired
private UserRepository userRepository;
#Test
public void test() throws Exception{
assertNotNull(userRepository.findByToken(UUID.randomUUID()));
}
}
Your custom implementation is named wrong. It should be named after the class name of the Repository, not after the interface declaring the custom method.
Just renamed UserRepositoryCustomImpl to UserRepositoryImpl
The reason the method currently returns null is because Spring Data creates a query from the name and doesn't find a User with the specified token.

Call spring inject class from a static method from another class

Well, i have a class with #Component anotation, this class makes some selects in database, see:
#Component(value = "parametroRelatorioHelper")
public class ParametroRelatorioHelper {
#Autowired
private BasicDAO dao;
public ParametroRelatorio getParametroByNome(String nome) {
List<ParametroRelatorio> parametros = (List<ParametroRelatorio>) dao
.findByNamedQuery(ParametroRelatorio.FIND_BY_NOME,
new NamedParams("nome", nome));
if (parametros.size() > 0)
return parametros.get(0);
else
return null;
}
public List<ParametroRelatorio> getAll() {
return (List<ParametroRelatorio>) dao
.findByNamedQuery(ParametroRelatorio.FIND_ALL);
}
public BasicDAO getDao() {
return dao;
}
public void setDao(BasicDAO dao) {
this.dao = dao;
}
}
Now, i have a "Helper" class, where user can call your method directly (static method) but i need call a method from ParametroRelatorioHelper, see:
public class ReportHelper {
public static void call(){
//how can i do it without #Component injection
parametroRelatorioHelper.getAll();
}
}
It sounds like your architecture is incorrect, instead the ReportHelper should be a component too and the dependencies should be injected in it, otherwise it collides with the idea of the Spring IOC, helper methods should not rely on components on services...

Spring won't inject a JPARepository bean

Dao
#Repository
public interface LoginDao extends JpaRepository<Login, Integer> {
Login findByLogin(String login);
}
Validator
#Component
public class PasswordChangeValidator implements Validator {
private LoginDao loginDao;
#Override
public boolean supports(Class<?> aClass) {
return PasswordChange.class.equals(aClass);
}
#Override
public void validate(Object o, Errors errors) {
PasswordChange passwordChange = (PasswordChange) o;
**// There is a null pointer here because loginDao is null**
Login login = loginDao.findByLogin(passwordChange.getLoginKey());
}
public LoginDao getLoginDao() {
return loginDao;
}
#Autowired
public void setLoginDao(LoginDao loginDao) {
**// There is a debug point on the next line and it's hit on server startup and I can
// see the parameter us non-null**
this.loginDao = loginDao;
}
}
Controller
#Controller
#RequestMapping("api")
public class PasswordController {
#Autowired
PasswordService passwordService;
#InitBinder("passwordChange")
public void initBinder(WebDataBinder webDataBinder, WebRequest webRequest) {
webDataBinder.setValidator(new PasswordChangeValidator());
}
#RequestMapping(value = "/passwordChange", method = RequestMethod.POST)
public #ResponseBody PasswordInfo passwordInfo(#RequestBody #Valid PasswordChange passwordChange)
throws PasswordChangeException {
return passwordService.changePassword(passwordChange.getLoginKey(), passwordChange.getOldPassword(), passwordChange.getNewPassword());
}
}
I have the Dao listed above. This same dao bean gets injected in an #Service annotated class but not in #Component annotated Validator class. Well, not exactly the upon server startup I can see that the setter method gets called, but when I try to use this variable in a method the variable shows as null.
Does anybody see a problem with my configuration ? Please note that the loginDao bean gets injected into a service class, so the Context configuration is good.
Well there's your problem
webDataBinder.setValidator(new PasswordChangeValidator());
Spring can only manage beans it created. Here, you're creating the instance. Instead inject your bean into the #Controller and use it.
#Inject
private PasswordChangeValidator passwordChangeValidator;
...
webDataBinder.setValidator(passwordChangeValidator);

Basic Google Guice injection. Playframework 2.1

I'm very new to Google Guice and I'm having troubles to get UserService instanced.
In my Playframework application, I have a service called UserService which looks like this:
public class UserService { // Note it doesn't implement an interface
private UserDao userDao;
private Email email;
#Inject
public UserService(UserDao userDao, Email email) {
this.userDao = userDao;
this.email = email;
}
...
}
I have this controller:
public class UserController extends Controller {
#Inject
private UserService userService;
...
}
I have this configure() definition for my UserModule:
protected void configure() {
bind(UserDao.class).to(UserDaoJpa.class);
bind(Email.class).to(EmailHtml.class);
bind(UserController.class);
}
I get the injector on Playframework Global object which looks similar to the example provided by Guillaume Bort about getting a controller instance, which in this case fits perfectly for getting the injector (the method getControllerInstance is a new feature in Play 2.1, but this is not relevant here). See here if interested:
public class Global extends GlobalSettings {
private static final Injector injector = createInjector();
#Override
public <A> A getControllerInstance(Class<A> controllerClass) throws Exception {
return injector.getInstance(controllerClass);
}
private static Injector createInjector() {
return Guice.createInjector(new UsuarioModule());
}
}
At this point, everything works perfectly, the service is correctly instantiated with its parameters resolved. The controller gets the object graph as expected.
But, when I try to do #Inject UserService userService somewhere else in the application I get null for userService. For example:
public class EmailAvailableValidator {
#Inject
private static UserService userService; // This is not resolved :(
public static Map<String, List<ValidationError>> validateEmailAndGetErrorsIfAny(String email, String emailField) {
Map<String, List<ValidationError>> errors = new HashMap<String, List<ValidationError>>();
if (!userService.isEmailAvailable(email)) {
List<ValidationError> list = new ArrayList<ValidationError>();
list.add(new ValidationError("", UsuarioResource.getMessageEmailTaken()));
errors.put(emailField, list);
}
return errors;
}
}
The question is, what is correct way to get an instance of an object that doesn't implement an interface? Is it always necessary to implement an interface?
Isn't it supposed that guice knows how to solved UserDao and Email? Why it is not able to instantiate it except in UserController?
I need this instance with its dependencies resolved, I mean with UserDao and Email into it.
The official guice docs were not very helpful for me.
Thanks for your help!
Two things:
You need to use a Guice Injector to instantiate EmailAvailableValidator. Your "Global" class is doing that already. Using the injector creates an instance and then sets all of its injected fields.
You might have to use the static injection to fill a static field. For example, in your configure() method add:
requestStaticInjection(UserService.class);
However, I haven't personally used it so YMMV.
Reference:
http://code.google.com/p/google-guice/wiki/Injections

Categories

Resources