想要战胜他,必先理解他。这两天系统的学习Maven和跑springboot工程,从以前只是看着复杂到亲手体验一下,亲自实践的才是更可靠的了解。
第一就是首先Maven侵入代码结构,代码一般要按约定搞src/main/java。如果是能严格执行测试的项目用着还行,需求性的项目测试根本跟不上,多增加层级就是增加负担。
第二就是Maven所带来的好处和所需要的环境配置和学习成本不匹配,增加学习成本,好处并没有绝对优势,当然如果架构没解决业务脚本化的问题,那么不同模块之间肯定相互引用很多,这时候是有优势的。所以根源不是Maven有多好、而是没解决业务编码便捷性问题,为了解决这个问题而引入的新问题,还要鼓吹有多好。然后就是Maven编译打包Spring太慢了,每个操作都要一分钟起,给和咖啡带来了充分的时间。
同时经过测试发现Spring启动后太费内存了,什么都不做内存就占用1700兆了,同等启动的JRTWeb占用137兆,相差10倍以上。用多余内存给DolerGet做缓存用不香吗,多用的内存都能内存缓存千万级别的数据了。
然后就是环境太复杂,又要配Maven,又要运行ridis,又要运行nginx配置代理,还没进入开发过程就能浪费两天时间,发布还得再额外学。针对单体机构的软件真的有必要像互联网那样用ridis那些么?杀鸡焉用牛刀,部署的时候也得额外安装ridis(麻烦的要死)。
实现原理上Spring和JRT基本一致,只是面向发布和开发理念不同。两者都是内嵌Web服务器,然后按请求的url执行对应的业务类。不同的是Spring按注解反射初始化容器,按url通过注解从容器取对象执行。JRT按业务脚本目录给每个业务编译jar包,按请求url执行对应jar,理论上应该是JRT执行效率更高,因为不需要解析注解那些,直接按路径得到类全面执行就行了。注解用的好确实是简化程序,但是什么都注解也不一定好,感觉Spring用点过渡注解的意思。综合就是Spring流程定义更全面和规范、用着更复杂更加重量级,一个开发要全面掌控不容易。JRT更注重简化业务和环境,要哪些就做哪些,不引入多余的东西,可以达到一个开发全面掌控的效果。
Springboot编译用了55秒
用cmd启动要1分钟开外
idea启动用了52秒
JRT网站在5秒编译和启动完成,并用谷歌打开指定的页面
springboot网站启动后占用的内存
JRT网站启动后占用的内存
Spring启动定制测试程序。由于这个springboot的前后台分离没做到分而不离,所以需要nginx代码前端后后台来避免跨域,因此启动网站之前需要启动nginx,同时又依赖了ridis,这里一起给启动了。
package xxxxxx;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.scheduling.annotation.EnableAsync;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.Properties;
@ServletComponentScan(basePackages = "com.xxxxxx")
@MapperScan(basePackages = {"xxxxx.**.mapper"})
@SpringBootApplication
@EnableAsync
public class LimpApplication {
public static void main(String[] args) {
//尝试启动相关软件
try {
if (IsOSLinux() == false) {
System.out.println("检查D:\\nginx-1.23.2是否存在,尝试启动nginx");
File niginx = new File("D:\\nginx-1.23.2\\nginx.exe");
if (niginx.exists()) {
System.out.println("存在nginx,尝试启动");
StartExe(niginx.toString(), "D:\\nginx-1.23.2");
}
System.out.println("检查D:\\Redis-x64-3.2.100是否存在,尝试启动ridis");
File ridis = new File("D:\\Redis-x64-3.2.100\\start.bat");
if (ridis.exists()) {
System.out.println("存在ridis,尝试启动");
StartExe(ridis.toString(), "D:\\Redis-x64-3.2.100");
}
}
}
catch (Exception ex)
{
}
//启动前先启动ridis和nginx
SpringApplication.run(LimpApplication.class, args);
}
/**
* 判断OS
*
* @return 得到是否是Linux
*/
public static boolean IsOSLinux() {
Properties prop = System.getProperties();
String os = prop.getProperty("os.name");
if (os != null && os.toLowerCase().indexOf("linux") > -1) {
return true;
} else {
return false;
}
}
/**
* 启动Exe
* @param cmdStr 命令串
* @param runDir 运行路径
*/
private static void StartExe(String cmdStr,String runDir)
{
File directory = new File(runDir);
try
{
System.out.println("启动:"+cmdStr);
// 创建进程并执行命令
Process process = Runtime.getRuntime().exec(cmdStr,null,directory);
}
catch (Exception ex)
{
System.out.println(ex.getMessage());
}
}
}
由于很多开发不知道启动后该打开那个url使用,所以需要在Spring启动后执行一个逻辑,用谷歌打开特定页面
package xxxxxxx;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
/**
* 启动后执行,打开登录页面等
*/
@Component
public class SpringBootApplicationRunner implements ApplicationRunner {
/**
* 启动后执行的逻辑
* @param args
* @throws Exception
*/
@Override
public void run(ApplicationArguments args) throws Exception {
//打开页面辅助器
OpenUrl("http://localhost:8527/imedicallis-hos/imedicallis/webui/test/form/demoHtml.html");
}
/**
* 打印页面
*
* @param url 路径
* @throws Exception
*/
public static void OpenUrl(String url) throws Exception {
//获取操作系统类型
String os = System.getProperty("os.name").toLowerCase();
//根据操作系统类型调用不同的命令
if (os.contains("win")) {
String path = GetChromePath();
if (!path.isEmpty()) {
File file = new File(path);
if (file.exists()) {
//使用Runtime.exec执行命令
Runtime.getRuntime().exec(path + " "+ url);
return;
}
}
//使用Runtime.exec执行命令
Runtime.getRuntime().exec("cmd /c start " + url);
} else if (os.contains("nix") || os.contains("nux") || os.contains("mac")) {
//Linux or Mac
//使用ProcessBuilder创建一个进程
ProcessBuilder pb = new ProcessBuilder("google-chrome", url);
Process p = pb.start();
} else {
System.out.println("Unsupported OS");
}
}
/**
* 得到谷歌程序地址
*
* @return
* @throws Exception
*/
private static String GetChromePath() throws Exception {
String path = GetExeRegedit("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe");
if (path.isEmpty()) {
path = GetExeRegedit("HKEY_LOCAL_MACHINE\\HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe");
}
if (!path.isEmpty()) {
path += "\\chrome.exe";
}
return path;
}
/**
* 得到注册表值
*
* @param path 执行路径
* @return
* @throws Exception
*/
private static String GetExeRegedit(String path) throws Exception {
Process ps = null;
//当路径中有空格时,要把路径打上引号。
ps = Runtime.getRuntime().exec("reg query \"" + path + "\"");
ps.getOutputStream().close();
InputStreamReader i = new InputStreamReader(ps.getInputStream());
String line;
BufferedReader ir = new BufferedReader(i);
String ret = "";
while ((line = ir.readLine()) != null) {
if (line.contains("Path")) {
String[] arr = line.split(" ");
ret = arr[arr.length - 1];
}
}
return ret;
}
}
逻辑都是参照JRT启动来的,JRT的启动引导
package WebLoader;
import JRT.Core.Util.LogUtils;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.*;
import java.net.Socket;
import java.nio.file.Paths;
import java.util.Scanner;
/**
* 网站加载器,参照操作系统引导概念,引导加载网站
*/
public class Main {
/**
* 加载入口
* @param args
*/
public static void main(String[] args) {
//站点名称
String WebName="JRTWeb";
//自动打开的页面
String OpenUrlStr="https://localhost:8081/JRTWeb/sys/form/frmCodeTableManager.aspx";
boolean isWin=true;
System.out.println("本控制台将负责引导启动网站");
try
{
String osName = System.getProperty("os.name");
String StartCmd="startup.bat";
String ShutdownCmd="shutdown.bat";
//判断Windows
if(osName != null && !osName.startsWith("Windows")) {
isWin=false;
StartCmd="startup.sh";
ShutdownCmd="shutdown.sh";
}
File directory = new File("");// 参数为空
String courseFile = directory.getCanonicalPath();
System.out.println(courseFile);
String binPath= Paths.get(courseFile,"WebSrc","bin").toString();
String stopBatPath= Paths.get(courseFile,"WebSrc","bin",ShutdownCmd).toString();
String startBatPath= Paths.get(courseFile,"WebSrc","bin",StartCmd).toString();
if(isWin==true) {
//结束打开页面工具
KillProcess("DevOpenPage.exe");
}
System.out.println("尝试停止站点");
System.out.println("执行脚本:"+stopBatPath);
TryExecCmd(stopBatPath,binPath);
//存在就删除
File jrtOkFile=new File(Paths.get(courseFile,"WebSrc","webapps","JRTWeb","jrt.ok").toString());
//不存在就创建,网站启动成功会删除
if(!jrtOkFile.exists())
{
jrtOkFile.createNewFile();
}
//用线程打开页面
ThreadOpenUrl(jrtOkFile,OpenUrlStr);
System.out.println("尝试启动站点");
System.out.println("执行脚本:"+startBatPath);
TryExecCmd(startBatPath,binPath);
}
catch (Exception ex)
{
System.out.println(ex.getMessage());
}
}
/**
* 打开页面
* @param jrtOkFile 成功文件
* @param OpenUrlStr 打开地址
* @throws Exception
*/
private static void ThreadOpenUrl(File jrtOkFile,String OpenUrlStr) throws Exception
{
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
int tryNum = 0;
//等待网站启动
while (true) {
tryNum++;
//标志文件没了就是网站启动了
if (!jrtOkFile.exists()) {
break;
}
if (tryNum > 200) {
break;
}
Thread.sleep(100);
}
//打开页面辅助器
OpenUrl("https://localhost:8081/JRTWeb/sys/form/frmDevOpenPage.aspx");
//打开起始页面
OpenUrl(OpenUrlStr);
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
});
thread.start();
}
//结束指定名称进程
//processName:进程名
public static void KillProcess(String processName) {
try {
String line;
Process p = Runtime.getRuntime().exec(System.getenv("windir") + "\\system32\\" + "tasklist.exe");
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((line = input.readLine()) != null) {
if (line.contains(processName)) {
String processId = line.split("\\s+")[1];
Runtime.getRuntime().exec("taskkill /F /PID " + processId);
System.out.println("Process " + processName + " has been killed.");
}
}
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 启动Exe
* @param cmdStr 命令串
* @param runDir 运行路径
*/
private static void StartExe(String cmdStr,String runDir)
{
File directory = new File(runDir);
try
{
System.out.println("启动:"+cmdStr);
// 创建进程并执行命令
Process process = Runtime.getRuntime().exec(cmdStr,null,directory);
}
catch (Exception ex)
{
System.out.println(ex.getMessage());
}
}
/**
* 执行cmd
* @param cmdStr 命令串
* @param runDir 运行路径
*/
private static void TryExecCmd(String cmdStr,String runDir)
{
File directory = new File(runDir);
try
{
System.out.println("执行:"+cmdStr);
// 创建进程并执行命令
Process process = Runtime.getRuntime().exec(cmdStr,null,directory);
// 获取命令行程序的输出结果
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
// 等待命令行程序执行完毕
int exitCode=process.waitFor();
// 关闭资源
reader.close();
System.out.println("返回:"+exitCode);
}
catch (Exception ex)
{
System.out.println(ex.getMessage());
}
}
/**
* 打印页面
*
* @param url 路径
* @throws Exception
*/
public static void OpenUrl(String url) throws Exception {
//获取操作系统类型
String os = System.getProperty("os.name").toLowerCase();
//根据操作系统类型调用不同的命令
if (os.contains("win")) {
String path = GetChromePath();
if (!path.isEmpty()) {
File file = new File(path);
if (file.exists()) {
//使用Runtime.exec执行命令
Runtime.getRuntime().exec(path + " "+ url);
return;
}
}
//使用Runtime.exec执行命令
Runtime.getRuntime().exec("cmd /c start " + url);
} else if (os.contains("nix") || os.contains("nux") || os.contains("mac")) {
//Linux or Mac
//使用ProcessBuilder创建一个进程
ProcessBuilder pb = new ProcessBuilder("google-chrome", url);
Process p = pb.start();
} else {
System.out.println("Unsupported OS");
}
}
/**
* 得到谷歌程序地址
*
* @return
* @throws Exception
*/
private static String GetChromePath() throws Exception {
String path = GetExeRegedit("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe");
if (path.isEmpty()) {
path = GetExeRegedit("HKEY_LOCAL_MACHINE\\HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe");
}
if (!path.isEmpty()) {
path += "\\chrome.exe";
}
return path;
}
/**
* 得到注册表值
*
* @param path 执行路径
* @return
* @throws Exception
*/
private static String GetExeRegedit(String path) throws Exception {
Process ps = null;
//当路径中有空格时,要把路径打上引号。
ps = Runtime.getRuntime().exec("reg query \"" + path + "\"");
ps.getOutputStream().close();
InputStreamReader i = new InputStreamReader(ps.getInputStream());
String line;
BufferedReader ir = new BufferedReader(i);
String ret = "";
while ((line = ir.readLine()) != null) {
if (line.contains("Path")) {
String[] arr = line.split(" ");
ret = arr[arr.length - 1];
}
}
return ret;
}
}
我以前理解的和spring.net差不多的,配置一堆容器XML,搞多层。因为亲自经历过那种工程的失败,以及后面我推广的简化版Spring.net也因为维护麻烦被架空。实践springboot之后只能说比以前的有过之而无不及,工程和结构起码复杂了几倍的难度,没办法,谁让互联网大、分工详细的呢。传统软件生搬硬套只能说是在找死,哈哈哈。