基于 Spring Boot 2.1.0 、 Spring Boot Jpa、 JWT、Spring Security、Redis、Mysql、Vue、Element-UI 的前后端分离的研发过程管理系统, 项目采用按功能分模块的开发方式,支持数据字典与数据权限管理,支持一键生成前后端代码,支持前端菜单动态路由等。
此项目用的开源项目Eladmin(2.3版本)
前端地址
后端地址
本系统安全框架使用的是 Spring Security + Jwt Token, 访问后端接口需在请求头中携带token进行访问,请求头格式如下:
用户登录 -> 后端验证登录返回 token -> 前端带上token请求后端数据 -> 后端返回数据, 数据交互流程如下:
Spring Security 提供了Spring EL表达式,允许我们在定义接口访问的方法上面添加注解,来控制访问权限,常用的 EL如下
下面的接口表示用户拥有 admin、menu:edit 权限中的任意一个就能能访问update方法, 如果方法不加@preAuthorize注解,意味着所有用户都需要带上有效的 token 后能访问 update 方法
由于每个接口都需要给超级管理员放行,而使用 hasAnyRole('admin','user:list') 每次都需要重复的添加 admin 权限,因此加入了自定义权限验证方式,在验证的时候默认给拥有admin权限的用户放行。
使用方式:
在我们使用的时候,有写接口是不需要验证权限,这个时候就需要我们给接口放行,使用方式如下
1、使用注解方式
只需要在Controller的方法上加入该注解即可
2、修改配置文件方式
system -> modules -> security -> config -> SecurityConfig
TIP
使用 permitAll() 方法所有人都能访问,包括带上 token 访问
使用 anonymous() 所有人都能访问,但是带上 token 访问后会报错
这里演示使用 Idea 创建一个新的子模块
选择 File -> New -> Module
选择 Maven -> Next
选择父模块 -> 定义name -> Finish
TIP
需要在 system 模块中的 AppRun 中配置注解
因为 springboot 默认扫描规则是扫描启动器类的同包或者其子包的下的注解
给新模块起一个名字
区分DO、VO、DTO的好处:
本项目对 Jpa 的查询进行了封装,现可以通过 @Query 注解实现简单的查询与复杂查询
简单查询:等于(默认)、大于等于、小于等于、左模糊、右模糊、中模糊、多字段模糊、NOT_EQUAL 、BETWEEN 、NOT_NULL。
复杂查询:包含(IN)查询、左连接、右连接等
1、创建一个查询类 QueryCriteria
2、在控制器中使用
3、Service 中查询
TIP
引入后需要点击右边Maven中的刷新一下Maven库
TIP
加了nativeQuery = true 可以直接使用sql,如果没加则需要用hsql,hsql这里不过多赘述,想详细了解的自行百度一下。
更新/删除操作需要增加注解@Modifying
使用形参有两种方式
第一种方式:
?1和?2分别指形参id和testString
第二种方式:
形参加注解@Param,sql中用:ids指代
第一种:
返回全部对象,则返回类型为具体对象,如:
第二种:
返回特定列,则返回List<Map<String,Object>>
如:个人中心页面
本项目的动态菜单支持到 4级 菜单,支持 外链,支持自定义图标,添加教程如下:
创建完菜单还需要在角色管理中给角色分配菜单
可以引入权限判断函数或者使用全局指令函数实现前端的权限控制1、使用全局指令函数v-permission=""
2、使用判断函数 checkPermission()
首先我们需要在字典管理中创建一个字典
数据字典使用一共有两种方式。
TIP
建议使用该方式
使用方式:
打印如下:
1、完整数据
2、简化后的label数据
(1)引入组件
(2)使用钩子函数获取字典
(3)使用字典
定义DO对象
TIP
一个对象序列化的接口,一个类只有实现了Serializable接口,它的对象才能被序列化。
序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。
1、把对象转换为字节序列的过程称为对象的序列化
2、把字节序列恢复为对象的过程称为对象的反序列化
当我们需要把对象的状态信息通过网络进行传输,或者需要将对象的状态信息持久化,以便将来使用时都需要把对象进行序列化。方便下次使用的时候,可以很快捷的重建一个副本。
TIP
其中T代表接口返回值类型,ID代表主键类型,当复合主键时,可以建一个主键对象。
@Service用在类上,注册为一个bean,bean名称默认为类名称(首字母小写),也可以手动指定@Service("abc")或@Service(value = "abc")
在src/views/下新建test目录
现在测试界面还是空空如也
看一下测试界面
测试界面已经多了一个表格
但是我们发现这个表格的数据都是固定,字段也不符合要求。
由于此时数据需要从数据库查询则需要新增api接口
在src/api/目录下
引入创建钩子函数,这个created会在界面创建时调用一次
调用查询函数并且赋值给tableData
实现以下需求:
参考:
看如下错误信息:
1、看错误信息首先看第一行,一般第一行会告诉我们确切的错误原因
这个错误原因显而易见是空指针
2、接着去在这大长串的错误栈信息里找属于我们自己包的第一条!
一般第一个出现我们自己包的错误是改错误发生的位置,但不一定是错误真的原因位置我们需要打断点去自己排查
因为此入参是传过来的,所以接着看错误信息第二行:
可以看到代码因为我想故意使他报错,所以真正的原因是此变量被强行赋值为null了;
我们一般说 Spring 框架指的都是 Spring Framework,它是很多模块的集合,使用这些模块可以很方便地协助我们进行开发。这些模块是:核心容器、数据访问/集成,、Web、AOP(面向切面编程)、工具、消息和测试模块。比如:Core Container 中的 Core 组件是Spring 所有组件的核心,Beans 组件和 Context 组件是实现IOC和依赖注入的基础,AOP组件用来实现面向切面编程。
Spring 官网列出的 Spring 的 6 个特征:
IoC(Inverse of Control:控制反转)是一种设计思想,就是 将原本在程序中手动创建对象的控制权,交由Spring框架来管理。 IoC 在其他语言中也有应用,并非 Spring 特有。 IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个Map(key,value),Map 中存放的是各种对象。
将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。 IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。 在实际项目中一个 Service 类可能有几百甚至上千个类作为它的底层,假如我们需要实例化这个 Service,你可能要每次都要搞清这个 Service 所有底层类的构造函数,这可能会把人逼疯。如果利用 IoC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。
Spring 时代之前一般通过 XML 文件来配置 Bean,后来开发人员觉得 XML 文件来配置不太好,于是 SpringBoot 注解配置就慢慢开始流行起来。
IoC源码阅读
AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
Spring AOP就是基于动态代理的,如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候Spring AOP会使用Cglib ,这时候Spring AOP会使用 Cglib 生成一个被代理对象的子类来作为代理。
使用 AOP 之后我们可以把一些通用功能抽象出来,在需要用到的地方直接使用即可,这样大大简化了代码量。我们需要增加新功能时也方便,这样也提高了系统扩展性。日志功能、事务管理等等场景都用到了 AOP 。
<resultMap>、<parameterMap>、<sql>、<include>、<selectKey>,加上动态 sql 的 9 个标签,trim|where|set|foreach|if|choose|when|otherwise|bind等,其中为 sql 片段标签,通过<include>标签引入 sql 片段,<selectKey>为不支持自增的主键生成策略标签。
面向过程 :面向过程性能比面向对象高。 因为类调用时需要实例化,开销比较大,比较消耗资源,所以当性能是最重要的考量因素的时候,比如单片机、嵌入式开发、Linux/Unix 等一般采用面向过程开发。但是,面向过程没有面向对象易维护、易复用、易扩展。
面向对象 :面向对象易维护、易复用、易扩展。 因为面向对象有封装、继承、多态性的特性,所以可以设计出低耦合的系统,使系统更加灵活、更加易于维护。但是,面向对象性能比面向过程低。
== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象(基本数据类型比较的是值,引用数据类型比较的是内存地址)。
equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:
举个例子:
说明:
主要根据集合的特点来选用,比如我们需要根据键值获取到元素值时就选用Map接口下的集合,需要排序时选择TreeMap,不需要排序时就选择HashMap,需要保证线程安全就选用ConcurrentHashMap.当我们只需要存放元素值时,就选择实现Collection接口的集合,需要保证元素唯一时选择实现Set接口的集合比如TreeSet或HashSet,不需要就选择实现List接口的比如ArrayList或LinkedList,然后再根据实现这些接口的集合的特点来选用。