java前后端分离开发常用的配置
针对在java开发中常用的配置 工具设置做一个总结
1 简化开发 提高效率 利用 mybatis-plus
第一步 maven构建项目,pom.xml引入依赖坐标
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.3.1</version></dependency>
在项目主目录的config包创建配置类MybatisPlusConfig
@Configuration // 添加注解;
@EnableTransactionManagement // 开启事务注解
@MapperScan("包名") // 包名扫描mapper包; 这里如果配置了,SpringBoot主启动类可以不用加@MapperScan注解
public class MybatisPlusConfig {@Beanpublic OptimisticLockerInterceptor optimisticLockerInterceptor(){return new OptimisticLockerInterceptor(); // 乐观锁配置;}@Beanpublic PaginationInterceptor paginationInterceptor(){ return new PaginationInterceptor(); // 分页配置;}}
注意。如果你的mybatis-plus版本是mybatis-plus 3.4.0后的版本,那么配置就该如下了:
@Beanpublic 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.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>${mybatis-plus.version}</version>
</dependency><dependency><groupId>org.apache.velocity</groupId><artifactId>velocity-engine-core</artifactId><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接口的首字母Igc.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.Driverdsc.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/java</directory><includes><include>**/*.xml</include></includes><filtering>false</filtering></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;// msstatic 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添加
<!--swagger--><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId></dependency><!--swagger ui--><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId></dependency>
第二步 添加配置: Swagger2Config配置类
@Configuration // 加上注解
@EnableSwagger2 //开启Swagger2
public class Swagger2Config {/*** 展示包含/admin/路由的路径;* @return*/@Beanpublic Docket adminApiConfig(){return new Docket(DocumentationType.SWAGGER_2).groupName("adminApi").apiInfo(adminApiInfo()).select()//只显示admin路径下的页面.paths(Predicates.and(PathSelectors.regex("/admin/.*"))).build();}/*** 展示包含/api/的路由;* @return*/@Beanpublic 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开发常用的工具以及配置类,进行总结 更新。