unity dependency Injection and redirect to action - asp.net-mvc-3

i use Unity DI in project, but when redirect from one action to another get this error:
No parameterless constructor defined for this object. in FirstController _service property is initialized correct, and when call SecondController directly _service property is initialized correct, but when use RedirctToAction method from FirstController to redirect to SecondController get this Error:No parameterless constructor defined for this object.
my sample code is:
public interface IService
{
string Get();
}
public class Service : IService
{
public string Get()
{
return "Data";
}
}
public class FirstController : Controller
{
private readonly IService _service;
public FirstController(IService service)
{
_service = service;
}
public ActionResult Index()
{
return RedirectToAction("Index", "Second"); -----> this line
}
}
public class SecondController : Controller
{
private readonly IService _service;
public SecondController(IService service)
{
_service = service;
}
public ActionResult Index()
{
return View();
}
}
DI Code:
public static class Bootstrapper
{
public static IUnityContainer Initialise()
{
var container = BuildUnityContainer();
System.Web.Mvc.DependencyResolver.SetResolver(new DependencyResolver(container));
return container;
}
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
container.RegisterType<IService, Service>();
return container;
}
}
public class DependencyResolver : IDependencyResolver
{
private readonly IUnityContainer _unityContainer;
public DependencyResolver(IUnityContainer unityContainer)
{
_unityContainer = unityContainer;
}
public object GetService(System.Type serviceType)
{
try
{
var service = _unityContainer.Resolve(serviceType);
return service;
}
catch (Exception ex)
{
return null;
}
}
public IEnumerable<object> GetServices(System.Type serviceType)
{
try
{
return _unityContainer.ResolveAll(serviceType);
}
catch
{
return new List<object>();
}
}
}
Application_Start Initialise:
Bootstrapper.Initialise();

Related

JavaFX custom controls created with a Builder and binding expressions

