Apache Wicket on Spring Boot, with a single dependency.
Add one starter and you have a running Wicket application: the filter is registered,
@SpringBean injection works everywhere, and every setting lives in
application.properties. Then it gets out of your way.
<dependency>
<groupId>org.wicketstuff</groupId>
<artifactId>wicketstuff-spring-boot-starter</artifactId>
<version>10.12.0</version>
</dependency>
dependencies {
implementation 'org.wicketstuff:wicketstuff-spring-boot-starter:10.12.0'
}
The same three pieces, every time. Now they come for free.
Running Wicket on Spring Boot always needs a WebApplication, a filter
registration for it, and a SpringComponentInjector so that
@SpringBean works. None of it is hard, but it is the first thing standing
between you and a running Wicket page, and it is easy to get subtly wrong.
The goal goes one step further: Apache Wicket selectable on start.spring.io, the way Vaadin already is, so that a new Wicket project is one click away. See the roadmap.
One dependency
Wicket, the Wicket–Spring bridge and an embedded servlet container arrive together. Nothing else to declare.
Lines of setup
Start the application and a placeholder page is already served, telling you how to replace it with your own.
Room to grow
Past the setup, it is ordinary Wicket and ordinary Spring, documented by their own projects. The starter stays small on purpose.
Looking for batteries included (Spring Security, WebSockets, bean validation, session datastores and more)? Have a look at MarcGiffing/wicket-spring-boot. Both solve the same problem with opposite philosophies: pick that one for breadth, this one to start from the smallest thing that works. Being part of wicketstuff also means the starter is released with it: use the starter version matching your Wicket version.
From empty project to your first page in three steps.
Start from any Spring Boot 4 project, or an empty one with just a main class.
Add the starter and run
Add the dependency shown above and start your @SpringBootApplication.
There is nothing else to configure: the starter brings the web stack with it.
@SpringBootApplication
public class ExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
}
Register your own Wicket application
Subclass WebApplication and make it a Spring bean with
@Component. The moment it exists, the built-in default page steps
aside and your home page is served instead.
@Component
public class WicketApplication extends WebApplication {
@Override
public Class<? extends Page> getHomePage() {
return HomePage.class;
}
@Override
protected void init() {
super.init();
// mount pages, configure Wicket as usual...
}
}
Inject Spring beans into your pages
Use Wicket’s standard @SpringBean on fields of any page, panel or
component. Injection is wired up for you, and it already works inside your
application’s init().
public class HomePage extends WebPage {
@SpringBean
private GreetingService greetingService;
public HomePage() {
add(new Label("message", greetingService.greet()));
}
}
Everything Wicket needs from Spring Boot, and nothing more.
A single auto-configuration that switches itself on for servlet web applications with Wicket on the classpath.
Filter registration
Wicket’s WicketFilter is registered with the embedded servlet container, mapped and named from your settings.
Application discovery
Any WebApplication bean is picked up and bound. Without one, a friendly default page gets you going.
@SpringBean injection
Spring beans in pages, panels and components, and already inside your application’s init().
Externalised settings
Filter path, name, order and Wicket’s runtime mode are plain Spring properties, with autocompletion in your IDE.
Lives alongside Spring MVC
Requests Wicket does not handle fall through to Spring MVC, so your REST controllers keep working next to your pages.
DevTools ready
Automatic restarts work, stored pages restore correctly and sessions survive. See how.
Common tasks, one snippet each.
Everything below is optional. The defaults are chosen so that a new project needs none of it.
Add REST endpoints next to your pages
The starter brings Spring MVC along. Wicket’s filter is mapped at /*
but forwards anything it does not handle further down the chain, so a
@RestController just works.
Prefer a strict separation? Confine Wicket to its own prefix with
wicket.filter-path=/app/*.
@RestController
class GreetingController {
// "/" still renders your Wicket home page
@GetMapping("/api/greeting")
String greeting() {
return "hello";
}
}
Switch to production mode
Like Wicket itself, the starter runs in DEVELOPMENT mode unless told
otherwise: it reloads changed markup, keeps wicket:id attributes in the
HTML, enables the Ajax debug window, serves unminified JavaScript and shows full stack
traces. Great on your machine, not on a server.
Put DEPLOYMENT in a production profile and start with
--spring.profiles.active=prod, or set the environment variable
WICKET_CONFIGURATION=DEPLOYMENT.
wicket.configuration=DEPLOYMENT
Place Wicket in the filter chain
By default Wicket runs last, after every other filter, including Spring
Security’s filter chain (order -100). That is usually right:
security decisions happen before Wicket renders anything.
If another filter of yours has to run after Wicket, move Wicket up the chain.
# lower values run earlier
wicket.filter-order=0
Deploy as a WAR
The embedded Tomcat does not commit you to it. Use Spring Boot’s usual recipe:
war packaging, a SpringBootServletInitializer, and the
container re-declared at provided scope.
Your direct declaration wins, so Tomcat stays out of WEB-INF/lib while Wicket and the starter are packaged as normal.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
Test with a real server
Wicket keys its application registry by filter name, and Spring caches test contexts for the whole JVM. Two tests that each start a real servlet container would collide on the default name, so give each of them its own.
Tests using the default mock environment never start the filter and need nothing.
@SpringBootTest(
webEnvironment = WebEnvironment.RANDOM_PORT,
properties = "wicket.filter-name=my-test-filter")
class MyIntegrationTest {
// ...
}
Every setting is a Spring property.
Set them in application.properties, application.yml, environment variables or any other Spring Boot property source. Your IDE autocompletes them.
| Property | Default | What it does |
|---|---|---|
wicket.enabled | true | Set to false to switch the whole auto-configuration off. |
wicket.configuration | DEVELOPMENT | Wicket’s runtime mode: DEVELOPMENT or DEPLOYMENT. |
wicket.filter-path | /* | URL pattern the Wicket filter is mapped to, e.g. /app/*. |
wicket.filter-name | wicket-filter | Name of the registered filter, which also names the Wicket application. |
wicket.filter-order | LOWEST_PRECEDENCE | Position of the Wicket filter in the servlet filter chain; lower values run earlier. |
Replace any default with your own bean
Everything the starter contributes steps aside as soon as you define your own, so you can take over as much or as little as you need.
| Starter bean | Steps aside when | Define your own to |
|---|---|---|
webApplication | any WebApplication bean exists | use your own Wicket application (the usual case) |
springComponentInjectorRegistrar | any SpringComponentInjector bean exists | control how Spring injection is wired |
wicketFilterRegistration | a FilterRegistrationBean<WicketFilter> or WicketFilter bean exists | control every detail of the filter registration |
Migrating an existing app? If your init() already calls
getComponentInstantiationListeners().add(new SpringComponentInjector(this)),
drop that line and let the starter do it, or declare your own
SpringComponentInjector bean. Keeping both would register two injectors.
Automatic restarts, without broken pages.
Add spring-boot-devtools and keep coding: the application restarts whenever
your classes change. The starter makes sure Wicket plays along, with no configuration on
your side.
The problem
DevTools runs your classes in a restart classloader, while libraries stay in a
base classloader that also holds a stale copy of your classes. Wicket restores
stored pages (on the back button, or after a restart) from the base side, but rebuilds
their @SpringBean proxies from the restart side.
Same class names, different classes. Every stored page with an injected field then fails:
The fix
The starter ships a META-INF/spring-devtools.properties that moves every
Wicket and wicketstuff jar into the restart classloader, right next to your own classes.
Pages are now restored against exactly the classes that are running.
You do not need to do anything: DevTools reads the file straight from the starter jar.
- Back button worksOlder pages restore from Wicket’s page store with their injected beans intact.
- Restarts are cleanThe application is re-created on every restart, with
@SpringBeanready ininit(). - Sessions surviveDevTools persists HTTP sessions across restarts, so your Wicket session and everything in it is kept.
Keep page state across restarts, too
Your session survives a restart, but the pages in it do not by default: Wicket stores them in the servlet container’s temporary directory, and Spring Boot creates a new one each time Tomcat starts. An old page is then simply rendered afresh.
Give Tomcat a fixed base directory while developing, and pages keep their state across restarts as well.
# development only: keep pages across restarts
server.tomcat.basedir=target/tomcat
Pick the starter version matching your Wicket version.
The starter is released together with wicketstuff, so there is no second release cycle to wait for.
Next stop: start.spring.io.
The aim is a Wicket checkbox on Spring Initializr, right next to Vaadin, so that a new Wicket project is one click away. The Initializr only offers released versions, so the way there has three steps.
Merge into wicketstuff
The starter becomes a module of wicketstuff-core, built and tested with every change.
Publish to Maven Central
It ships with the next wicketstuff release, so there is a real version for the Initializr to reference, never a snapshot.
Request the listing
The entry is already prepared in the README; it is submitted to the Spring Initializr team and kept current for every new Spring Boot line.
Until then, generate a Spring Boot project as usual and add the starter by hand: it is the single dependency at the top of this page.