项目描述
一个项目中有很多资源,而资源的表现形式就是功能菜单,通过将不同菜单赋给不同的角色,而不同的用户拥有不同的角色来达到权限控制
原型设计
业务关系及表设计
Set names utf8;
source d:/dbpms.sql
核心 API 分析与设计
项目构建
parent中只有依赖,parent打包方式为pom
- common中是项目共性代码,实现复用
- 统一结果返回规范
- 全局异常处理
- 拦截器
- 工具类
- 切面
- …….
- admin中是具体业务的实现代码
父模块
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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--表示以下的模块依赖依赖继承我,添加依赖继承后自动生成-->
<modules>
<module>05-dbpms-common</module>
<module>05-dbpms-admin</module>
</modules>
<!--Spring Boot parent 依赖-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.8.RELEASE</version>
</parent>
<!--当前项目工程的坐标-->
<groupId>org.cy</groupId>
<artifactId>05-dbpms-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<!--定义一些依赖的版本,有些没有定义版本依赖是因为Spring boot parent中自带了某些依赖的版本-->
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<mybatis.starter>2.1.3</mybatis.starter>
<pagehelper.starter>1.3.0</pagehelper.starter>
<spring.shiro>1.7.1</spring.shiro>
</properties>
<!--依赖-->
<dependencies>
<!--Spring DataSource-->
<!--mysql驱动依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--JDBC依赖,封装了HikariCP数据库连接池-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<!--Spring mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.starter}</version>
</dependency>
<!--PageHelper (基于mybatis框架实现的分页插件)-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pagehelper.starter}</version>
</dependency>
<!--Spring Web (提供了web请求的分层设计)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Spring AOP (提供面向切面的实现)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--Lombok (提供对类的字节码功能增强(类中添加set,get,toString,...))-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!--热部署依赖(修改完业务代码和配置文件时自动重启服务)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<!--Spring Shiro (权限管理框架)-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>${spring.shiro}</version>
</dependency>
<!--单元测试依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<!--排除一些不需要的依赖-->
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<!--项目构建或打包时候需要的而一些依赖-->
<plugins>
<!--假如当前插件显示红色,可以为其指定version-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.8.RELEASE</version>
</plugin>
</plugins>
</build>
</project>
通用模块
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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<!--在创建此maven项目时,指定自己继承依赖于05-dbpms-parent项目-->
<parent>
<artifactId>05-dbpms-parent</artifactId>
<groupId>org.cy</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>05-dbpms-common</artifactId>
<groupId>org.cy</groupId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>
领域对象设计
- Node
- 基于此对象存储节菜单节点信息 ```java package com.cy.pj.common.pojo;
import lombok.Data; import java.io.Serializable; //菜单的结构图对应的对象 @Data public class Node implements Serializable { private static final long serialVersionUID = -5843725325348704821L; private Integer id; private String name; private String parentId; }
- CheckBox
- 存储复选框选项信息
```java
package com.cy.pj.common.pojo;
@Data
public class CheckBox implements Serializable{
private static final long serialVersionUID = 5531553051566760402L;
private Integer id;
private String name;
}
- JsonResult
- 统一响应标准 API,对服务端响应到客户端的数据进行统一标准设计 ```java package com.cy.pj.common.pojo;
/**
通过此对象封装服务端响应到客户端的数据,让数据以一种规范化的格式呈现给客户端 / public class JsonResult implements Serializable{ private static final long serialVersionUID = 5531553051566760402L; /**状态码/ private Integer state = 1; //1表示OK,0表示Error /状态码对应的信息*/ private String message=”ok”; /封装正确的查询结果*/ private Object data;
public JsonResult(){} public JsonResult(String message){
this.message = message;
} public JsonResult(Integer state, String message){
this(message); this.state = state;
} public JsonResult(Object data){ //new JsonResult(list)
this.data = data;
} //当出现异常时,可以通过此构造方法对异常信息进行封装 public JsonResult(Throwable exception){ //new JsonResult(exception)
this(0,exception.getMessage());
日志切面
- 定义切入点注解 ```java package com.cy.pj.common.annotation;
/**
- 自定义注解,希望通过此注解对一些业务方法做标记(对方法进行描述)
- ,在面向切面编程中,被该注解标记的方法为我们的切入点方法。
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequiredLog {
String operation();
}
```
异常类设计
定义业务层特定异常处理类,基于此类封装业务层异常信息
package com.cy.pj.common.exception; /**自定义业务异常*/ public class ServiceException extends RuntimeException{ public ServiceException() { } public ServiceException(String message) { super(message); } public ServiceException(Throwable cause) { super(cause); } }
工具类
IpUtil
- 获得请求IP的工具类 ```java package com.cy.pj.common.util;
public class IpUtil { private static Logger log = LoggerFactory.getLogger(IpUtil.class);
public static String getIpAddr() {
HttpServletRequest request = ((ServletRequestAttributes)
RequestContextHolder.getRequestAttributes()).getRequest();
String ip = null;
try {
ip = request.getHeader("x-forwarded-for");
if (StringUtil.isEmpty(ip) ||
"unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (StringUtil.isEmpty(ip) || ip.length() == 0 ||
"unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtil.isEmpty(ip) ||
"unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (StringUtil.isEmpty(ip) ||
"unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (StringUtil.isEmpty(ip) ||
"unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
} catch (Exception e) {
log.error("IPUtils ERROR ", e);
}
return ip;
}
}
- StringUtil
- 判断字符串是否为null或空串的工具类
```java
package com.cy.pj.common.util;
public class StringUtil {
public static boolean isEmpty(String str){
return str==null||"".equals(str);
}
}
- ServletUtil
- 获得请求对象的工具类 ```java package com.cy.pj.common.util;
public class ServletUtil { /*获取请求对象/ public static HttpServletRequest getRequest(){ return getServletRequestAttributes().getRequest(); }
/**通过RequestContextHolder类型获取请求属性*/
public static ServletRequestAttributes getServletRequestAttributes(){
return (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
}
}
- PageUtil
- 分页工具类,使用了分页插件PageHelper
```java
package com.cy.pj.common.util;
public class PageUtil {
/**
* 通过此方法启动分页查询
* @param <T> 这里的T为泛型,返回值类型左侧有<T>这种符号的表示方法为泛型方法
* @return
*/
public static <T>Page<T> startPage(){
HttpServletRequest request = ServletUtil.getRequest();
//页面大小(每页最多显示多少条记录)
String pageSizeStr = request.getParameter("pageSize");
//当前页码值(要查第几页的数据)
String pageCurrentStr = request.getParameter("pageCurrent");
System.out.println(pageSizeStr);
System.out.println(pageCurrentStr);
//在此位置调用PageHelper中的一个方法启动分页
//在项目中去添加一个PageHelper依赖(后缀是starter的)
Integer pageCurrent = StringUtil.isEmpty(pageCurrentStr)?1:Integer.parseInt(pageCurrentStr);
Integer pageSize = StringUtil.isEmpty(pageSizeStr)?10:Integer.parseInt(pageSizeStr);
//启动PageHelper中的分页拦截器(PageInterceptor)
return PageHelper.startPage(pageCurrent, pageSize);
}
}
统一异常处理
package com.cy.pj.common.web;
/**
* 此注解描述的类为全局异常处理类
* 从控制层传来的异常会经过前端处理器,前端处理器找被@RestControllerAdvice修饰的全局异常处理类来处理
* 传过来的异常类型是@ExceptionHandler注解定义的异常类型(此处为RuntimeException)或者它的子异常类型时
* 才可以使用注解@ExceptionHandler所修饰的方法进行处理
*/
@RestControllerAdvice //@ControllerAdvice+@ResponseBody
@Slf4j
public class GlobalExceptionHandler {
//private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**登录时异常处理
*/
@ExceptionHandler(ShiroException.class)
public JsonResult doShiroException(ShiroException e){
e.printStackTrace();
JsonResult jr = new JsonResult();
jr.setState(0);
if (e instanceof UnknownAccountException){
jr.setMessage("账户不存在");
}else if (e instanceof IncorrectCredentialsException){
jr.setMessage("密码不正确");
}else if (e instanceof LockedAccountException){
jr.setMessage("账户被锁定");
}else if (e instanceof AuthorizationException){
jr.setMessage("没有权限");
}else {
jr.setMessage("认知或授权失败");
}
return jr;
}
/**@ExceptionHandler注解描述的方法为异常处理方法,
* 注解中定义的异常类型为方法可以处理的异常类型.*/
@ExceptionHandler(RuntimeException.class)
public JsonResult doHandleRuntimeException(RuntimeException e){
e.printStackTrace(); //打印异常信息到控制台
log.error("exception msg is {}"+e.getMessage());
return new JsonResult(e);
}
//......
}
拦截器
定义时间拦截器对象
package com.cy.pj.common.web; /** * 定义spring web 模块中的拦截器,通过此拦截器对handler中某些方法的进行时间访问限制。 */ @Slf4j //lombok会在当前类编译成class文件时,自动在类文件中添加一个log对象 public class TimeAccessInterceptor implements HandlerInterceptor { // private static final Logger log= LoggerFactory.getLogger(TimeAccessInterceptor.class); /**preHandle方法会在目标handler方法执行之前进行访问拦截 * 方法返回值为true时表示请求放行,false表示请求到此结束, * 不再去执行handler中的方法 * */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { LocalTime now = LocalTime.now(); //JDK8中的时间对象 int hour = now.getHour(); //获取当前时间对应小时 log.info("hour="+hour); if(hour<8 || hour>22){ throw new RuntimeException("请在8-22点进行访问"); } return true; } }
SpringWebConfig
- 定义 SpringWebConfig 对象,用于注册拦截器,过滤器等 ```java package com.cy.pj.common.web;
@Configuration //此注解为spring中的一个配置类bean对象 public class SpringWebConfig implements WebMvcConfigurer { /注册拦截器,并且设置要拦截的路径,此方法会在项目启动时就会调用*/ @Override public void addInterceptors(InterceptorRegistry registry) { //注册拦截器,并且设置要拦截的路径 //1、注册拦截器(将拦截器添加到spring容器) registry.addInterceptor(new TimeAccessInterceptor()) //2、设置要拦截的url, .addPathPatterns(“/notice/“); //表示通配符,**表示多层url,拦截所有 .addPathPatterns(“/notice/doSelectBySysNotice”,”/notice/doDeleteSysNotice/“); } }
<a name="A75Pm"></a>
### 主模块
<a name="eHXtJ"></a>
#### pom
```xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<!--在创建此maven项目时,指定自己继承依赖于05-dbpms-parent项目-->
<parent>
<artifactId>05-dbpms-parent</artifactId>
<groupId>org.cy</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>05-dbpms-admin</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--添加common依赖,让admin工程依赖于common工程-->
<dependency>
<groupId>org.cy</groupId>
<artifactId>05-dbpms-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
配置文件 application.yml
#server.port server: port: 8088 #spring spring: datasource: url: jdbc:mysql:///dbpms?serverTimezone=GMT%2B8&characterEncoding=utf8 username: root password: tarena #mybatis mybatis: mapper-locations: classpath:/mapper/*/*.xml #logging logging: level: com.cy: debug #shiro shiro: loginUrl: /login.html
添加启动类
package com.cy; @SpringBootApplication public class DbpmsApplication { public static void main(String[] args) { SpringApplication.run(DbpmsApplication.class, args); } }
启动…