共计 7914 个字符,预计需要花费 20 分钟才能阅读完成。
背景
最近在着手公司框架优化及我的项目理论利用,原先计划是 springboot+html 前后端拆散独自部署,后端人员兼职前端开发,后续产品线业务进行优化,面向企业应用局部由挪动网站人员负责设计开发,外部配置后盾治理还是由后端负责,随着框架不停迭代与应用的我的项目越来越多,我的项目降级框架变得非常麻烦,后端局部能够通过 maven 私服进行版本迭代,后盾治理页面降级则须要进行各个我的项目拷贝,所以决定对框架进行整合,将后盾治理页面与框架后端代码进行整合公布。
结构设计
- 框架打包后盾治理相干规范资源及页面(框架 public 文件夹)
- 我的项目应用框架,开发具体业务配置管理页面(我的项目 static 文件夹)
- 我的项目须要个性化框架页面时,在我的项目 static 文件夹建设与框架同目录同名称资源文件进行笼罩,拜访时优先级高于框架目录
SpringBoot 动态资源拜访
自定义拜访门路
自定义 WebConfig 实现 WebMvcConfigurer,重写 addResourceHandlers 办法
@Configuration
public class WebConfig implements WebMvcConfigurer {@Value("${system.projectName}")
private String projectName;
/**
* 增加动态资源文件,内部能够间接拜访地址
*
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 第一个办法设置拜访门路前缀,第二个办法设置资源门路
registry.addResourceHandler("/" + projectName + "/**").addResourceLocations("classpath:/static/","classpath:/public/","file:static/");
}
}
图标与字体文件夹拜访失败问题
将动态文件拷贝到 static/public/resource 文件夹下拜访时,图标与字体文件会进行过滤导致损坏,须要在 pom 文件中进行设置
<build>
<resources>
<resource>
<filtering>true</filtering>
<directory>src/main/resources</directory>
<excludes>
<exclude>**/*.woff</exclude>
<exclude>**/*.ttf</exclude>
<exclude>**/*.ico</exclude>
</excludes>
</resource>
<resource>
<filtering>false</filtering>
<directory>src/main/resources</directory>
<includes>
<include>**/*.woff</include>
<include>**/*.ttf</include>
<include>**/*.ico</include>
</includes>
</resource>
</resources>
</build>
自定义欢送页面
在对动态内目录设置自定义拜访门路替换原有的 /** 后,无奈找到目录下的 index 页面,须要建设拦截器手动进行判断,成果为拜访 http://localhost:port/project… 会主动跳转到 http://localhost:port/project…
@Component
public class PageRedirectInterceptor implements HandlerInterceptor {@Value("${system.projectName}")
private String projectName;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String requestURL = request.getRequestURL().toString();
String scheme = request.getScheme();
String servaerName = request.getServerName();
int port = request.getServerPort();
String rootPageURL = scheme + ":" + "//" + servaerName + ":" + port + "/" + projectName;
if (requestURL.equals(rootPageURL)) {response.sendRedirect(request.getContextPath() + "/"+projectName + "/index.html");
return false;
}
return true;
}
}
自定义页面图标
在对动态内目录设置自定义拜访门路替换原有的 /** 后,无奈找到目录下的 favcion.ico 图标,须要在页面援用对立 js 对立设置,同时须要在配置文件中敞开默认图标,替换 spring 的小叶子
spring:
mvc:
favicon:
enabled: false
function GetRootPath() {
var loc = window.location,
host = loc.hostname,
protocol = loc.protocol,
port = loc.port ? (':' + loc.port) : '';
var path = location.pathname;
if (path.indexOf('/') === 0) {path = path.substring(1);
}
var mypath = '/' + path.split('/')[0];
path = (mypath != undefined ? mypath : ('/' + loc.pathname.split('/')[1])) + '/';
var rootPath = protocol + '//' + host + port + path;
return rootPath;
}
var iconurl = GetRootPath()+"favicon.ico"
document.write('<link rel="shortcut icon"href=' + iconurl + 'rel="external nofollow"rel="external nofollow"></link>');
我的项目拜访框架动态资源
框架动态资源文件获取
我的项目启动时,因为是援用框架的 jar 包,咱们须要先找到指定 jar 包,再将 jar 包进行解压,找到对应目录将资源拷贝到咱们须要的中央便于拜访
扫描 jar 包
public static void copyFrameStaticFile() {
String packageName = "com.haopan.frame";
// 获取包的名字 并进行替换
String packageDirName = packageName.replace('.', '/');
// 定义一个枚举的汇合 并进行循环来解决这个目录下的 things
Enumeration<URL> dirs;
try {dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
// 循环迭代上来
while (dirs.hasMoreElements()) {
// 获取下一个元素
URL url = dirs.nextElement();
// 失去协定的名称
String protocol = url.getProtocol();
if ("jar".equals(protocol)) {
// 如果是 jar 包文件
// 定义一个 JarFile
JarFile jar;
try {
// 获取 jar
jar = ((JarURLConnection) url.openConnection()).getJarFile();
String templateDecompressPath = "tempfiles/decompress/" + CommonUtil.getNewGuid() + "/";
File targetFile = new File(templateDecompressPath);
if (!targetFile.exists()) {targetFile.mkdirs();
}
decompressJarFile(jar, templateDecompressPath);
String frameStaticPath = templateDecompressPath + "public/";
File frameStaticFile = new File(frameStaticPath);
if (frameStaticFile.exists()) {
String copyTargetPath = "static/";
File copyTargetFolder = new File(copyTargetPath);
if (copyTargetFolder.exists()) {FileUtil.deleteDirectory(copyTargetPath);
}
copyTargetFolder.mkdirs();
FileUtil.copyFileFolder(frameStaticPath, copyTargetPath);
}
FileUtil.deleteDirectory(templateDecompressPath);
System.out.println("框架动态文件复制结束!");
} catch (IOException e) {e.printStackTrace();
}
}
}
} catch (IOException e) {e.printStackTrace();
}
}
解压 jar 包
对 JarFile 中的 JarEntry 对象进行遍历,判断是文件还是目录分类解决
public static synchronized void decompressJarFile(JarFile jf,String outputPath){if (!outputPath.endsWith(File.separator)) {outputPath += File.separator;}
File dir = new File(outputPath);
if (!dir.exists()) {dir.mkdirs();
}
try{for (Enumeration<JarEntry> e = jf.entries(); e.hasMoreElements();) {JarEntry je = (JarEntry) e.nextElement();
String outFileName = outputPath + je.getName();
File f = new File(outFileName);
if(je.isDirectory()){if(!f.exists()){f.mkdirs();
}
}else{File pf = f.getParentFile();
if(!pf.exists()){pf.mkdirs();
}
InputStream in = jf.getInputStream(je);
OutputStream out = new BufferedOutputStream(new FileOutputStream(f));
byte[] buffer = new byte[2048];
int nBytes = 0;
while ((nBytes = in.read(buffer)) > 0) {out.write(buffer, 0, nBytes);
}
out.flush();
out.close();
in.close();}
}
}catch(Exception e){System.out.println("解压"+jf.getName()+"出错 ---"+e.getMessage());
}finally{if(jf!=null){
try {jf.close();
File jar = new File(jf.getName());
if(jar.exists()){jar.delete();
}
} catch (IOException e) {e.printStackTrace();
}
}
}
}
拷贝目录到指定地位
public class FileUtil {private static void copy(String f1, String f2) throws IOException {File file1=new File(f1);
/* File file2=new File(f2);*/
File[] flist=file1.listFiles();
for (File f : flist) {if(f.isFile()){copyFile(f.getPath(),f2+"/"+f.getName()); // 调用复制文件的办法
//System.out.println("原门路 ["+f.getPath()+"] 被复制门路 ["+f2+"/"+f.getName()+"]");
}else if(f.isDirectory()){copyFileFolder(f.getPath(),f2+"/"+f.getName()); // 调用复制文件夹的办法
//System.out.println("原门路 ["+f.getPath()+"] 被复制门路 ["+f2+"/"+f.getName()+"]");
}
}
}
/**
* 复制文件夹
* @throws IOException
*/
public static void copyFileFolder(String sourceFolderPath,String targetFolderPath) throws IOException {
// 创立文件夹
File file=new File(targetFolderPath);
if(!file.exists()){file.mkdirs();
}
copy(sourceFolderPath,targetFolderPath);
}
/**
* 复制文件
* @throws IOException
*/
public static void copyFile(String sourceFilePath, String tagretFilePath) throws IOException {
try {InputStream in = new FileInputStream(sourceFilePath);
OutputStream out = new FileOutputStream(tagretFilePath);
byte[] buffer = new byte[2048];
int nBytes = 0;
while ((nBytes = in.read(buffer)) > 0) {out.write(buffer, 0, nBytes);
}
out.flush();
out.close();
in.close();} catch (FileNotFoundException e) {e.printStackTrace();
}
}
public static boolean delete(String fileName) {File file =new File(fileName);
if (!file.exists()) {//System.out.println("删除文件失败:" + fileName +"不存在!");
return false;
}else {if (file.isFile())
return deleteFile(fileName);
else
return deleteDirectory(fileName);
}
}
/**
* 删除单个文件
*
* @param fileName:要删除的文件的文件名
* @return 单个文件删除胜利返回 true,否则返回 false
*/
public static boolean deleteFile(String fileName) {File file =new File(fileName);
// 如果文件门路所对应的文件存在,并且是一个文件,则间接删除
if (file.exists() && file.isFile()) {if (file.delete()) {//System.out.println("删除单个文件" + fileName +"胜利!");
return true;
}else {//System.out.println("删除单个文件" + fileName +"失败!");
return false;
}
}else {//System.out.println("删除单个文件失败:" + fileName +"不存在!");
return false;
}
}
/**
* 删除目录及目录下的文件
*
* @param dir:要删除的目录的文件门路
* @return 目录删除胜利返回 true,否则返回 false
*/
public static boolean deleteDirectory(String dir) {
// 如果 dir 不以文件分隔符结尾,主动增加文件分隔符
if (!dir.endsWith(File.separator))
dir = dir + File.separator;
File dirFile =new File(dir);
// 如果 dir 对应的文件不存在,或者不是一个目录,则退出
if ((!dirFile.exists()) || (!dirFile.isDirectory())) {System.out.println("删除目录失败:" + dir +"不存在!");
return false;
}
boolean flag =true;
// 删除文件夹中的所有文件包含子目录
File[] files = dirFile.listFiles();
for (int i =0; i < files.length; i++) {
// 删除子文件
if (files[i].isFile()) {flag = deleteFile(files[i].getAbsolutePath());
if (!flag)
break;
}
// 删除子目录
else if (files[i].isDirectory()) {flag = deleteDirectory(files[i].getAbsolutePath());
if (!flag)
break;
}
}
if (!flag) {//System.out.println("删除目录失败!");
return false;
}
// 删除当前目录
if (dirFile.delete()) {//System.out.println("删除目录" + dir +"胜利!");
return true;
}else {return false;}
}
}
内部动态资源拜访与优先级设置
设置 yml 文件中的 static-locations 配置项,多个应用,隔开,同时指定程序为拜访的优先级
spring:
resources:
static-locations: classpath:static/,classpath:public/,file:static/
最终目录结构图如下,框架局部齐全是我的项目启动时主动解压拷贝的,我的项目局部则是由具体我的项目进行开发,我的项目局部也能够很不便的进行框架局部性能重构,例如登录页,主页面批改等,本形式反对 jar 包和 war 包两种打包形式