介紹一款超厲害的國產Java工具——Hutool。Hutool是一個Java工具包類庫,對文件、流、加密解密、轉碼、正則、線程、XML等JDK方法進行封裝,組成各類Util工具類。適用於不少項目以及Web開發,而且與其餘框架沒有耦合性。html
=========================================================java
DateUtil
針對日期時間操做提供一系列靜態方法DateTime
提供相似於Joda-Time中日期時間對象的封裝,繼承DateFastDateFormat
提供線程安全的針對Date對象的格式化和日期字符串解析支持。此對象在實際使用中並不須要感知,相關操做已封裝在DateUtil和DateTime的相關方法中。DateBetween
計算兩個時間間隔的類,除了經過構造新對象使用外,相關操做也已封裝在DateUtil和DateTime的相關方法中。TimeInterval
一個簡單的計時器類,經常使用於計算某段代碼的執行時間,提供包括毫秒、秒、分、時、天、周等各類單位的花費時長計算,對象的靜態構造已封裝在DateUtil中。DatePattern
提供經常使用的日期格式化模式,包括String和FastDateFormat兩種類型。主要須要瞭解DateUtil類,DateTime類以及DatePattern類就能夠應對大多數時間日期的操做。mysql
DateUtil中都是靜態方法, 下面是一些簡單方法;git
now():String //當前日期時間 yyyy-MM-dd hh:mm:ss
today():String //今天日期 yyyy-MM-dd
date():DateTime
/*當前時間的DateTime對象(至關於new DateTime()或者new Date()), 此外還提供一個重載方法,傳遞long類型參數,是給定一個Unix時間戳, 返回這個時間戳的時間。*/
lastWeek():DateTime //上週今天(往前7天)
lastMonth():DateTime //上個月今天(往前一個月)
nextWeek():DateTime //下週今天(日後7天)
nextMonth():DateTime //下個月今天(日後一個月)
yesterday():DateTime //昨天同時
tomorrow():DateTime //明天同時
currentSeconds():long //毫秒數
thisYear():int //年份
thisMonth():int //月份(從0開始)
thisWeekOfMonth():int //本月周次(從1開始)
thisWeekOfYear():int //本年周次(從1開始)
thisDayOfMonth():int //本月第幾天(從1開始)
thisDayOfWeek():int //本週第幾天(從1開始)
thisHour(boolean is24HourClock):int //當前小時
thisMinute():int //當前分
thisSecond():int //當前秒
複製代碼
將一些固定格式的字符串-->Date對象:ajax
yyyy-MM-dd hh:mm:ss
yyyy-MM-dd
hh:mm:ss
yyyy-MM-dd hh:mm
若是你的日期格式不是這幾種格式,則須要指定日期格式,對於以上格式還有專門的方法對應:
parseDateTime parseDate ParseTime
DateUtil.parse()
DateUtil.parse(String,String) //Date 轉換爲指定格式的Date對象
複製代碼
須要將日期時間格式化輸出,Hutool提供了一些方法實現:
DateUtil.formatDateTime(Date date):String //將返回「yyyy-MM-dd hh:mm:ss」格式字符串
DateUtil.formatDate(Date date):String //將返回「yyyy-MM-dd「格式字符串
DateUtil.formatTime(Date date):String //將返回「hh:mm:ss「格式字符串
DateUtil.format(Date,String):String //將返回指定格式的字符串
複製代碼
Hutool能夠很方便的得到某天/某月/某年的開始時刻和結束時刻:正則表達式
beginOfDay(Date):Date 一天開始時刻
endOfDay(Date):Date 一天結束時刻
beginOfMonth(Date):Date endOfMonth(Date):Date beginOfYear(Date):Date endOfYear(Date):Date getBeginTimeOfDay 得到給定日期當天的開始時間,開始時間是00:00 getEndTimeOfDay 得到給定日期當天的結束時間,結束時間是23:59。 複製代碼
須要指定日期作偏移,則使用offsiteDay、offsiteWeek、offsiteMonth來得到指定日期偏移天、偏移周、偏移月,指定的偏移量正數向將來偏移,負數向歷史偏移。
若是以上還不能知足偏移要求,則使用offsiteDate偏移制定量,其中參數calendarField爲偏移的粒度大小(小時、天、月等)使用Calendar類中的常數。算法
1.between方法
Hutool能夠方便的計算時間間隔,使用 DateUtil.between(Date begin,Date end,DateUnit):long
計算間隔,方法有三個參數,前兩個分別是開始時間和結束時間,第三個參數是DateUnit的枚舉,
表示差值以什麼爲單位。
DateUnit的取值能夠是DateUnit.DAY(天),DateUnit.HOUR(小時),DateUnit.WEEK(周),
DateUnit.SECOND(秒),DateUnit.MINUTE(分鐘);
2.formatBetween方法
也能夠將差值轉爲指定具體的格式,好比 XX天XX小時XX分鐘XX秒 這樣的格式,可使用
DateUtil.formatBetween(Date begin,Date end,Level):String 方法,有三個參數,
前兩個依然是開始和結束時間,第三個參數表示精確度,好比Level.SECOND表示精確到秒,
即XX天XX小時XX分鐘XX秒的格式。
3.diff方法
返回兩個日期的時間差,參數diffField定義了這個差的單位,單位的定義在DateUtil的常量中,
例如DateUtil.SECOND_MS表示兩個日期相差的秒數。
複製代碼
用了Hutool能夠瞬間計算年齡,你還須要擔憂什麼虛歲週歲嗎?不須要,Hutool說多少就是多少。咱們可使用以下方法:sql
age(Date begin,Date end):int 出生和去世日期計算年齡
ageOfNow(String birthday):int 計算到當前日期的年齡
ageOfNow(Date birthday):int 計算到當前日期的年齡
複製代碼
使用DateUtil.isLeapYear(in year):boolean判斷是否爲閏年。shell
瞭解了DateUtil,再來看DateTime應該是很簡單的,由於DateTime裏面的大多數方法和DateUtil是相似的。DateTime類繼承自java.util.Date,徹底能夠替代Date的使用而且還有其餘的實用方法!數據庫
首先來了解一下DateTime的構造方法:
方法一:使用new
DateTime dt=new DateTime(); //建立當前時刻
DateTime dt=new DateTime(Date date); //使用Date對象構造
DateTime dt=new DateTime(long timeMillis); //使用毫秒數構造
方法二:使用of()方法
DateTime dt=DateTime.of();
方法三:使用now()方法
DateTime dt=DateTime.now(); //建立當前時刻
DateTime裏面還有兩個很是實用的方法,就是before(Date when):boolean 和 after(Date when):boolean,
它們能夠判斷時間的前後。
複製代碼
最後瞭解一下DatePattern類,這個類主要是提供了一些指定的時間日期格式(都是String類型),包括地區的區別表示:
格式化模板:
DatePattern.CHINESE_DATE_PATTERN //yyyy年MM月dd日
DatePattern.NORM_DATE_PATTERN //yyyy-MM-dd
DatePattern.NORM_TIME_PATTERN //HH:mm:ss
DatePattern.NORM_DATETIME_PATTERN //yyyy-MM-dd HH:mm:ss
DatePattern.UTC_PATTERN //yyyy-MM-dd'T'HH:mm:ss'Z'
DatePattern.PURE_DATE_PATTERN //yyyyMMdd
DatePattern.PURE_TIME_PATTERN //HHmmss
DatePattern.PURE_DATETIME_PATTERN //yyyyMMddHHmmss
複製代碼
做者:「612星球的一隻天才豬」路小磊
原文連接:https://blog.csdn.net/tianc_pig/article/details/87826810
https://my.oschina.net/looly/blog/268552
來源:CSDN oschina
複製代碼
=========================================================
字符串工具指cn.hutool.core.util.StrUtil
類,其中對String的多種方法進行了封裝而且提供了其餘方便實用的方法。StrUtil中的方法是靜態方法。
這裏的空有兩層含義:一是null或者「」(空串),二是不可見字符構成的字符串(不可見字符串),好比由空格構成的字符串(「 」)。針對兩種狀況Hutool提供了不一樣的方法來解決。
相似於Apache Commons Lang
中的StringUtil,之因此使用StrUtil而不是使用StringUtil是由於前者更短,並且Str這個簡寫我想已經深刻人心。經常使用的方法, 例如isBlank、isNotBlank、isEmpty、isNotEmpty這些就不作介紹了,判斷字符串是否爲空,下面我說幾個比較好用的功能。
經常使用的方法以下表所示:
isBlank(CharSequence arg0):boolean //判斷字符串是否爲null或者」」或者是不可見字符串
isEmpty(charSequence arg0):boolean //判斷字符串是否爲null或者」」
hasBlank(CharSequence…arg0):boolean //判斷多個字符串中是否有null或者」」或者是不可見字符串
hasEmpty(CharSequence…arg0):boolean //判斷多個字符串中是否有null或者」」
表單登陸時,經常使用hasEmpty()方法。
複製代碼
sub()方法至關於subString()方法;
有三個參數:第一個是截取的字符串,後兩個是首尾位置。sub()方法有一個最大的特色就是容錯率很是高,而且-1表示最後一個字符,-2表示倒數第二個字符,以此類推。 而且首尾位置也能夠顛倒。
String testStr="my name is smartPig";
String result01=StrUtil.sub(testStr, 0, 4); //my n
/*雖然是4-0,可是實際上仍是算成0-4*/
String result02=StrUtil.sub(testStr, 4, 0); //my n
String result03=StrUtil.sub(testStr, -1, 3); //name is smartPi
String result04=StrUtil.sub(testStr, -4, 3); //name is smar
複製代碼
format方法相似於JDBC中PreparedStatement中的?佔位符,能夠在字符串中使用」{}」做爲佔位符。
String testStr="{} name is smart{}";
String result=StrUtil.format(testStr, "my","Pig"); //my對應第一個{},Pig對應第二個{}
//結果以下:my name is smartPig
複製代碼
Hutool能夠去除字符串的前綴/後綴,這個特色很是適用於去除文件的後綴名。相關方法以下表:
removeSuffix(CharSequence str, CharSequence suffix) //去除後綴
removeSuffixIgnoreCase(CharSequence str, CharSequence suffix) //去除後綴,忽略大小寫
removePrefix(CharSequence str, CharSequence suffix) //去除前綴
removePrefixIgnoreCase(CharSequence str, CharSequence suffix) //去除前綴,忽略大小寫
複製代碼
Hutool定義了一些字符常量,能夠靈活使用。部分常量以下所示:
複製代碼
StrUtil.DOT //點.
StrUtil.DOUBLE_DOT //雙點..
StrUtil.UNDERLINE //下劃線_
StrUtil.EMPTY //橫槓_
StrUtil.BACKSLASH //反斜槓\
StrUtil.DASHED //破折-
StrUtil.BRACKET_END //右中擴號]
StrUtil.BRACKET_START //左中括號[
StrUtil.COLON //冒號:
StrUtil.COMMA //逗號,
StrUtil.DELIM_END //右大括號}
StrUtil.DELIM_START //左大括號{
複製代碼
Hutool能夠很容易的實現字符串逆序。可使用StrUtil.reverse(String str):String方法。
複製代碼
把String.getByte(String charsetName)方法封裝在這裏了,原生的String.getByte()這個方法太坑了,使用系統編碼,
常常會有人跳進來致使亂碼問題,因此就加了這兩個方法強制指定字符集了,包了個try拋出一個運行時異常,
省的我得在業務代碼裏處理那個噁心的UnsupportedEncodingException。
複製代碼
做者:「612星球的一隻天才豬」路小磊
原文連接:https://blog.csdn.net/tianc_pig/article/details/87944463
https://my.oschina.net/looly/blog/262775
來源:CSDN oschina
複製代碼
=========================================================
隨機工具的主要方法以下:
RandomUtil.randomInt 得到指定範圍內的隨機數
RandomUtil.randomEle 隨機得到列表中的元素
RandomUtil.randomString 得到一個隨機的字符串(只包含數字和字符)
RandomUtil.randomNumbers 得到一個只包含數字的字符串
RandomUtil.randomNumber 得到一個隨機數
RandomUtil.randomChar 得到隨機字符
複製代碼
這些方法都有一些重載方法,以下表:
randomInt():int //得到一個隨機整數
randomInt(int limit):int //得到一個<limit的隨機整數
randomInt(int min,int max):int //得到一個隨機整數n(min<=n<max)
randomChar():char //得到一個隨機字符
randomChar(Strint str):char //從指定字符串中得到一個隨機字符
randomNumber():int //得到一個隨機數
randomNumbers(int length):String //得到長度爲length由數字組成的字符串
randomString(int length):String //得到一個長度爲length的字符串,只包含數字和字符
randomString(String str,int length):String //從str中隨機產生指定長度的字符串(全部字符來源於str)
randomEle(T array[]):T //從數組中隨機獲取一個數據
randomEle(List<T> list):T //從List中隨機獲取一個數據
randomEle(List<T> list,int limit):T //從List的前limit箇中隨機獲取一個數據(limit從1開始)
複製代碼
做者:「612星球的一隻天才豬」
原文連接:https://blog.csdn.net/tianc_pig/article/details/88034976
來源:CSDN
複製代碼
=========================================================
使用 isMatch(String regex,CharSequence content):boolean
第一個參數是正則,第二個是匹配的內容。
複製代碼
delFirst(String regex,CharSequence content):String //刪除匹配的第一個字符串,返回刪除後的字符串
delAll(String regex,CharSequence content):String //刪除匹配的全部字符串,返回刪除後的字符串
複製代碼
使用findAll(String regex, CharSequence content, int group): List<String>
能夠得到全部匹配的內容
複製代碼
使用replaceAll(CharSequence content, String regex, String replacementTemplate):String
能夠替換匹配的內容。
複製代碼
使用escape(CharSequence arg0):String
能夠將用於正則表達式中的某些特殊字符進行轉義,變成轉義字符。
複製代碼
做者:「612星球的一隻天才豬」
原文連接:https://blog.csdn.net/tianc_pig/article/details/88134882
來源:CSDN
複製代碼
=========================================================
PageUtil的靜態方法
複製代碼
transToStartEnd(int,int):int[] //將頁數和頁容量轉換爲數據表中的起始位置
/*第一個參數是頁碼數,第二個參數是每一頁的容量, 返回值是一個長度爲2的數組,分別表示開始位置和結束位置。*/
/*得到指定頁的結果集 @param pageNum 頁數(從1開始) @param pageSize 每頁容量 int startEnd[]=PageUtil.transToStartEnd(pageNum, pageSize);*/
totalPage(int,int):int //由總記錄數和頁容量得到頁數
//第一個參數是總的記錄數,第二個參數是每頁的容量,返回值是總頁數。
/* @param totalCount 總記錄數 @param pageSize 每頁的記錄數 int totalPage = PageUtil.totalPage(totalCount, pageSize);*/
彩虹分頁算法
rainbow(int,int):int[]
//第一個參數表示當前頁碼,第二個參數表示總頁數,這種狀況默認是一次顯示全部的頁碼
//(這個方法我的以爲沒有什麼用)
rainbow(int,int,int):int[]
//第一個參數表示當前頁碼,第二個參數表示總頁數,第三個參數表示每一次最多顯示的頁數
複製代碼
彩虹分頁算法應用於網頁中須要常常顯示【上一頁】1 2 3 4 5 【下一頁】這種頁碼的狀況,通常都根據當前的頁碼動態的更新所顯示出來的頁碼,由於每次所顯示的頁碼的數量是固定的。
/** * 測試分頁彩虹算法 * @param curPage 當前頁數 * @param totalPage 總頁數 */
public static String rainbowPage(int curPage,int totalPage) {
int rainbow[]=PageUtil.rainbow(curPage,totalPage);
return Arrays.toString(rainbow);
}
/** * 測試分頁彩虹算法 * @param curPage 當前頁數 * @param totalPage 總頁數 * @param size 每次顯示的頁數量 */
public static String rainbowPage(int curPage,int totalPage,int size) {
int rainbow[]=PageUtil.rainbow(curPage,totalPage,size);
return Arrays.toString(rainbow);
}
複製代碼
MyBatis就提供了分頁工具,而且支持插件分頁
做者:「612星球的一隻天才豬」
原文連接:https://blog.csdn.net/tianc_pig/article/details/88628323
來源:CSDN
複製代碼
=========================================================
主要增長了對數組、集合類的操做。
將集合轉換爲字符串,這個方法仍是挺經常使用,是StrUtil.split的反方法。這個方法的參數支持各類類型對象的集合,最後鏈接每一個對象時候調用其toString()方法。以下:
String[] col= new String[]{a,b,c,d,e};
String str = CollectionUtil.join(col, "#"); //str -> a#b#c#d#e
複製代碼
功能是:將給定的多個集合放到一個列表(List)中,根據給定的Comparator對象排序,而後分頁取數據。這個方法很是相似於數據庫多表查詢後排序分頁,這個方法存在的意義也是在此。sortPageAll2功能和sortPageAll的使用方式和結果是 同樣的,區別是sortPageAll2使用了BoundedPriorityQueue這個類來存儲組合後的列表,不知道哪一種性能更好一些,因此就都保留了。以下:
Comparator<Integer> comparator = new Comparator<Integer>(){ //Integer比較器
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
};
//新建三個列表,CollectionUtil.newArrayList方法表示新建ArrayList並填充元素
List<Integer> list1 = CollectionUtil.newArrayList(1, 2, 3);
List<Integer> list2 = CollectionUtil.newArrayList(4, 5, 6);
List<Integer> list3 = CollectionUtil.newArrayList(7, 8, 9);
//參數表示把list1,list2,list3合併並按照從小到大排序後,取0~2個(包括第0個,不包括第2個),結果是[1,2]
@SuppressWarnings("unchecked")
List<Integer> result = CollectionUtil.sortPageAll(0, 2, comparator, list1, list2, list3);
System.out.println(result); //輸出 [1,2]
複製代碼
主要是對Entry<Long, Long>
按照Value的值作排序,使用侷限性較大
這個方法傳入一個棧對象,而後彈出指定數目的元素對象,彈出是指pop()方法,會從原棧中刪掉。
這些方法是新建相應的數據結構,數據結構元素的類型取決於你變量的類型,以下:
HashMap<String, String> map = CollectionUtil.newHashMap();
HashSet<String> set = CollectionUtil.newHashSet();
ArrayList<String> list = CollectionUtil.newArrayList();
複製代碼
在給定數組裏末尾加一個元素,其實List.add()也是這麼實現的,這個方法存在的意義是隻有少許的添加元素時使用,由於內部使用了System.arraycopy,每調用一次就要拷貝數組一次。這個方法也是爲了在某些只能使用數組的狀況下使用,省去了先要轉成List,添加元素,再轉成Array。
從新調整數據的大小,若是調整後的大小比原來小,截斷,若是比原來大,則多出的位置空着。(貌似List在擴充的時候會用到相似的方法)
將多個數據合併成一個數組
這個方法來源於Python的一個語法糖,給定開始和結尾以及步進,就會生成一個等差數列(列表)
int[] a1 = CollectionUtil.range(6); //[0,1,2,3,4,5]
int[] a2 = CollectionUtil.range(4, 7); //[4,5,6]
int[] a3 = CollectionUtil.range(4, 9, 2); //[4,6,8]
複製代碼
對集合切片,其餘類型的集合會轉換成List,封裝List.subList方法,自動修正越界等問題,徹底避免IndexOutOfBoundsException異常。
判斷集合是否爲空(包括null和沒有元素的集合)。
此方法也是來源於Python的一個語法糖,給定兩個集合,而後兩個集合中的元素一一對應,成爲一個Map。此方法還有一個重載方法,能夠傳字符,而後給定分隔符,字符串會被split成列表。
String[] keys = new String[]{"a", "b", "c"};
Integer[] values = new Integer[]{1, 2, 3};
Map<String, Integer> map = CollectionUtil.zip(keys,values);
System.out.println(map); // {b=2, c=3, a=1}
String a = "a,b,c";
String b = "1,2,3";
Map<String, String> map2 = CollectionUtil.zip(a,b, ",");
System.out.println(map2); // {b=2, c=3, a=1}
複製代碼
做者:路小磊
原文連接:https://my.oschina.net/looly/blog/262786
來源:oschina
複製代碼
=========================================================
主要是封裝了一些反射的方法,而這個類中最有用的方法是scanPackage方法,這個方法會掃描classpath下全部類,這個在Spring中是特性之一,主要爲Hulu框架中類掃描的一個基礎
此方法惟一的參數是包的名稱,返回結果爲此包以及子包下全部的類。方法使用很簡單,可是過程複雜一些,包掃面首先會調用 getClassPaths方法得到ClassPath,而後掃描ClassPath,若是是目錄,掃描目錄下的類文件,或者jar文件。若是是jar包,則直接從jar包中獲取類名。這個方法的做用顯而易見,就是要找出全部的類,在Spring中用於依賴注入,在Hulu中則用於找到Action類。固然,也能夠傳一個ClassFilter對象,用於過濾不須要的類。
此方法同Class對象的·getMethods·方法,只不過只返回方法的名稱(字符串),封裝很是簡單。
此方法是得到當前線程的ClassPath,核心是Thread.currentThread().getContextClassLoader().getResources的調用。
此方法用於得到java的系統變量定義的ClassPath。
此方法封裝了強制類型轉換,首先會調用對象自己的cast方法,失敗則嘗試是否爲基本類型(int,long,double,float等),再失敗則嘗試日期、數字和字節流,總之這是一個包容性較好的類型轉換方法,省去咱們在不知道類型的狀況下屢次嘗試的繁瑣。
此方法被parse方法調用,專門用於將字符集串轉換爲基本類型的對象(Integer,Double等等)。能夠說這是一個一站式的轉換方法,JDK的方法名太彆扭了,例如你要轉換成Long,你得調用Long.parseLong方法,直接Long.parse不就好了……真搞不懂,因此纔有了這個方法。
這個方法比較彆扭,就是把例如Integer類變成int.class,貌似沒啥用處,忘了哪裏用了,若是你能用到,就太好了。
後者只是得到當前線程的ClassLoader,前者在獲取失敗的時候獲取ClassUtil這個類的ClassLoader。
實例化對象,封裝了Class.forName(clazz).newInstance()方法。
克隆對象。對於有些對象沒有實現Cloneable接口的對象想克隆下真是費勁,例如封裝Redis客戶端的時候,配置對象想克隆下基本不可能,因而寫了這個方法,原理是使用ObjectOutputStream複製對象流。
做者:路小磊
原文連接:https://my.oschina.net/looly/blog/268087
來源:oschina
複製代碼
=========================================================
在Java工具中,文件操做應該也是使用至關頻繁的,可是Java對文件的操做因爲牽涉到流,因此較爲繁瑣,各類Stream也是眼花繚亂,所以大部分項目裏的util包中我想都有一個FileUtil的類,而本類就是對衆多FileUtil的總結。
這些方法都是按照Linux命令來命名的,方便熟悉Linux的用戶見名知意,例如:
ls 返回給定目錄的全部文件對象列表,路徑能夠是相對ClassPath路徑或者絕對路徑,不能夠是壓縮包裏的路徑。
listFileNames 則是返回指定目錄下的全部文件名,支持jar等壓縮包。
touch 建立文件,若是給定路徑父目錄不存在,也一同建立。
del 刪除文件或者目錄,目錄下有嵌套目錄或者文件會一塊兒刪除。
mkdir 建立目錄,父目錄不存在自動建立。
createTempFile 建立臨時文件,在程序運行完畢的時候,這個文件會被刪除。
copy 複製文件或目錄,目標文件對象能夠是目錄,自動用原文件名,能夠選擇是否覆蓋目標文件。
move 移動文件或目錄,原理是先複製,再刪除原文件或目錄
isExist 文件或者目錄是否存在。
複製代碼
getAbsolutePath 得到絕對路徑,若是給定路勁已是絕對路徑,返回原路徑,不然根據ClassPath
或者給定類的相對位置得到其絕對位置
close 對於實現了Closeable接口的對象,能夠直接調用此方法關閉,且是靜默關閉,關閉出錯將不
會有任何調試信息。這個方法也是使用很是頻繁的,例如文件流的關閉等等。
equals 比較兩個文件是否相同
複製代碼
getBufferedWriter 得到帶緩存的寫入對象,能夠寫字符串等。
getPrintWriter 對 getBufferedWriter的包裝,能夠有println等方法按照行寫出。
getOutputStream 會的文件的寫出流想對象。
writeString直接寫字符串到文件,會覆蓋以前的內容。
appendString 追加字符串到文本。
writeLines appendLines 覆蓋寫入和追加文本列表,每一個元素都是一行。
writeBytes 寫字節碼。
writeStream 寫流中的內容到文件裏。
複製代碼
getReader 得到帶緩存的Reader對象。
readLines 按行讀取文件中的數據,返回List,每個元素都是一行文本。
load 按照給定的ReaderHandler對象讀取文件中的數據,ReaderHandler是一個藉口,實現後就能夠操做Reader對象了,這個方法存在是爲了不用戶手動調用close方法。
readString 直接讀出文件中的全部文本。
readBytes 讀字節碼
複製代碼
isModifed 文件是否被修改過,須要傳入一個時間戳,用來比對最後修改時間。
getExtension 得到文件的擴展名。
複製代碼
做者:路小磊
原文連接:https://my.oschina.net/looly/blog/288525
來源:oschina
複製代碼
=========================================================
舉個例子: 我有一個用戶表,這個表根據用戶名被Hash到不一樣的數據庫實例上,我要找出這些用戶中最熱門的5個,怎麼作?我是這麼作的:
在每一個數據庫實例上找出最熱門的5個
將每一個數據庫實例上的這5條數據按照熱門程度排序,最後取出前5條
複製代碼
這個過程看似簡單,可是你應用服務器上的代碼要寫很多。首先須要Query N個列表,加入到一個新列表中,排序,再取前5。這個過程不但代碼繁瑣,並且牽涉到多個列表,很是浪費空間。因而,BoundedPriorityQueu
e應運而生。
/** * 有界優先隊列 */
public class BoundedPriorityQueueDemo {
public static void main(String[] args) {
//初始化隊列,設置隊列的容量爲5(只能容納5個元素),
//元素類型爲integer使用默認比較器,在隊列內部將按照從小到大排序
BoundedPriorityQueue<Integer> queue = new BoundedPriorityQueue<Integer>(5);
//初始化隊列,使用自定義的比較器
queue = new BoundedPriorityQueue<>(5, new Comparator<Integer>(){
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
});
//定義了6個元素,當元素加入到隊列中,會按照從小到大排序,
//當加入第6個元素的時候,隊列末尾(最大的元素)將會被拋棄
int[] array = new int[]{5,7,9,2,3,8};
for (int i : array) {
queue.offer(i);
}
//隊列能夠轉換爲List
ArrayList<Integer> list = queue.toList();
System.out.println(queue);
}
}
原理: 設定好隊列的容量,而後把全部的數據add或者offer進去(兩個方法相同),就會獲得前5條數據。
複製代碼
做者:路小磊
原文連接:https://my.oschina.net/looly/blog/389940
來源:oschina
複製代碼
=========================================================
Java中定時任務使用的最多的就是quartz了,可是這個框架太過龐大,並且也不須要用到這麼多東西,使用方法也是比較複雜。因而便尋找新的框架代替。用過Linux的crontab的人都知道,使用其定時的表達式能夠很是靈活的定義定時任務的時間以及頻率(Linux的crontab精確到分,而quaeta的精確到秒,不過對來講精確到分已經夠用了,精確到秒的可使用Timer能夠搞定),而後就是crontab的配置文件,能夠把定時任務很清晰的羅列出來。(記得當時Spring整合quartz的時候那XML看的眼都花了)。因而我便找到了一個輕量調度框架——cron4j
爲了隱藏這個框架裏面的東西,對其作了封裝,所謂封裝,就是把任務調度放在一個配置文件裏,而後啓動便可(與Linux的crontab很是像)。
對於Maven項目,首先在src/main/resources/config下放入cron4j.setting文件(默認是這個路徑的這個文件),而後在文件中放入定時規則,規則以下:
#註釋
[包名]
TestJob = */10 * * * *
TestJob2 = */10 * * * *
第二行等號前面是要執行的定時任務類名,等號後面是定時表達式。
TestJob是一個實現了Runnable接口的類,在start()方法裏就能夠加邏輯。
複製代碼
關於定時任務表達式,它與Linux的crontab表達式如出一轍,具體請看這裏: www.cnblogs.com/peida/archi…
調用CronUtil.start()既可啓動定時任務服務,CrontabUtil.stop()關閉服務。想動態的添加定時任務,使用CronUtil.schedule(String schedulingPattern, Runnable task)方法便可(使用此方法加入的定時任務不會被寫入到配置文件)。
做者:路小磊
原文連接:https://my.oschina.net/looly/blog/379677
來源:oschina
複製代碼
=========================================================
在Java開發中會面對各類各樣的類型轉換問題,尤爲是從命令行獲取的用戶參數、從HttpRequest獲取的Parameter等等,這些參數類型多種多樣,怎麼去轉換他們呢?經常使用的辦法是先整成String,而後調用xxx.parsexxx方法,還要承受轉換失敗的風險,不得不加一層try catch,這個小小的過程混跡在業務代碼中會顯得很是難看和臃腫,因而把這種類型轉換的任務封裝在了Conver類中。
1. toStr、toInt、toLong、toDouble、toBool方法
這幾個方法基本代替了JDK的XXX.parseXXX方法,傳入兩個參數,第一個是Object類型的被轉換的值,
第二個參數是默認值。這些方法作轉換並不拋出異常,當轉換失敗或者提供的值爲null時,
只會返回默認值,返回的類型所有使用了包裝類,方便咱們須要null的狀況。
2. 半角轉全角toSBC和全角轉半角toDBC
在不少文本的統一化中這兩個方法很是有用,主要對標點符號的全角半角轉換。
複製代碼
做者:路小磊
原文連接:https://my.oschina.net/looly/blog/270829
來源:oschina
複製代碼
=========================================================
使用單例不外乎兩種方式:
在對象里加個靜態方法getInstance()來獲取。
此方式能夠參考 【轉】線程安全的單例模式[https://my.oschina.net/looly/blog/152865] 這篇博客,
可分爲餓漢和飽漢模式。
經過Spring這類容器統一管理對象,用的時候去對象池中拿。Spring也能夠經過配置決定懶漢或者餓漢模式
複製代碼
說實話更傾向於第二種,可是Spring更對的的注入,而不是拿,因而想作Singleton這個類,維護一個單例的池,用這個單例對象的時候直接來拿就能夠,這裏用的懶漢模式。只是想把單例的管理方式換一種思路,但願管理單例的是一個容器工具,而不是一個大大的框架,這樣能大大減小單例使用的複雜性。
import com.xiaoleilu.hutool.Singleton;
/** * 單例樣例 */
public class SingletonDemo {
/** * 動物接口 */
public static interface Animal{
public void say();
}
/** * 狗實現 */
public static class Dog implements Animal{
@Override
public void say() {
System.out.println("汪汪");
}
}
/** * 貓實現 */
public static class Cat implements Animal{
@Override
public void say() {
System.out.println("喵喵");
}
}
public static void main(String[] args) {
Animal dog = Singleton.get(Dog.class);
Animal cat = Singleton.get(Cat.class);
//單例對象每次取出爲同一個對象,除非調用Singleton.destroy()或者remove方法
System.out.println(dog == Singleton.get(Dog.class)); //true
System.out.println(cat == Singleton.get(Cat.class)); //true
dog.say(); //汪汪
cat.say(); //喵喵
}
}
複製代碼
若有興趣能夠看下這個類,實現很是簡單,一個HashMap用於作爲單例對象池,經過newInstance()實例化對象(不支持帶參數的構造方法),不管取仍是建立對象都是線程安全的(在單例對象數量很是龐大且單例對象實例化很是耗時時可能會出現瓶頸),考慮到在get的時候使雙重檢查鎖,可是並非線程安全的,故直接加了synchronized作爲修飾符,歡迎在此給出建議。
做者:路小磊
原文連接:https://my.oschina.net/looly/blog/284922
來源:oschina
複製代碼
=========================================================
對於JDK自帶的Properties讀取的Properties文件,有不少限制,首先是ISO8859-1編碼致使無法加中文的value和註釋(用日本的那個插件在Eclipse裏能夠讀寫,放到服務器上讀就費勁了),再就是不支持變量分組等功能,所以有了Setting類。
配置文件中使用變量這個需求由來已久,在Spring中PropertyPlaceholderConfigurer類就用於在ApplicationContext.xml中使用Properties文件中的變量替換。 分組的概念我第一次在Linux的rsync的/etc/rsyncd.conf配置文件中有所瞭解,發現特別實用具體你們能夠百度。
而這兩種功能後來在jodd的Props纔有所發現,它的這個配置文件擴展類十分強大,甚至支持多行等等功能,原本想直接使用,避免重複造輪子,但是發現不少特性徹底用不到,並且沒有須要的便捷功能,因而便造了Setting這個輪子。
配置文件格式example.setting
<!-- lang: shell -->
# -------------------------------------------------------------
# ----- Setting File with UTF8-----
# ----- 數據庫配置文件 -----
# -------------------------------------------------------------
#中括表示一個分組,其下面的全部屬性歸屬於這個分組,在此分組名爲demo,也能夠沒有分組
[demo]
#自定義數據源設置文件,這個文件會針對當前分組生效,用於給當前分組配置單獨的數據庫鏈接池參數,
#沒有則使用全局的配置
ds.setting.path = config/other.setting
#數據庫驅動名,若是不指定,則會根據url自動斷定
driver = com.mysql.jdbc.Driver
#JDBC url,必須
url = jdbc:mysql://fedora.vmware:3306/extractor
#用戶名,必須
user = root${driver}
#密碼,必須,若是密碼爲空,請填寫 pass =
pass = 123456
複製代碼
配置文件能夠放在任意位置,具體Setting類如何尋在在構造方法中提供了多種讀取方式,具體稍後介紹。如今說下配置文件的具體格式, Setting配置文件相似於Properties文件,規則以下:
註釋用#開頭表示,只支持單行註釋,空行和沒法正常被識別的鍵值對也會被忽略,可做爲註釋,可是建議顯式指定註釋。
鍵值對使用key = value 表示,key和value在讀取時會trim掉空格,因此不用擔憂空格。
分組爲中括號括起來的內容(例如配置文件中的[demo]),中括號如下的行都爲此分組的內容,無分組至關於空字符分組,即[]。若某個key是name,分組是group,加上分組後的key至關於group.name。
支持變量,默認變量命名爲{driver}會被替換爲com.mysql.jdbc.Driver,爲了性能,Setting建立的時候構造方法會指定是否開啓變量替換,默認不開啓。
<!-- lang: java -->
import java.io.IOException;
import com.xiaoleilu.hutool.CharsetUtil;
import com.xiaoleilu.hutool.FileUtil;
import com.xiaoleilu.hutool.Setting;
/** * Setting演示樣例類 */
public class SettingDemo {
public static void main(String[] args) throws IOException {
//-------------------------初始化
//讀取classpath下的XXX.setting,不使用變量
Setting setting = new Setting("XXX.setting");
//讀取classpath下的config目錄下的XXX.setting,不使用變量
setting = new Setting("config/XXX.setting");
//讀取絕對路徑文件/home/looly/XXX.setting(沒有就建立,關於touch請查閱FileUtil)
//第二個參數爲自定義的編碼,請保持與Setting文件的編碼一致
//第三個參數爲是否使用變量,若是爲true,則配置文件中的每一個key均可以被以後的條目中的value引用形式爲 ${key}
setting = new Setting(FileUtil.touch("/home/looly/XXX.setting"), CharsetUtil.UTF_8, true);
//讀取與SettingDemo.class文件同包下的XXX.setting
setting = new Setting("XXX.setting", SettingDemo.class, CharsetUtil.UTF_8, true);
//--------------------------使用
//獲取key爲name的值
setting.getString("name");
//獲取分組爲group下key爲name的值
setting.getString("name", "group1");
//當獲取的值爲空(null或者空白字符時,包括多個空格),返回默認值
setting.getStringWithDefault("name", "默認值");
//完整的帶有key、分組和默認值的得到值得方法
setting.getStringWithDefault("name", "group1", "默認值");
//若是想得到其它類型的值,能夠調用相應的getXXX方法,參數類似
//有時候須要在key對應value不存在的時候(沒有這項設置的時候)告知用戶,故有此方法打印一個debug日誌
setting.getWithLog("name");
setting.getWithLog("name", "group1");
//從新讀取配置文件,能夠啓用一個定時器調用此方法來定時更新配置
setting.reload();
//當經過代碼加入新的鍵值對的時候,調用store會保存到文件,可是會覆蓋原來的文件,並丟失註釋
setting.setSetting("name1", "value");
setting.store("/home/looly/XXX.setting");
//得到全部分組名
setting.getGroups();
//將key-value映射爲對象,原理是原理是調用對象對應的setXX方法
//setting.toObject();
//設定變量名的正則表達式。
//Setting的變量替換是經過正則查找替換的,若是Setting中的變量名和其餘衝突,
//能夠改變變量的定義方式
//整個正則匹配變量名,分組1匹配key的名字
setting.setVarRegex("\\$\\{(.*?)\\}");
}
}
複製代碼
對Properties的簡單封裝Props(版本2.0.0開始提供)
對於Properties的普遍使用使我也無能爲力,有時候遇到Properties文件又想方便的讀寫也不容易,因而對Properties作了簡單的封裝,提供了方便的構造方法(與Setting一致),並提供了與Setting一致的getXXX方法來擴展Properties類,Props類繼承自Properties,因此能夠兼容Properties類,具體再也不作介紹,有興趣能夠看下cn.hutool.setting.dialect.Props
做者:路小磊
原文連接:https://my.oschina.net/looly/blog/302407
來源:oschina
複製代碼
=========================================================
AES和DES同屬對稱加密算法,數據發信方將明文(原始數據)和加密密鑰一塊兒通過特殊加密算法處理後,使其變成複雜的加密密文發送出去。收信方收到密文後,若想解讀原文,則須要使用加密用過的密鑰及相同算法的逆算法對密文進行解密,才能使其恢復成可讀明文。在對稱加密算法中,使用的密鑰只有一個,發收信雙方都使用這個密鑰對數據進行加密和解密,這就要求解密方事先必須知道加密密鑰。
在Java世界中,AES、DES加密解密須要使用Cipher對象構建加密解密系統,Hutool中對這一對象作再包裝,簡化了加密解密過程。
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.1.2</version>
</dependency>
複製代碼
** AES加密解密
String content = "test中文";
byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue()).getEncoded();//隨機生成密鑰
AES aes = SecureUtil.aes(key);//構建
byte[] encrypt = aes.encrypt(content);//加密
byte[] decrypt = aes.decrypt(encrypt);//解密
String encryptHex = aes.encryptHex(content);//加密爲16進製表示
String decryptStr = aes.decryptStr(encryptHex);//解密爲原字符串
** DES加密解密, DES的使用方式與AES基本一致
String content = "test中文";
byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.DES.getValue()).getEncoded();//隨機生成密鑰
DES des = SecureUtil.des(key);//構建
byte[] encrypt = des.encrypt(content);//加密解密
byte[] decrypt = des.decrypt(encrypt);
String encryptHex = des.encryptHex(content);//加密爲16進制,解密爲原字符串
String decryptStr = des.decryptStr(encryptHex);
複製代碼
Hutool中針對JDK支持的全部對稱加密算法作了封裝,封裝爲SymmetricCrypto類,AES和DES兩個類是此類的簡化表示。經過實例化這個類傳入相應的算法枚舉便可使用相同方法加密解密字符串或對象。
Hutool支持的對稱加密算法枚舉有:
AES
ARCFOUR
Blowfish
DES
DESede
RC2
PBEWithMD5AndDES
PBEWithSHA1AndDESede
PBEWithSHA1AndRC2_40
複製代碼
這些枚舉所有在SymmetricAlgorithm中被列舉
對稱加密對象的使用也很是簡單:
String content = "test中文";
byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue()).getEncoded();//隨機生成密鑰
SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, key);//構建
byte[] encrypt = aes.encrypt(content);//加密
byte[] decrypt = aes.decrypt(encrypt);//解密
String encryptHex = aes.encryptHex(content);//加密爲16進製表示
String decryptStr = aes.decryptStr(encryptHex);//解密爲字符串
複製代碼
做者:路小磊
原文連接:https://my.oschina.net/looly/blog/1504160
來源:oschina
複製代碼
Hutool工具是一個國產開源Java工具集,旨在簡化Java開發中繁瑣的過程,Hutool-crypto模塊即是針對JDK加密解密作了大大簡化。
此文主要介紹利用Hutool-crypto簡化非對稱加密解密。
對於非對稱加密,最經常使用的就是RSA和DSA,在Hutool中使用AsymmetricCrypto對象來負責加密解密。
非對稱加密有公鑰和私鑰兩個概念,私鑰本身擁有,不能給別人,公鑰公開。根據應用的不一樣,咱們能夠選擇使用不一樣的密鑰加密:
簽名:使用私鑰加密,公鑰解密。用於讓全部公鑰全部者驗證私鑰全部者的身份而且用來防止私鑰全部者發佈的內容被篡改,
可是不用來保證內容不被他人得到。
加密:用公鑰加密,私鑰解密。用於向公鑰全部者發佈信息,這個信息可能被他人篡改,可是沒法被他人得到。
複製代碼
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.1.2</version>
</dependency>
複製代碼
在非對稱加密中,咱們能夠經過AsymmetricCrypto(AsymmetricAlgorithm algorithm)構造方法,經過傳入不一樣的算法枚舉,得到其加密解密器。
固然,爲了方便,咱們針對最經常使用的RSA和DSA算法構建了單獨的對象:RSA和DSA。
以RSA爲例,介紹使用RSA加密和解密
在構建RSA對象時,能夠傳入公鑰或私鑰,當使用無參構造方法時,Hutool將自動生成隨機的公鑰私鑰密鑰對:
RSA rsa = new RSA();
rsa.getPrivateKey()//得到私鑰
rsa.getPrivateKeyBase64()
rsa.getPublicKey()//得到公鑰
rsa.getPublicKeyBase64()
byte[] encrypt = rsa.encrypt(StrUtil.bytes("我是一段測試aaaa", CharsetUtil.CHARSET_UTF_8),
KeyType.PublicKey);//公鑰加密,私鑰解密
byte[] decrypt = rsa.decrypt(encrypt, KeyType.PrivateKey);
Assert.assertEquals("我是一段測試aaaa", StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8));
//私鑰加密,公鑰解密
byte[] encrypt2 = rsa.encrypt(StrUtil.bytes("我是一段測試aaaa", CharsetUtil.CHARSET_UTF_8), KeyType.PrivateKey);
byte[] decrypt2 = rsa.decrypt(encrypt2, KeyType.PublicKey);
Assert.assertEquals("我是一段測試aaaa", StrUtil.str(decrypt2, CharsetUtil.CHARSET_UTF_8));
複製代碼
對於加密和解密能夠徹底分開,對於RSA對象,若是隻使用公鑰或私鑰,另外一個參數能夠爲null
有時候想自助生成密鑰對能夠:
KeyPair pair = SecureUtil.generateKeyPair("RSA");
pair.getPrivate();
pair.getPublic();
複製代碼
自助生成的密鑰對是byte[]形式,咱們可使用Base64.encode方法轉爲Base64,便於存儲爲文本。
固然,若是使用RSA對象,也可使用encryptStr和decryptStr加密解密爲字符串
已知私鑰和密文,如何解密密文?
String PRIVATE_KEY = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAIL7pbQ+5KKGYRhw7jE31hmA"
+ "f8Q60ybd+xZuRmuO5kOFBRqXGxKTQ9TfQI+aMW+0lw/kibKzaD/EKV91107xE384qOy6IcuBfaR5lv39OcoqNZ"
+ "5l+Dah5ABGnVkBP9fKOFhPgghBknTRo0/rZFGI6Q1UHXb+4atP++LNFlDymJcPAgMBAAECgYBammGb1alndta"
+ "xBmTtLLdveoBmp14p04D8mhkiC33iFKBcLUvvxGg2Vpuc+cbagyu/NZG+R/WDrlgEDUp6861M5BeFN0L9O4hz"
+ "GAEn8xyTE96f8sh4VlRmBOvVdwZqRO+ilkOM96+KL88A9RKdp8V2tna7TM6oI3LHDyf/JBoXaQJBAMcVN7fKlYP"
+ "Skzfh/yZzW2fmC0ZNg/qaW8Oa/wfDxlWjgnS0p/EKWZ8BxjR/d199L3i/KMaGdfpaWbYZLvYENqUCQQCobjsuCW"
+ "nlZhcWajjzpsSuy8/bICVEpUax1fUZ58Mq69CQXfaZemD9Ar4omzuEAAs2/uee3kt3AvCBaeq05NyjAkBme8SwB0iK"
+ "kLcaeGuJlq7CQIkjSrobIqUEf+CzVZPe+AorG+isS+Cw2w/2bHu+G0p5xSYvdH59P0+ZT0N+f9LFAkA6v3Ae56OrI"
+ "wfMhrJksfeKbIaMjNLS9b8JynIaXg9iCiyOHmgkMl5gAbPoH/ULXqSKwzBw5mJ2GW1gBlyaSfV3AkA/RJC+adIjsRGg"
+ "JOkiRjSmPpGv3FOhl9fsBPjupZBEIuoMWOC8GXK/73DHxwmfNmN7C9+sIi4RBcjEeQ5F5FHZ";
RSA rsa = new RSA(PRIVATE_KEY, null);
String a = "2707F9FD4288CEF302C972058712F24A5F3EC62C5A14AD2FC59DAB93503AA0FA17113A020EE4EA35EB53F"
+ "75F36564BA1DABAA20F3B90FD39315C30E68FE8A1803B36C29029B23EB612C06ACF3A34BE815074F5EB5AA3A"
+ "C0C8832EC42DA725B4E1C38EF4EA1B85904F8B10B2D62EA782B813229F9090E6F7394E42E6F44494BB8";
byte[] aByte = HexUtil.decodeHex(a);
byte[] decrypt = rsa.decrypt(aByte, KeyType.PrivateKey);
Assert.assertEquals("虎頭闖杭州,多擡頭看天,切勿只管種地", StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8));
複製代碼
做者:路小磊
原文連接:https://my.oschina.net/looly/blog/1523094
來源:oschina
複製代碼
=========================================================
cn.hutool.core.bean
包下的DynaBean、BeanDesc、BeanDesc.PropDesc、BeanPath、BeanUtil
做者:豢龍先生
原文連接:https://blog.csdn.net/sanjun333/article/details/90645420
來源:CSDN
複製代碼
=========================================================
使用Hutool爬取開源中國的開源資訊, 演示Hutool-http的http請求功能.
1.打開oschina的主頁,咱們找到最顯眼的開源資訊模塊,而後點擊「更多」,打開「開源資訊」板塊。
使用Hutool-http配合ReUtil請求並提取頁面內容很是簡單,代碼以下:
//請求列表頁
String listContent = HttpUtil.get("http://www.oschina.net/action/ajax/get_more_news_list?newsType=&p=2");
//使用正則獲取全部標題
List<String> titles = ReUtil.findAll("<span class=\"text-ellipsis\">(.*?)</span>", listContent, 1);
for (String title : titles) {
Console.log(title);//打印標題
}
第一行請求頁面內容,第二行正則定位全部標題行並提取標題部分。
這裏解釋下正則部分:
ReUtil.findAll方法用於查找全部匹配正則表達式的內容部分,第二個參數1表示提取第一個括號(分組)中的內容,
0表示提取全部正則匹配到的內容。這個方法能夠看下core模塊中ReUtil章節瞭解詳情。
<span class=\"text-ellipsis\">(.*?)</span>這個正則就是咱們上面分析頁面源碼後獲得的正則,
其中(.*?)表示咱們須要的內容,.表示任意字符,*表示0個或多個,?表示最短匹配,整個正則的意思就是。
,以<span class=\"text-ellipsis\">開頭,</span>結尾的中間全部字符,中間的字符要達到最短。
?的做用其實就是將範圍限制到最小,否則</span>極可能匹配到後面去了。
複製代碼
不得不說,抓取自己並不困難,尤爲配合Hutool會讓這項工做變得更加簡單快速,而其中的難點即是分析頁面和定位咱們須要的內容。
真正的內容抓取分爲連個部分:
並且在抓取過程當中咱們也會遇到各類問題,包括但不限於:
做者:路小磊
原文連接:https://my.oschina.net/looly/blog/1575851
來源:oschina
複製代碼
=========================================================
使用Hutool [生成和驗證] 圖形驗證碼
隨着攻擊防禦作的愈來愈全面,而圖形驗證碼又是一種簡單有效的防攻擊和防抓取手段,所以應用愈來愈廣。而Hutool中抽象了驗證碼的實現,也提供了幾個簡單的驗證碼實現,從而大大減小服務端開發成本。
因爲對驗證碼需求量巨大,且我以前項目中有所積累,所以在Hutool中加入驗證碼生成和校驗功能。
瞭解Hutool的更多信息請訪問:http://hutool.cn/
<dependency>
<groupId>com.xiaoleilu</groupId>
<artifactId>hutool-all</artifactId>
<version>3.2.3</version>
</dependency>
複製代碼
驗證碼功能位於com.hutool.captcha
包中,核心接口爲ICaptcha,此接口定義瞭如下方法:
createCode 建立驗證碼,實現類需同時生成隨機驗證碼字符串和驗證碼圖片
getCode 獲取驗證碼的文字內容
verify 驗證驗證碼是否正確,建議忽略大小寫
write 將驗證碼寫出到目標流中
複製代碼
其中write方法只有一個OutputStream,ICaptcha實現類能夠根據這個方法封裝寫出到文件等方法。
AbstractCaptcha爲一個ICaptcha抽象實現類,此類實現了驗證碼文本生成、非大小寫敏感的驗證、寫出到流和文件等方法,經過繼承此抽象類只需實現createImage方法定義圖形生成規則便可。
//定義圖形驗證碼的長和寬
LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 100);
//LineCaptcha lineCaptcha = new LineCaptcha(200, 100, 4, 150);
//圖形驗證碼寫出,能夠寫出到文件,也能夠寫出到流
lineCaptcha.write("d:/line.png");
//驗證圖形驗證碼的有效性,返回boolean值
lineCaptcha.verify("1234");
複製代碼
//定義圖形驗證碼的長、寬、驗證碼字符數、干擾元素個數
CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(200, 100, 4, 20);
//CircleCaptcha captcha = new CircleCaptcha(200, 100, 4, 20);
//圖形驗證碼寫出,能夠寫出到文件,也能夠寫出到流
captcha.write("d:/circle.png");
//驗證圖形驗證碼的有效性,返回boolean值
captcha.verify("1234");
複製代碼
//定義圖形驗證碼的長、寬、驗證碼字符數、干擾線寬度
ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(200, 100, 4, 4);
//ShearCaptcha captcha = new ShearCaptcha(200, 100, 4, 4);
//圖形驗證碼寫出,能夠寫出到文件,也能夠寫出到流
captcha.write("d:/shear.png");
//驗證圖形驗證碼的有效性,返回boolean值
captcha.verify("1234");
複製代碼
ICaptcha captcha = ...;
captcha.write(response.getOutputStream());
//Servlet的OutputStream記得自行關閉!
複製代碼
做者:路小磊
原文連接:https://my.oschina.net/looly/blog/1591079
來源:oschina
複製代碼
=========================================================
cron模塊,用於定時發送郵件
extra模塊,MailUtil,用於發送郵件
poi模塊,WordWriter,用於生成日報的word
複製代碼
demo項目能夠訪問碼雲地址獲取:https://gitee.com/loolly_admin/daily-work
src/main/java
cn/hutool/example/dailyWork/
DailyWorkGenerator.java ---- 日報生成器,用於生成Word文檔
MailSendTask.java ---- 郵件發送任務,用於發送郵件
DailyWorkMain.java ---- 定時任務主程序,用於啓動定時任務
src/main/resources
config/
cron.setting ---- 定時任務配置文件
mail.setting ---- 郵箱配置文件
複製代碼
1.生成日報Word
/** 標題字體 */
private static final Font TITLE_FONT = new Font("黑體", Font.PLAIN, 22);
/** 正文字體 */
private static final Font MAIN_FONT = new Font("宋體", Font.PLAIN, 14);
/** * 生成日報 * * @return 日報word文件 */
public static File generate() {
// 一、準備文件
File wordFile = FileUtil.file(StrUtil.format("每日工做彙報_{}.docx", DateUtil.today()));
if(FileUtil.exist(wordFile)) {
// 若是文件存在,刪除之(可能上次發送遺留)
wordFile.delete();
}
// 生成並寫出word
Word07Writer writer = new Word07Writer(wordFile);
writer.addText(ParagraphAlignment.CENTER, TITLE_FONT, "工做日報");
writer.addText(MAIN_FONT, "");
writer.addText(MAIN_FONT, "尊敬的領導:");
writer.addText(MAIN_FONT, " 今天我在Hutool羣裏摸魚,什麼工做也沒作。");
writer.close();
return wordFile;
}
複製代碼
2.發送郵件在mail.setting中配置發件箱信息
# 發件人(必須正確,不然發送失敗)
from = Hutool<hutool@yeah.net>
# 用戶名(注意:若是使用foxmail郵箱,此處user爲qq號)
user = hutool
# 密碼
pass = XXXX
#使用 STARTTLS安全鏈接
startttlsEnable = true
#使用 SSL安全鏈接
sslEnable = true
=========================================================================
// 今天的日期,格式相似:2019-06-20
String today = DateUtil.today();
// 生成彙報Word
File dailyWorkDoc = DailyWorkGenerator.generate();
// 發送郵件
MailUtil.sendHtml("hutool@foxmail.com", StrUtil.format("{} 工做日報", today), "請見附件。", dailyWorkDoc);
StaticLog.debug("{} 工做日報已發送給領導!", today);
複製代碼
3.定時發送,將剛纔的發送郵件做爲定時任務加入到配置文件:
[cn.hutool.example.dailyWork]
# 天天下午6點定時發送
MailSendTask.execute = 00 00 18 * * *
複製代碼
4.啓動定時任務
// 設置秒匹配(只有在定時任務精確到秒的時候使用)
CronUtil.setMatchSecond(true);
// 啓動定時任務,自動加載配置文件中的內容
CronUtil.start();
複製代碼
5.結果
做者:路小磊
原文連接:https://my.oschina.net/looly/blog/3064203
來源:oschina
複製代碼
=========================================================
* Slf4j
全稱Simple Logging Facade for JAVA,真正的日誌門面,只提供接口方法,當配合特定的日誌實現時,
須要引入相應的橋接包
* Common-logging
Apache提供的一個通用的日誌接口,common-logging會經過動態查找的機制,在程序運行時自動找出真正
使用的日誌庫,本身也自帶一個功能很弱的日誌實現。
* 差異:
Common-logging 動態查找日誌實現(程序運行時找出日誌實現),
Slf4j 則是靜態綁定(編譯時找到實現),動態綁定由於依賴ClassLoader尋找和載入日誌實現,所以相似於
OSGI那種使用獨立ClassLoader就會形成沒法使用的狀況。
Slf4j 支持參數化的log字符串,避免了以前爲了減小字符串拼接的性能損耗而不得不寫的
if(logger.isDebugEnable()),如今能夠直接寫:logger.debug(「current user is: {}」, user)。
複製代碼
Log4j
Log4j多是Java世界裏最出名的日誌框架了,支持各類目的地各類級別的日誌輸出。
LogBack
Log4j做者的又一力做(據說是受不了收費文檔搞了個開源的,不須要橋接包完美適配Slf4j),感受迄今爲止最棒的日誌框架了,一直都在用,配置文件夠簡潔,性能足夠好。
JDK Logging 從JDK1.4開始引入,不得不說,你去Google下這個JDK自帶的日誌組件,並不如Log4j和LogBack之類好用,沒有配置文件,日誌級別很差理解,想順心的用估計還得本身封裝下,總之你們已經被Log4j慣壞了,JDK的設計並不能被你們認同,惟一的優勢想就是不用引入新額jar包。
看了以上介紹,若是你不是混跡(深陷)Java多年的老手,估計會蒙圈兒了吧,那你確定會問,要門面幹嗎。有了手機就有手機殼、手機膜,框架也同樣,門面的做用更多的仍是三個字:解耦合。說白了,加入一個項目用了一個日誌框架,想換咋整啊?那就一行一行的找日誌改唄,想一想都是噩夢。因而,門面出來了,門面說啦, 你用個人格式寫日誌,把日誌想寫哪兒寫哪兒,例如Slf4j-api加上後,想換日誌框架,直接把橋接包一換就行。方便極了。
說實話,如今Slf4j基本能夠是Java日誌的一個標準了,按照它寫基本能夠實現全部日誌實現通吃,可是就有人不服,還寫了門面的門面(沒錯,這我的就是我)。
若是看過Netty的源碼,推薦你看下io.netty.util.internal.logging這個包裏內容,會發現Netty又對日誌封裝了一層。
不管是Netty的日誌模塊仍是Hutool-log模塊,思想相似於Common Logging,作動態日誌實現查找,
而後找到相應的日誌實現來寫入日誌,核心代碼以下:
/** * 決定日誌實現 * @return 日誌實現類 */
public static Class<? extends AbstractLog> detectLog(){
List<Class<? extends AbstractLog>> logClassList = Arrays.asList(
Slf4jLog.class,
Log4jLog.class,
Log4j2Log.class,
ApacheCommonsLog.class,
JdkLog.class
);
for (Class<? extends AbstractLog> clazz : logClassList) {
try {
clazz.getConstructor(Class.class).newInstance(LogFactory.class).info("Use Log Framework: [{}]", clazz.getSimpleName());
return clazz;
} catch (Error | Exception e) {
continue;
}
}
return JdkLog.class;
}
複製代碼
按順序實例化相應的日誌實現,若是實例化失敗(通常是ClassNotFoundException),說明jar不存在,那實例化下一個,經過不停的嘗試,最終若是沒有引入日誌框架,那使用JDK Logging(這個確定會有的),固然這種方式也和Common-logging存在相似問題,不過不用到跨ClassLoader仍是很好用的。
對於JDK Logging,也作了一些適配,使之能夠與Slf4j的日誌級別作對應,這樣就將各個日誌框架差別化降到最小。另外一方面,若是你看過個人這篇日誌,那你必定了解了個人類名自動識別功能,這樣你們在複製類名的時候,就不用修改日誌的那一行代碼了,在全部類中,日誌的初始化只有這一句:
Log log = LogFactory.get();
//=======實現方式 =======
/** * @return 得到調用者的日誌 * 經過堆棧引用得到當前類名。 */
public static Log get() {
return getLog(new Exception().getStackTrace()[1].getClassName());
}
/** * 日誌統一接口 */
public interface Log extends TraceLog, DebugLog, InfoLog, WarnLog, ErrorLog 這樣就實現了單一使用,各個日誌框架靈活引用的做用了。 複製代碼
做者:路小磊
原文連接:https://my.oschina.net/looly/blog/543000
來源:oschina
複製代碼
=========================================================