java前后端分离开发常用的配置
针对在java开发中常用的配置 工具设置做一个总结
1 简化开发 提高效率 利用 mybatis-plus
第一步 maven构建项目,pom.xml引入依赖坐标
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.3.1version>
dependency>
在项目主目录的config包创建配置类MybatisPlusConfig
@Configuration // 添加注解;
@EnableTransactionManagement // 开启事务注解
@MapperScan("包名") // 包名扫描mapper包; 这里如果配置了,SpringBoot主启动类可以不用加@MapperScan注解
public class MybatisPlusConfig {
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor(){
return new OptimisticLockerInterceptor(); // 乐观锁配置;
}
@Bean
public PaginationInterceptor paginationInterceptor(){
return new PaginationInterceptor(); // 分页配置;
}
}
注意。如果你的mybatis-plus版本是mybatis-plus 3.4.0后的版本,那么配置就该如下了:
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 分页;
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());//乐观锁
return interceptor;
}
需要添加其他配置的话,就是设置interceptor,将其返回即可。
需要使用mybatis-plus的自动生成代码,创建实体类,service及其实现类,controller类等功能,需要进行以下步骤:
1 pom.xml引入依赖坐标
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-generatorartifactId>
<version>${mybatis-plus.version}version>
dependency>
<dependency>
<groupId>org.apache.velocitygroupId>
<artifactId>velocity-engine-coreartifactId>
<version>${velocity.version}version>
dependency>
${mybatis-plus.version} 和 ${velocity.version}是版本。
2 代码生成器类:
public class CodeGenerator {
@Test // 如果pom.xml测试是junit,就不需要@RunWith,junit单元测试不需要依赖Spring上下文环境;
public void genCode() {
// 1、创建代码生成器
AutoGenerator mpg = new AutoGenerator();
// 2、全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir"); // projectPath 获取的是就是x项目的根目录;
gc.setOutputDir(projectPath + "/src/main/java"); // java代码的位置;
gc.setAuthor("boger"); // 设置作者
gc.setOpen(false); //生成后是否打开资源管理器; 一般不打开;
gc.setServiceName("%sService"); //去掉Service接口的首字母I
gc.setIdType(IdType.AUTO); //主键策略
gc.setSwagger2(true);//开启Swagger2模式
mpg.setGlobalConfig(gc);
// 3、数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/数据库名?serverTimezone=GMT%2B8&characterEncoding=utf-8");
dsc.setDriverName("com.mysql.jdbc.Driver"); // 数据库驱动;
// 如果是mysql8.0版本,驱动就是com.mysql.cj.jdbc.Driver
dsc.setUsername("数据库用户名");
dsc.setPassword("数据库密码");
dsc.setDbType(DbType.MYSQL); /// 数据库类型;
mpg.setDataSource(dsc);
// 4、包配置
PackageConfig pc = new PackageConfig();
pc.setParent("源代码的包名");
pc.setEntity("pojo.entity"); //此对象与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。
mpg.setPackageInfo(pc);
// 5、策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
strategy.setEntityLombokModel(true); // lombok 生成@Data的注解;
strategy.setLogicDeleteFieldName("is_deleted"); //逻辑删除字段名,加入逻辑删除注解;
strategy.setEntityBooleanColumnRemoveIsPrefix(true); //去掉布尔值的is_前缀(确保tinyint(1))
strategy.setRestControllerStyle(true); //restful api风格控制器,在Controller类上添加@RestController注解;
mpg.setStrategy(strategy);
// 6、执行
mpg.execute();
}
}
运行此文件,就会把对应数据库的表结构,转换为java的实体类,并且会生成对应的mapper和xml,service 及其实现类 controller类。并且会加上相应的注解。简化创建实体 Java类。
对于数据库表结构的如果有创建时间(create_time) 更新时间(update_time)字段。如果需要根据业务的插入 更新来改变,有两种方法。
方法一: 数据库表结构的此字段 设计表勾选根据当前时间戳更新。
方法二: 先在实体类的对应字段添加注解。例如创建时间和更新时间。
@TableField(fill = FieldFill.INSERT) // 数据库字段是create_time,实体类是createTime,不需要做字段映射,Mybatisplus自动映射;
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
在配置包下面添加配置类MyMetaObjectHandler
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
public void insertFill(MetaObject metaObject) {
log.info("插入时自动填充");
this.setFieldValByName("createTime",new Date(),metaObject);
this.setFieldValByName("updateTime",new Date(),metaObject);
}
public void updateFill(MetaObject metaObject) {
log.info("更新时自动填充");
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}
注意 createTime updateTime 和实体类对应。
同时,注意代码生成器默认将mapper和对应的xml放在了src/main/java目录下。此时启动springboot项目,会出错。因为mapper和xml文件默认是放在资源目录下,即在resources目录下。此时,我们需要在pom.xml里面配置以下内容:
<build>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
resources>
build>
重新编译,启动就不会出错了。
2 统一结果返回类。
前后端开发,设置统一结果返回类,restful风格开发。
@Data // pom.xml需要lombok依赖
public class R {
private Integer code; // 返回状态码;
private String message; // 返回消息;
private Map<String, Object> data = new HashMap<>(); // 返回数据;
/**
* 构造器私有
*/
private R(){}
/**
* 返回成功
*/
public static R ok(){
R r = new R();
r.setCode(ResponseEnum.SUCCESS.getCode());
r.setMessage(ResponseEnum.SUCCESS.getMessage());
return r;
}
/**
* 返回失败
*/
public static R error(){
R r = new R();
r.setCode(ResponseEnum.ERROR.getCode());
r.setMessage(ResponseEnum.ERROR.getMessage());
return r;
}
/**
* 设置特定结果
*/
public static R setResult(ResponseEnum responseEnum){
R r = new R();
r.setCode(responseEnum.getCode());
r.setMessage(responseEnum.getMessage());
return r;
}
/**
* 设置特定的响应信息;
* @param message
* @return
*/
public R message(String message){
this.setMessage(message);
return this;
}
/**
* 设置特定的响应码;
* @param code
* @return
*/
public R code(Integer code){
this.setCode(code);
return this;
}
public R data(String key, Object value){
this.data.put(key, value);
return this;
}
public R data(Map<String, Object> map){
this.setData(map);
return this;
}
}
统一结果返回,每次后端返回的结果封装到R类中。R类设置返回的状态码 返回消息 以及数据。比如某个业务需要执行成功(不需要返回数据),我们返回
{
“code":200,
“message”,执行成功
}
如果执行成功并返回数据,我们返回
{
“code":200,
“message”,执行成功,
“data":需要返回的数据
}
这样的话,统一用json格式返回给前端。前端从Json数据取就可以了。
3 针对密码加密类 这里以MD5加密工具为例.
public final class MD5 {
public static String encrypt(String strSrc) {
try {
char hexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'a', 'b', 'c', 'd', 'e', 'f' };
byte[] bytes = strSrc.getBytes();
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(bytes);
bytes = md.digest();
int j = bytes.length;
char[] chars = new char[j * 2];
int k = 0;
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
chars[k++] = hexChars[b >>> 4 & 0xf];
chars[k++] = hexChars[b & 0xf];
}
return new String(chars);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException("MD5加密出错!!+" + e);
}
}
public static void main(String[] args) {
String s = "hello";
System.out.println("加密后的结果为"+ encrypt(s)); // 5d41402abc4b2a76b9719d911017c592
}
}
4 随机生成验证码工具 (常用于生成手机验证码业务 – 这里针对4或者6位的数字验证码)
public class RandomUtils {
private static final Random random = new Random();
private static final DecimalFormat fourdf = new DecimalFormat("0000");
private static final DecimalFormat sixdf = new DecimalFormat("000000");
public static String getFourBitRandom() {
return fourdf.format(random.nextInt(10000));
}
public static String getSixBitRandom() {
return sixdf.format(random.nextInt(1000000));
}
}
5 正则表达式工具 (用于校验 手机号 QQ号 身份证 邮箱工具等业务)
public class RegexValidateUtils {
static boolean flag = false;
static String regex = "";
public static boolean check(String str, String regex) {
try {
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
flag = matcher.matches();
} catch (Exception e) {
flag = false;
}
return flag;
}
/**
* 验证邮箱
* @param email
* @return
*/
public static boolean checkEmail(String email) {
String regex = "^\\w+[-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$ ";
return check(email, regex);
}
/**
* 验证手机号码
* 移动号码段:139、138、137、136、135、134、150、151、152、157、158、159、182、183、187、188、147
* 联通号码段:130、131、132、136、185、186、145
* 电信号码段:133、153、180、189
* @param cellphone
* @return
*/
public static boolean checkCellphone(String cellphone) {
String regex = "^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0,5-9]))\\d{8}$";
return check(cellphone, regex);
}
/**
* 验证固话号码
* @param telephone
* @return
*/
public static boolean checkTelephone(String telephone) {
String regex = "^(0\\d{2}-\\d{8}(-\\d{1,4})?)|(0\\d{3}-\\d{7,8}(-\\d{1,4})?)$";
return check(telephone, regex);
}
/**
* 验证传真号码
* @param fax
* @return
*/
public static boolean checkFax(String fax) {
String regex = "^(0\\d{2}-\\d{8}(-\\d{1,4})?)|(0\\d{3}-\\d{7,8}(-\\d{1,4})?)$";
return check(fax, regex);
}
/**
* 验证QQ号码
* @param QQ
* @return
*/
public static boolean checkQQ(String QQ) {
String regex = "^[1-9][0-9]{4,} $";
return check(QQ, regex);
}
}
6 统一异常处理类以及自定义异常类
@Slf4j
@Component //Spring容易自动管理
@RestControllerAdvice //在controller层添加通知。如果使用@ControllerAdvice,则方法上需要添加@ResponseBody 使用的是AOP切面编程;
public class UnifiedExceptionHandler {
@ExceptionHandler(value = Exception.class) //当controller中抛出Exception,则捕获
public R handleException(Exception e){
log.error(e.getMessage(),e); // 打印日志;
return R.error();
}
/**
* 特定异常 SQL异常;
*/
@ExceptionHandler(BadSqlGrammarException.class)
public R handleBadSqlGrammarException(BadSqlGrammarException e){
log.error(e.getMessage(), e);
return R.setResult(ResponseEnum.BAD_SQL_GRAMMAR_ERROR); // 返回特定的响应码;
}
/**
* 捕捉自定义异常类; 只需在业务层抛出自定义异常; throw new BusinessException(枚举响应码,响应信息)
* @param e
* @return
*/
@ExceptionHandler(value = BusinessException.class)
public R handleBusinessException(BusinessException e){
log.error(e.getMessage(),e);
return R.error().message(e.getMessage()).code(e.getCode());
}
/**
* Controller上一层相关异常; 即未进入controller方法之前执行的异常;
*/
@ExceptionHandler({
NoHandlerFoundException.class,
HttpRequestMethodNotSupportedException.class,
HttpMediaTypeNotSupportedException.class,
MissingPathVariableException.class,
MissingServletRequestParameterException.class,
TypeMismatchException.class,
HttpMessageNotReadableException.class,
HttpMessageNotWritableException.class,
MethodArgumentNotValidException.class,
HttpMediaTypeNotAcceptableException.class,
ServletRequestBindingException.class,
ConversionNotSupportedException.class,
MissingServletRequestPartException.class,
AsyncRequestTimeoutException.class
})
public R handleServletException(Exception e) {
log.error(e.getMessage(), e);
//SERVLET_ERROR(-102, "servlet请求异常"),
return R.error().message(ResponseEnum.SERVLET_ERROR.getMessage()).code(ResponseEnum.SERVLET_ERROR.getCode());
}
}
BusinessException 自定义异常类
@Data
@NoArgsConstructor
public class BusinessException extends RuntimeException{
private Integer code;
private String message;
/**
* @param message 错误消息
*/
public BusinessException(String message) {
this.message = message;
}
/**
*
* @param message 错误消息
* @param code 错误码
*/
public BusinessException(String message, Integer code) {
this.message = message;
this.code = code;
}
/**
* @param message 错误消息
* @param code 错误码
* @param cause 原始异常对象
*/
public BusinessException(String message, Integer code, Throwable cause) {
super(cause);
this.message = message;
this.code = code;
}
/**
* @param resultCodeEnum 接收枚举类型
*/
public BusinessException(ResponseEnum resultCodeEnum) {
this.message = resultCodeEnum.getMessage();
this.code = resultCodeEnum.getCode();
}
/**
* @param resultCodeEnum 接收枚举类型
* @param cause 原始异常对象
*/
public BusinessException(ResponseEnum resultCodeEnum, Throwable cause) {
super(cause);
this.message = resultCodeEnum.getMessage();
this.code = resultCodeEnum.getCode();
}
}
要完成统一异常类 和 自定义异常,还需要定义一个枚举类 ResponseEnum。响应枚举类ResponseEnum。可以设置一系列的状态码和信息。
示例如下:
@Getter
@AllArgsConstructor
@ToString
public enum ResponseEnum {
// 响应状态码
private Integer code;
// 响应信息
private String message;
SUCCESS(0, "成功"),
ERROR(-1, "服务器内部错误");
}
7 HttpUtils工具类 简化和抽象HTTP请求和响应的处理过程,提供方便易用的方法,使开发者能够更轻松地与服务器进行通信
@Slf4j
public final class HttpUtils {
static final String POST = "POST";
static final String GET = "GET";
static final int CONN_TIMEOUT = 30000;// ms
static final int READ_TIMEOUT = 30000;// ms
/**
* post 方式发送http请求.
* @param strUrl
* @param reqData
* @return
*/
public static byte[] doPost(String strUrl, byte[] reqData) {
return send(strUrl, POST, reqData);
}
/**
* get方式发送http请求.
* @param strUrl
* @return
*/
public static byte[] doGet(String strUrl) {
return send(strUrl, GET, null);
}
/**
* @param strUrl
* @param reqmethod
* @param reqData
* @return
*/
public static byte[] send(String strUrl, String reqmethod, byte[] reqData) {
try {
URL url = new URL(strUrl);
HttpURLConnection httpcon = (HttpURLConnection) url.openConnection();
httpcon.setDoOutput(true);
httpcon.setDoInput(true);
httpcon.setUseCaches(false);
httpcon.setInstanceFollowRedirects(true);
httpcon.setConnectTimeout(CONN_TIMEOUT);
httpcon.setReadTimeout(READ_TIMEOUT);
httpcon.setRequestMethod(reqmethod);
httpcon.connect();
if (reqmethod.equalsIgnoreCase(POST)) {
OutputStream os = httpcon.getOutputStream();
os.write(reqData);
os.flush();
os.close();
}
BufferedReader in = new BufferedReader(new InputStreamReader(httpcon.getInputStream(),"utf-8"));
String inputLine;
StringBuilder bankXmlBuffer = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
bankXmlBuffer.append(inputLine);
}
in.close();
httpcon.disconnect();
return bankXmlBuffer.toString().getBytes();
} catch (Exception ex) {
log.error(ex.toString(), ex);
return null;
}
}
/**
* 从输入流中读取数据
* @param inStream
* @return
* @throws Exception
*/
public static byte[] readInputStream(InputStream inStream) throws Exception {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outStream.write(buffer, 0, len);
}
byte[] data = outStream.toByteArray();// 网页的二进制数据
outStream.close();
inStream.close();
return data;
}
}
8 Swagger2 接口测试
使用Swagger2 能够方便地测试项目的接口,不需要依赖第三方软件例如postman等
第一步 导入依赖 pom.xml添加
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger2artifactId>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger-uiartifactId>
dependency>
第二步 添加配置: Swagger2Config配置类
@Configuration // 加上注解
@EnableSwagger2 //开启Swagger2
public class Swagger2Config {
/**
* 展示包含/admin/路由的路径;
* @return
*/
@Bean
public Docket adminApiConfig(){
return new Docket(DocumentationType.SWAGGER_2)
.groupName("adminApi")
.apiInfo(adminApiInfo())
.select()
//只显示admin路径下的页面
.paths(Predicates.and(PathSelectors.regex("/admin/.*")))
.build();
}
/**
* 展示包含/api/的路由;
* @return
*/
@Bean
public Docket webApiConfig(){
return new Docket(DocumentationType.SWAGGER_2)
.groupName("webApi")
.apiInfo(webApiInfo())
.select().paths(Predicates.and(PathSelectors.regex("/api/.*")))
.build();
}
/**
* /admin/的路由添加文档说明;
* @return
*/
private ApiInfo adminApiInfo(){
return new ApiInfoBuilder()
.title("xxxx管理系统-API文档")
.description("本文档描述了xxxx管理系统接口")
.version("1.0")
.contact(new Contact("boger", "网址", "邮箱"))
.build();
}
/**
* /api/的路由添加文档说明;
* @return
*/
private ApiInfo webApiInfo(){
return new ApiInfoBuilder()
.title("xxxx网站-API文档")
.description("本文档描述了xxxx网站接口")
.version("1.0")
.contact(new Contact("boger", "网址", "邮箱"))
.build();
}
}
对于路径带有admin表明是后台系统的业务路由,带有api主要针对是前台系统的业务。如果一个接口写好了,我们怎么利用Swagger2 测试接口呢?
启动项目,打开浏览器地址栏访问 localhost:项目端口/swagger-ui.html,利用界面操作测试很方便。
今天就到这里吧,后续继续针对java开发常用的工具以及配置类,进行总结 更新。