1.前后端分离为了什么

1.1前言

​ 本文作为系列文章的第一篇,是铁柱在工作之余对自我学习的总结,以下内容是搭建基础的前后端分离的Demo来展开的,需要对MySQL,Spring,SpringMVC,Mybatis,Vue,Node.js有一定的前置掌握,也可以先行进行简单的学习,下面也会贴出以上几种技术推荐的学习教程供小伙伴们参考,后续还会有更多干货文章,JUC,多线程,集合,Netty,Redis,中间件技术等,全部结合实际展开,感兴趣的小伙伴可以关注点赞走起。

本文全部代码已上传到Gitee仓库: https://gitee.com/lu-tiezhu/separationVue-boot.git

喜欢的小伙伴可以一键start,感谢支持。

1.2前后端分离的前因后果

​ 前后端分离已成为互联网项目开发的业界标准使用方式,通过nginx+tomcat的方式(也可以中间加一个nodejs)有效的进行解耦,并且前后端分离会为以后的大型分布式架构、弹性计算架构、微服务架构、多端化服务(多种客户端,例如:浏览器,车载终端,安卓,IOS等等)打下坚实的基础。这个步骤是系统架构从猿进化成人的必经之路。

​ 核心思想是前端html页面通过http请求调用后端的restuful 风格接口并使用json数据进行交互。以前的JavaWeb项目大多数都是java程序员又当爹又当妈,又搞前端,又搞后端。

​ 随着时代的发展,渐渐的许多大中小公司开始把前后端的界限分的越来越明确,前端工程师只管前端的事情,后端工程师只管后端的事情。正所谓术业有专攻,一个人如果什么都会,那么他毕竟什么都不精。

对于后端java工程师:

​ 把精力放在java基础,设计模式,jvm原理,spring+springmvc原理及源码,linux,mysql事务隔离与锁机制,mongodb,http/tcp,多线程,分布式架构,弹性计算架构,微服务架构,java性能优化,以及相关的项目管理等等。

后端追求的是:三高(高并发,高可用,高性能),安全,存储,业务等等。

对于前端工程师:

​ 把精力放在html5,css3,jquery,angularjs,bootstrap,reactjs,vuejs,webpack,less/sass,gulp,nodejs,Google V8引擎,javascript多线程,模块化,面向切面编程,设计模式,浏览器兼容性,性能优化等等。

前端追求的是:页面表现,速度流畅,兼容性,用户体验等等。

1.3 使用SpringBoot框架开发前后端分离的原因

来看看百度百科关于SpringBoot的介绍:

SpringBoot框架中还有两个非常重要的策略:开箱即用和约定优于配置。开箱即用,Outofbox,是指在开发过程中,通过在MAVEN项目的pom文件中添加相关依赖包,然后使用对应注解来代替繁琐的XML配置文件以管理对象的生命周期。这个特点使得开发人员摆脱了复杂的配置工作以及依赖的管理工作,更加专注于业务逻辑。约定优于配置,Convention over configuration,是一种由SpringBoot本身来配置目标结构,由开发者在结构中添加信息的软件设计范式。这一特点虽降低了部分灵活性,增加了BUG定位的复杂性,但减少了开发人员需要做出决定的数量,同时减少了大量的XML配置,并且可以将代码编译、测试和打包等工作自动化。

SpringBoot应用系统开发模板的基本架构设计从前端到后台进行说明:前端常使用模板引擎,主要有FreeMarker和Thymeleaf,它们都是用Java语言编写的,渲染模板并输出相应文本,使得界面的设计与应用的逻辑分离,同时前端开发还会使用到Bootstrap、AngularJS、JQuery等;在浏览器的数据传输格式上采用Json,非xml,同时提供RESTfulAPI;SpringMVC框架用于数据到达服务器后处理请求;到数据访问层主要有Hibernate、MyBatis、JPA等持久层框架;数据库常用MySQL;开发工具推荐IntelliJIDEA。

用一句话来说:SpringBoot可以快速搭建后端来实现一个前后端分离的项目,避免大量繁琐的配置,且由于天然良好的支持SpringMVC,可以非常敏捷的开发接口

2.搭建后端基础环境

2.1 技术选型

Java企业级开发常见技术栈:

开发工具:IDEA 2020,VS Code

Java version “1.8.0_301”
Java™ SE Runtime Environment (build 1.8.0_301-b09)
Java HotSpot™ 64-Bit Server VM (build 25.301-b09, mixed mode)

后端框架:

  • Spring 5.x
  • SpringMVC 5.x
  • MySQL 5.7
  • SpringBoot 2.2.11.RELEASE
  • Redis 5.5
  • MybatisPlus 3.4.0
  • spring-boot-devtools
  • hutool
  • swagger-ui
  • Git

前端:

  • Node.js

  • Vue2

  • ElementUI

  • Axios

  • 后续随着文章的进度,将会整合更多技术,争取一个项目吃透Java开发常用技术

2.2搭建数据表

