SpringBoot系列文章目录
SpringBoot知识范围-学习步骤【JSB系列之000】
文章目录
本项目效果图
本项目特色, springBoot+ mytis,文件上传,layui界面(JSP),easy-captcha验证码,pagehelper分页
SpringBoot技术很多很多
韩顺平说:学习JAVA的人有两大难
第一困惑,JAVA能百度到的知识太多太多,完全不知道学啥
第二困惑,就是不知道以什么样的顺序去学,有的时候乱看一堆视频有遗漏,有的时候,两边的视频还有重复。
然后,再来看一般的springboot 的书籍中内容。
一般是这样。右侧为基础
一眼看上去直接就晕了。然后这么多东西,哪个是哪个的基础?
直接就懵了。上网查吧?
不上网还好,一上网一查,结果查到的是满天的学习完了JAVA 学到了springboot 工资不到3000。
我还能学下去?其实,这些人大多数都是对着视频上的代码说自己会。事实上会不会自己心里最有数。
如果基础不牢,要回避的技术
假定,你在学校做过了JAVA的小项目了。(我指的是swing,servlet 之类) 可能会点SSM,也可能不会,那我马上就要毕业了,我得搞项目,搞毕设呀。啊!!!
- 第一个要回避的就是JWT。
- 上面的不使用了,shiro 也就不需要了
- 然后是redis
说一下理由。第一,你一个人使用的系统,你用啥JWT?Session. cookie , JWT 都是你一个人在用。完全不存在用户信息泄漏。
单点登录就更用不上了。一共就一个系统。
redis 也不需要,别说你一个人在使用,就是全班的人都在使用50人在线,redis 的优势也自不出来。mysql 数据库连这一点点的数据都不能快速响应,那PHP的平台的不是要直接宕机?而且mybatis 本身还有缓存。
当然了,这是指你自己要把代码弄懂的这个前提下说的。如果说别人运行好的。那用啥环境也不是你能决定了,是吧?
推荐一下个人项目使用的技术
springBoot + mybatis(mybatis plus) + layui(可以JSP也可以html)
登陆使用session
其实,这一套技术是最最符合大三的时候能够灵活掌握的,并且可以做出完整功能的系统。
thymeleaf 远不如VUE实用。
而用了VUE之后,就必然是前后端分离的。
前后端分离layui 也有支持VUE的开源框架。而且思想是一致的。
环境及工具:
本系列环境
环境 | win11 |
---|---|
工具 | idea 2018 |
jdk | 1.8 |
数据库 | mysql5.5 |
maven | 3.6.0 |
VUE(本案例不使用) | node 14.15.3 |
VUE(本案例不使用) | node-sass 4.14 sass-loader: 7.2.x |
项目导入方式 | maven 导入 |
数据库前端工具 | mysql-front (navicat 也可以) |
后台访问地址 | http://localhost:8088/ 菜单进入 |
前台访问地址 | http://localhost:8088/ |
有无小程序 | 没有使用微信小程序预约 |
数据库前端工具:mysql-front (navicat 也可以)
主要是这些跟PHPStudy 2018 整合了,所以有的时候懒的打开navicat
关于环境的重要性,直接看上面的《SpringBoot 的项目编译即报错处理–JSB系列之001》
项目里可能要用到的技术
前后端分离
这是一个好技术。前后端分离的主要概念就是:后台只需提供API接口,前端调用AJAX实现数据呈现。 现状与分歧. 作为一名前端开发人员,我们应该尝试一些新颖的技术,完善每一个细节,后端的开发人员就无脑疯狂的输出json。
在传统的web应用开发中,大多数的程序员会将浏览器作为前后端的分界线。将浏览器中为用户进行页面展示的部分称之为前端,而将运行在服务器,为前端提供业务逻辑和数据准备的所有代码统称为后端。
难不难工作量是不一定,但是,两边的难度是真的降了。
后端的只用idea community IDEA社区版(Community)版本就足够。前端高级的玩玩webStorm,不行用hbuilder,vscode 好象都能写代码。甚至还有人用editplus ++ , nodepad 之流了,这就有点过份了哈。
json
JSON(JavaScript Object Notation, JS对象简谱)是一种轻量级的数据交换格式。它基于 ECMAScript(European Computer Manufacturers Association, 欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写。
这个东东一出来就直接把xml 的这种格式按地上摩擦了。而且可以可读性好,也把一些不可读的数据包形式也给取代了。
layui
Layui 是一套开源免费的 Web UI 组件库,采用自身轻量级模块化规范,遵循原生态的 HTML/CSS/JavaScript 开发模式,非常适合网页界面的快速构建。Layui 区别于一众主流的前端框架,它更多是面向于后端开发者,即无需涉足各类构建工具,只需面向浏览器本身,便可将页面所需呈现的元素与交互信手拈来。
在 2021 年 Layui 原官网下线后,互联网出现了许多第三方的 Layui
文档镜像站点,这在当时,给大家能继续阅读文档确实提供了便利,但原官网的下线,只是单纯一个网站自身生命周期的结束,它并不意味着 Layui
这样一个开源项目的停更,当时的公告其实也说的很清楚了,但或许是由于那则公告的用词过于「悲壮」,使得各大自媒体有所误读,从而让大家都以为是
Layui 停更了。
但是给人的感觉,好象是layui 把原来的官网下线了之后,这个框架反而比之前还活跃了。
其它的必要知识
- 网页三剑客,这个不用说了
- JAVA,你总得会idea吧
- maven 这个也是必须的
配置文件
这个项目的重点是配置文件,因为这个项目使用的springboot + mybatis 并非是mybatis plus。
POM
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.spring</groupId>
<artifactId>zhiyuan_persion</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>boot</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.10</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!--<dependency>-->
<!--<groupId>com.microsoft.sqlserver</groupId>-->
<!--<artifactId>mssql-jdbc</artifactId>-->
<!--<scope>runtime</scope>-->
<!--</dependency>-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
<!--<version>8.0.25</version><!– MYSQL 8 以上把版本对应就好 –>-->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- servlet依赖. -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<!-- tomcat的支持.-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<!--上传-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<!-- JSON 库 -->
<!--验证码-->
<dependency>
<groupId>com.github.whvcse</groupId>
<artifactId>easy-captcha</artifactId>
<version>1.6.2</version>
</dependency>
<!-- 整合分页 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.10</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.49</version>
</dependency>
<!--jwt token -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.10.7</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.10.7</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.10.7</version>
<scope>runtime</scope>
</dependency>
<!--jwt-->
<dependency>
<groupId>jexcelapi</groupId>
<artifactId>jxl</artifactId>
<version>2.4.2</version>
</dependency>
<dependency>
<groupId>com.jntoo</groupId>
<artifactId>db-query</artifactId>
<version>1.2.10</version>
</dependency>
<dependency>
<groupId>com.jntoo</groupId>
<artifactId>query-jstl</artifactId>
<version>0.0.1</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/webapp</directory>
<targetPath>META-INF/resources</targetPath>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.4.2.RELEASE</version>
<configuration>
<!-- 工程主入口-->
<mainClass>com.spring.BootApplication</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
yml配置
这个项目没有使用较新的yml 的配置,而是使用的properties 文件
# 关闭缓存,及时刷新,上线生产环境需要修改为true
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/zhiyuan?useUnicode=true&characterEncoding=UTF-8&useSSL=FALSE&serverTimezone=UTC&useOldAliasMetadataBehavior=true&allowPublicKeyRetrieval=true
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#初始化连接
#datasource
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.minimum-idle=20
spring.datasource.hikari.maximum-pool-size=100
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.idle-timeout=30000
spring.datasource.hikari.pool-name=DatebookHikariCP
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.connection-test-query=SELECT 1
# 开启连接泄露检测
#spring.datasource.hikari.register-mbeans=true
#spring.datasource.hikari.leakDetectionThreshold=5000
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp
spring.mvc.pathmatch.use-suffix-pattern=false
spring.mvc.pathmatch.use-registered-suffix-pattern=true
spring.mvc.contentnegotiation.favor-path-extension=false
server.port=8088
server.servlet.context-path=/
#关闭whitelabel Error page
server.error.whitelabel.enabled=false
#默认never,无法输出trace
server.error.include-stacktrace=always
# 整合mybatis
mybatis.type-aliases-package=com.spring.entity
mybatis.mapper-locations=classpath:mapper/*.xml
mapper.mappers=com.spring.config.MapperBase
mapper.not-empty=false
mapper.identity=MYSQL
#分页插件
pagehelper.helper-dialect=mysql
pagehelper.reasonable=true
pagehelper.supportMethodsArguments=true
pagehelper.params=count=countSql
# 设置文件上传无限制
spring.servlet.multipart.maxFileSize=-1
spring.servlet.multipart.maxRequestSize=-1
admin.account=admin
上代码
Controller代码
package com.spring.controller;
import com.alibaba.fastjson.*;
import com.jntoo.db.DB;
import com.jntoo.db.QueryMap;
import com.jntoo.db.model.Options;
import com.jntoo.db.utils.Convert;
import com.jntoo.db.utils.StringUtil;
import com.spring.entity.*;
import com.spring.service.*;
import com.spring.util.*;
import java.util.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
/**
* 用户登录更新密码控制器
*/
@Controller
public class UserController extends BaseController {
@Resource
private AdminsService adminsService;
@Resource
private YonghuService yonghuService;
/**
* 登录页面
* @return
*/
@RequestMapping("/login")
public String Index() {
return "login";
}
/**
* 退出
* @return
*/
@RequestMapping("/logout")
public String Logout() {
request.getSession().invalidate();
return showSuccess("退出成功", "./");
}
/**
* 验证登录用户
* @param isAdmin
* @param username
* @param pwd
* @param cx
* @return
*/
protected String authLoginUser(boolean isAdmin, String username, String pwd, String cx) {
if (username == null || "".equals(username)) {
return showError("账号不允许为空");
}
if (pwd == null || "".equals(pwd)) {
return showError("密码不允许为空");
}
if (cx == null) {
return showError("请选中登录类型");
}
String random;
// 获取 token方式的验证码值
if (isAjax() && request.getParameter("captchToken") != null) {
random = DESUtil.decrypt("CaptchControllerPassword", request.getParameter("captchToken"));
} else {
random = (String) request.getSession().getAttribute("random");
}
String pagerandom = request.getParameter("pagerandom") == null ? "" : request.getParameter("pagerandom");
if (request.getParameter("a") != null && !pagerandom.equalsIgnoreCase(random)) {
return showError("验证码不正确", 20);
}
String table = "";
if (cx.equals("管理员")) {
table = "admins";
Admins user = adminsService.login(username, pwd);
if (user == null) {
return showError("用户名或密码错误");
}
session.setAttribute("id", user.getId());
session.setAttribute("username", user.getUsername());
session.setAttribute("cx", cx);
session.setAttribute("login", cx);
session.setAttribute("username", user.getUsername());
session.setAttribute("pwd", user.getPwd());
}
if (cx.equals("用户")) {
table = "yonghu";
Yonghu user = yonghuService.login(username, pwd);
if (user == null) {
return showError("用户名或密码错误");
}
session.setAttribute("id", user.getId());
session.setAttribute("username", user.getYonghuming());
session.setAttribute("cx", cx);
session.setAttribute("login", cx);
session.setAttribute("cx", user.getCx());
session.setAttribute("yonghuming", user.getYonghuming());
session.setAttribute("mima", user.getMima());
session.setAttribute("xingming", user.getXingming());
session.setAttribute("xingbie", user.getXingbie());
session.setAttribute("shouji", user.getShouji());
session.setAttribute("shenfenzheng", user.getShenfenzheng());
session.setAttribute("suozaishequ", user.getSuozaishequ());
session.setAttribute("gerenmiaoshu", user.getGerenmiaoshu());
}
if (session.getAttribute("username") == null) {
return showError("账号或密码错误");
}
if (this.isAjax()) {
return json();
} else {
String referer = request.getParameter("referer");
if (referer == null) {
if (isAdmin) {
referer = "./main.do";
} else {
referer = "./";
}
}
return showSuccess("登录成功", referer);
}
}
/**
* 后台主页面
* @return
*/
@RequestMapping("/main")
public String main() {
return "main";
}
/**
* 后台初始页面
* @return
*/
@RequestMapping("/sy")
public String sy() {
return "sy";
}
/**
* 不一定有
* @return
*/
@RequestMapping("/mygo")
public String mygo() {
return "mygo";
}
/**
* 头部页面
* @return
*/
@RequestMapping("/top")
public String top() {
return "top";
}
/**
* 验证登录页面
* @return
*/
@RequestMapping("/authLogin")
public String authLogin() {
String username = Request.get("username");
String pwd = Request.get("pwd");
String cx = Request.get("cx");
return authLoginUser(false, username, pwd, cx);
}
/**
* 验证后台登录
* @return
*/
@RequestMapping("/authAdminLogin")
public String authAdminLogin() {
String username = Request.get("username");
String pwd = Request.get("pwd");
String cx = Request.get("cx");
return authLoginUser(true, username, pwd, cx);
}
/**
* 修改登录密码页面
* @return
*/
@RequestMapping("/mod")
public String mod() {
return "mod";
}
/**
* 保存修改密码
* @return
*/
@RequestMapping("/editPassword")
public String editPassword() {
String username = request.getSession().getAttribute("username").toString();
String cx = request.getSession().getAttribute("login").toString();
String oldPassword = Request.get("oldPassword");
String newPwd = Request.get("newPwd");
String newPwd2 = Request.get("newPwd2");
if (!newPwd.equals(newPwd2)) {
return showError("两次密码不一致");
}
if (cx.equals("管理员")) {
Admins user = adminsService.login(username, oldPassword);
if (user == null) {
return showError("原密码不正确");
}
adminsService.updatePassword(user.getId(), newPwd);
}
if (cx.equals("用户")) {
Yonghu user = yonghuService.login(username, oldPassword);
if (user == null) {
return showError("原密码不正确");
}
yonghuService.updatePassword(user.getId(), newPwd);
}
return showSuccess("修改密码成功", "./mod.do");
}
@RequestMapping("/db/select")
public String select(@RequestBody Map<String, Object> data) {
String sql = String.valueOf(data.get("sql"));
String type = String.valueOf(data.get("type"));
List binds = null;
if (data.containsKey("binds") && data.get("binds") instanceof List) {
binds = (List) data.get("binds");
} else {
binds = new ArrayList();
}
Object[] datas = binds.toArray();
if ("select".equals(type)) {
return showSuccess(DB.select(sql, datas));
} else {
return showSuccess(DB.find(sql, datas));
}
}
@PostMapping("/db/query")
public String query(@RequestBody Map map) {
if (!map.containsKey("name")) {
return showError("找不到相关名称");
}
if (!map.containsKey("options") && !(map.get("options") instanceof Map)) {
return showError("找不到相关配置");
}
if (!map.containsKey("func")) {
return showError("找不到引用");
}
JSONObject object = new JSONObject();
object.putAll((Map) map.get("options"));
QueryMap queryWrapper = DB.name(map.get("name").toString());
queryWrapper.setOptions(object.toJavaObject(Options.class));
String func = map.get("func").toString().toLowerCase();
Object result = null;
List args = (List) map.get("args");
try {
if (func.equals("select")) {
result = queryWrapper.select();
} else if (func.equals("find")) {
if (args == null) {
result = queryWrapper.find();
} else {
result = queryWrapper.find(args.get(0));
}
} else if (func.equals("count")) {
if (args == null) {
result = queryWrapper.count();
} else {
result = queryWrapper.count(String.valueOf(args.get(0)));
}
} else if (func.equals("avg")) {
result = queryWrapper.avg(String.valueOf(args.get(0)));
} else if (func.equals("sum")) {
result = queryWrapper.sum(String.valueOf(args.get(0)));
} else if (func.equals("max")) {
result = queryWrapper.max(String.valueOf(args.get(0)));
} else if (func.equals("min")) {
result = queryWrapper.min(String.valueOf(args.get(0)));
}
} catch (Exception e) {
e.printStackTrace();
return showError(e.getMessage());
}
return showSuccess(result);
}
}
上传的配置类
@Configuration
public class WebConfigure implements WebMvcConfigurer {
/**
* 设置上传路径存储位置,默认放在运行目录下的 upload
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 设置方位地址
System.out.println(Configure.UPLOAD_DIR);
registry
.addResourceHandler("/upload/**")
.addResourceLocations("file:" + Configure.UPLOAD_DIR)
.addResourceLocations("file:" + System.getProperty("user.dir") + "/src/webapp/upload/");
}
}
防SQL攻击的配置
/**
* 配置数据库访问源
*/
@Component
public class QueryConfigRuntime implements ApplicationRunner {
@Resource
private QueryConfigConnect config;
/**
* Callback used to run the bean.
*
* @param args incoming application arguments
* @throws Exception on error
*/
@Override
public void run(ApplicationArguments args) throws Exception {
QueryConfig queryConfig = new QueryConfig();
queryConfig.setPrefix("");
queryConfig.setConnectionConfig(config);
queryConfig.setDebug(true);
Configuration.setQueryConfig(queryConfig);
}
}
启动页面的配置类
package com.spring.config;
import java.lang.reflect.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringBootConfiguration;
/**
* 系统启动后执行这里,运行浏览器
*/
@SpringBootConfiguration
public class AutoStartProjectInDefaultBrowser implements CommandLineRunner {
@Value("${server.port}")
private String port;
@Value("${server.servlet.context-path}")
private String path;
/**
springboot自带的监听任务,启动浏览器
@param args
@throws Exception
*/
@Override
public void run(String... args) throws Exception {
try {
String url = "http://localhost:" + port + path;
browse(url);
//Runtime.getRuntime().exec("cmd /c start http://localhost:" + port + ""+path);
} catch (Exception ex) {
ex.printStackTrace();
}
}
private static void browse(String url) {
try {
String osName = System.getProperty("os.name", "");
if (osName.startsWith("Mac OS")) {
Class fileMgr = Class.forName("com.apple.eio.FileManager");
Method openURL = fileMgr.getDeclaredMethod("openURL", String.class);
openURL.invoke(null, url);
} else if (osName.startsWith("Windows")) {
Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url);
} else {
// Unix or Linux
String[] browsers = { "firefox", "opera", "konqueror", "epiphany", "mozilla", "netscape" };
String browser = null;
for (int count = 0; count < browsers.length && browser == null; count++) // 执行代码,在brower有值后跳出,
// 这里是如果进程创建成功了,==0是表示正常结束。
if (Runtime.getRuntime().exec(new String[] { "which", browsers[count] }).waitFor() == 0) browser = browsers[count];
if (browser == null) throw new Exception("Could not find web browser"); else // 这个值在上面已经成功的得到了一个进程。
Runtime.getRuntime().exec(new String[] { browser, url });
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
实体类
这里有必要说一下idea2018并不直接支持lombok,需要自己导入一下。
跑起来
后台
数据库的部分你自己搞定吧,如果到了做毕设还不会数据库,这个还是要下点功夫了。
这么点代码+数据库就能跑起来?没错,他确实是能运行
用浏览器最原始的方式验证
在浏览器上念出一段神奇的古老埃及法老的字符。本系统是自动打开
http://localhost:8088/
页面展示
后面管理界面
好,一步成功点亮,关电,拉闸,领盒饭!走人。
总结
提示:IT是一个要多动手的职业,一定要多练不要贪快:
这一部涉及到的知识可以说非常之多。能花一周的时间把这篇文章里的东西弄一个大概并运行出来,也是很不容易的,而且难度也不低,准确一点说,培训机构也能让你似懂非懂的把这个代码跑起来。
做到这一步,你会了springBoot了么?会了,但是现在你会的都是初级基本上就是对付一下简单项目。传的对象复杂了,数据信息多了,都可能会出现这样那样的问题。
还有就是springboot 的缓存,JWT,消息队列,安全机制 ,任务管理其实他们从SSM甚至SSH的时候就有了。只是springboot 一下子全集成过来了。你去看别人的SSM项目 ,这些东西也都有。
所以,程序猿与学员纯在着本质的差别。那么如何提高呢?当然要多读别人的成功项目了。
GITEE上面存在着大量的springboot的项目,英文没有问题的话,可以去github。
配套资源
SpringBoot志愿者管理系统-课程设计-【JSB项目实战】
https://download.csdn.net/download/dearmite/88178070
非VIP的
https://download.csdn.net/download/dearmite/88189181