问题: 目前正在看jfinal,考虑使用webjars来管理静态资源,发现没有现成的实现方式,自己就抛砖引玉
解决方案:
需要添加如下依赖
<dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.4.1</version> </dependency> <dependency> <groupId>org.webjars.npm</groupId> <artifactId>bootstrap</artifactId> <version>4.4.1</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.9</version> </dependency>
工具类:
package com.demo.common;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.apache.commons.io.IOUtils;
public class WebjarsUtil {
public static void main(String[] args) throws Exception {
/* 资源文件路径,不能以'/'字符字符开头 */
String resourcePath = "META-INF/resources/webjars/bootstrap/4.4.1/dist/css/bootstrap.min.css";
/* 获取ClassPath下的所有jar路径 */
String[] cps = System.getProperty("java.class.path").split(";");
for (String cp : cps) {
if (!cp.endsWith(".jar")) {
continue;
}
if (cp.indexOf("bootstrap")<0) {
continue;
}
InputStream in = loadResourceFromJarFile(cp, resourcePath);
if (in != null) {
IOUtils.copy(in, new FileOutputStream(new File("aaa.txt")));
System.err.println(IOUtils.toString(in,Charset.forName("UTF-8")));
in.close();
}
}
/* 读取本地Jar文件 */
for (String cp : cps) {
if (!cp.endsWith(".jar")) {
continue;
}
InputStream in = loadResourceFromJarURL(cp, resourcePath);
if (in != null) {
//System.err.println(IOUtils.toString(in,Charset.forName("UTF-8")));
in.close();
}
}
/* 读取网络Jar文件 */
// InputStream in = loadResourceFromJarURL(
// "http://localhost:8080/SpringStruts2Integration/struts2-spring-plugin-2.3.4.1.jar", resourcePath);
// if (in != null) {
// System.err.println(IOUtils.toString(in));
// in.close();
// }
}
/**
* 读取Jar文件中的资源
*
* @param jarPath 本地jar文件路径
* @param resPath 资源文件所在jar中的路径(不能以'/'字符开头)
* @return 如果资源加载失败,返回null
*/
public static InputStream loadResourceFromJarFile(String jarPath, String resPath) {
if (!jarPath.endsWith(".jar")) {
return null;
}
try {
JarFile jarFile = new JarFile(jarPath);
JarEntry jarEntry = jarFile.getJarEntry(resPath);
if (jarEntry == null) {
return null;
}
System.out.println(jarPath);
return jarFile.getInputStream(jarEntry);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
/**
* 读取Jar文件中的资源
*
* @param jarUrl 本地jar文件或网络上(ttp://host:port/subpath/jarfile.jar)jar文件路径
* @param resPath 资源文件所在jar中的路径(不能以'/'字符开头)
* @return 如果资源加载失败,返回null
*/
public static InputStream loadResourceFromJarURL(String jarUrl, String resPath) {
if (!jarUrl.endsWith(".jar")) {
return null;
}
URL url = null;
if (jarUrl.startsWith("http://")) {
try {
url = new URL("jar:" + jarUrl + "!/");
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
}
} else {
try {
url = new URL("jar:file:/" + jarUrl + "!/");
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
}
}
try {
JarURLConnection jarURLConnection;
jarURLConnection = (JarURLConnection) url.openConnection();
JarFile jarFile = jarURLConnection.getJarFile();
JarEntry jarEntry = jarFile.getJarEntry(resPath);
if (jarEntry == null) {
return null;
}
return jarFile.getInputStream(jarEntry);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
} Handler实现类
package com.demo.common;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import com.jfinal.handler.Handler;
public class MyHandler extends Handler{
@Override
public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) {
if (target.contains("/webjars/")) {
String resourcePath = StringUtils.substringAfter(target, "/webjars/");
resourcePath = "META-INF/resources/webjars/"+ resourcePath;
/* 获取ClassPath下的所有jar路径 */
String[] cps = System.getProperty("java.class.path").split(";");
boolean findFlag = false;
for (String cp : cps) {
if (!cp.endsWith(".jar")) {
continue;
}
try(
InputStream in = WebjarsUtil.loadResourceFromJarFile(cp, resourcePath);
){
if(in!=null) {
IOUtils.copy(in, response.getOutputStream());
findFlag = true;
break;
}
} catch (IOException e) {
System.out.println("cant get static resource : " + resourcePath + " from jar");
}
}
if(!findFlag) {
//TODO 没有找到静态资源
response.setStatus(404);
}
isHandled[0] = true;
}else {
this.next.handle(target, request, response, isHandled);
}
}
}启动类:
package com.demo.common;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import com.demo.blog.BlogController;
import com.demo.common.model._MappingKit;
import com.demo.index.IndexController;
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.kit.PathKit;
import com.jfinal.kit.Prop;
import com.jfinal.kit.PropKit;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.jfinal.plugin.druid.DruidPlugin;
import com.jfinal.server.undertow.UndertowServer;
import com.jfinal.template.Engine;
/**
* 本 demo 仅表达最为粗浅的 jfinal 用法,更为有价值的实用的企业级用法
* 详见 JFinal 俱乐部: http://jfinal.com/club
*
* API 引导式配置
*/
public class DemoConfig extends JFinalConfig {
static Prop p;
/**
* 启动入口,运行此 main 方法可以启动项目,此 main 方法可以放置在任意的 Class 类定义中,不一定要放于此
* @throws IOException
* @throws URISyntaxException
*/
public static void main(String[] args) throws IOException, URISyntaxException {
String path = PathKit.class.getResource("/").toURI().getPath();
String file = new File(path).getParentFile().getParentFile().getCanonicalPath();
System.out.println(file);
path = getClassLoader().getResource("").toURI().getPath();
file = new File(path).getAbsolutePath();
System.out.println(file);
UndertowServer.start(DemoConfig.class);
}
private static ClassLoader getClassLoader() {
ClassLoader ret = Thread.currentThread().getContextClassLoader();
return ret != null ? ret : PathKit.class.getClassLoader();
}
/**
* PropKit.useFirstFound(...) 使用参数中从左到右最先被找到的配置文件
* 从左到右依次去找配置,找到则立即加载并立即返回,后续配置将被忽略
*/
static void loadConfig() {
if (p == null) {
p = PropKit.useFirstFound("demo-config-pro.txt", "demo-config-dev.txt");
}
}
/**
* 配置常量
*/
public void configConstant(Constants me) {
loadConfig();
me.setDevMode(p.getBoolean("devMode", false));
/**
* 支持 Controller、Interceptor、Validator 之中使用 @Inject 注入业务层,并且自动实现 AOP
* 注入动作支持任意深度并自动处理循环注入
*/
me.setInjectDependency(true);
// 配置对超类中的属性进行注入
me.setInjectSuperClass(true);
}
/**
* 配置路由
*/
public void configRoute(Routes me) {
me.add("/", IndexController.class, "/index"); // 第三个参数为该Controller的视图存放路径
me.add("/blog", BlogController.class); // 第三个参数省略时默认与第一个参数值相同,在此即为 "/blog"
}
public void configEngine(Engine me) {
me.addSharedFunction("/common/_layout.html");
me.addSharedFunction("/common/_paginate.html");
}
/**
* 配置插件
*/
public void configPlugin(Plugins me) {
// 配置 druid 数据库连接池插件
DruidPlugin druidPlugin = new DruidPlugin(p.get("jdbcUrl"), p.get("user"), p.get("password").trim());
me.add(druidPlugin);
// 配置ActiveRecord插件
ActiveRecordPlugin arp = new ActiveRecordPlugin(druidPlugin);
// 所有映射在 MappingKit 中自动化搞定
_MappingKit.mapping(arp);
me.add(arp);
}
public static DruidPlugin createDruidPlugin() {
loadConfig();
return new DruidPlugin(p.get("jdbcUrl"), p.get("user"), p.get("password").trim());
}
/**
* 配置全局拦截器
*/
public void configInterceptor(Interceptors me) {
}
/**
* 配置处理器
*/
public void configHandler(Handlers me) {
me.add(new MyHandler());
}
}layouthtml文件
#define layout() <!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xml:lang="zh-CN" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8" /> <link href="/assets/css/manage.css" media="screen" rel="stylesheet" type="text/css" /> <script src="/assets/js/jquery-1.4.4.min.js" type="text/javascript" ></script> <link href="/webjars/bootstrap/4.4.1/dist/css/bootstrap.min.css" media="screen" rel="stylesheet" type="text/css"/> <link href="/webjars/bootstrap/4.4.1/dist/js/bootstrap.min.js" media="screen" rel="stylesheet" type="text/css"/> <link href="/webjars/bootstrap/4.4.1/dist/css/bootstrap.min.js" media="screen" rel="stylesheet" type="text/css"/> </head> <body> <div> <div> <div> <a href="http://www.jfinal.com" target="_blank">JFinal web framework</a> </div> <div id="nav"> <ul> <li><a href="/"><b>首页</b></a></li> <li><a href="/blog"><b>Blog管理</b></a></li> </ul> </div> </div> <div> #@main() </div> </div> </body> </html> #end
效果

思路清晰,代码简洁,谢谢你的分享