I'm using spring 2.5, and am using annotations to configure my controllers. My controller works fine if I do not implement any additional interfaces, but the spring container doesn't recognize the controller/request mapping when I add interface implementations.
I can't figure out why adding an interface implementation messes up the configuration of the controller and the request mappings. Any ideas?
So, this works:
package com.shaneleopard.web.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.providers.encoding.Md5PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.shaneleopard.model.User;
import com.shaneleopard.service.UserService;
import com.shaneleopard.validator.RegistrationValidator;
import com.shaneleopard.web.command.RegisterCommand;
@Controller
public class RegistrationController {
@Autowired
private UserService userService;
@Autowired
private Md5PasswordEncoder passwordEncoder;
@Autowired
private RegistrationValidator registrationValidator;
@RequestMapping( method = RequestMethod.GET, value = "/register.html" )
public void registerForm(@ModelAttribute RegisterCommand registerCommand) {
// no op
}
@RequestMapping( method = RequestMethod.POST, value = "/register.html" )
public String registerNewUser( @ModelAttribute RegisterCommand command,
Errors errors ) {
String returnView = "redirect:index.html";
if ( errors.hasErrors() ) {
returnView = "register";
} else {
User newUser = new User();
newUser.setUsername( command.getUsername() );
newUser.setPassword( passwordEncoder.encodePassword( command
.getPassword(), null ) );
newUser.setEmailAddress( command.getEmailAddress() );
newUser.setFirstName( command.getFirstName() );
newUser.setLastName( command.getLastName() );
userService.registerNewUser( newUser );
}
return returnView;
}
public Validator getValidator() {
return registrationValidator;
}
}
but this doesn't:
package com.shaneleopard.web.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.providers.encoding.Md5PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.shaneleopard.model.User;
import com.shaneleopard.service.UserService;
import com.shaneleopard.validator.RegistrationValidator;
import com.shaneleopard.web.command.RegisterCommand;
@Controller
public class RegistrationController extends ValidatingController {
@Autowired
private UserService userService;
@Autowired
private Md5PasswordEncoder passwordEncoder;
@Autowired
private RegistrationValidator registrationValidator;
@RequestMapping( method = RequestMethod.GET, value = "/register.html" )
public void registerForm(@ModelAttribute RegisterCommand registerCommand) {
// no op
}
@RequestMapping( method = RequestMethod.POST, value = "/register.html" )
public String registerNewUser( @ModelAttribute RegisterCommand command,
Errors errors ) {
String returnView = "redirect:index.html";
if ( errors.hasErrors() ) {
returnView = "register";
} else {
User newUser = new User();
newUser.setUsername( command.getUsername() );
newUser.setPassword( passwordEncoder.encodePassword( command
.getPassword(), null ) );
newUser.setEmailAddress( command.getEmailAddress() );
newUser.setFirstName( command.getFirstName() );
newUser.setLastName( command.getLastName() );
userService.registerNewUser( newUser );
}
return returnView;
}
public Validator getValidator() {
return registrationValidator;
}
}
You can extend one spring controller by another spring controller.
The @Controller annotation indicates that a particular class serves the role of a controller. There is no need to extend any controller base class or reference the Servlet API.
Now, when Spring MVC controllers implement an interface, they do this not only in the standard Java way but also inherit all web request related functionality defined in the interface. As always, we may find the corresponding code snippets on our GitHub repository.
When a Controller has been found to handle the request, the handleRequest method of the located Controller will be invoked; the located Controller is then responsible for handling the actual request and — if applicable — returning an appropriate ModelAndView .
layne, you described the problem as happening when your controller class implements an interface, but in the code sample you provided, the problem occurs when your controller class extends another class of yours, ValidatingController
.
Perhaps the parent class also defines some Spring annotations, and the Spring container noticed them first and classified the controller class as that type of managed object and did not bother to check for the @Controller
annotation you also defined in the subclass. Just a guess, but if that pans out, I'd suggest reporting it to the Spring team, as it sounds like a bug.
By default JDK proxy are created using interface and if controller implements an interface the RequestMapping annotation gets ignored as the targetClass is not being used
Add this in your servlet context config:
<aop:aspectj-autoproxy proxy-target-class="true"/>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With