今天接着上一篇博文,聊 java开发常用的工具以及配置类。
13 日期操作相关的java类。
比如在开发预约旅游 预约体检 预约售票等相关和日期相关的业务,需要获取日期以及判断是一周的哪一天等。
/**
* 日期操作工具类
*/
public class DateUtils {
/**
* 日期转换- 字符串时间 转换为Date类型时间;
*
* @param dateString 字符串时间
* @return Date类型信息
* @throws Exception 抛出异常
*/
public static Date parseString2Date(String dateString) throws Exception {
if (dateString == null) {
return null;
}
return parseString2Date(dateString, "yyyy-MM-dd");
}
/**
* 日期转换- String -> Date 转换为指定格式的时间类型;
*
* @param dateString 字符串时间
* @param pattern 格式模板
* @return Date类型信息
* @throws Exception 抛出异常
*/
public static Date parseString2Date(String dateString, String pattern) throws Exception {
if (dateString == null) {
return null;
}
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
Date date = sdf.parse(dateString);
return date;
}
/**
* 日期转换 Date -> 字符串时间
* @param date Date类型信息
* @return 字符串时间
* @throws Exception 抛出异常
*/
public static String parseDate2String(Date date) throws Exception {
if (date == null) {
return null;
}
return parseDate2String(date, "yyyy-MM-dd");
}
/**
* 日期转换 Date -> 字符串时间 指定格式;
*
* @param date Date类型信息
* @param pattern 格式模板
* @return 字符串时间
* @throws Exception 抛出异常
*/
public static String parseDate2String(Date date, String pattern) throws Exception {
if (date == null) {
return null;
}
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
String strDate = sdf.format(date);
return strDate;
}
/**
* 获取当前日期的本周一是几号
* @return 本周一的日期
*/
public static Date getThisWeekMonday() {
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
// 获得当前日期是一个星期的第几天
int dayWeek = cal.get(Calendar.DAY_OF_WEEK);
if (1 == dayWeek) {
cal.add(Calendar.DAY_OF_MONTH, -1);
}
// 设置一个星期的第一天,一个星期的第一天是星期一
cal.setFirstDayOfWeek(Calendar.MONDAY);
// 获得当前日期是一个星期的第几天
int day = cal.get(Calendar.DAY_OF_WEEK);
// 根据日历的规则,给当前日期减去星期几与一个星期第一天的差值
cal.add(Calendar.DATE, cal.getFirstDayOfWeek() - day);
return cal.getTime();
}
/**
* 获取当前日期周的最后一天
* @return 当前日期周的最后一天
*/
public static Date getSundayOfThisWeek() {
Calendar c = Calendar.getInstance();
int dayOfWeek = c.get(Calendar.DAY_OF_WEEK) - 1;
if (dayOfWeek == 0) {
dayOfWeek = 7;
}
c.add(Calendar.DATE, -dayOfWeek + 7);
return c.getTime();
}
/**
* 根据日期区间获取月份列表
* @param minDate 开始时间
* @param maxDate 结束时间
* @return 月份列表
* @throws Exception
*/
public static List<String> getMonthBetween(String minDate, String maxDate, String format) throws Exception {
ArrayList<String> result = new ArrayList<>();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
Calendar min = Calendar.getInstance();
Calendar max = Calendar.getInstance();
min.setTime(sdf.parse(minDate));
min.set(min.get(Calendar.YEAR), min.get(Calendar.MONTH), 1);
max.setTime(sdf.parse(maxDate));
max.set(max.get(Calendar.YEAR), max.get(Calendar.MONTH), 2);
SimpleDateFormat sdf2 = new SimpleDateFormat(format);
Calendar curr = min;
while (curr.before(max)) {
result.add(sdf2.format(curr.getTime()));
curr.add(Calendar.MONTH, 1);
}
return result;
}
/**
* 根据日期获取年度中的周索引
* @param date 日期
* @return 周索引
* @throws Exception
*/
public static Integer getWeekOfYear(String date) throws Exception {
Date useDate = parseString2Date(date);
Calendar cal = Calendar.getInstance();
cal.setTime(useDate);
return cal.get(Calendar.WEEK_OF_YEAR);
}
/**
* 根据年份获取年中周列表
* @param year 年分
* @return 周列表
* @throws Exception
*/
public static Map<Integer, String> getWeeksOfYear(String year) throws Exception {
Date useDate = parseString2Date(year, "yyyy");
Calendar cal = Calendar.getInstance();
cal.setTime(useDate);
//获取年中周数量
int weeksCount = cal.getWeeksInWeekYear();
Map<Integer, String> mapWeeks = new HashMap<>(55);
for (int i = 0; i < weeksCount; i++) {
cal.get(Calendar.DAY_OF_YEAR);
mapWeeks.put(i + 1, parseDate2String(getFirstDayOfWeek(cal.get(Calendar.YEAR), i)));
}
return mapWeeks;
}
/**
* 获取某年的第几周的开始日期
* @param year 年分
* @param week 周索引
* @return 开始日期
* @throws Exception
*/
public static Date getFirstDayOfWeek(int year, int week) throws Exception {
Calendar c = new GregorianCalendar();
c.set(Calendar.YEAR, year);
c.set(Calendar.MONTH, Calendar.JANUARY);
c.set(Calendar.DATE, 1);
Calendar cal = (GregorianCalendar) c.clone();
cal.add(Calendar.DATE, week * 7);
return getFirstDayOfWeek(cal.getTime());
}
/**
* 获取某年的第几周的结束日期
* @param year 年份
* @param week 周索引
* @return 结束日期
* @throws Exception
*/
public static Date getLastDayOfWeek(int year, int week) throws Exception {
Calendar c = new GregorianCalendar();
c.set(Calendar.YEAR, year);
c.set(Calendar.MONTH, Calendar.JANUARY);
c.set(Calendar.DATE, 1);
Calendar cal = (GregorianCalendar) c.clone();
cal.add(Calendar.DATE, week * 7);
return getLastDayOfWeek(cal.getTime());
}
/**
* 获取当前时间所在周的开始日期
* @param date 当前时间
* @return 开始时间
*/
public static Date getFirstDayOfWeek(Date date) {
Calendar c = new GregorianCalendar();
c.setFirstDayOfWeek(Calendar.SUNDAY);
c.setTime(date);
c.set(Calendar.DAY_OF_WEEK, c.getFirstDayOfWeek());
return c.getTime();
}
/**
* 获取当前时间所在周的结束日期
* @param date 当前时间
* @return 结束日期
*/
public static Date getLastDayOfWeek(Date date) {
Calendar c = new GregorianCalendar();
c.setFirstDayOfWeek(Calendar.SUNDAY);
c.setTime(date);
c.set(Calendar.DAY_OF_WEEK, c.getFirstDayOfWeek() + 6);
return c.getTime();
}
//获得上周一的日期
public static Date geLastWeekMonday(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(getThisWeekMonday(date));
cal.add(Calendar.DATE, -7);
return cal.getTime();
}
//获得本周一的日期
public static Date getThisWeekMonday(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
// 获得当前日期是一个星期的第几天
int dayWeek = cal.get(Calendar.DAY_OF_WEEK);
if (1 == dayWeek) {
cal.add(Calendar.DAY_OF_MONTH, -1);
}
// 设置一个星期的第一天,一个星期的第一天是星期一
cal.setFirstDayOfWeek(Calendar.MONDAY);
// 获得当前日期是一个星期的第几天
int day = cal.get(Calendar.DAY_OF_WEEK);
// 根据日历的规则,给当前日期减去星期几与一个星期第一天的差值
cal.add(Calendar.DATE, cal.getFirstDayOfWeek() - day);
return cal.getTime();
}
//获得下周一的日期
public static Date getNextWeekMonday(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(getThisWeekMonday(date));
cal.add(Calendar.DATE, 7);
return cal.getTime();
}
// 获得今天日期
public static Date getToday(){
return new Date();
}
// 获得本月一日的日期
public static Date getFirstDay4ThisMonth(){
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.DAY_OF_MONTH,1);
return calendar.getTime();
}
//获得本月最后一日的日期
public static Date getLastDay4ThisMonth(){
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MONTH, 1);
calendar.set(Calendar.DAY_OF_MONTH, 0);
return calendar.getTime();
}
/**
* 获取指定月份的最后一天
* @param yearMonth
* @return
*/
public static String getLastDayOfMonth(String yearMonth) {
int year = Integer.parseInt(yearMonth.split("-")[0]); //年
int month = Integer.parseInt(yearMonth.split("-")[1]); //月
Calendar cal = Calendar.getInstance();
// 设置年份
cal.set(Calendar.YEAR, year);
// 设置月份
// cal.set(Calendar.MONTH, month - 1);
cal.set(Calendar.MONTH, month); //设置当前月的上一个月
// 获取某月最大天数
//int lastDay = cal.getActualMaximum(Calendar.DATE);
int lastDay = cal.getMinimum(Calendar.DATE); //获取月份中的最小值,即第一天
// 设置日历中月份的最大天数
//cal.set(Calendar.DAY_OF_MONTH, lastDay);
cal.set(Calendar.DAY_OF_MONTH, lastDay - 1); //上月的第一天减去1就是当月的最后一天
// 格式化日期
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(cal.getTime());
}
}
14 处理请求路径的URL工具类
比如,有时需要根据传入的路径url获取请求参数或者参数值,再进行下一步业务。
public class UrlUtil {
/**
* 获取请求地址中的某个参数
* @param url
* @param name
* @return
*/
public static String getParam(String url, String name) {
return urlSplit(url).get(name);
}
/**
* 去掉url中的路径,留下请求参数部分
* @param url url地址
* @return url请求参数部分
*/
private static String truncateUrlPage(String url) {
String strAllParam = null;
String[] arrSplit = null;
url = url.trim();
arrSplit = url.split("[?]");
if (url.length() > 1) {
if (arrSplit.length > 1) {
for (int i = 1; i < arrSplit.length; i++) {
strAllParam = arrSplit[i];
}
}
}
return strAllParam;
}
/**
* 将参数存入map集合
* @param url url地址
* @return url请求参数部分存入map集合
*/
public static Map<String, String> urlSplit(String url) {
Map<String, String> mapRequest = new HashMap<String, String>();
String[] arrSplit = null;
String strUrlParam = truncateUrlPage(url);
if (strUrlParam == null) {
return mapRequest;
}
arrSplit = strUrlParam.split("[&]");
for (String strSplit : arrSplit) {
String[] arrSplitEqual = null;
arrSplitEqual = strSplit.split("[=]");
//解析出键值
if (arrSplitEqual.length > 1) {
//正确解析
mapRequest.put(arrSplitEqual[0], strSplit.substring(arrSplitEqual[0].length()+1));
} else {
if (arrSplitEqual[0] != "") {
//只有参数没有值,不加入
mapRequest.put(arrSplitEqual[0], "");
}
}
}
return mapRequest;
}
public static void main(String[] args) {
String param = getParam("http://www.baidu.com/#/liveInfo/4?recommend=1", "recommend");
System.out.println(param); // 1
String s = truncateUrlPage("http://www.baidu.com/#/liveInfo/4?recommend=1");
System.out.println(s); // recommend=1
}
}
测试中一个方法获取参数的值,一个方法将参数和值取出来。
15 生成订单号 (根据时间戳和3位随机数生成订单号)
public class OrderNoUtils {
/**
* 获取订单号
* @return
*/
public static String getOrderNo() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String newDate = sdf.format(new Date());
String result = "";
Random random = new Random();
for (int i = 0; i < 3; i++) {
result += random.nextInt(10);
}
return newDate + result;
}
public static void main(String[] args) {
String orderNo = getOrderNo();
System.out.println(orderNo); // 20230610210349887
}
}
测试中,生成的订单号为20230610210349887,前14位为20230610210349,根据我们指定的时间戳生成。后3位是生成的随机数。
16 拦截器配置
对不同的url进行拦截配置,比如,带/api/是前台系统的路由,带/admin/是后台系统的路由。因此,需要进行拦截处理。
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Bean
LoginInterceptor loginInterceptor(){
return new LoginInterceptor();
}
@Bean
CrossInterceptor crossInterceptor(){
return new CrossInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
/**
* 拦截全部路径,这个跨域需要放在最上面
*/
registry.addInterceptor(crossInterceptor()).addPathPatterns("/**");
//拦截全部
registry.addInterceptor(loginInterceptor()).addPathPatterns("/api/*/*/**","/admin/*/**")
//不拦截哪些路径 斜杠一定要加
.excludePathPatterns("/api/user/login","/api/user/register");
WebMvcConfigurer.super.addInterceptors(registry);
}
}
//跨域;
public class CrossInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//表示接受任意域名的请求,也可以指定域名
response.setHeader("Access-Control-Allow-Origin", request.getHeader("origin"));
//该字段可选,是个布尔值,表示是否可以携带cookie
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS");
response.setHeader("Access-Control-Allow-Headers", "*");
/**
* 非简单请求是对那种对服务器有特殊要求的请求,
* 比如请求方式是PUT或者DELETE,或者Content-Type字段类型是application/json。
* 都会在正式通信之前,增加一次HTTP请求,称之为预检。浏览器会先询问服务器,当前网页所在域名是否在服务器的许可名单之中,
* 服务器允许之后,浏览器会发出正式的XMLHttpRequest请求
*/
if(HttpMethod.OPTIONS.toString().equals(request.getMethod())){
return true;
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
// 登录拦截器;
public class LoginInterceptor implements HandlerInterceptor {
/**
* 进入到controller之前的方法;
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
try{
String accessToken = request.getHeader("token"); //从请求头获取token; 因为 一般将jwt生成的token放在请求头中
if(accessToken == null){
accessToken = request.getParameter("token"); //从请求参数获取token;
}
// token不为空;进行校验;
if(StringUtils.isNotBlank(accessToken)) {
Claims claims = JWTUtils.checkJWT(accessToken);
// claims为空,token错误或者token过期;
if (claims == null) {
// 重新登陆;
sendJsonMessage(response, JsonData.buildError("重新登陆"));
return false;
}
Integer id = (Integer) claims.get("id"); //拿到用户id;
String name = (String) claims.get("name"); //拿到用户昵称;
request.setAttribute("user_id", id);
request.setAttribute("name", name);
return true;
}
}catch (Exception e){
e.printStackTrace();
}
return false;
}
/**
* 响应json数据给前端;
* @param response
* @param object
*/
public static void sendJsonMessage(HttpServletResponse response,Object object){
try{
ObjectMapper objectMapper = new ObjectMapper();
response.setContentType("application/json;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print(objectMapper.writeValueAsString(object));
writer.close();
response.flushBuffer();
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
带有/api/ 和/admin/都拦截,不过用户登录和用户注册不用拦截。上面同时给出了跨域的解决配置,在前面的博文在网关微服务里面提到过跨域配置,如果网关没有配置跨域,那么需要单独配置跨域。
在 LoginInterceptor类的preHandle方法中,不一定如案例所示。大致的逻辑是,从请求头获取jwt生成的token字符串,然后判断token字符串是否过期或者为空,如果token出错或者为空,则需要重新登录。页面跳转到登录页面。
不过在开发,使用到了网关,那么跨域和拦截路由都将在网关中配置。