# jfinal-4.9-spring-boot-2.3.0
**Repository Path**: litongjava_admin/jfinal-4.9-spring-boot-2.3.0
## Basic Information
- **Project Name**: jfinal-4.9-spring-boot-2.3.0
- **Description**: jfinal整合spring-boot,采用HotSwap热加载,在eclipse中修改controller,按Ctrl+S直接生效,用于支持spring-boot快速开发
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2021-06-23
- **Last Updated**: 2022-06-21
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
jfinal-undertow整合spring-boot
## 1.1.背景描述
[背景描述]
使用jfinal开发有一个非常大的便利性就是热加载即修改java文件后保存后即可生效,但是spring-boot却不行,能不能让spring-boot向jfinal一样修改java文件后保存后即可生效呢?
[jfinal-undertow]
jfinal-undertow使用波总对undertow容器二开的一个undertow容器,添加了一些新的功能,例如hotswap热加载
hotswap热加载代码实现地址
https://gitee.com/jfinal/jfinal-undertow/tree/master/src/main/java/com/jfinal/server/undertow/hotswap
[思路]
spring-boot默认使用的servlet容器是tomcat,但是spring-boot也支持jetty,undertow,我的思路是让spring-boot使用jfinal-undertow作为sevlet容器,jfinal-undertow具有热加载的功能
## 1.2.创建spring-boot工程
首先创建一个spring-boot工程,工程名jfinal-4.9-spring-boot-2.3.0,使用undertow作为web容器
### 1.2.1.pom.xml配置如下
```
org.springframework.boot
spring-boot-starter-parent
2.3.0.RELEASE
org.springframework.boot
spring-boot-maven-plugin
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-tomcat
org.springframework.boot
spring-boot-starter-undertow
org.projectlombok
lombok
provided
```
### 1.2.2.启动类
```
package com.litongjava.spring.boot.study.jfinal.undertow;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author create by ping-e-lee on 2021年6月18日 上午9:30:32
* @version 1.0
* @desc
*/
@SpringBootApplication
public class BootApplication {
public static void main(String[] args) {
long start = System.currentTimeMillis();
SpringApplication.run(BootApplication.class, args);
long end = System.currentTimeMillis();
System.out.println((end - start) + "ms");
}
}
```
### 1.2.3.controller
编写一个spring的controller
```
package com.litongjava.spring.boot.study.jfinal.undertow.contorller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author create by ping-e-lee on 2021年6月18日 上午9:31:52
* @version 1.0
* @desc
*/
@RestController
@RequestMapping()
public class HelloController {
@Autowired
private ApplicationContext applicationContext;
@RequestMapping
public String hello() {
//拼接成下面的样式返回
StringBuffer stringBuffer = new StringBuffer();
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
stringBuffer.append("@Import({");
for (String beanDefinitionName : beanDefinitionNames) {
if(beanDefinitionName.endsWith("AutoConfiguration")){
stringBuffer.append(beanDefinitionName+".class,\r\n");
}
}
stringBuffer.append("})");
return stringBuffer.toString();
}
@RequestMapping("test")
public String test() {
return "Test12";
}
}
```
### 1.2.4.logback配置文件
添加logback.xml文件,内容如下
```
%d{yyyy-MM-dd HH:mm:ss.SSS} %-6level%logger{0}.%M:%L - %m%n
${LOG_HOME}/project-name-%d{yyyy-MM-dd}.log
120
%d{yyyy-MM-dd HH:mm:ss.SSS} %-6level%logger{0}.%M:%L - %m%n
10MB
```
### 1.2.5.启动spring-boot项目
启动项目
```
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.0.RELEASE)
2021-06-20 16:32:23.147 INFO BootApplication.logStarting:55 - Starting BootApplication on DESKTOP-FAUAFH1 with PID 11692 (E:\dev_workspace\eclipse-jee-2019-12\jfinal-4.9-spring-boot-2.3.0\target\classes started by Administrator in E:\dev_workspace\eclipse-jee-2019-12\jfinal-4.9-spring-boot-2.3.0)
2021-06-20 16:32:23.150 INFO BootApplication.logStartupProfileInfo:651 - No active profile set, falling back to default profiles: default
2021-06-20 16:32:24.209 WARN jsr.handleDeployment:68 - UT026010: Buffer pool was not set on WebSocketDeploymentInfo, the default pool will be used
2021-06-20 16:32:24.231 INFO servlet.log:364 - Initializing Spring embedded WebApplicationContext
2021-06-20 16:32:24.232 INFO ContextLoader.prepareWebApplicationContext:284 - Root WebApplicationContext: initialization completed in 1043 ms
2021-06-20 16:32:24.371 INFO ThreadPoolTaskExecutor.initialize:181 - Initializing ExecutorService 'applicationTaskExecutor'
2021-06-20 16:32:24.480 INFO undertow.start:117 - starting server: Undertow - 2.1.0.Final
2021-06-20 16:32:24.489 INFO xnio.:95 - XNIO version 3.8.0.Final
2021-06-20 16:32:24.499 INFO nio.:59 - XNIO NIO Implementation Version 3.8.0.Final
2021-06-20 16:32:24.555 INFO threads.:52 - JBoss Threads version 3.1.0.Final
2021-06-20 16:32:24.593 INFO UndertowWebServer.start:133 - Undertow started on port(s) 8080 (http)
2021-06-20 16:32:24.600 INFO BootApplication.logStarted:61 - Started BootApplication in 1.719 seconds (JVM running for 2.125)
2043ms
```
访问测试
## 1.3.改造为spring项目
### 1.3.1.spring-boot-starter-undertow依赖
```
org.springframework.boot
spring-boot-dependencies
2.3.0.RELEASE
pom
import
io.undertow
undertow-core
compile
io.undertow
undertow-servlet
compile
jboss-servlet-api_4.0_spec
org.jboss.spec.javax.servlet
jboss-annotations-api_1.2_spec
org.jboss.spec.javax.annotation
io.undertow
undertow-websockets-jsr
compile
jboss-servlet-api_4.0_spec
org.jboss.spec.javax.servlet
jboss-annotations-api_1.2_spec
org.jboss.spec.javax.annotation
jakarta.servlet
jakarta.servlet-api
compile
org.glassfish
jakarta.el
compile
```
### 1.3.2.添加jfinal依赖到spring-boot项目
添加jfinal依赖,jfinal-undertow依赖,和spring-boot-starter-undertow依赖到spring-boot项目
添加后的pom.xml完整依赖如下
```
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-tomcat
com.jfinal
jfinal
4.9.12
com.jfinal
jfinal-undertow
2.5
io.undertow
undertow-core
compile
io.undertow
undertow-servlet
compile
jboss-servlet-api_4.0_spec
org.jboss.spec.javax.servlet
jboss-annotations-api_1.2_spec
org.jboss.spec.javax.annotation
io.undertow
undertow-websockets-jsr
compile
jboss-servlet-api_4.0_spec
org.jboss.spec.javax.servlet
jboss-annotations-api_1.2_spec
org.jboss.spec.javax.annotation
jakarta.servlet
jakarta.servlet-api
compile
org.glassfish
jakarta.el
compile
```
org.projectlombok
lombok
provided
```
### 1.3.3.spring-boot的启动方式
spring-boot的启动方式总体可以分为两种
第一种
使用嵌入式servlet容器启动
第二种
石红外部servlet容器自动,主要涉及到的类有
```
javax.servlet.ServletContainerInitializer.onStartup()
org.springframework.web.SpringServletContainerInitializer.onStartup(webAppInitializerClasses.servletContext)
org.springframework.web.WebApplicationInitializer.onStartup()
org.springframework.boot.web.servlet.support.SpringBootServletInitializer.onStartup()
```
我的思路是使用第二种
先启动jfinal-undertow容器
使用ServletContainerInitializer的特性使用spring-boot
### 1.3.4.SpringMVCHandler
SpringMVCHandler的主要作用是找到spring mvc的controller
笔者通过观察jfinal源码了解到
在UndertowServer.configJFinalFilter()配置JFinalFiler拦截"/"
在JFinalFiler.doFilter中转发到ActionHanlder,ActionHanlder处理请求,但是ActionHanlder不会去查找spring-mvc的sevlet
所以我编写了SpringMVCHandler,SpringMVCHandler先查找JFinal的Action,如果找到JFinal的Action则结束方法,JFinalFiler会继续执行向下查找servlet
```
package com.litongjava.spring.boot.study.jfinal.undertow.config;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.jfinal.core.Action;
import com.jfinal.core.JFinal;
import com.jfinal.handler.Handler;
import lombok.extern.slf4j.Slf4j;
public class SpringMVCHandler extends Handler {
public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) {
// 1.静态文件返回
if (target.indexOf('.') != -1) {
return;
}
String[] urlPara = { null };
Action action = JFinal.me().getAction(target, urlPara);
if (action == null) {
return;
}
// 让actionHanlder执行
next.handle(target, request, response, isHandled);
}
}
```
JFinal配置类
```
package com.litongjava.spring.boot.study.jfinal.undertow.config;
import com.jfinal.config.Constants;
import com.jfinal.config.Handlers;
import com.jfinal.config.Interceptors;
import com.jfinal.config.JFinalConfig;
import com.jfinal.config.Plugins;
import com.jfinal.config.Routes;
import com.jfinal.template.Engine;
/**
* @author create by ping-e-lee on 2021年6月18日 下午12:37:49
* @version 1.0
* @desc
*/
public class AppConfig extends JFinalConfig {
public void configConstant(Constants me) {
}
public void configRoute(Routes me) {
}
public void configEngine(Engine me) {
}
public void configPlugin(Plugins me) {
}
public void configInterceptor(Interceptors me) {
}
public void configHandler(Handlers me) {
me.add(new SpringMVCHandler());
}
}
```
### 1.3.5.BootInitializer
BootInitializer链接到SpringBoot的启动类
```
package com.litongjava.spring.boot.study.jfinal.undertow.config;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import com.litongjava.spring.boot.study.jfinal.undertow.UndertowApplication;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class BootInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
// 传入SpringBoot应用的主程序
// 测试回调
log.info("启动spring-boot");
return builder.sources(BootApplication.class);
}
}
```
### 1.3.6.JFinal启动类
JFinal启动类将BootInitializer添加到undertow-server
```
package com.litongjava.spring.boot.study.jfinal.undertow;
import java.util.HashSet;
import org.springframework.web.SpringServletContainerInitializer;
import com.jfinal.server.undertow.UndertowServer;
import com.litongjava.spring.boot.study.jfinal.undertow.config.AppConfig;
import com.litongjava.spring.boot.study.jfinal.undertow.config.BootInitializer;
import io.undertow.servlet.api.InstanceFactory;
import io.undertow.servlet.api.ServletContainerInitializerInfo;
import io.undertow.servlet.util.ImmediateInstanceFactory;
/**
* @author create by ping-e-lee on 2021年6月18日 下午12:37:29
* @version 1.0
* @desc
*/
public class JFinalApplication {
public static void main(String[] args) {
long start = System.currentTimeMillis();
// 启动
// UndertowServer.start(AppConfig.class, 80, true);
UndertowServer.create(AppConfig.class, "undertow.properties").configWeb(builder -> {
//添加SpringServletContainerInitializer到undertow
SpringServletContainerInitializer initializer = new SpringServletContainerInitializer();
InstanceFactory instanceFactory = new ImmediateInstanceFactory<>(initializer);
HashSet> hashSet = new HashSet>();
hashSet.add(BootInitializer.class);
ServletContainerInitializerInfo sciInfo = new ServletContainerInitializerInfo(SpringServletContainerInitializer.class, instanceFactory,
hashSet);
builder.getDeploymentInfo().addServletContainerInitializers(sciInfo);
}).start();
long end = System.currentTimeMillis();
System.out.println((end - start) + "ms");
}
}
```
### 1.3.7.undertow.properties
```
undertow.properties开启开发模式
undertow.devMode=true
```
### 1.3.8.启动测试
启动JFinalApplication
```
Starting JFinal 4.9.12 -> http://0.0.0.0:80
Info: jfinal-undertow 2.5, undertow 2.1.0.Final, jvm 1.8.0_121
2021-06-20 17:04:50.092 INFO servlet.log:364 - 1 Spring WebApplicationInitializers detected on classpath
2021-06-20 17:04:50.174 INFO BootInitializer.configure:17 - 启动spring-boot
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.0.RELEASE)
2021-06-20 17:04:50.447 INFO BootInitializer.logStarting:55 - Starting BootInitializer on DESKTOP-FAUAFH1 with PID 4716 (E:\dev_workspace\java\java-study\java-ee-sutdy\java-ee-spring-boot-2.3.0-study\ee-spring-boot-2.3.0-jfinal-undertow\target\classes started by Administrator in E:\dev_workspace\java\java-study\java-ee-sutdy\java-ee-spring-boot-2.3.0-study\ee-spring-boot-2.3.0-jfinal-undertow)
2021-06-20 17:04:50.448 INFO BootInitializer.logStartupProfileInfo:651 - No active profile set, falling back to default profiles: default
2021-06-20 17:04:51.049 INFO servlet.log:364 - Initializing Spring embedded WebApplicationContext
2021-06-20 17:04:51.050 INFO ContextLoader.prepareWebApplicationContext:284 - Root WebApplicationContext: initialization completed in 560 ms
2021-06-20 17:04:51.298 INFO ThreadPoolTaskExecutor.initialize:181 - Initializing ExecutorService 'applicationTaskExecutor'
2021-06-20 17:04:51.414 INFO BootInitializer.logStarted:61 - Started BootInitializer in 1.237 seconds (JVM running for 1.809)
2021-06-20 17:04:51.479 INFO undertow.start:117 - starting server: Undertow - 2.1.0.Final
2021-06-20 17:04:51.487 INFO xnio.:95 - XNIO version 3.8.0.Final
2021-06-20 17:04:51.495 INFO nio.:59 - XNIO NIO Implementation Version 3.8.0.Final
2021-06-20 17:04:51.904 INFO threads.:52 - JBoss Threads version 3.1.0.Final
Starting Complete in 2.1 seconds. Welcome To The JFinal World (^_^)
2235ms
```
使用Eclipse修改spring-boot的controller
```
@RequestMapping("test")
public String test() {
return "Test123";
}
```
修改之后立即生效
````Loading changes ......
`Loading changes ......
2021-06-20 17:05:26.430 INFO undertow.stop:252 - stopping server: Undertow - 2.1.0.Final
2021-06-20 17:05:26.447 INFO servlet.log:364 - 1 Spring WebApplicationInitializers detected on classpath
2021-06-20 17:05:26.452 INFO BootInitializer.configure:17 - 启动spring-boot
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.0.RELEASE)
2021-06-20 17:05:26.474 INFO BootInitializer.logStarting:55 - Starting BootInitializer on DESKTOP-FAUAFH1 with PID 4716 (E:\dev_workspace\java\java-study\java-ee-sutdy\java-ee-spring-boot-2.3.0-study\ee-spring-boot-2.3.0-jfinal-undertow\target\classes started by Administrator in E:\dev_workspace\java\java-study\java-ee-sutdy\java-ee-spring-boot-2.3.0-study\ee-spring-boot-2.3.0-jfinal-undertow)
2021-06-20 17:05:26.475 INFO BootInitializer.logStartupProfileInfo:651 - No active profile set, falling back to default profiles: default
2021-06-20 17:05:26.668 INFO servlet.log:364 - Initializing Spring embedded WebApplicationContext
2021-06-20 17:05:26.669 INFO ContextLoader.prepareWebApplicationContext:284 - Root WebApplicationContext: initialization completed in 192 ms
2021-06-20 17:05:26.724 INFO ThreadPoolTaskExecutor.initialize:181 - Initializing ExecutorService 'applicationTaskExecutor'
2021-06-20 17:05:26.768 INFO BootInitializer.logStarted:61 - Started BootInitializer in 0.315 seconds (JVM running for 37.163)
2021-06-20 17:05:26.818 INFO undertow.start:117 - starting server: Undertow - 2.1.0.Final
Loading complete in 0.4 seconds (^_^)
```
访问测试
http://127.0.0.1/test