JFinal: a Java web and ORM framework with no XML and no third-party dependencies
JAVA WEB + ORM Framework
At a glance
- What is it?
- JFinal is a Java MVC plus ORM framework whose README promises fast development, a small footprint and zero XML configuration. Here is how its Controller, Service, Model and Validator layers fit together, how to install it from Maven, and where the documentation stops being useful.
- Who is it for?
- JFinal is worth a look if you are building a Java web application, want an ActiveRecord-style Model plus a Db and Record escape hatch, and are willing to work from the README examples rather than a full setup guide. It is a poor fit if you need documented configuration keys, a formal migration path between versions, or a framework with a large English-language tutorial corpus, since the README does not document any of those.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 57 days ago.
- What is it written in?
- Mainly Java, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.
Star history
- jfinal/jfinal3,269 ★ · -1
| Repository | 2026-09-24 | 2026-11-14 |
|---|---|---|
| jfinal/jfinal | 3.3k | 3.3k |
-1 Stars in the last 51 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The problem JFinal is aimed at: Java web apps without XML ceremony
Most Java web stacks ask you to declare things before you can write them. Spring-era projects carried web.xml, applicationContext.xml and an annotation layer on top, and the configuration surface often grew faster than the business logic. JFinal takes the opposite position. Its README states the core design goals as fast development, little code, easy learning, lightweight and easy to extend, and it lists zero configuration and no XML as a consequence of following the COC (convention over configuration) principle.
The intended audience is a Java developer building a server-rendered or JSON web application who does not want a container-managed configuration file. The README's own framing is efficiency: it claims the framework gives Java the development speed associated with Ruby and Python. That is marketing language rather than a measured claim, and no benchmark is published in the README, so treat it as a stated goal rather than a verified number.
Two concrete facts separate JFinal from a generic MVC framework. First, the README says it ships as a single 832 KB artifact with no third-party dependencies, which matters if you are trying to keep a dependency tree shallow or are deploying into a restricted environment. Second, it introduces a Db plus Record mode alongside ActiveRecord, so you are not forced into one data-access style for the whole codebase.
Controller, Service, Model, Validator: how a JFinal request flows
The README's Blog example is the clearest description of the architecture, and it is worth reading as a data-flow diagram rather than as sample code.
A request enters a Controller. The BlogController extends Controller and carries a class-level @Before(BlogInterceptor.class) annotation, so the interceptor runs before the action. Inside the action, the controller pulls input with getInt(), loads a Model or a page of Models through an injected BlogService, pushes values into the view with set(), and finishes with render("blog.html") or redirect("/blog"). The README notes that rendering supports Enjoy, JSP, JSON and custom view renderers, so the view layer is not locked to one template engine.
The Service layer is where the README puts business logic and SQL. BlogService holds a private Blog dao = new Blog().dao() and exposes methods such as paginate(int, int), findById(int) and deleteById(int). Pagination is expressed as two SQL fragments: a select clause and a from clause, which the framework combines. That is a design choice worth noticing. Because the SQL lives in Java strings, you get compile-time safety for the Java around it but not for the SQL itself.
The Model is deliberately empty. The README describes Blog as extending Model<Blog> with no XML, no annotation and no attribute. Field mapping is implied by convention rather than declared, which is the COC principle in its purest form and also the part most likely to surprise someone arriving from a framework where every column is mapped explicitly.
Validation is handled by a Validator subclass. BlogValidator calls validateRequiredString("blog.title", "titleMsg", "...") and handleError() calls controller.keepModel(Blog.class), which repopulates the form after a failed submission. The README argues this API-guided style is easier to check than XML validation. Interceptors implement Interceptor and wrap the action with inv.invoke(), as BlogInterceptor does with its before and after print statements.
The README also mentions automatic reloading of modified Java files during development, which removes the restart step from the edit loop. It does not explain the mechanism or its limits.
Installing JFinal from Maven and writing a first controller
The README gives the Maven coordinates directly and nothing else about installation. There is no quickstart section, no directory layout, no note on which servlet container or embedded server to use, and no build command. What follows is assembled from that coordinate plus the Blog example, so treat the project structure as yours to choose.
Add the dependency to your pom.xml. The version shown is the one the README publishes.
<dependency>
<groupId>com.jfinal</groupId>
<artifactId>jfinal</artifactId>
<version>5.3.0</version>
</dependency>After Maven resolves it, you should see a single jfinal artifact on the classpath with no transitive dependencies, matching the README's claim that the framework has none.
A minimal Model is an empty subclass. This is the whole file.
public class Blog extends Model<Blog> {
}A minimal Controller renders a view and reads a path or form value. The getInt() call takes a default, so a missing parameter does not throw.
public class BlogController extends Controller {
public void index() {
set("blogPage", new Blog().dao().paginate(getInt(0, 1), 10,
"select *", "from blog order by id asc"));
render("blog.html");
}
}What you should see: a request to the controller's index action renders blog.html with a blogPage variable holding one page of ten rows. The README does not document how to register routes, which template engine resolves "blog.html", or what the default port is, so those three gaps are the first things you will have to resolve from the project's website rather than the README.
Where JFinal gets in your way
The documentation is the first limitation. The README is a feature list plus one worked example. It does not document configuration keys, route registration, plugin registration, database connection setup, or how to run the thing. The README also points readers to the official site at jfinal.com for more support, which effectively means the repository is not self-contained for a new user.
The second limitation is the SQL-in-Java-strings approach the Service example demonstrates. Passing "select *" and "from blog order by id asc" as separate arguments is convenient for pagination, but nothing in the README describes parameter binding, injection protection or how the fragments are validated. If your team's standard is that all SQL must be reviewable outside Java source, JFinal's Service layer as documented will not satisfy it.
The third is the empty Model. Because Blog has no declared fields, a column rename or a table change produces a runtime failure rather than a compile error. The README presents this as a feature, and for a small schema it is. For a schema with dozens of tables and a team that rotates, the absence of an explicit mapping is a real cost.
The fourth is the reloading feature. Automatic reloading of modified Java files is listed as a development convenience, and it is exactly the kind of mechanism that behaves differently under a debugger, under a build tool, or inside a container. The README does not describe its scope, so do not assume it works everywhere you develop.
Finally, JFinal is the wrong tool if you need a documented upgrade path. The repository has no published GitHub releases, and the README does not discuss version migration. The repository does contain a CHANGELOG.md, which is where you would look, but the README itself gives you nothing to plan a version bump against.
JFinal versus Spring Boot and the full-stack Java frameworks
The honest comparison is with Spring Boot, because that is what most Java teams reach for by default. The difference is not features, it is where configuration lives and how much of it you write.
Spring Boot starts from auto-configuration driven by starter dependencies and a properties file, and it expects you to override defaults explicitly when you need to. JFinal starts from convention and expects you to write almost no configuration at all; the README's Blog example has no configuration file anywhere in it. Spring Boot brings a large dependency tree by design, because each starter pulls in what it needs. JFinal's README states the framework is 832 KB with no third-party dependencies, which is a fundamentally different packaging decision.
On data access, Spring's ecosystem centers on JPA or MyBatis with explicit entity mappings and mapper files or annotations. JFinal offers ActiveRecord with an empty Model, plus the Db and Record mode for cases where a typed model is overkill. If your team already knows JPA and has mapper conventions, JFinal's approach will feel like losing a layer of structure.
On views, Spring Boot leans on Thymeleaf or a front-end build. JFinal ships its own Enjoy template engine, described in the README as learnable to 90 percent in ten minutes, and also supports FreeMarker and JSP. That is more view options than most teams need, and the README does not compare them.
The practical difference: if you want a framework that tells you where everything goes and has an answer for every configuration question, Spring Boot is the safer default. If you want a small artifact, no XML, and you are comfortable reading source examples instead of documentation, JFinal's trade-off is defensible.
Licence, maintenance and what a version bump costs you
JFinal is licensed under Apache-2.0. That is a permissive licence, which in practice means you can use it in closed-source and commercial applications, and it includes a patent grant. This is a description of the licence identifier in the repository, not legal advice; if your organisation has a licence review process, run the Apache-2.0 text through it rather than taking this paragraph as clearance.
The repository is not archived, and its last push was on 2026-09-18, which is recent. The README does not describe a release cadence, and the repository has no published releases, so the only artefact you can use to judge what changed between versions is CHANGELOG.md at the repository root.
Upgrade cost is the part the material cannot answer. There is no migration guide in the README, no compatibility statement, and no deprecation policy. The Maven coordinate in the README pins 5.3.0, which tells you the version the documentation was written against but nothing about how a jump from an earlier 5.x would behave. If you adopt JFinal, budget for reading CHANGELOG.md before every bump and for testing the reloading behaviour after each one, since that feature is the most likely to be sensitive to how you run the application.
Editorial conclusion
JFinal is worth a look if you are building a Java web application, want an ActiveRecord-style Model plus a Db and Record escape hatch, and are willing to work from the README examples rather than a full setup guide. It is a poor fit if you need documented configuration keys, a formal migration path between versions, or a framework with a large English-language tutorial corpus, since the README does not document any of those. Before committing, verify two things yourself: that your build resolves com.jfinal:jfinal 5.3.0 from Maven Central, and that your servlet container or embedded server is one the project's own examples actually target. The repository's CHANGELOG.md is the only place to check what changed between releases, because the repository has no published releases.
Frequently asked questions
How do I install JFinal in a Maven project?
Add the dependency com.jfinal:jfinal with version 5.3.0 to your pom.xml, as the README's Maven coordinate section shows. The README does not give any other installation method or build step.
Does JFinal require XML configuration?
No. The README states that JFinal follows the COC principle and supports zero configuration with no XML, and the Blog example it provides contains no configuration file.
What is the difference between JFinal's Model and Db plus Record mode?
The README lists both as separate features: ActiveRecord, which the Blog example uses through an empty Model subclass, and the Db plus Record mode, which it describes as flexible and convenient. It does not explain when to choose one over the other.
Which template engines can JFinal render?
The README lists Enjoy, FreeMarker and JSP under multi-view support, and the Controller example notes that Enjoy, JSP, JSON and custom view renderers are all possible. Enjoy is the engine the project itself describes as its own.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/jfinal-jfinal)Star history chart for your README
[](https://hysenlabs.com/projects/jfinal-jfinal)Was this useful?