I’m using Spring together with JavaFx. To use spring bean as a custom control I need to use BuilderFactory and a Builder to get a bean from the context. Otherwice I don't have an application context
Parent.java
#Component
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class ParentControl extends VBox {
#Autowired
ControlFXMLLoader controlFXMLLoader;
#Value("classpath:/parent.fxml")
private Resource fxml;
#PostConstruct
void load() throws IOException {
controlFXMLLoader.load(fxml.getURL(), this);
}
public ParentControl() {
//no application context
}
public LocalDate getDate() {
return LocalDate.now();
}
}
BeanBuilderFactory.java
#Component
public class BeanBuilderFactory implements BuilderFactory {
private Logger logger = LogManager.getLogger(BeanBuilderFactory.class);
#Autowired
private ConfigurableApplicationContext context;
public BeanBuilderFactory() {
}
private JavaFXBuilderFactory defaultBuilderFactory = new JavaFXBuilderFactory();
#Override
public Builder<?> getBuilder(Class<?> type) {
try {
String[] beanNames = context.getBeanNamesForType(type);
if (beanNames.length == 1) {
return new Builder<Object>() {
#Override
public Object build() {
return context.getBean(beanNames[0]);
}
};
} else {
return defaultBuilderFactory.getBuilder(type);
}
} catch (BeansException e) {
return defaultBuilderFactory.getBuilder(type);
}
}
}
And then I user this BuilderFactory to load fxml for a custom control
ControlFXMLLoader.java
#Component
public class ControlFXMLLoader {
private Logger logger = LogManager.getLogger(ControlFXMLLoader.class);
#Autowired
protected ConfigurableApplicationContext context;
#Autowired
protected BeanBuilderFactory beanBuilderFactory;
public Object load(URL fxmlUrl, Parent root, Object controller) throws IOException {
logger.debug("load");
javafx.fxml.FXMLLoader loader = new javafx.fxml.FXMLLoader(fxmlUrl);
loader.setControllerFactory(context::getBean);
loader.setBuilderFactory(beanBuilderFactory);
loader.setRoot(root);
loader.setController(controller);
return loader.load();
}
public Object load(URL fxmlUrl, Parent root) throws IOException {
return load(fxmlUrl, root, root);
}
}
Now I have a child custom control
ChildControl.java
#Component
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class ChildControl extends VBox {
public ChildControl() {
}
#Autowired
ControlFXMLLoader controlFXMLLoader;
#Value("classpath:/child.fxml")
private Resource fxml;
#PostConstruct
void load() throws IOException {
controlFXMLLoader.load(fxml.getURL(), this);
}
ObjectProperty<LocalDate> date = new SimpleObjectProperty<LocalDate>();
public LocalDate getDate() {
return date.get();
}
public void setDate(LocalDate date) {
this.date.set(date);
}
public ObjectProperty<LocalDate> dateProperty() {
return date;
}
#FXML
protected void doSomething() {
System.out.println("The button was clicked! " + date.get().toString());
}
}
And want to assign the date to the child from parent fxml
parent.fxml
<fx:root type="com.example.javafx.ParentControl" xmlns:fx="http://javafx.com/fxml">
<ChildControl date="${controller.date}"/>
</fx:root>
child.fxml
<fx:root type="com.example.javafx.ChildControl" xmlns:fx="http://javafx.com/fxml">
<TextField fx:id="textField"/>
<Button text="Click Me" onAction="#doSomething"/>
</fx:root>
The problem is that FXMLLoader doesn’t not allow to use Binding Expression together with a Builder. I got "Cannot bind to builder property." exception.
Below is the part of the code from FXMLLoader.java and the very last if that causes the problem.
Is there some other solution?
FXMLLoader.java
public void processPropertyAttribute(Attribute attribute) throws IOException {
String value = attribute.value;
if (isBindingExpression(value)) {
// Resolve the expression
Expression expression;
if (attribute.sourceType != null) {
throw constructLoadException("Cannot bind to static property.");
}
if (!isTyped()) {
throw constructLoadException("Cannot bind to untyped object.");
}
// TODO We may want to identify binding properties in processAttribute()
// and apply them after build() has been called
if (this.value instanceof Builder) {
throw constructLoadException("Cannot bind to builder property.");
}

Configure Autofac DI container in ASP.NET CORE 3.1 Web API and consumer service from controller

I need to configure Autofac DI container in ASP.NET CORE 3.1 Web API application and call register class from the container in Web API controller. I install Autofac.Extensions.DependencyInjection (6.0.0) and try to register container in my Startup.cs class but I am not able to use service. Also, do I need to configure the container in ConfigureServices(IServiceCollection services) class? The debugger does not hit IoCConfigurator() class after hitting point builder.RegisterModule(new IoCConfigurator());
Program.cs
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Startup.cs
public class Startup
{
public IConfiguration Configuration { get; }
public ContainerBuilder containerBuilder { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
containerBuilder = new ContainerBuilder();
}
public void ConfigureServices(IServiceCollection services)
{
ServicesConfigurator.Configure(services, Configuration);
ConfigureIoC(services, containerBuilder);
}
public void ConfigureIoC(IServiceCollection services, ContainerBuilder builder)
{
builder.RegisterModule(new IoCConfigurator());
}
IoCConfigurator.cs
public class IoCConfigurator: Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<NotifyService>().As<INotificationService>();
builder.RegisterType<UsersService>().AsSelf();
}
}
INotification Interface & Class
public interface INotificationService
{
void notifyUsernameChanged(Users users);
}
public class NotifyService : INotificationService
{
public void notifyUsernameChanged(Users users)
{
string changedUsername = users.Username;
Console.WriteLine("Username has changed to ... ");
Console.WriteLine(changedUsername);
}
}
User & User Service Class
public class Users
{
public string Username { get; set; }
public Users(string username)
{
this.Username = username;
}
}
public class UsersService
{
private INotificationService _notificationService;
public UsersService(INotificationService notificationService)
{
this._notificationService = notificationService;
}
public void ChangeUsername(Users users, string newUsername)
{
users.Username = newUsername;
_notificationService.notifyUsernameChanged(users);
}
}
API Controller where I want to class the UserService Class and get reference from DI container
[Authorize]
[Route("txn/v1/[controller]/[action]")]
[ApiController]
public class DashboardController : ControllerBase
{
[HttpPost("{name}")]
public ActionResult<HelloMessage> GetMessage(string name)
{
// call container here...
var result = new HelloMessage()
{
GivenName = name,
ReturnMessage = "Dashboard# Hello, Welcome to Texanite Digital"
};
return result;
}
Here is how I set it up. From command line:
md autof
cd autof
dotnet new webapi
dotnet add package Autofac.Extensions.DependencyInjection
Then edit using VS or VSCode. Program.cs - as you had it:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
Next in Startup.cs, forget about ConfigureIoC, just register the services you want/need:
public void ConfigureContainer(ContainerBuilder builder)
{
// Register your own things directly with Autofac, like:
//builder.RegisterModule();
builder.RegisterType<NotifyService>().As<INotificationService>();
}
Then in DashboardController.cs you need to "inject" the needed services from the constructor:
public class HelloMessage {
public string GivenName { get; set; }
public string ReturnMessage { get; set; }
}
//[Authorize] Easier without Auth - don't need
[Route("[controller]")]
[ApiController]
public class DashboardController : ControllerBase
{
private readonly INotificationService _notifyService;
public DashboardController(INotificationService notifyService)
{
_notifyService = notifyService;
}
//[HttpPost("{name}")] - easier to test Get
[HttpGet("{name}")]
public ActionResult<HelloMessage> GetMessage(string name)
{
// call container here...
_notifyService.notifyUsernameChanged(new Users(name));
var result = new HelloMessage()
{
GivenName = name,
ReturnMessage = $"Dashboard {name}, Welcome to Texanite Digital"
};
return result;
}
}
My Results:
With console output:
Your UserService was a little "out of the loop" but you can add an Interface for it and register with container and add it to injected services of the controller(s).
I could zip the whole thing up, just don't know where to put it or send it...
Change your code like, that is all I think
Program.cs
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureContainer<ContainerBuilder>(builder =>
{
builder.RegisterModule(new IoCConfigurator());
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Startup.cs
public class Startup
{
public IConfiguration Configuration { get; }
public ContainerBuilder containerBuilder { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
containerBuilder = new ContainerBuilder();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}

Problems with #Autowired, with a ManagedBean and an abstract class

Well, I have an abstract class like this:
public abstract class BasicCrudMBImpl<Bean, BO> extends BasicMBImpl {
protected Bean bean;
protected List<Bean> beans;
protected BO boPadrao;
public void deletar() {
try {
((BasicBO) boPadrao).delete((AbstractBean) bean);
addInfoMessage("Registro deletado com sucesso");
beans = retornaBeansDoBanco();
bean = null;
} catch (BOException e) {
addErrorMessage(e.getMessage());
}
}
public void salvar(ActionEvent event) {
try {
if (((AbstractBean) bean).getId() == null) {
bean = (Bean) ((BasicBO) boPadrao).save((AbstractBean) bean);
addInfoMessage("Registro salvo com sucesso");
} else {
((BasicBO) boPadrao).update((AbstractBean) bean);
addInfoMessage("Registro atualizado com sucesso");
}
beans = retornaBeansDoBanco();
} catch (BOException e) {
FacesContext.getCurrentInstance().validationFailed();
addErrorMessage(e.getMessage());
}
}
public Bean getBean() {
return bean;
}
public void setBean(Bean bean) {
this.bean = bean;
}
public List<Bean> getBeans() {
try {
if (beans == null)
beans = (List<Bean>) retornaBeansDoBanco();
return beans;
} catch (BOException e) {
addErrorMessage(e.getMessage());
}
return null;
}
public void setBeans(List<Bean> beans) {
this.beans = beans;
}
// Deve ser implementado para carregar a query adequada ao bean necessário
public abstract List<Bean> retornaBeansDoBanco();
public abstract void novo(ActionEvent event);
public abstract void alterar(ActionEvent event);
public BO getBoPadrao() {
return boPadrao;
}
public abstract void setBoPadrao(BO boPadrao);
public void addErrorMessage(String componentId, String errorMessage) {
addMessage(componentId, errorMessage, FacesMessage.SEVERITY_ERROR);
}
public void addErrorMessage(String errorMessage) {
addErrorMessage(null, errorMessage);
}
public void addInfoMessage(String componentId, String infoMessage) {
addMessage(componentId, infoMessage, FacesMessage.SEVERITY_INFO);
}
public void addInfoMessage(String infoMessage) {
addInfoMessage(null, infoMessage);
}
private void addMessage(String componentId, String errorMessage,
FacesMessage.Severity severity) {
FacesMessage message = new FacesMessage(errorMessage);
message.setSeverity(severity);
FacesContext.getCurrentInstance().addMessage(componentId, message);
}
}
In ManagedBean I tried to inject the "boPadrao" with #Autowired, like this:
#ManagedBean(name = "enderecoMB")
#ViewScoped
public class EnderecoMBImpl extends BasicCrudMBImpl<Endereco, BasicBO> {
private static Logger logger = Logger.getLogger(EnderecoMBImpl.class);
private List<TipoEndereco> tiposEndereco;
private List<Logradouro> logradouros;
#PostConstruct
public void init() {
logger.debug("Inicializando componentes no PostConstruct");
beans = retornaBeansDoBanco();
tiposEndereco = (List<TipoEndereco>) boPadrao
.findByNamedQuery(TipoEndereco.FIND_ALL);
logradouros = (List<Logradouro>) boPadrao
.findByNamedQuery(Logradouro.FIND_ALL_COMPLETO);
}
#Override
public List<Endereco> retornaBeansDoBanco() {
return (List<Endereco>) getBoPadrao().findByNamedQuery(Endereco.FIND_ALL_COMPLETO);
}
#Override
public void novo(ActionEvent event) {
bean = new Endereco();
}
#Override
public void alterar(ActionEvent event) {
// TODO Auto-generated method stub
}
public List<TipoEndereco> getTiposEndereco() {
return tiposEndereco;
}
public void setTiposEndereco(List<TipoEndereco> tiposEndereco) {
this.tiposEndereco = tiposEndereco;
}
public List<Logradouro> getLogradouros() {
return logradouros;
}
public void setLogradouros(List<Logradouro> logradouros) {
this.logradouros = logradouros;
}
#Autowired
public void setBoPadrao(BasicBO boPadrao) {
this.boPadrao = boPadrao;
}
}
But this doesn't works, the boPadrao is always null, getting a "NullPointerException". The error occurs in method retornaBeansDoBanco();

arquillian - how to get persistent session scoped beans across tests

Have been struggling with a test case where a service is dependent on having a session scoped bean being in the session fails;
The class producing #SessionScoped User:
public class LoginService {
private User user;
public void login(String name) {
if ("userA".equals(name)) {
user = new User(name, "permissionA");
} else if ("userB".equals(name)) {
user = new User(name, "permissionB");
} else {
user = new User("anonymous", "");
}
}
#Produces
#LoggedIn
#SessionScoped
public User getLoggedIn() {
return this.user;
}
}
The service using the #LoggedIn User:
public class MediaService {
#Inject
#LoggedIn
private User user;
public void updateImage(){
if("permissionA".equals(user.getPermission())) {
System.out.println("user can update image");
} else {
System.out.println("user can not update image");
}
}
}
And the test:
#RunWith(Arquillian.class)
public class ServiceTest {
#Deployment
public static WebArchive deployment() {
return ShrinkWrap
.create(WebArchive.class, "test.war")
.addClass(LoggedIn.class)
.addClass(LoginService.class)
.addClass(MediaService.class)
.addClass(User.class)
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
}
#Inject
private LoginService lservice;
#Inject
private MediaService mservice;
#Test
public void testLogin() {
lservice.login("userA");
}
#Test
public void testUpdateImage(){
mservice.updateImage();
}
}
Testing with the arquillian remote container, first test (testLogin) passes and the testUpdateImage fails with null User in session.
WELD-000052 Cannot return null from a non-dependent producer method: [method] #Produces #LoggedIn #SessionScoped public org.arg.service.LoginService.getLoggedIn()
Thanks

Spring MVC Annotated Controller Interface with #PathVariable

Is there any reason not to map Controllers as interfaces?
In all the examples and questions I see surrounding controllers, all are concrete classes. Is there a reason for this? I would like to separate the request mappings from the implementation. I hit a wall though when I tried to get a #PathVariable as a parameter in my concrete class.
My Controller interface looks like this:
#Controller
#RequestMapping("/services/goal/")
public interface GoalService {
#RequestMapping("options/")
#ResponseBody
Map<String, Long> getGoals();
#RequestMapping(value = "{id}/", method = RequestMethod.DELETE)
#ResponseBody
void removeGoal(#PathVariable String id);
}
And the implementing class:
#Component
public class GoalServiceImpl implements GoalService {
/* init code */
public Map<String, Long> getGoals() {
/* method code */
return map;
}
public void removeGoal(String id) {
Goal goal = goalDao.findByPrimaryKey(Long.parseLong(id));
goalDao.remove(goal);
}
}
The getGoals() method works great; the removeGoal(String id) throws an exception
ExceptionHandlerExceptionResolver - Resolving exception from handler [public void
todo.webapp.controllers.services.GoalServiceImpl.removeGoal(java.lang.String)]:
org.springframework.web.bind.MissingServletRequestParameterException: Required
String parameter 'id' is not present
If I add the #PathVariable annotation to the concrete class everything works as expected, but why should i have to re-declare this in the concrete class? Shouldn't it be handled by whatever has the #Controller annotation?
Apparently, when a request pattern is mapped to a method via the #RequestMapping annotation, it is mapped to to the concrete method implementation. So a request that matches the declaration will invoke GoalServiceImpl.removeGoal() directly rather than the method that originally declared the #RequestMapping ie GoalService.removeGoal().
Since an annotation on an interface, interface method, or interface method parameter does not carry over to the implementation there is no way for Spring MVC to recognize this as a #PathVariable unless the implementing class declares it explicitly. Without it, any AOP advice that targets #PathVariable parameters will not be executed.
The feature of defining all bindings on interface actually got implement recently in Spring 5.1.5.
Please see this issue: https://github.com/spring-projects/spring-framework/issues/15682 - it was a struggle :)
Now you can actually do:
#RequestMapping("/random")
public interface RandomDataController {
#RequestMapping(value = "/{type}", method = RequestMethod.GET)
#ResponseBody
RandomData getRandomData(
#PathVariable(value = "type") RandomDataType type, #RequestParam(value = "size", required = false, defaultValue = "10") int size);
}
#Controller
public class RandomDataImpl implements RandomDataController {
#Autowired
private RandomGenerator randomGenerator;
#Override
public RandomData getPathParamRandomData(RandomDataType type, int size) {
return randomGenerator.generateRandomData(type, size);
}
}
You can even use this library: https://github.com/ggeorgovassilis/spring-rest-invoker
To get a client-proxy based on that interface, similarly to how RestEasys client framework works in the JAX-RS land.
It works in newer version of Spring.
import org.springframework.web.bind.annotation.RequestMapping;
public interface TestApi {
#RequestMapping("/test")
public String test();
}
Implement the interface in the Controller
#RestController
#Slf4j
public class TestApiController implements TestApi {
#Override
public String test() {
log.info("In Test");
return "Value";
}
}
It can be used as:
Rest client
Recently I had the same problem. Following has worked for me:
public class GoalServiceImpl implements GoalService {
...
public void removeGoal(#PathVariableString id) {
}
}
i resolved this problem.
ON CLIENT SIDE:
I'm using this library https://github.com/ggeorgovassilis/spring-rest-invoker/. This library generate a proxy from interface to invoke spring rest service.
I extended this library:
I created an annotations and a factory client class:
Identify a Spring Rest Service
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface SpringRestService {
String baseUri();
}
This class generates a client rest from interfaces
public class RestFactory implements BeanFactoryPostProcessor,EmbeddedValueResolverAware {
StringValueResolver resolver;
#Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.resolver = resolver;
}
private String basePackage = "com";
public void setBasePackage(String basePackage) {
this.basePackage = basePackage;
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
createBeanProxy(beanFactory,SpringRestService.class);
createBeanProxy(beanFactory,JaxrsRestService.class);
}
private void createBeanProxy(ConfigurableListableBeanFactory beanFactory,Class<? extends Annotation> annotation) {
List<Class<Object>> classes;
try {
classes = AnnotationUtils.findAnnotatedClasses(basePackage, annotation);
} catch (Exception e) {
throw new BeanInstantiationException(annotation, e.getMessage(), e);
}
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
for (Class<Object> classType : classes) {
Annotation typeService = classType.getAnnotation(annotation);
GenericBeanDefinition beanDef = new GenericBeanDefinition();
beanDef.setBeanClass(getQueryServiceFactory(classType, typeService));
ConstructorArgumentValues cav = new ConstructorArgumentValues();
cav.addIndexedArgumentValue(0, classType);
cav.addIndexedArgumentValue(1, baseUri(classType,typeService));
beanDef.setConstructorArgumentValues(cav);
registry.registerBeanDefinition(classType.getName() + "Proxy", beanDef);
}
}
private String baseUri(Class<Object> c,Annotation typeService){
String baseUri = null;
if(typeService instanceof SpringRestService){
baseUri = ((SpringRestService)typeService).baseUri();
}else if(typeService instanceof JaxrsRestService){
baseUri = ((JaxrsRestService)typeService).baseUri();
}
if(baseUri!=null && !baseUri.isEmpty()){
return baseUri = resolver.resolveStringValue(baseUri);
}else{
throw new IllegalStateException("Impossibile individuare una baseUri per l'interface :"+c);
}
}
private static Class<? extends FactoryBean<?>> getQueryServiceFactory(Class<Object> c,Annotation typeService){
if(typeService instanceof SpringRestService){
return it.eng.rete2i.springjsonmapper.spring.SpringRestInvokerProxyFactoryBean.class;
}else if(typeService instanceof JaxrsRestService){
return it.eng.rete2i.springjsonmapper.jaxrs.JaxRsInvokerProxyFactoryBean.class;
}
throw new IllegalStateException("Impossibile individuare una classe per l'interface :"+c);
}
}
I configure my factory:
<bean class="it.eng.rete2i.springjsonmapper.factory.RestFactory">
<property name="basePackage" value="it.giancarlo.rest.services" />
</bean>
ON REST SERVICE SIGNATURE
this is an example interface:
package it.giancarlo.rest.services.spring;
import ...
#SpringRestService(baseUri="${bookservice.url}")
public interface BookService{
#Override
#RequestMapping("/volumes")
QueryResult findBooksByTitle(#RequestParam("q") String q);
#Override
#RequestMapping("/volumes/{id}")
Item findBookById(#PathVariable("id") String id);
}
ON REST SERVICE IMPLEMENTATION
Service implementation
#RestController
#RequestMapping("bookService")
public class BookServiceImpl implements BookService {
#Override
public QueryResult findBooksByTitle(String q) {
// TODO Auto-generated method stub
return null;
}
#Override
public Item findBookById(String id) {
// TODO Auto-generated method stub
return null;
}
}
To resolve annotation on parameters I create a custom RequestMappingHandlerMapping that looks all interfaces annotated with #SpringRestService
public class RestServiceRequestMappingHandlerMapping extends RequestMappingHandlerMapping{
public HandlerMethod testCreateHandlerMethod(Object handler, Method method){
return createHandlerMethod(handler, method);
}
#Override
protected HandlerMethod createHandlerMethod(Object handler, Method method) {
HandlerMethod handlerMethod;
if (handler instanceof String) {
String beanName = (String) handler;
handlerMethod = new RestServiceHandlerMethod(beanName,getApplicationContext().getAutowireCapableBeanFactory(), method);
}
else {
handlerMethod = new RestServiceHandlerMethod(handler, method);
}
return handlerMethod;
}
public static class RestServiceHandlerMethod extends HandlerMethod{
private Method interfaceMethod;
public RestServiceHandlerMethod(Object bean, Method method) {
super(bean,method);
changeType();
}
public RestServiceHandlerMethod(Object bean, String methodName, Class<?>... parameterTypes) throws NoSuchMethodException {
super(bean,methodName,parameterTypes);
changeType();
}
public RestServiceHandlerMethod(String beanName, BeanFactory beanFactory, Method method) {
super(beanName,beanFactory,method);
changeType();
}
private void changeType(){
for(Class<?> clazz : getMethod().getDeclaringClass().getInterfaces()){
if(clazz.isAnnotationPresent(SpringRestService.class)){
try{
interfaceMethod = clazz.getMethod(getMethod().getName(), getMethod().getParameterTypes());
break;
}catch(NoSuchMethodException e){
}
}
}
MethodParameter[] params = super.getMethodParameters();
for(int i=0;i<params.length;i++){
params[i] = new RestServiceMethodParameter(params[i]);
}
}
private class RestServiceMethodParameter extends MethodParameter{
private volatile Annotation[] parameterAnnotations;
public RestServiceMethodParameter(MethodParameter methodParameter){
super(methodParameter);
}
#Override
public Annotation[] getParameterAnnotations() {
if (this.parameterAnnotations == null){
if(RestServiceHandlerMethod.this.interfaceMethod!=null) {
Annotation[][] annotationArray = RestServiceHandlerMethod.this.interfaceMethod.getParameterAnnotations();
if (this.getParameterIndex() >= 0 && this.getParameterIndex() < annotationArray.length) {
this.parameterAnnotations = annotationArray[this.getParameterIndex()];
}
else {
this.parameterAnnotations = new Annotation[0];
}
}else{
this.parameterAnnotations = super.getParameterAnnotations();
}
}
return this.parameterAnnotations;
}
}
}
}
I created a configuration class
#Configuration
public class WebConfig extends WebMvcConfigurationSupport{
#Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RestServiceRequestMappingHandlerMapping handlerMapping = new RestServiceRequestMappingHandlerMapping();
handlerMapping.setOrder(0);
handlerMapping.setInterceptors(getInterceptors());
handlerMapping.setContentNegotiationManager(mvcContentNegotiationManager());
PathMatchConfigurer configurer = getPathMatchConfigurer();
if (configurer.isUseSuffixPatternMatch() != null) {
handlerMapping.setUseSuffixPatternMatch(configurer.isUseSuffixPatternMatch());
}
if (configurer.isUseRegisteredSuffixPatternMatch() != null) {
handlerMapping.setUseRegisteredSuffixPatternMatch(configurer.isUseRegisteredSuffixPatternMatch());
}
if (configurer.isUseTrailingSlashMatch() != null) {
handlerMapping.setUseTrailingSlashMatch(configurer.isUseTrailingSlashMatch());
}
if (configurer.getPathMatcher() != null) {
handlerMapping.setPathMatcher(configurer.getPathMatcher());
}
if (configurer.getUrlPathHelper() != null) {
handlerMapping.setUrlPathHelper(configurer.getUrlPathHelper());
}
return handlerMapping;
}
}
and I configurated it
<bean class="....WebConfig" />

Resources