先创建数据库 separation_boot,再创建数据表

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for db_scores
-- ----------------------------
DROP TABLE IF EXISTS `db_scores`;
CREATE TABLE `db_scores`  (`id` bigint(20) NOT NULL COMMENT '主键id',`subjects` varchar(50)  COMMENT '考试科目',`stu_number` varchar(100)  COMMENT '学号',`score` int(11) NULL DEFAULT NULL COMMENT '分数',`describe` varchar(255)  COMMENT '考试说明',`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB;-- ----------------------------
-- Records of db_scores
-- ----------------------------
INSERT INTO `db_scores` VALUES (3458451354, '数学', '413026200510101213', 135, '高三年级十月月中考试', '2022-10-10 21:38:37');
INSERT INTO `db_scores` VALUES (3458451351236, '数学', '413026200510101216', 120, '高三年级十月月中考试', '2022-10-10 21:38:37');
INSERT INTO `db_scores` VALUES (3458451354112, '数学', '413026200510101214', 95, '高三年级十月月中考试', '2022-10-10 21:38:37');
INSERT INTO `db_scores` VALUES (3458451389471, '数学', '413026200510101212', 115, '高三年级十月月中考试', '2022-10-10 21:38:37');-- ----------------------------
-- Table structure for db_students
-- ----------------------------
DROP TABLE IF EXISTS `db_students`;
CREATE TABLE `db_students`  (`id` bigint(20) NOT NULL COMMENT '主键ID',`stu_name` varchar(50)  COMMENT '姓名',`stu_
number` varchar(100)  COMMENT '学号',`sex` varchar(10)  COMMENT '性别',`birth` datetime(0) NULL DEFAULT NULL COMMENT '出生日期',`address` varchar(255)  COMMENT '地址',`stu_class` varchar(50)  COMMENT '班级',`stu_grade` varchar(50)  COMMENT '年级',`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB;-- ----------------------------
-- Records of db_students
-- ----------------------------
INSERT INTO `db_students` VALUES (357453451, '刘玉兰', '413026200510101213', '女', '2005-10-10 21:33:57', '河南省郑州市高新区', '02班', '高中三年级', '2022-10-10 21:35:15');
INSERT INTO `db_students` VALUES (357453454, '李辉', '413026200510101214', '男', '2005-10-10 21:33:57', '河南省郑州市高新区', '01班', '高中三年级', '2022-10-10 21:35:15');
INSERT INTO `db_students` VALUES (357453455, '张梦怡', '413026200510101212', '女', '2005-10-10 21:33:57', '河南省郑州市高新区', '01班', '高中三年级', '2022-10-10 21:35:15');
INSERT INTO `db_students` VALUES (357453457, '王明明', '413026200510101216', '男', '2005-10-10 21:33:57', '河南省郑州市高新区', '04班', '高中三年级', '2022-10-10 21:35:15');SET FOREIGN_KEY_CHECKS = 1;SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for db_dictionary
-- ----------------------------
DROP TABLE IF EXISTS `db_dictionary`;
CREATE TABLE `db_dictionary`  (`id` int(11) NOT NULL COMMENT '主键',`code_name` varchar(100)  COMMENT '字典名称',`fid` int(11) NULL DEFAULT NULL COMMENT '父id',`describe` varchar(255)  DEFAULT NULL COMMENT '描述',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB ;-- ----------------------------
-- Records of db_dictionary
-- ----------------------------
INSERT INTO `db_dictionary` VALUES (101, '年级', 1, NULL);
INSERT INTO `db_dictionary` VALUES (102, '班级', 1, NULL);
INSERT INTO `db_dictionary` VALUES (103, '学科', 1, NULL);
INSERT INTO `db_dictionary` VALUES (10101, '高中一年级', 101, NULL);
INSERT INTO `db_dictionary` VALUES (10102, '高中二年级', 101, NULL);
INSERT INTO `db_dictionary` VALUES (10103, '高中三年级', 101, NULL);
INSERT INTO `db_dictionary` VALUES (10104, '初中一年级', 101, NULL);
INSERT INTO `db_dictionary` VALUES (10105, '初中二年级', 101, NULL);
INSERT INTO `db_dictionary` VALUES (10106, '初中三年级', 101, NULL);
INSERT INTO `db_dictionary` VALUES (10201, '01班', 102, NULL);
INSERT INTO `db_dictionary` VALUES (10202, '02班', 102, NULL);
INSERT INTO `db_dictionary` VALUES (10203, '03班', 102, NULL);
INSERT INTO `db_dictionary` VALUES (10204, '04班', 102, NULL);
INSERT INTO `db_dictionary` VALUES (10205, '05班', 102, NULL);
INSERT INTO `db_dictionary` VALUES (10301, '语文', 103, NULL);
INSERT INTO `db_dictionary` VALUES (10302, '数学', 103, NULL);
INSERT INTO `db_dictionary` VALUES (10303, '英语', 103, NULL);
INSERT INTO `db_dictionary` VALUES (10304, '政治', 103, NULL);
INSERT INTO `db_dictionary` VALUES (10305, '历史', 103, NULL);
INSERT INTO `db_dictionary` VALUES (10306, '地理', 103, NULL);
INSERT INTO `db_dictionary` VALUES (10307, '物理', 103, NULL);
INSERT INTO `db_dictionary` VALUES (10308, '化学', 103, NULL);
INSERT INTO `db_dictionary` VALUES (10309, '生物', 103, NULL);
INSERT INTO `db_dictionary` VALUES (103010, '体育', 103, NULL);SET FOREIGN_KEY_CHECKS = 1;

2.3 添加maven依赖和配置文件

IDEA创建项目


废话不多说,直接上POM文件内容

    <groupId>com.tiezhu</groupId><artifactId>separationVue-boot</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.11.RELEASE</version><relativePath/></parent><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.2.11.RELEASE</spring-boot.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.46</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.4</version></dependency><!--代码生成器--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.4.0</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.0</version></dependency><dependency><groupId>org.apache.velocity</groupId><artifactId>velocity-engine-core</artifactId><version>2.0</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.16</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.10</version></dependency><dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.11.2</version></dependency><!--swagger接口注解插件--><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.8.0</version></dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId><version>2.8.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional><scope>runtime</scope></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target></configuration></plugin></plugins><resources><resource><directory>src/main/java</directory><includes><include>**/*.yml</include><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>false</filtering></resource><resource><directory>src/main/resources</directory><includes> <include>**/*.yml</include><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>false</filtering></resource></resources></build>

下面是application.yml文件的内容

# 应用服务 WEB 访问端口
server:port: 9090
# 应用名称
spring:application:name: separationVue-bootdevtools:restart:enabled: falsedatasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/separation_boot?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8username: rootpassword: 123456
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpltype-aliases-package: com.tiezhu.separation.pojomapper-locations: classpath:com/tiezhu/separation/mapper/xml/*.xml

2.4 构建基础目录

目录如下图:


2.5 整合MybatisPlus和代码生成器

对MybatisPlus不熟悉的小伙伴可以阅读我的这篇入门教程:

Mybatis-Plus入门(一)_道上叫我卢铁柱的博客-CSDN博客

MybatisPlus的相关配置参数已经放在yml文件当中:

mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 配置打印sql执行日志
type-aliases-package: com.tiezhu.separation.pojo 实体类路径
mapper-locations: classpath:com/tiezhu/separation/mapper/xml/*.xml 各个类的Mybatis实现文件

代码生成器,名如其字(也可以说顾名思义),这玩意是根据数据库的表生成相关增删改查代码的,这样避免了繁琐的重复代码编写,前面提到我们使用的是MybatisPlus框架,它是Mybatis的超集,就是说我们可以同时使用Mybatis,在代码生成器的加持上,我们可以减少编写基础的Mybatis代码,相关依赖已经贴在maven中,废话不多说,直接上代码

package com.tiezhu.separation;import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.junit.Test;/*** @author tiezhu* @since 2021/8/7*/
public class CodeGenerator {@Testpublic void run() {// 1、创建代码生成器AutoGenerator mpg = new AutoGenerator();// 2、全局配置GlobalConfig gc = new GlobalConfig();String projectPath = System.getProperty("user.dir");gc.setOutputDir(projectPath + "/src/main/java");gc.setAuthor("tiezhu");gc.setOpen(false); //生成后是否打开资源管理器gc.setFileOverride(false); //重新生成时文件是否覆盖gc.setServiceName("%sService");    //去掉Service接口的首字母Igc.setIdType(IdType.ID_WORKER); //主键策略gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型gc.setSwagger2(true);//开启Swagger2模式mpg.setGlobalConfig(gc);// 3、数据源配置DataSourceConfig dsc = new DataSourceConfig();dsc.setUrl("jdbc:mysql://127.0.0.1:3306/separation_boot?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8");dsc.setDriverName("com.mysql.jdbc.Driver");dsc.setUsername("root");dsc.setPassword("123456");dsc.setDbType(DbType.MYSQL);mpg.setDataSource(dsc);// 4、包配置PackageConfig pc = new PackageConfig();pc.setModuleName("separation"); //模块名pc.setParent("com.tiezhu");pc.setController("controller");pc.setEntity("pojo");pc.setService("service");pc.setMapper("mapper");mpg.setPackageInfo(pc);// 5、策略配置StrategyConfig strategy = new StrategyConfig();strategy.setInclude("db_students");//此处为表名,将需要生成的数据表放在这里即可strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略strategy.setTablePrefix("db_"); //生成实体时去掉表前缀strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作strategy.setRestControllerStyle(true); //restful api风格控制器strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符mpg.setStrategy(strategy);// 6、执行mpg.execute();}
}

2.6 整合Redis

Redis作为一个快速的缓存数据库,使用键值对结构可以完成很多操作,如分布式锁,秒杀,页面缓存,消息传输等功能,Redis也是将在本项目发挥很多种可能。

在配置文件中添加

redis:host: 127.0.0.1port: 6379lettuce:shutdown-timeout: 2000timeout: 2000jedis:pool:max-wait: -1

在config文件夹下面添加redis的优化配置类:

@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();RedisSerializer<String> redisSerializer = new StringRedisSerializer();Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);template.setConnectionFactory(factory);//key序列化方式template.setKeySerializer(redisSerializer);//value序列化template.setValueSerializer(jackson2JsonRedisSerializer);//value hashmap序列化template.setHashValueSerializer(jackson2JsonRedisSerializer);return template;}@Beanpublic CacheManager cacheManager(RedisConnectionFactory factory) {RedisSerializer<String> redisSerializer = new StringRedisSerializer();Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);//解决查询缓存转换异常的问题ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);// 配置序列化(解决乱码的问题),过期时间600秒RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(600)).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)).disableCachingNullValues();RedisCacheManager cacheManager = RedisCacheManager.builder(factory).cacheDefaults(config).build();return cacheManager;}
}

为了一步到位我们直接整上Redisson的配置类,在config文件夹下面添加:

package com.tiezhu.separation.config;import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RedissonConfiguration {@Beanpublic RedissonClient redissonClient(){Config config = new Config();// 可以用"rediss://"来启用SSL连接config.useSingleServer().setAddress("redis://127.0.0.1:6379");return Redisson.create(config);}}

在项目和日常使用中我们可以通过两种方法来注入使用redis

@Autowired
private RedissonClient redissonClient;//需要配置类RedissonConfiguration
@Autowired
private RedissonClient redissonClient;

2.7配置SwaggerUI

先来讲讲Swagger是什么:

使用Swagger根据在代码中使用自定义的注解来生成接口文档,这个在前后端分离的项目中很重要。这样做的好处是 在开发接口时可以通过swagger 将接口文档定义好,同时也方便以后的维护。

  • 号称时最流行的 API 框架
  • 接口文档在线生成,避免同步的麻烦
  • 可以支持在线对接口执行测试
  • 支持多语言

同样的在config文件里添加Swagger配置类:

@Configuration
@EnableSwagger2
public class SwaggerConfig {@Beanpublic Docket adminApiConfig(){return new Docket(DocumentationType.SWAGGER_2).groupName("indexApi").apiInfo(webApiInfo()).select().paths(Predicates.and(PathSelectors.regex("/index/.*"))).build();}private ApiInfo webApiInfo(){return new ApiInfoBuilder().title("separation-SpringBoot前后端分离学习项目API文档").description("本文描述了separationVue-boot接口的定义").version("1.0").contact(new Contact("tiezhu", "http://127.0.0.1", "1766419110@qq.com")).build();}
}

最终启动起来是这样的效果(后面的代码还没有写,所以现在还是启动不了的,后续跟上就ok):

3.实现业务代码

基本的东西料理妥当大概这样:

3.1 实体类和工具类

实体类由代码生成器生成之后可不用修改,在createTime上加入如下代码:

@TableField(fill = FieldFill.INSERT_UPDATE)

Result类是实现前后端分离的核心关键代码,后端所查询的数据均通过它渲染成JSON传输到前端。

@Data
public class Result {@ApiModelProperty(value = "是否成功")private Boolean success;@ApiModelProperty(value = "状态码")private Integer code;@ApiModelProperty(value = "返回消息")private String message;@ApiModelProperty(value = "返回数据")private Map<String, Object> data=new HashMap<>();private final static int SUCCESS=200;private final static int ERROR=404;private Result(){}public static Result ok(){Result r= new Result();r.setSuccess(true);r.setCode(SUCCESS);r.setMessage("success");return r;}public static Result error(){Result r = new Result();r.setSuccess(false);r.setCode(ERROR);r.setMessage("error");return r;}public Result success(Boolean success){this.setSuccess(success);return this;}public Result message(String message){this.setMessage(message);return this;}public Result code(Integer code){this.setCode(code);return this;}public Result data(String key, Object value){this.data.put(key, value);return this;}public Result data(Map<String, Object> map){this.setData(map);return this;}
}

3.2 业务层和数据层

业务层和数据层使用代码生成器生成的即可,可以直接在Controller层调用简单的增删改查操作,阅读本文的小伙伴们也可以尝试使用Mybatis去实现一些自己的想实现的功能。

3.3 SpringMVC和前后端分离的关系

学过SpringMVC的小伙伴都知道,它是一个实现MVC模式的Web开发框架,通过DispatchServlet实现对用户请求的解析,调用业务层实现数据的增删改查,将返回的数据渲染的前端,在传统的前后端不分离的项目中,Controller层一般使用的注解为 @Controller ,前端一般使用模板引擎来渲染数据。

当项目来到前后端分离的模式后,我们就需要在controller层加入更多的注解,如 @CrossOrigin @RestController @RequestMapping,其中RestController 注解尤其重要,配合restful风格的接口,将数据转化为JSON或者其他格式返回给前端,这些结果的如何渲染到页面上去就是前端的事情了,SpringMVC完美的支持了Http的几种请求方式,POST,GET,DELETE,PUT等,这些配合JQuery或者Axios等前端请求技术,构建起一个完美的前后端开发技术栈。

3.4 基础CRUD接口开发

对于我们已经建的三个数据库且已经有了基础的代码,剩下的就是开发一些简单的接口,足以本篇文章使用即可,有创造力的小伙伴也可以着急实现更多复杂的功能。

/*** <p>*  前端控制器* </p>** @author tiezhu* @since 2022-10-10*/
@Slf4j
@Api(description = "成绩数据操作接口")
@CrossOrigin
@RestController
@RequestMapping("/index/score")
public class ScoresController {@Autowiredprivate RedisTemplate redisTemplate;@Autowiredprivate ScoresService scoresService;@Autowiredprivate DictionaryService dictionaryService;@ApiOperation("查询成绩信息列表")@GetMapping("/list")public Result selectStudentList(){List<Scores> list = scoresService.list();return Result.ok().data("list",list);}@ApiOperation("根据学号查询学生成绩")@GetMapping("/get/{key}")public Result selectByIdOrName(@ApiParam(name = "key", value = "查询关键字", required = true) @PathVariable String key){QueryWrapper<Scores> queryWrapper=new QueryWrapper<>();queryWrapper.eq("stu_number",key);List<Scores> list = scoresService.list(queryWrapper);if (list==null||list.size()==0){return Result.error().message("您输入的数据有误,请重新输入");}else {return Result.ok().data("scores",list);}}@ApiOperation("添加学生成绩信息")@PostMapping("/insert")public Result saveStudentInfo(@ApiParam(value = "插入数据") Scores score ){if (score!=null){boolean save = this.scoresService.save(score);if (save){return Result.ok();}else {return Result.error();}}else {return Result.error().message("插入失败");}}@ApiOperation("修改学生成绩信息")@PostMapping("/update")public Result editStudentInfo(@ApiParam(value = "插入数据") Scores sc ){if (sc!=null){UpdateWrapper<Scores> updateWrapper=new UpdateWrapper<>();updateWrapper.eq("id",sc.getId());boolean flag = this.scoresService.update(sc,updateWrapper);if (flag){return Result.ok();}else {return Result.error();}}else {return Result.error().message("修改失败");}}@ApiOperation("删除学生成绩信息")@DeleteMapping("/delete/{number}")public Result delScores(@PathVariable Long number ) {if (this.scoresService.removeById(number)){return Result.ok();}else {return Result.error();}}@GetMapping("/dic/get/{mode}")public Result selectDictionaryList(@ApiParam(name = "mode", value = "查询关键字", required = true)@PathVariable Integer mode){//传入0或者null就是查询字典里所有内容,101 年级 102班级 103 学科QueryWrapper<Dictionary> queryWrapper=new QueryWrapper<>();if (mode==null||mode==0){queryWrapper.eq("fid",101);List<Dictionary> list0 = this.dictionaryService.list(queryWrapper);queryWrapper.eq("fid",102);List<Dictionary> list1 = this.dictionaryService.list(queryWrapper);queryWrapper.eq("fid",103);List<Dictionary> list2 = this.dictionaryService.list(queryWrapper);Map<String, Object> maps=new HashMap<>();maps.put("grade",list0);maps.put("class",list1);maps.put("subject",list2);return Result.ok().data(maps);}else{queryWrapper.eq("fid",mode);List<Dictionary> list0 = this.dictionaryService.list(queryWrapper);if (mode==101){return Result.ok().data("grade",list0);}else if (mode==102){return Result.ok().data("class",list0);}else if (mode==102){return Result.ok().data("subject",list0);}}return Result.error().message("输入的查询条件有误!");}
}
@Slf4j
@Api(description = "学生数据操作接口")
@CrossOrigin
@RestController
@RequestMapping("/index/stu")
public class StudentsController {@Autowiredprivate RedisTemplate redisTemplate;@Autowiredprivate StudentsService studentsService;@ApiOperation("查询学生信息列表")@GetMapping("/list")public Result selectStudentList(){List<Students> list = studentsService.list();return Result.ok().data("list",list);}@ApiOperation("根据条件查询学生信息")@GetMapping("/get/{key}")public Result selectByIdOrName(@ApiParam(name = "key", value = "查询关键字", required = true) @PathVariable String key){Students stu = studentsService.getByNumberOrName(key);if (stu==null){return Result.error().message("您输入的数据有误,请重新输入");}else {return Result.ok().data("stu",stu);}}@ApiOperation("添加学生信息")@PostMapping("/insert")public Result saveStudentInfo(@ApiParam(value = "插入数据") Students students ){if (students!=null){boolean save = this.studentsService.save(students);if (save){return Result.ok();}else {return Result.error();}}else {return Result.error().message("插入失败");}}@ApiOperation("修改学生信息")@PostMapping("/update")public Result editStudentInfo(@ApiParam(value = "插入数据") Students students ){if (students!=null){UpdateWrapper<Students> updateWrapper=new UpdateWrapper<>();updateWrapper.eq("id",students.getId());boolean flag = this.studentsService.update(students,updateWrapper);if (flag){return Result.ok();}else {return Result.error();}}else {return Result.error().message("修改失败");}}@ApiOperation("删除学生信息")@DeleteMapping("/delete/{stuId}")public Result delStudents(@PathVariable Long stuId ) {if (this.studentsService.removeById(stuId)){return Result.ok();}else {return Result.error();}}
}

对SeparationVueApplication类中代码做一些补充:

@Slf4j
@MapperScan(basePackages = {"com.tiezhu.separation.mapper"})
@SpringBootApplication
public class SeparationVueApplication {public static void main(String[] args) throws UnknownHostException {ConfigurableApplicationContext application = SpringApplication.run(SeparationVueApplication.class,args);Environment env = application.getEnvironment();String ip = InetAddress.getLocalHost().getHostAddress();String port = env.getProperty("server.port");String contextPath = env.getProperty("server.servlet.context-path");if (contextPath == null) {contextPath = "";}log.info("\n----------------------------------------------------------\n\t" +"Application is running! Access URLs:\n\t" +"Local: \t\thttp://127.0.0.1:" + port + "/swagger-ui.html" + "\n\t" +"External: \thttp://" + ip + ':' + port + contextPath + '\n' +"----------------------------------------------------------");}
}

启动项目:

点击debug启动,下图为启动成功的控制台:

3.5 接口测试,使用SwaggerUI

打开浏览器访问:Swagger UI http://127.0.0.1:9090/swagger-ui.html 即可看到可以操作的接口的网页:

代码已经编写的有十一个接口,本节只会拿部分接口测试一下效果,其他接口都一样的测试方法。

查询全部学生信息:

4.搭建前端基础框架代码

4.1 电脑需要具备的前端环境

前端开发软件方面常用的都是VS code,大家可以去官网下载免费的。

前端基础和前端进阶方面推荐看黑马程序员或者尚硅谷的,直接在哔哩哔哩可以搜到很多。

https://www.bilibili.com/video/BV1YW411T7GX

电脑需要安装好node环境,没有按照过的可以先进按照菜鸟教程上的步骤操作,非常简单。

Node.js 安装配置 | 菜鸟教程 (runoob.com)

安装完成之后在CMD命令框输入node -v 即可查看版本

node环境安装完成之后安装pnpm也是在cmd中执行一条命令即可:

npm i pnpm -g

安装完成后可以看到一些信息,也可以查看pnpm的镜像源。

4.2 导入前端项目和安装依赖

separationVue-boot的前端直接采用现成的前端脚手架不从零搭建,感兴趣的小伙伴可以学习一下Vue和ElementUI,电脑如果没有Git环境可以通过项目的Git链接下载代码。

vue-admin-template这是一个极简的 vue admin 管理后台。它只包含了 Element UI & axios & iconfont & permission control & lint,这些搭建后台必要的东西。

https://gitee.com/panjiachen/vue-admin-template.git

也可以选择直接解压本项目代码下载后note目录里面的separationVue-web.zip文件,该文件为已开发的前端代码。

下载后的代码通过vs code打开代码文件夹。

# 克隆项目
git clone https://github.com/PanJiaChen/vue-admin-template.git# 进入项目目录
cd vue-admin-template# 安装依赖
npm install# 建议不要直接使用 cnpm 安装以来,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题
npm install --registry=https://registry.npm.taobao.org# 启动服务
npm run dev

运行成功后效果如下:

4.3 整合Vue编写简易页面

在前端基础框架的代码运行成功后,就可以开始写前端的页面代码了,先找到前端代码目录下 src/router/index.js,修改其中一部分内容。

{

​ path: ‘/login’,

​ component: () => import(‘@/views/login/index’),

​ hidden: true

},

{

​ path: ‘/404’,

​ component: () => import(‘@/views/404’),

​ hidden: true

},

{

​ path: ‘/’,

​ component: Layout,

​ redirect: ‘/dashboard’,

​ children: [{

​ path: ‘dashboard’,

​ name: ‘Dashboard’,

​ component: () => import(‘@/views/dashboard/index’),

​ meta: { title: ‘Dashboard’, icon: ‘dashboard’ }

​ }]

},

{

​ path: ‘/student’,

​ component: Layout,

​ redirect: ‘/student/list’,

​ name: ‘Student’,

​ meta: { title: ‘学生信息管理’, icon: ‘el-icon-s-help’ },

​ children: [

​ {

​ path: ‘/student/list’,

​ name: ‘StuList’,

​ component: () => import(‘@/views/stu/list’),

​ meta: { title: ‘学生信息列表’, icon: ‘table’ }

​ },

​ {

​ path: ‘/student/add’,

​ name: ‘StuAdd’,

​ component: () => import(‘@/views/stu/form’),

​ meta: { title: ‘添加学生’, icon: ‘form’ }

​ },

​ {

​ path: ‘/student/edit/:id’,

​ name: ‘StuEdit’,

​ component: () => import(‘@/views/stu/form’),

​ meta: { title: ‘编辑学生’ },

​ hidden: true

​ },

​ {

​ path: ‘/score/list’,

​ name: ‘ScoreList’,

​ component: () => import(‘@/views/scores/list’),

​ meta: { title: ‘学生成绩信息列表’, icon: ‘nested’ }

​ },

​ {

​ path: ‘/score/look/:id’,

​ name: ‘ScoreList’,

​ component: () => import(‘@/views/scores/list’),

​ meta: { title: ‘查看学生成绩’, icon: ‘nested’ },

​ hidden: true

​ },

​ {

​ path: ‘/score/add/:id’,

​ name: ‘ScoreAdd’,

​ component: () => import(‘@/views/scores/form’),

​ meta: { title: ‘添加学生成绩’, icon: ‘nested’ },

​ hidden: true

​ }

​ ]

},

此部分修改是为前端的路由添加内容,修改后的效果如下:

在src/view/ 路径下添加两个文件夹,stu和scores文件夹。

stu下为学生的相关页面,在该文件夹下添加 form.vue 和list.vue 两个vue页面文件。

## list.vue页面代码<template><div class="app-container"><el-tablev-loading="listLoading":data="list"stripeelement-loading-text="Loading"borderhighlight-current-row><el-table-column align="center" label="ID" width="95"><template slot-scope="scope">{{ scope.$index+2-1 }}</template></el-table-column><el-table-column label="姓名" width="110"><template slot-scope="scope">{{ scope.row.stuName }}</template></el-table-column><el-table-column label="学号" width="180" align="center"><template slot-scope="scope"><span>{{ scope.row.stuNumber }}</span></template></el-table-column><el-table-column label="性别" width="95" align="center"><template slot-scope="scope">{{ scope.row.sex }}</template></el-table-column><el-table-column align="center" label="出生日期" width="250"><template slot-scope="scope"><i class="el-icon-time" /><span>{{ scope.row.birth }}</span></template></el-table-column><el-table-column label="地址" width="200" align="center"><template slot-scope="scope">{{ scope.row.address }}</template></el-table-column><el-table-column label="班级" width="150" align="center"><template slot-scope="scope">{{ scope.row.stuGrade }}{{ scope.row.stuClass }}</template></el-table-column><el-table-column label="操作" width="350" align="center"><template slot-scope="scope"><router-link :to="'/score/look/'+scope.row.stuNumber"><el-button type="primary" size="small">查看成绩</el-button></router-link><router-link :to="'/score/add/'+scope.row.stuNumber"><el-button type="success" size="small">录入成绩</el-button></router-link><router-link :to="'/student/edit/'+scope.row.id"><el-button type="primary" size="small">修改</el-button></router-link><el-button type="warning" size="small" @click="deleteStu(scope.row.id)">删除</el-button></template></el-table-column></el-table></div>
</template><script>
import stuApi from '@/api/students'export default {filters: {statusFilter(status) {const statusMap = {published: 'success',draft: 'gray',deleted: 'danger'}return statusMap[status]}},data() {return {list: null,listLoading: true}},created() {this.fetchData()},methods: {fetchData() {this.listLoading = truestuApi.getList().then(response => {console.log(response.data.list)this.list = response.data.listthis.listLoading = false})},deleteStu(stuId) {this.$confirm('此操作将永久删除该学生信息, 是否继续?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => {stuApi.deleteStuent(stuId).then(response => {this.fetchData()this.$message.success(response.message)})}).then(response => {this.fetchData()this.$message.success(response.message)})}}
}
</script>## form.vue页面代码
<template><div class="app-container"><el-form ref="form" :model="form" label-width="80px"><el-form-item label="学生姓名"><el-input v-model="form.stuName" /></el-form-item><el-form-item label="学号"><el-input v-model="form.stuNumber" /></el-form-item><el-form-item label="性别"><el-select v-model="form.sex" placeholder="请选择性别"><el-option key="男" label="男" value="男" /><el-option key="女" label="女" value="女" /></el-select></el-form-item><el-form-item label="出生时间"><el-date-picker v-model="form.birth" value-format="yyyy-MM-dd" /></el-form-item><el-form-item label="所属年级"><el-select v-model="form.stuGrade"><el-optionv-for="item in optionsGrade":key="item.codeName":label="item.codeName":value="item.codeName"/></el-select></el-form-item><el-form-item label="所属班级"><el-select v-model="form.stuClass"><el-optionv-for="item in optionsClass":key="item.codeName":label="item.codeName":value="item.codeName"/></el-select></el-form-item><el-form-item label="地址"><el-input v-model="form.address" type="textarea" /></el-form-item><el-form-item><el-button type="primary" @click="saveOrUpdate()">立即创建</el-button><el-button>取消</el-button></el-form-item></el-form></div>
</template>
<script>
import stuApi from '@/api/students'
import ScoreApi from '@/api/scores'export default {data() {return {form: { },optionsClass: [],optionsGrade: [],saveBtnDisabled: false // 保存按钮是否禁用,防止表单重复提交}},// 页面渲染成功created() {if (this.$route.params.id) {this.fetchDataById(this.$route.params.id)}ScoreApi.getByDictionary(0).then(response => {this.optionsGrade = response.data.gradethis.optionsClass = response.data.class})},methods: {fetchDataById(id) {stuApi.getById(id).then(response => {this.form = response.data.stu})},saveOrUpdate() {// 禁用保存按钮this.saveBtnDisabled = trueif (!this.form.id) {this.saveData(this.form)} else {this.updateData(this.form)}},saveData() {stuApi.save(this.form).then(response => {this.$message({type: 'success',message: response.message})this.$router.push({ path: '/student/list' })})},// 根据id更新记录updateData() {stuApi.updateById(this.form).then(response => {this.$message({type: 'success',message: response.message})this.$router.push({ path: '/student/list' })})}}
}
</script>

在scores文件夹下,同样新建两个文件 list.vue和form.vue

##list.vue 页面代码:
<template><div class="app-container"><el-tablev-loading="listLoading":data="list"stripeelement-loading-text="Loading"borderhighlight-current-row><el-table-column align="center" label="ID" width="95"><template slot-scope="scope">{{ scope.$index+2-1 }}</template></el-table-column><el-table-column label="考试科目" width="110" align="center"><template slot-scope="scope">{{ scope.row.subjects }}</template></el-table-column><el-table-column label="学号" width="180" align="center"><template slot-scope="scope"><span>{{ scope.row.stuNumber }}</span></template></el-table-column><el-table-column label="分数" width="95" align="center"><template slot-scope="scope">{{ scope.row.score }}</template></el-table-column><el-table-column align="center" label="考试说明" width="250"><template slot-scope="scope"><span>{{ scope.row.description }}</span></template></el-table-column><el-table-column label="操作" width="100" align="center"><template slot-scope="scope"><el-button type="warning" size="small" @click="deleteScore(scope.row.id)">删除</el-button></template></el-table-column></el-table></div>
</template>
<script>
import ScoreApi from '@/api/scores'
export default {data() {return {list: null,listLoading: true}}, created() {this.fetchData()},methods: {fetchData() {this.listLoading = trueif (this.$route.params.id) {ScoreApi.getListByStuNumber(this.$route.params.id).then(response => {this.list = response.data.listthis.listLoading = false})} else {ScoreApi.getList().then(response => {console.log(response.data.list)this.list = response.data.listthis.listLoading = false})}},deleteScore(param) {this.$confirm('此操作将永久删除该条考试信息, 是否继续?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => {ScoreApi.deleteScore(param).then(response => {this.fetchData()this.$message.success(response.message)})}).then(response => {this.fetchData()this.$message.success(response.message)})}}
}
</script>## form.vue 页面代码
<template><div class="app-container"><el-form ref="form" :model="form" label-width="80px"><el-form-item label="考试科目"><el-select v-model="form.subjects"><el-optionv-for="item in optionsSubject":key="item.codeName":label="item.codeName":value="item.codeName"/></el-select></el-form-item><el-form-item label="学号"><el-input v-model="form.stuNumber" readonly="true" /></el-form-item><el-form-item label="分数"><el-inputv-model="form.score"type="text"placeholder="请输入分数"maxlength="10"max="150"show-word-limit/></el-form-item><el-form-item label="考试说明"><el-input v-model="form.description" type="textarea" /></el-form-item><el-form-item><el-button type="primary" @click="saveOrUpdate()">立即创建</el-button><el-button>取消</el-button></el-form-item></el-form></div>
</template>
<script>
import ScoreApi from '@/api/scores'export default {data() {return {form: { },optionsSubject: [],saveBtnDisabled: false // 保存按钮是否禁用,防止表单重复提交}},// 页面渲染成功created() {if (this.$route.params.id) {this.fetchDataById(this.$route.params.id)}ScoreApi.getByDictionary(103).then(response => {this.optionsSubject = response.data.subject})},methods: {fetchDataById(id) {this.form.stuNumber = this.$route.params.id},saveOrUpdate() {// 禁用保存按钮this.saveBtnDisabled = truethis.saveData()},saveData() {ScoreApi.save(this.form).then(response => {this.$message({type: 'success',message: response.message})this.$router.push({ path: '/student/list' })})}}
}
</script>一共四个vue文件需要新添加

4.4 使用Axios进行接口请求

vue前端项目的请求一般都写在src/api文件夹下面,可以根据自己的后端接口来自定义请求的js方法。

在src/api文件夹下面新增两个js文件分别为,scores.js 和students.js 文件,两个文件的内容如下:

//scores.js文件内容import request from '@/utils/request'const api_name = 'http://127.0.0.1:9090/index/score/'
//请求的接口的基础路径export default {getList(params) {return request({url: `${api_name}/list`,method: 'get',params})},getByDictionary(key) {return request({url: `${api_name}/dic/get/${key}`,method: 'get'})},getListByStuNumber(number) {return request({url: `${api_name}/get/${number}`,method: 'get'})},deleteScore(id) {return request({url: `${api_name}/delete/${id}`,method: 'delete'})},save(info) {return request({url: `${api_name}/insert`,method: `post`,data: info})}
}//students.js文件内容
import request from '@/utils/request'const api_name = 'http://127.0.0.1:9090/index/stu/'export default {getList(params) {return request({url: `${api_name}/list`,method: 'get',params})},deleteStuent(stuId) {return request({url: `${api_name}/delete/${stuId}`,method: 'delete'})},getById(id) {return request({url: `${api_name}/get/${id}`,method: 'get'})},save(student) {return request({url: `${api_name}/insert`,method: `post`,data: student})},updateById(student) {return request({url: `${api_name}/update`,method: `post`,data: student})}
}

以上代码全部完成后,就可以在vs code 的终端中输入 npm run dev 然后回车启动项目(启动之前请先关闭运行,Ctrl+C 关闭)。启动成功及各个页面的效果如下。






到这里我们就实现了一个简单的前后端分离的Demo了,所有的代码均上传Gitee仓库,

https://gitee.com/lu-tiezhu/separationVue-boot.git小伙伴们可以通过直接下载代码或者Git到IDEA里,如果觉得这篇文章尚可,可以收藏点赞转发哦。

前后端分离:SpringBoot治好了我的时间内耗相关推荐

  1. 前后端分离(SpringBoot+Vue)-基础的权限管理系统

    前后端分离(SpringBoot+Vue)-基础的权限管理系统 简介 前端项目代码地址:前端代码 后端项目代码地址:后端代码 最后的附录记录了自己在开发过程遇到问题及实现.部分文件的介绍 采用前后端分 ...

  2. 网易音乐网站系统|前后端分离springboot+vue实现在线音乐网站

    作者主页:编程千纸鹤 作者简介:Java.前端.Python开发多年,做过高程,项目经理,架构师 主要内容:Java项目开发.毕业设计开发.面试技术整理.最新技术分享 收藏点赞不迷路  关注作者有好处 ...

  3. 前后端分离 springboot shiro+jwt token认证 权限校验

    项目源码 国涛/springboot-shiro-jwthttps://gitee.com/dugt/springboot-shiro-jwt GitHub - dugt-1998/springboo ...

  4. 疫苗预约|疫苗管理|前后端分离Springboot+Vue儿童疫苗预约系统

    作者主页:编程指南针 作者简介:Java领域优质创作者.CSDN博客专家 .掘金特邀作者.多年架构师设计经验.腾讯课堂常驻讲师 主要内容:Java项目.毕业设计.简历模板.学习资料.面试题库.技术互助 ...

  5. 前后端分离springboot+vue社区志愿者招募管理系统idea

    社区志愿者招募管理系统采用Spring.SpringMVC和MyBatis作为主体框架,系统设计遵循界面层.业务逻辑层和数据访问层的Web开发三层架构.采用B/S结构,使得系统更加容易维护.社区志愿者 ...

  6. 前后端分离必备工具:Swagger快速搞定(整合SpringBoot详细教程)

    本文根据狂神教学视屏同步所做笔记 目录 一.Swagger简介 1. 前后端分离 2. Swagger引入 二.SpringBoot集成Swagger 1. 新建springboot项目 2. 导入S ...

  7. SpringBoot+Vue前后端分离的三只松鼠商城实现毕业设计论文

    题目名称 基于SpringBoot的B2C三只松鼠 电商平台 学 院 理工农学院 专业年级 软件工程2018级 填写时间 2021年10月19日 目 录 错误!未找到图形项目表. 基于SpringBo ...

  8. 黑加仑妞 使用vue+flask做全栈开发的全过程(实现前后端分离)

    黑加仑妞 使用vue+flask做全栈开发的全过程(实现前后端分离) 花了几天的时间终于在本地把前后端跑通了,以一篇博客记录我这几天的心酸... 1.安装nodejs(自带npm,可能会出现版本错误, ...

  9. SpringBoot+Vue前后端分离项目的搭建及简单开发(这次保证看明白~)

    文章目录 概述 一.搭建SpringBoot后端 1.sql脚本 2.新建SpringBoot项目 3.MP代码生成 4.编写Controller 二.搭建Vue前端 1.IDEA安装Vue.js插件 ...

最新文章

  1. DFS:图的联通块 AOJ-0118 Property Distribution
  2. Vivado 随笔(1) 综合属性之 ram_style rom_style?
  3. 剑指offer(11-25题)详解
  4. 重新洗牌的网约车,谁在接管下沉市场?
  5. 关于用例需要多少文档以及业务用例等等
  6. IBM 计划在公司内部推行基于比特币的开源项目Hyperledger
  7. [PHP] 近期接手現有的企邮前端框架业务所遇困难
  8. mysql 命令 g_MySQL命令行的几个用法
  9. python机制_python异常机制个人理解(参考网上资料)
  10. java的equals方法_Java LocalDateTime类| 带示例的equals()方法
  11. STM32的CAN过滤器详解
  12. hadoop+hive+hbase+spark补充内容
  13. Vue packages version mismatch: 版本冲突;Error: EPERM: operation not permitted
  14. 【HDOJ】【3415】Max Sum of Max-K-sub-sequence
  15. 软件项目管理 2.3.项目章程
  16. Video Classification with Channel-Separated Convolutional Netwroks 论文阅读
  17. win7下计算机假死,造成Windows7系统假死的原因及处理方法
  18. 十大热门职位发布 高薪行业一目了然
  19. codeforces1467D. Sum of Paths
  20. K8S教程(7)使用探针对容器进行健康检查

热门文章

  1. lj245a引脚功能图_TI LJ245A做电平转换的问题
  2. Regex.Replace( )
  3. Swing开发教程从入门到实践(一)
  4. [实变函数]5.2 非负简单函数的 Lebesgue 积分
  5. 【Unity3D 教程系列第 16 篇】Scene视图很清楚,但是Game视图却很模糊的解决方案
  6. 车船税减半优惠最新名单出炉,仅有一款自主入
  7. FI--SAP财务成本知识库 .
  8. Struts2 OGNL标签
  9. Windows下默认远程下载
  10. 索尼mcx500切换台说明书_索尼MCX500特技切换台