Reputation: 18861
I work on a web-app (using Play Framework) and I'm trying to migrate to traditional Servlet model with Spring MVC. I'd like to run in an embedded Jetty container alongs an already existing one (netty).
I'm trying to re-use a created Spring context (that contains all application beans, including newly-added Spring MVC controllers), but the request mappings aren't picked up.
I debugged Spring's Dispatcher Servlet and there were no mappings registered indeed (so it could handle no path).
Here's the manual Jetty setup code:
@RequiredArgsConstructor
public class EmbeddedJetty {
private final int port;
private final AnnotationConfigWebApplicationContext existingContext;
@SneakyThrows
public void start() {
Assert.notNull(existingContext.getBean(UserController.class));
val server = new Server(port);
ServletContextHandler handler = new ServletContextHandler();
ServletHolder servlet = new ServletHolder(new DispatcherServlet(existingContext));
handler.addServlet(servlet, "/");
handler.addEventListener(new ContextLoaderListener(existingContext));
server.setHandler(handler);
server.start();
log.info("Server started at port {}", port);
}
}
And here's the controller being ignored:
@Controller
public class UserController {
@GetMapping("/users/{userId}")
public ResponseEntity<?> getUser(@PathVariable("userId") long userId) {
return ResponseEntity.ok("I work");
}
}
What do I needed to do to make my embedded jetty setup pick up the existing controller beans and serve the mappings?
Upvotes: 7
Views: 3951
Reputation: 18861
This ended up being a bit of pain and re-work, but the final solution was making sure that existingContext
is NOT started and refreshed prior to starting the DispatcherServlet.
Upvotes: 1
Reputation: 4818
I believe you are missing the MVC Java Config that handles request to @RequestMapping
inside your Controller.
So basically what you would need to add is a WebMVC config class like:
package my.spring.config;
//necessary imported packages avoided for shortening the example
@EnableWebMvc
@Configuration
@ComponentScan({ "my.jetty.test" })
public class SpringWebConfig extends WebMvcConfigurerAdapter {
}
Then you would need to indicate to your AnnotationConfigWebApplicationContext
where the config is located, add to your start
method this:
webAppContext.setConfigLocation("my.spring.config");
And voila, the below (very simple) Controller serves request on my localhost:
package my.jetty.test;
//necessary imported packages avoided for shortening the example
@Controller
public class HelloController {
@GetMapping(value = "/")
@ResponseBody
public String printWelcome(HttpServletRequest request) {
return "hello";
}
@GetMapping(value = "/hello/{name:.+}")
@ResponseBody
public String hello(@PathVariable("name") String name) {
return "hello " + name;
}
}
If needed, I can give the full example. Several links that helped me:
Edit: The repo with my code working
Upvotes: 2
Reputation: 6158
If you are going to migrate to servlet model, you may would like to get familiar with the normal structure:
.
├── pom.xml
├── README
├── src
│ ├── main
│ │ ├── java
│ │ ├── resources
│ │ │ ├── spring
│ │ └── webapp
│ │ └── WEB-INF
│ │ └── web.xml
│ └── test
│ ├── java
│ └── resources
The web.xml
is the core descriptor that j2ee server used to deploy the app. There exists some important components in a app which is defined in web.xml
:
When server starts, it will setup listener for monitoring the lifecycle of whole app or a request; it will setup filter to filter requests; it will setup servlet to handle request.
Spring is a very light-weight way to integrate many convenient method/utility into our applications. It is light weight because it is attached into our projects only by two things:
web.xml
to initialize So, back to our problem.
ServletContextHandler
which is just related to mapping, but not listener (i.e. no spring config will be init). You should start with WebAppContext
;web.xml
to pick up the existing controller beans and serve the mappings;Upvotes: 1
Reputation: 497
Try this in your Web configuration file
@ComponentScan(basePackages = {"com.our.controller"})
Upvotes: -1