🐇Gradle
Gradle Wrapper实际上就是一个脚本,使用它可以下载和使用指定版本的gradle,根据需要进行在使用之前进行下载,有效避免本地机器的设定等环境一致性问题。
虽然gradle的安装已经非常简单,但是使用gradle wrapper是的开发这能够以一种更为标准化的方式创建gradle项目。
每一个用gradle编译的工程,都会有一个gradle/wrapper
目录,目录下的两个文件:
gradle-wrapper.jar
:用于下载Gradle的相关代码实现
gradle-wrapper.properties
:wrapper所使用的配置信息,比如gradle的版本等信息
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionBase
和distributionPath
组合在一起,是解压gradle-4.8.1-bin.zip之后的文件的存放位置。distributionPath
是distributionBase
指定的目录下的子目录。
下载位置可以和解压位置不一样。
zipStoreBase
和distributionBase
有两种取值:GRADLE_USER_HOME
和PROJECT
。
其中,GRADLE_USER_HOME
表示用户目录。
在Windows下是%USERPROFILE%/.gradle
,例如C:\Users\<user_name>\.gradle\
。
在Linux下是$HOME/.gradle
,例如~/.gradle
。
PROJECT
表示工程的当前目录,即gradlew
所在的目录。
各种属性的具体含义如下:
- 去
https\://services.gradle.org/distributions/gradle-4.8.1-bin.zip
下载gradle的4.8.1版本 - 下载的gradle-4.8.1-bin.zip存放到
C:\Users\<user_name>\.gradle\wrapper\dists
目录中
(注:具体还有2级目录,即全路径为C:\Users\<user_name>\.gradle\wrapper\dists\gradle-3.1-bin\<url-hash>\
,gradle-3.1-bin目录是根据下载的gradle的文件名来定的,目录名是根据distribution url路径字符串计算md5值得来的,具体参考PathAssembler.java中的rootDirName()和getHash(),PathAssembler.java的位置见本文最后的参考路径) - 解压gradle-4.8.1-bin.zip,将解压后的文件存放到
C:\Users\<user_name>\.gradle\wrapper\dists
中(注:具体还有2级目录,同上)
🐇build.gradle
buildscript {
//ext用于定义属性
ext {
springBootVersion = '1.5.10.RELEASE'
}
//使用了Maven的中央仓库(也可指定其他仓库)
repositories {
maven {url 'http://maven.aliyun.com/nexus/content/groups/public/'}
}
//依赖关系
dependencies {
//classpath 声明在执行其它脚本时ClassLoader可以使用这些依赖库
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
//使用插件
apply plugin: 'java'
apply plugin: 'eclipse-wtp'
apply plugin: 'org.springframework.boot'
apply plugin: 'war'
group = 'com.zmh'
version = '0.0.1'
//指定编译.java文件的JDK版本
sourceCompatibility = 1.8
//默认使用Maven的中央仓库,此处改用自定义的镜像库
repositories {
maven {url 'http://maven.aliyun.com/nexus/content/groups/public/'}
}
//configurations是依赖集合,可为工程声明额外依赖
configurations {
providedRuntime
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-web')
//数据库相关
compile('mysql:mysql-connector-java')
compile('org.mybatis.spring.boot:mybatis-spring-boot-starter:1.3.1')
//友好的HTML规范 搭配spring.thymeleaf.mode=LEGACYHTML5
compile('org.springframework.boot:spring-boot-starter-thymeleaf')
compile('net.sourceforge.nekohtml:nekohtml:1.9.22')
//热部署 搭配spring.thymeleaf.cache=false
compile('org.springframework.boot:spring-boot-devtools')
//SpringBoot内置的Redis
compile('org.springframework.boot:spring-boot-starter-data-redis')
//内置tomcat 仅开发测试用
//runtime('org.springframework.boot:spring-boot-starter-tomcat')
//spring-boot-admin 图形化管理页面
compile('de.codecentric:spring-boot-admin-server:1.5.7')
compile('de.codecentric:spring-boot-admin-server-ui:1.5.7')
compile('de.codecentric:spring-boot-admin-starter-client:1.5.7')
//pagehelper
compile group: 'com.github.pagehelper', name: 'pagehelper-spring-boot-starter', version: '1.2.3'
//使用的是shiro-spring 而非shiro
compile('org.apache.shiro:shiro-spring:1.4.0')
compile('com.github.theborakompanioni:thymeleaf-extras-shiro:1.2.1')
//测试
testCompile('org.springframework.boot:spring-boot-starter-test')
}
🐇gradlew.bat
针对Windows的shell脚本与批处理命令,当项目被push到远程后,其他用户使用Git克隆下来,只需在本地执行./gradlew
即可进行项目的构建与任务的执行,同时用户本机并不需要提前安装好Gradle分发包.
🚢代码分析
controller–>service–>mapper
- AdminController
在页面标签栏里显示日志文件
package com.dwz.xietongoa.controller;
import com.dwz.xietongoa.dto.ReturnDto;
import com.dwz.xietongoa.util.ReadFileUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author DWZ
* @date 2020/4/18 16:24
* 在页面标签栏里显示日志文件
*/
@Controller
@RequestMapping(value = "/admin")
public class AdminController {
//这两个参数从application.properties获取
@Value("${logback.filepath}")
private String filePath;
@Value("${logback.charset}")
private String charSet;
/**
* 获取所有日志文件的文件名
* @return
*/
@RequestMapping(value = "/getFileNames")
@ResponseBody
public ReturnDto getFileNames(){
return ReadFileUtil.getFileName(filePath);
}
/**
* 获取所有日志文件的文件名
* @return
*/
@RequestMapping(value = "/readFiles")
@ResponseBody
public ReturnDto readFiles(@RequestParam("fileName")String fileName){
return ReadFileUtil.readFileByLines(filePath,fileName,charSet);
}
/**
* 日志
*/
@RequestMapping(value = "/logs")
public String logs(){
return "logs";
}
/**
* 系统监控
*/
@RequestMapping(value = "/springbootadmin")
public String springbootadmin(){
return "springbootadmin";
}
}
- CalendarController-CalendarMapper
日程表
package com.dwz.xietongoa.controller;
import com.dwz.xietongoa.dto.ReturnDto;
import com.dwz.xietongoa.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
@Controller
public class CalendarController {
@Autowired
private RedisService redisService;
/**
* 日程表
*/
@RequestMapping(value = "/calendar")
public String calendar() {
return "calendar";
}
/**
* 缓存日程表备注
* key是 calendar加userID
* value是 JSON格式备注
*/
@RequestMapping(value = "/calendarSetValue")
@ResponseBody
public ReturnDto calendarSetValue(@RequestParam("value") String value, HttpServletRequest request) {
String key = "calendar_" + request.getSession().getAttribute("userID");
redisService.setValue(key, value);
return ReturnDto.buildSuccessReturnDto();
}
/**
* 获取日程表备注
* key是 calendar加userID
* return是 JSON格式备注
*/
@RequestMapping(value = "/calendarGetValue")
@ResponseBody
public ReturnDto calendarGetValue(HttpServletRequest request) {
String key = "calendar_" + request.getSession().getAttribute("userID");
String value = redisService.getValue(key);
if (value != null) {
return ReturnDto.buildSuccessReturnDto(value);
} else {
return ReturnDto.buildFailedReturnDto("value is null");
}
}
}
package com.dwz.xietongoa.mapper;
import com.dwz.xietongoa.model.Calendar;
import com.dwz.xietongoa.model.CalendarExample;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface CalendarMapper {
long countByExample(CalendarExample example);
int deleteByExample(CalendarExample example);
int deleteByPrimaryKey(Integer id);
int insert(Calendar record);
int insertSelective(Calendar record);
List<Calendar> selectByExample(CalendarExample example);
Calendar selectByPrimaryKey(Integer id);
int updateByExampleSelective(@Param("record") Calendar record, @Param("example") CalendarExample example);
int updateByExample(@Param("record") Calendar record, @Param("example") CalendarExample example);
int updateByPrimaryKeySelective(Calendar record);
int updateByPrimaryKey(Calendar record);
}
- DepartmentController-DepartmentsMapper-DepartmentService
package com.dwz.xietongoa.controller;
import com.dwz.xietongoa.dto.ReturnDto;
import com.dwz.xietongoa.model.Departments;
import com.dwz.xietongoa.service.DepartmentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
/**
* Created by DWZ
* 2020/4/14
* @author dwz
*/
@Controller
//用来标识http请求地址与Controller类的方法之间的映射
@RequestMapping("/department")
public class DepartmentController {
@Autowired
DepartmentService departmentService;
/**
* 查询部门
* @return
*/
@RequestMapping(value = "/query")
@ResponseBody
public ReturnDto queryDepartment(){
List<Departments> departments = departmentService.queryDepartment();
return ReturnDto.buildSuccessReturnDto(departments);
}
}
package com.dwz.xietongoa.mapper;
import com.dwz.xietongoa.model.Departments;
import com.dwz.xietongoa.model.DepartmentsExample;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface DepartmentsMapper {
long countByExample(DepartmentsExample example);
int deleteByExample(DepartmentsExample example);
int deleteByPrimaryKey(Integer id);
int insert(Departments record);
int insertSelective(Departments record);
List<Departments> selectByExample(DepartmentsExample example);
Departments selectByPrimaryKey(Integer id);
int updateByExampleSelective(@Param("record") Departments record, @Param("example") DepartmentsExample example);
int updateByExample(@Param("record") Departments record, @Param("example") DepartmentsExample example);
int updateByPrimaryKeySelective(Departments record);
int updateByPrimaryKey(Departments record);
}
- IndexController
- LoginController
- MessageController-MessagesMapper-MessageService
- NoticeController-NoticesMapper-NoticeService
- PositionController-PositionsMapper-PositionService
- UserinfoController-UserinfoMapper-UserinfoService
- UsersController-UsersMapper-UsersService
🐇Mapper
Mapper.xml映射文件中定义了操作数据库的sql,每一个sql都是一个statement,映射文件是MyBatis的核心.MyBatis的真正强大在于映射语句,映射器的xml文件显得相对简单。与具有相同功能的JDBC代码进行对比,节省了将近95%的代码。MyBatis是针对SQL构建的,而且比普通方法做得好。映射文件是以<mapper>
为根节点,在节点中支持9个元素,分别是cache,cache-ref,resultMap,parameterMap,sql,insert,update,delete,select
。
- 🌿
insert、update、delet
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">
<!-- mapper 为根元素节点, 一个namespace对应一个dao -->
<mapper namespace="com.dy.dao.UserDao">
<insert
<!-- 1. id (必须配置)
id是命名空间中的唯一标识符,可被用来代表这条语句。
一个命名空间(namespace) 对应一个dao接口,
这个id也应该对应dao里面的某个方法(相当于方法的实现),因此id 应该与方法名一致 -->
id="addUser"
<!-- 2. parameterType (可选配置, 默认为mybatis自动选择处理)
将要传入语句的参数的完全限定类名或别名, 如果不配置,mybatis会通过ParameterHandler 根据参数类型默认选择合适的typeHandler进行处理
parameterType 主要指定参数类型,可以是int, short, long, string等类型,也可以是复杂类型(如对象) -->
parameterType="user"
<!-- 3. flushCache (可选配置,默认配置为true)
将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:true(对应插入、更新和删除语句) -->
flushCache="true"
<!-- 4. statementType (可选配置,默认配置为PREPARED)
STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 -->
statementType="PREPARED"
<!-- 5. keyProperty (可选配置, 默认为unset)
(仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键值,默认:unset。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。 -->
keyProperty=""
<!-- 6. keyColumn (可选配置)
(仅对 insert 和 update 有用)通过生成的键值设置表中的列名,这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。 -->
keyColumn=""
<!-- 7. useGeneratedKeys (可选配置, 默认为false)
(仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系数据库管理系统的自动递增字段),默认值:false。 -->
useGeneratedKeys="false"
<!-- 8. timeout (可选配置, 默认为unset, 依赖驱动)
这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。 -->
timeout="20">
</mapper>
- 🌿logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--debug当次属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态,默认值为false-->
<!--根节点configuration还包含其他的两个属性:scan和scanPeriod.scan设置为true时配置文件如果发生改变也将会被重新加载,默认为false.scanPeriod设置检测配置文件是否有修改的时间间隔(默认为毫秒),默认时间为1分钟-->
<configuration debug="false">
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
<property resource="application.properties" />
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!--encoder默认配置为PatternLayoutEncoder-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<!--encoder负责两件事:一是把日志信息转换成字节数组,二是把字节数组写入到输出流,目前PatternLayoutEncoder是唯一有用的且默认的encoder,有一个pattern节点用来设置日志的输入格式-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<!-- 按照每天生成日志文件 -->
<!--appender是configuration的子节点,是负责写日志的组件,有两个必要属性name和class,name指appender名称,class指appender的全限定名-->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名 ${logback.filepath}是从application.properties里面取的-->
<FileNamePattern>${logback.filepath}/projectoa.%d{yyyyMMdd}.log</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
<!--日志文件最大的大小-->
<!--triggeringPolicy告知RollingFileAppender适合激活滚动-->
<!--SizedBasedTriggeringPolicy查看当前活动文件的大小,如果超过指定大小会告知-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<!--maxFileSize是活动文件的大小,默认值是10MB-->
<MaxFileSize>10MB</MaxFileSize>
</triggeringPolicy>
</appender>
<!--myibatis log configure-->
<!--将ibatis,Connection,Statement等所有类的日志打印出来,并分别设置日志级别-->
<logger name="com.apache.ibatis" level="TRACE"/>
<logger name="java.sql.Connection" level="DEBUG"/>
<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>
<!-- 日志输出级别 默认WARN -->
<root level="WARN">
<!--appender的配置表示打印到控制台-->
<!--未设置addtivity默认为true,因此此logger的打印信息不再向上级传递并且制定了名字为"STDOUT"的appender-->
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>
<!--日志异步到数据库 -->
<!--<appender name="DB" class="ch.qos.logback.classic.db.DBAppender">-->
<!--<!–日志异步到数据库 –>-->
<!--<connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">-->
<!--<!–连接池 –>-->
<!--<dataSource class="com.mchange.v2.c3p0.ComboPooledDataSource">-->
<!--<driverClass>com.mysql.jdbc.Driver</driverClass>-->
<!--<url>jdbc:mysql://127.0.0.1:3306/databaseName</url>-->
<!--<user>root</user>-->
<!--<password>root</password>-->
<!--</dataSource>-->
<!--</connectionSource>-->
<!--</appender>-->
</configuration>