Using ZK With Spring Boot
Let's integrate ZK with Spring Boot!
Join the DZone community and get the full member experience.
Join For FreeIn recent years, Spring Boot has become a more and more popular alternative to building and deploying web applications. Following that popularity, questions arose whether existing or new ZK applications can also integrate with Spring Boot instead of building a classical war file.
The answer has always been "yes." At the same time, the transition from web.xml configuration into Java configuration has proven to be inconvenient, error-prone, and full of questions. That's why we at ZK decided to provide the zkspringboot integration module containing default and adjustable autoconfig and a starter dependency.
This was done with the help and inspiration of our active community. Special thanks go to Dirk and his zk-spring-boot-starter project.
In this article, I assume you are mostly familiar with Spring and Spring Boot and/or ZK.
As an introduction, I'd suggest Spring Boot — First Steps or the ZK Documentation.
Quick Start
For the impatient, here is the command sequence to clone and run one of our zkspringboot-demos.
git clone --branch v1.0.4 https://github.com/zkoss/zkspringboot.git
cd zkspringboot/zkspringboot-demos/zkspringboot-minimal-jar
../mvnw spring-boot:run
Then, open http://localhost:8080/ in a browser.
Understanding the Project
Let's start with a look in to the project structure of zkspringboot-minimal-jar:
$ find . -type f
./build.gradle
./pom.xml
./src/main/java/org/zkoss/zkspringboot/demo/MinimalApplication.java
./src/main/resources/application.properties
./src/main/resources/web/zul/minimal.zul
Yes, that's all; only 5 files (I'll be ignoring build.gradle for this article — just assume it works similarly). In the following sections, I'll explain the purpose of all these and how to run the project.
pom.xml
The pom file contains a single dependency, zkspringboot-starter
, referencing the default dependencies needed to get started easily (hence the name suffix "-starter"), which include zkspringboot-autoconfig
, minimal ZK dependencies ( zkbind
+ dependencies), and a recent Spring Boot version 2.1.0 ( spring-boot-starter-web
+ dependencies).
In real life projects, you should pick your own project dependencies with newer ZK and Spring Boot versions by replacing the zkspringboot-starter
dependency with zkspringboot-autoconfig
alongside your hand-selected dependencies/versions. In contrast to the zkspringboot-starter, the autoconfig doesn't add any transitive dependencies and only contains the ZK-Spring Boot configuration classes.
The spring-boot-maven-plugin at the end of pom.xml file is a standard Spring Boot plugin to run and repackage the project into a runnable jar file during the build process (nothing ZK specific here).
MinimalApplication.java
A minimalistic spring configuration class startup class annotated with @SpringBootApplication.
xxxxxxxxxx
public class MinimalApplication {
public static void main(String[] args) {
SpringApplication.run(MinimalApplication.class);
}
}
The main method specifies the entry point for the application, which will come in handy when running the application inside an IDE.
You might ask, "How does the Spring Boot application know about ZK?"
The answer lies in the file spring.factories inside zkspringboot-autoconfig.jar dependency. By adding this jar to the classpath, Spring will discover this file (by convention) and initialize ZK automatically.
minimal.zul
A simple zul file just to check ZK is working. Since we chose jar packaging (default), there is no classic web-content-folder. No problem for ZK, it can load and display zul files also from the classpath directly. These so-called class-web-resources (in ZK terms) are located below the web package (which can also contain style, script, image...resources).
src/main/resources/web/zul/minimal.zul
I chose to place zul files into the zul folder (you can choose your own package folder(s) below web, as needed). One might think that's a new feature, but in fact, ZK uses this mechanism to provide internal resources from the various zk-jar files — it just wasn't necessary in a classical web application since you had the web-content folder, which is not available in a Spring Boot jar application.
That alone would be sufficient to run/deploy ZK on Spring Boot.
Using the maven plugin, we can start the application easily with a single command and get the familiar output.
xxxxxxxxxx
$ ../mvnw spring-boot:run
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building zkspringboot-minimal-jar 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
...
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.0.RELEASE)
2019-01-16 16:38:16.753 INFO 21788 --- [ main] o.z.z.demo.MinimalApplication : Starting MinimalApplication on Robert-NB with PID 21788
...
2019-01-16 16:38:18.736 INFO 21788 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1917 ms
2019-01-16 16:38:18.996 INFO 21788 --- [ main] o.z.zkspringboot.ZkAutoConfiguration : ZK-Springboot: ServletRegistrationBean for DHtmlUpdateServlet with path /zkau
...
2019-01-16 16:38:20.138 INFO 21788 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2019-01-16 16:38:20.142 INFO 21788 --- [ main] o.z.z.demo.MinimalApplication :
Started MinimalApplication in 3.816 seconds (JVM running for 7.618)
You can now access the zul file directly using ZK's ajax and resource servlet mapped to /zkau (this also works in your existing classic ZK application, provided the zul files requested are on the classpath inside the web package folder).
http://localhost:8080/zkau/web/zul/minimal.zul
Starting from here, you may decide to go "freestyle" — using your existing Spring MVC skills — and configure @Controller classes with @RequestMappings internal forwards and view resolvers. Or, you can read on to check what else is already provided.
application.properties
Obviously the URL above isn't nice, which we can change using the zk.homepage
property inside application.properties (ideal for applications with a single entry point SPAs).
zk.homepage=zul/minimal
That's all you need to internally map from your application root —"http://localhost:8080/" — to the home page — "http://localhost:8080/zkau/web/zul/minimal.zul".
(If you need multiple entry points, simply create Spring MVC controller as demonstrated in another more complete demo. See: DemoApplication.java and application.properties).
For the interested folks, here's what happens internally:
The optional configuration class ZkHomepageConfiguration.java simply maps the request path "/" to the configured view name "zul/minimal".
The provided zul-view-resolver (enabled by default) then prepends the update-uri "/zkau" and the class-web-resource path "/web". Finally adds the suffix ".zul" to locate the resource. All this conveniently happens in ZkWebMvcConfiguration.java (part of zkspringboot-autoconfig), and if you need something else, you can just disable it ( zk.zul-view-resolver-enabled=false
) and implement your own ViewResolver.
Other ZK Config Files
Due to the lack of a WEB-INF folder, additional zk configuration files (such as zk.xml, lang-addon.xml, zk-label.properties) are placed into "src/main/resources/application/metainfo(/zk)".
Again, this is nothing new and just relies on the built-in mechanism traditionally used by ZK's own and addon jars.
Staying Independent
For maximum flexibility building/running, the Spring Boot jar application doesn't require an IDE and works from command line:
xxxxxxxxxx
../mvnw clean package
java -jar target/zkspringboot-minimal-jar-0.0.1-SNAPSHOT.jar
Naturally, IDEs supporting maven or gradle are able to import and run the application directly (no additional plugins needed, not even a Spring(-Boot)-Plugin). Often, it suffices to right click the main class MinimalApplication.java and choose something like "run/debug as ... Java Application".
Here a typical run configuration (just like any HelloWorld application):
Just by specifying a "Main class," Spring Boot will be able to do its job and start the embedded Tomcat server and application context including ZK's and your own application configuration.
Becoming Efficient
While the trivial minimal example has reasonably short startup times, those won't remain constant as your project gets larger and more complex. That's why it's vital to be able to change and reload resources without having to restart the application for each tiny change. Again, this might have never affected you from the ZK side as long as you had your zul and resource files in the web-content-folder. Now your resources are on the classpath, which behaves slightly different. By default, various resource caches prevent dynamic reloading of zul pages and other resources. Luckily, those can be disabled during development either inside a zk.xml or via Spring java config.
As a template, you can use the DevelopmentConfig.java (part of zkspringboot-demo-jar), which disables all kinds of caches. The configuration is only activated via the Spring configuration profile "dev." You can activate it in your run config using the VM option -Dspring.profiles.active=dev
(there are multiple ways, please refer to the Spring documentation on profiles).
x
"dev") (
public class DevelopmentConfig {
private static Logger logger = LoggerFactory.getLogger(DevelopmentConfig.class);
public void initDevelopmentProperties() throws Exception {
logger.info("**************************************************************");
logger.info("**** ZK-Springboot-Demo: development configuration active ****");
logger.info("**************************************************************");
//disable various caches to avoid server restarts
Library.setProperty("org.zkoss.zk.ZUML.cache", "false");
Library.setProperty("org.zkoss.zk.WPD.cache", "false");
Library.setProperty("org.zkoss.zk.WCS.cache", "false");
Library.setProperty("org.zkoss.web.classWebResource.cache", "false");
Library.setProperty("org.zkoss.util.label.cache", "false");
// enable non minified js
WebApps.getCurrent().getConfiguration().setDebugJS(true);
// enable for debugging MVVM commands and binding (very verbose)
Library.setProperty("org.zkoss.bind.DebuggerFactory.enable", "false");
}
}
This development configuration allows changing .zul files and other resources without having to restart the server when running in debug mode.
There's a tiny catch: You might be used to simply saving a file and then reload the browser. Since files are on the classpath, they need to be "built" by the IDE in order to be reloaded in the running application. Being just resource files building involves copying them into the runtime classpath folder. In IntelliJ, this means you have to "recompile" the file (CTRL-SHIFT-F9 on Windows), or in Eclipse, enable "auto-build" and save the file. (Maybe there's also a general function I missed in IntelliJ to auto-rebuild resources on auto-save).
Once that's done, refresh the browser once (make sure it doesn't cache as well, you might have to hard reload (CTRL-F5) to get rid of long term cached resources once).
If someone knows a pure command-line + any-texteditor variant of this, let me know in the comments.
Conclusion
Spring Boot and ZK are both nontrivial frameworks, each having their own conventions and built-in mechanisms. Integration is possible but not always without friction and requires insight into both frameworks.
I hope the "ZK - Spring Boot" module helps during this process and that the explanations above clarify and demystify common questions and misunderstandings. If I missed something critical, please let me know in the comments.
Opinions expressed by DZone contributors are their own.
Comments