background
Recently, we started to optimize the company's framework and the practical application of the project. The original scheme was that the front and back ends of springboot+html were separately deployed, and the back-end personnel worked part-time on the front-end development, and the subsequent product line business was optimized. The part for enterprise use was designed and developed by the mobile website personnel, and the back-end was responsible for the internal configuration and background management, With more and more projects iterating and using the framework, it becomes very troublesome to upgrade the framework. The back-end part can iterate the version through maven private server, and the background management page needs to copy each project. Therefore, it is decided to integrate the framework and integrate and publish the back-end management page and the framework back-end code.
Structural design
- Framework packaging background management related standard resources and pages (framework public folder)
- The project uses the framework to develop the specific business configuration management page (project static folder)
- When a personalized framework page is required for a project, a resource file with the same directory and name as the framework is created in the project static folder to overwrite it. When accessing, the priority is higher than the framework directory
SpringBoot static resource access
Custom access path
Customize WebConfig to implement WebMvcConfigurer and override addResourceHandlers method
@Configuration public class WebConfig implements WebMvcConfigurer { @Value("${system.projectName}") private String projectName; /** * Add a static resource file, and the external can directly access the address * * @param registry */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { //The first method sets the access path prefix, and the second method sets the resource path registry.addResourceHandler("/" + projectName + "/**").addResourceLocations("classpath:/static/","classpath:/public/","file:static/"); } }
Icon and font folder access failure
When the static file is copied to the static/public/resource folder for access, the icon and font files will be filtered and damaged. It needs to be set in the pom file
<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>
Customize welcome page
After setting the custom access path to the static directory and replacing the original / * * path, the index page under the directory cannot be found. An interceptor needs to be established to judge manually. The effect is access http://localhost:port/projectName Will automatically jump to http://localhost:port/projectName/index.html
@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; } }
Custom page icon
After setting the custom access path to the static directory and replacing the original / * * path, the favcion.com under the directory cannot be found ICO icon, you need to refer to unified js settings on the page. At the same time, you need to turn off the default icon in the configuration file to replace the small leaves of 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>');
Project access framework static resources
Framework static resource file acquisition
When the project starts, because it is a jar package that references the framework, we need to find the specified jar package first, then unzip the jar package, find the corresponding directory, and copy the resources to the place we need for easy access
Scan jar package
public static void copyFrameStaticFile() { String packageName = "com.haopan.frame"; // Get the name of the package and replace it String packageDirName = packageName.replace('.', '/'); // Define a collection of enumerations and loop to handle things in this directory Enumeration<URL> dirs; try { dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName); // Loop iteration while (dirs.hasMoreElements()) { // Get next element URL url = dirs.nextElement(); // Get the name of the agreement String protocol = url.getProtocol(); if ("jar".equals(protocol)) { // If it is a jar package file // Define a JarFile JarFile jar; try { // Get 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("Frame static file copy completed!"); } catch (IOException e) { e.printStackTrace(); } } } } catch (IOException e) { e.printStackTrace(); } }
Unzip the jar package
Traverse the JarEntry object in JarFile to determine whether it is file or directory classification processing
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("decompression"+jf.getName()+"error---"+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(); } } } }
Copy directory to specified location
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()); //Call the method to copy the file //System.out.println("original path [" + f.getPath() + "] copied path [" + f2+"/"+f.getName() + "]); }else if(f.isDirectory()){ copyFileFolder(f.getPath(),f2+"/"+f.getName()); //Call the method to copy the folder //System.out.println("original path [" + f.getPath() + "] copied path [" + f2+"/"+f.getName() + "]); } } } /** * Copy folder * @throws IOException */ public static void copyFileFolder(String sourceFolderPath,String targetFolderPath) throws IOException { //create folder File file=new File(targetFolderPath); if(!file.exists()){ file.mkdirs(); } copy(sourceFolderPath,targetFolderPath); } /** * Copy file * @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("failed to delete file:" + fileName + "does not exist!"); return false; }else { if (file.isFile()) return deleteFile(fileName); else return deleteDirectory(fileName); } } /** * Delete a single file * * @param fileName: The file name of the file to delete * @return If a single file is deleted successfully, return true; otherwise, return false */ public static boolean deleteFile(String fileName) { File file =new File(fileName); // If the file corresponding to the file path exists and is a file, it will be deleted directly if (file.exists() && file.isFile()) { if (file.delete()) { //System.out.println("delete single file" + fileName + "success!"); return true; }else { //System.out.println("delete single file" + fileName + "failed!"); return false; } }else { //System.out.println("failed to delete a single file:" + fileName + "does not exist!"); return false; } } /** * Delete directory and files under it * * @param dir: The file path of the directory to delete * @return If the directory is deleted successfully, return true; otherwise, return false */ public static boolean deleteDirectory(String dir) { // If dir does not end with a file separator, the file separator is automatically added if (!dir.endsWith(File.separator)) dir = dir + File.separator; File dirFile =new File(dir); // If the file corresponding to dir does not exist or is not a directory, exit if ((!dirFile.exists()) || (!dirFile.isDirectory())) { System.out.println("Failed to delete directory:" + dir +"non-existent!"); return false; } boolean flag =true; // Delete all files in the folder, including subdirectories File[] files = dirFile.listFiles(); for (int i =0; i < files.length; i++) { // Delete sub file if (files[i].isFile()) { flag = deleteFile(files[i].getAbsolutePath()); if (!flag) break; } // delete a sub dir else if (files[i].isDirectory()) { flag = deleteDirectory(files[i].getAbsolutePath()); if (!flag) break; } } if (!flag) { //System.out.println("failed to delete directory!"); return false; } // remove the current directory if (dirFile.delete()) { //System.out.println("delete directory" + dir + "succeeded!"); return true; }else { return false; } } }
External static resource access and priority setting
Set the static locations configuration item in the yml file. Multiple items are used and separated. At the same time, the order is specified as the access priority
spring: resources: static-locations: classpath:static/,classpath:public/,file:static/
The final directory structure diagram is as follows. The framework part is automatically decompressed and copied when the project is started, and the project part is developed by specific projects. The project part can also easily reconstruct the functions of the framework part, such as login page and main page modification. This method supports two packaging methods: jar package and war package