Springboot整合Shiro框架(包含mybatis、Durid、Html5、Thymeleaf)
在Springboot中使用Shrio,實現賬號的登入限制、賬號的角色的授權、資源的授權。
以下是我個人從零開始在springboot專案中去整合Shiro框架,僅供參考,但是按照我這個整合是絕對可行的!
我以專案結構的方式去介紹了:
先貼個專案結構目錄圖:
首先是pom檔案:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com</groupId> <artifactId>shirodemo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>shirodemo</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!--Spring框架基本的核心工具--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency> <!-- shiro-spring --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.3.2</version> </dependency> <!-- shiro ehcache --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.3.2</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-core --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.3.2</version> </dependency> <!--配置shiro標籤 必須使用,不然找不到AbstractTextChildModifierAttrProcessor--> <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency> <!-- ehchache --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> </dependency> <!--mysql連線包--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.1</version> </dependency> <!-- druid資料來源驅動 1.1.10解決springboot從1.0——2.0版本問題--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <!-- springboot-log4j --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j</artifactId> <version>1.3.8.RELEASE</version> </dependency> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> 這個pom檔案,改說明的我都在註釋上做了說明,請認真閱讀。
配置完所需要用的jar包後,我們整合Shiro框架需要做的第一步是什麼呢?
就是建一個shiro資料夾,在這個資料夾裡面,我們需要建2個類:
一個是config配置類(顧名思義,就是各自配置),一個是自定義的Realm域類(這麼說吧,就是個做類似連線效果的東西,shiro裡面很多東西都得經過這個圈兒)
ShiroConfig類:
package com.shirodemo.shiro; import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; import org.apache.shiro.cache.ehcache.EhCacheManager; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.Map; /** * @Author: JCccc * @CreateTime: 2018-10-25 * @Description: */ @Configuration public class ShiroConfig { /** * Subject: 使用者主體(把操作交給SecurityManager) * SecurityManager: 安全管理器(關聯Realm) * Realm: Shiro連線資料的橋樑 */ //一. 建立ShiroFilterFactoryBean @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean(); //設定安全管理器 shiroFilterFactoryBean.setSecurityManager(securityManager); //新增Shiro內建過濾器 /** * Shiro內建過濾器,可以實現許可權相關的攔截器 * 常用的過濾器: * anon:無需認證(登入)可以訪問 * authc:必須認證才可以訪問 * user:如果使用rememberMe的功能可以直接訪問 * perms:該資源必須得到資源許可權才可以訪問 * role:該資源必須得到角色許可權才可以訪問 * * * */ Map<String,String> filterMap = new LinkedHashMap<String, String>(); /*filterMap.put("/add","authc"); filterMap.put("/update","authc");*/ //使用通配方式統一配置攔截的URL // 注意:*******************下面的是按順序執行的********************** filterMap.put("/testThymeleaf","anon"); //放行login.html filterMap.put("/login","anon"); //授權過濾器 ,以下為例 加上了授權perms 後,那麼自動會跳轉到 執行認證授權AuthenticationInfo doGetAuthenticationInfo // 當授權攔截時,會自動跳轉到未授權提示頁面 filterMap.put("/add","perms[user-add]"); filterMap.put("/update","perms[user-update]"); filterMap.put("/*","authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap); //修改攔截後返回的登入頁面 shiroFilterFactoryBean.setLoginUrl("/toLogin"); //設定未授權提示頁面 shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth"); return shiroFilterFactoryBean; } //二. 建立DefaultWebSecurityMangager @Bean(name = "securityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){ DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager(); //關聯Realm securityManager.setRealm(userRealm); //注入快取管理器; securityManager.setCacheManager(ehCacheManager()); //這個如果執行多次,也是同樣的一個物件; return securityManager; } //三. 建立Realm @Bean(name = "userRealm") public UserRealm getRaalm(){ return new UserRealm(); } /** * 配置ShiroDialect,用於thymeleaf和shiro標籤配合使用 */ @Bean public ShiroDialect getShiroDialect(){ return new ShiroDialect(); } /** * shiro快取管理器; * 需要注入對應的其它的實體類中: * 1、安全管理器:securityManager * 可見securityManager是整個shiro的核心; * @return */ @Bean public EhCacheManager ehCacheManager(){ System.out.println("ShiroConfiguration.getEhCacheManager()"); EhCacheManager cacheManager = new EhCacheManager(); cacheManager.setCacheManagerConfigFile("classpath:config/shiro-ehcache.xml"); return cacheManager; } }
這個配置類裡面配置的東西以及意義,我也做了相關的詳細註釋。至於你想更深入瞭解,那請更深入地自己去網上了解了。
然後是自定義的Realm,我這邊定義的名字叫UserRealm:
package com.shirodemo.shiro; import com.shirodemo.pojo.User; import com.shirodemo.service.UserService; import com.shirodemo.util.StringUtils; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; /** * @Author: JCccc * @CreateTime: 2018-10-25 * @Description:這是一個自定義的Realm類,繼承AuthorizingRealm類 */ public class UserRealm extends AuthorizingRealm{ @Autowired UserService userService; /** * 執行授權邏輯 * @param principals * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { System.out.println("執行授權邏輯!"); //給資源進行授權 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //先到資料庫去查詢當前登入使用者的授權字串 Subject subject= SecurityUtils.getSubject(); User user=(User)subject.getPrincipal(); User userNow=userService.queryUserInfoById(user.getId()); System.out.println("許可權是L:"+userNow.getPerms()); //新增資源的授權字串 info.addStringPermission(userNow.getPerms()); clearCachedAuthorizationInfo(); return info; } /** * 執行認證邏輯 * @param token * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("執行認證邏輯!"); //編寫Shiro的判斷邏輯,判斷使用者名稱和密碼 //1.判斷使用者名稱 UsernamePasswordToken tokenNow=(UsernamePasswordToken)token; User user=userService.queryUserInfoByaccountname(tokenNow.getUsername()); if(StringUtils.isNull(user)){ //使用者名稱不存在 //此刻shiro會丟擲一個UnknownAccountException,表示使用者名稱不存在 return null; } // String password=user.getPassword(); //2.判斷密碼 return new SimpleAuthenticationInfo(user,user.getPassword(),""); } /** * 清理快取許可權 */ public void clearCachedAuthorizationInfo() { this.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals()); } } 同理,註釋裡面我認為改說的也明明白白了。
配置完這兩個大核心,接下來,再加上一個配置快取的xml,那麼關於shiro的配置部分(除了filter,這裡我沒用到)就大功告成了。
快取配置的xml:
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false"> <diskStore path="java.io.tmpdir/Tmp_EhCache"/> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="false" diskPersistent="false" diskExpiryThreadIntervalSeconds="120"/> <!-- 登入記錄快取鎖定1小時 --> <cache name="passwordRetryCache" maxEntriesLocalHeap="2000" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false" statistics="true"/> <cache name="shiro-activeSessionCache" maxEntriesLocalHeap="10000" overflowToDisk="false" eternal="false" diskPersistent="false" timeToLiveSeconds="300" timeToIdleSeconds="600" statistics="true"/> </ehcache> <!--<?xml version="1.0" encoding="UTF-8"?>--> <!--<ehcache name="swmmotors">--> <!--<!– 磁碟快取位置 –>--> <!--<diskStore path="java.io.tmpdir"/>--> <!--<!– 預設快取 –>--> <!--<defaultCache--> <!--maxEntriesLocalHeap="1000"--> <!--eternal="false"--> <!--timeToIdleSeconds="3600"--> <!--timeToLiveSeconds="3600"--> <!--overflowToDisk="false">--> <!--</defaultCache>--> <!--<!– 登入記錄快取 鎖定10分鐘 –>--> <!--<cache name="loginRecordCache"--> <!--maxEntriesLocalHeap="2000"--> <!--eternal="false"--> <!--timeToIdleSeconds="600"--> <!--timeToLiveSeconds="0"--> <!--overflowToDisk="false"--> <!--statistics="true">--> <!--</cache>--> <!--<!– 系統使用者快取 沒必要過期 –>--> <!--<cache name="sys-userCache"--> <!--maxEntriesLocalHeap="10000"--> <!--overflowToDisk="false"--> <!--eternal="false"--> <!--diskPersistent="false"--> <!--timeToLiveSeconds="0"--> <!--timeToIdleSeconds="0"--> <!--statistics="true"/>--> <!--<!– 系統使用者授權快取 沒必要過期 –>--> <!--<cache name="sys-authCache"--> <!--maxEntriesLocalHeap="10000"--> <!--overflowToDisk="false"--> <!--eternal="false"--> <!--diskPersistent="false"--> <!--timeToLiveSeconds="0"--> <!--timeToIdleSeconds="0"--> <!--memoryStoreEvictionPolicy="LRU"--> <!--statistics="true"/>--> <!--<!– 選單快取 沒必要過期 –>--> <!--<cache name="sys-menuCache"--> <!--maxEntriesLocalHeap="10000"--> <!--overflowToDisk="false"--> <!--eternal="false"--> <!--diskPersistent="false"--> <!--timeToLiveSeconds="0"--> <!--timeToIdleSeconds="0"--> <!--statistics="true"/>--> <!--<!– shiro 會話快取 不需要序列化到磁碟 此處我們放到db中了 此處cache沒必要過期 因為我們存放到db了 –>--> <!--<cache name="shiro-activeSessionCache"--> <!--maxEntriesLocalHeap="10000"--> <!--overflowToDisk="false"--> <!--eternal="false"--> <!--diskPersistent="false"--> <!--timeToLiveSeconds="300"--> <!--timeToIdleSeconds="600"--> <!--statistics="true"/>--> <!--</ehcache>--> 我去,差點忘了yml檔案了:
server: servlet: context-path: / port: 8012 spring: datasource: druid: # 資料庫訪問配置, 使用druid資料來源 type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/shirotest?useSSL=false&useUnicode=true&characterEncoding=utf-8 username: root password: 123456 # 連線池配置 initial-size: 5 min-idle: 5 max-active: 20 # 連線等待超時時間 max-wait: 30000 # 配置檢測可以關閉的空閒連線間隔時間 time-between-eviction-runs-millis: 60000 # 配置連線在池中的最小生存時間 min-evictable-idle-time-millis: 300000 validation-query: select '1' from dual test-while-idle: true test-on-borrow: false test-on-return: false # 開啟PSCache,並且指定每個連線上PSCache的大小 pool-prepared-statements: true max-open-prepared-statements: 20 max-pool-prepared-statement-per-connection-size: 20 # 配置監控統計攔截的filters, 去掉後監控介面sql無法統計, 'wall'用於防火牆 filters: commons-log.connection-logger-name: stat,wall,log4j useGlobalDataSourceStat: true # 通過connectProperties屬性來開啟mergeSql功能;慢SQL記錄 connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 # StatViewServlet配置 stat-view-servlet: enabled: true # 訪問路徑為/druid時,跳轉到StatViewServlet url-pattern: /druid/* # 是否能夠重置資料 reset-enable: false # 需要賬號密碼才能訪問控制檯 login-username: root login-password: 123456 # IP白名單 # allow: 127.0.0.1 # IP黑名單(共同存在時,deny優先於allow) # deny: 192.168.1.218 # 配置StatFilter filter: stat: log-slow-sql: true mybatis: # type-aliases掃描路徑 type-aliases-package: com.shirodemoe.pojo # mapper xml實現掃描路徑 mapper-locations: classpath:mybatis/mapper/*.xml property: order: BEFORE
配置完關於shiro的一些條條框框,那麼我們可以直接開始使用了。
當然我這裡的使用是指,已經配置了mybatis啊、Durid連線池啊、Thymeleaf啊那些其他的東西。
開始使用,先從資料庫開始:
資料庫表,一張(資料庫連線的配置資訊在yml檔案裡可以看,當然這個應該自己就會):
然後就是回到我們熟悉的SSM框架了其實。
廢話不多說,
先上個pojo的類:
package com.shirodemo.pojo; /** * @Author: JCccc * @CreateTime: 2018-10-26 * @Description: */ public class User { private Integer id; private String accountname; private String password; private String perms; @Override public String toString() { return "User{" + "id=" + id + ", accountname='" + accountname + '\'' + ", password='" + password + '\'' + ", perms='" + perms + '\'' + '}'; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getAccountname() { return accountname; } public void setAccountname(String accountname) { this.accountname = accountname; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getPerms() { return perms; } public void setPerms(String perms) { this.perms = perms; } } 然後到了dao層了,也就是mapper:
package com.shirodemo.mapper; import com.shirodemo.pojo.User; import org.apache.ibatis.annotations.Mapper; /** * @Author: JCccc * @CreateTime: 2018-10-26 * @Description: */ @Mapper public interface UserMapper { User queryUserInfoByaccountname(String accountname); User queryUserInfoById(Integer id); } 接著,就是mapper.xml咯,當然你可以用註解SQL方式。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.shirodemo.mapper.UserMapper"> <select id="queryUserInfoByaccountname" resultType="com.shirodemo.pojo.User"> SELECT *FROM user WHERE accountname=#{accountname} </select> <select id="queryUserInfoById" resultType="com.shirodemo.pojo.User"> SELECT *FROM user WHERE id=#{id} </select> </mapper> 然後就是service了,
先service介面:
package com.shirodemo.service; import com.shirodemo.pojo.User; /** * @Author: JCccc * @CreateTime: 2018-10-26 * @Description: */ public interface UserService { User queryUserInfoByaccountname(String accountname); User queryUserInfoById(Integer id); }
再是impl實現類:
package com.shirodemo.service.impl; import com.shirodemo.mapper.UserMapper; import com.shirodemo.pojo.User; import com.shirodemo.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @Author: JCccc * @CreateTime: 2018-10-26 * @Description: */ @Service public class UserServiceImpl implements UserService { @Autowired public UserMapper userMapper; @Override public User queryUserInfoByaccountname(String accountname){ return userMapper.queryUserInfoByaccountname(accountname); } @Override public User queryUserInfoById(Integer id) { return userMapper.queryUserInfoById(id); } } 好了!終於到了controller層了:
package com.shirodemo.controller; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.apache.shiro.web.util.WebUtils; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; /** * @Author: JCccc * @CreateTime: 2018-10-25 * @Description: */ @Controller public class UserController { @ResponseBody @RequestMapping("/hello") public String helloContro(){ return "hello man"; } @RequestMapping("/add") public String add(){ return "/user/add"; } @RequestMapping("/update") public String update(){ return "/user/update"; } //用於攔截完之後去登入頁面 @RequestMapping("/toLogin") public String login(){ return "login"; } //用於未授權識別後跳轉的提示頁面 @RequestMapping("/noAuth") public String noAuth(){ return "noAuth"; } //測試頁面主頁 @RequestMapping("/testThymeleaf") public String testThymeleafController(Model model){ model.addAttribute("name","測試模板引擎"); return "test"; } @RequestMapping("/login") public String loginController(String accountname, String password, Model model, HttpServletRequest request){ /** * * 使用Shiro編寫認證操作 */ System.out.println("開始執行認證"+"name為"+accountname); //1.獲取Subject Subject subject= SecurityUtils.getSubject(); //2.封裝使用者賬號資訊資料 UsernamePasswordToken token=new UsernamePasswordToken(accountname,password); //3.執行登入方法 try { subject.login(token); //當執行subject的login方法,那麼就必定會到shiro裡面的認證操作方法裡面AuthenticationInfo doGetAuthenticationInfo //登入成功 //跳轉到test.html return "test"; } catch (UnknownAccountException e) { //e.printStackTrace(); //如果是catch到UnknownAccountException異常,那麼就是使用者名稱不存在 model.addAttribute("msg","使用者名稱不存在"); //如果這裡使用重定向,那麼msg將不會跟著過去下個函式方法 return "login"; } catch (IncorrectCredentialsException e) { // e.printStackTrace(); //登陸失敗,密碼錯誤 model.addAttribute("msg","密碼錯誤"); //如果這裡使用重定向,那麼msg將不會跟著過去下個函式方法 return "login"; } } }
頁面是時候給你們看一眼了,簡簡單單的:
test.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"> <head> <meta charset="UTF-8"> <title>測試Thymeleaf模板的使用</title> </head> <body> <h3 th:text="${name}"></h3> <hr/> <div shiro:hasPermission="user-add"> 點選進入使用者新增頁面:<a href="add">使用者新增</a><br/> </div> <div shiro:hasPermission="user-update"> 點選進入使用者更新:<a href="update">使用者更新</a><br/> </div> <a href="tologin">使用者登入</a> </body> </html> noAuth.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>未授權提示頁面</title> </head> <body> 親,你暫時沒有被授權訪問頁面。 </body> </html> login.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>登入頁面</title> </head> <body> <h3>登入</h3> <h3 th:text="${msg}" style="color: red"/> <form method="post" action="/login"> 使用者名稱:<input type="text" name="accountname"/><br/> 密碼:<input type="password" name="password"/><br/> <input type="submit" value="登入"> </form> </body> </html> 然後是新增和更新的驗證頁面, add.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Add使用者</title> </head> <body> 使用者新增 </body> </html>
update.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>update使用者</title> </head> <body> 使用者的更新 </body> </html>
好了,其實裡面我還用到了一個工具類是關於判斷字元是否為空的:
StringUtils,也貼了吧:
package com.shirodemo.util; import org.apache.commons.lang.text.StrBuilder; import java.util.Collection; import java.util.Map; /** * 字串工具類 */ public class StringUtils { /** 空字串 */ private static final String NULLSTR = ""; /** 下劃線 */ private static final char SEPARATOR = '_'; /** * 獲取引數不為空值 * * @param value defaultValue 要判斷的value * @return value 返回值 */ public static <T> T nvl(T value, T defaultValue) { return value != null ? value : defaultValue; } /** * * 判斷一個Collection是否為空, 包含List,Set,Queue * * @param coll 要判斷的Collection * @return true:為空 false:非空 */ public static boolean isEmpty(Collection<?> coll) { return isNull(coll) || coll.isEmpty(); } /** * * 判斷一個Collection是否非空,包含List,Set,Queue * * @param coll 要判斷的Collection * @return true:非空 false:空 */ public static boolean isNotEmpty(Collection<?> coll) { return !isEmpty(coll); } /** * * 判斷一個物件陣列是否為空 * * @param objects 要判斷的物件陣列 ** @return true:為空 false:非空 */ public static boolean isEmpty(Object[] objects) { return isNull(objects) || (objects.length == 0); } /** * * 判斷一個物件陣列是否非空 * * @param objects 要判斷的物件陣列 * @return true:非空 false:空 */ public static boolean isNotEmpty(Object[] objects) { return !isEmpty(objects); } /** * * 判斷一個Map是否為空 * * @param map 要判斷的Map * @return true:為空 false:非空 */ public static boolean isEmpty(Map<?, ?> map) { return isNull(map) || map.isEmpty(); } /** * * 判斷一個Map是否為空 * * @param map 要判斷的Map * @return true:非空 false:空 */ public static boolean isNotEmpty(Map<?, ?> map) { return !isEmpty(map); } /** * * 判斷一個字串是否為空串 * * @param str String * @return true:為空 false:非空 */ public static boolean isEmpty(String str) { return isNull(str) || NULLSTR.equals(str.trim()); } /** * * 判斷一個字串是否為非空串 * * @param str String * @return true:非空串 false:空串 */ public static boolean isNotEmpty(String str) { return !isEmpty(str); } /** * * 判斷一個物件是否為空 * * @param object Object * @return true:為空 false:非空 */ public static boolean isNull(Object object) { return object == null; } /** * * 判斷一個物件是否非空 * * @param object Object * @return true:非空 false:空 */ public static boolean isNotNull(Object object) { return !isNull(object); } /** * * 判斷一個物件是否是陣列型別(Java基本型別的陣列) * * @param object 物件 * @return true:是陣列 false:不是陣列 */ public static boolean isArray(Object object) { return isNotNull(object) && object.getClass().isArray(); } /** * 去空格 */ public static String trim(String str) { return (str == null ? "" : str.trim()); } /** * 擷取字串 * * @param str 字串 * @param start 開始 * @return 結果 */ public static String substring(final String str, int start) { if (str == null) { return NULLSTR; } if (start < 0) { start = str.length() + start; } if (start < 0) { start = 0; } if (start > str.length()) { return NULLSTR; } return str.substring(start); } /** * 擷取字串 * * @param str 字串 * @param start 開始 * @param end 結束 * @return 結果 */ public static String substring(final String str, int start, int end) { if (str == null) { return NULLSTR; } if (end < 0) { end = str.length() + end; } if (start < 0) { start = str.length() + start; } if (end > str.length()) { end = str.length(); } if (start > end) { return NULLSTR; } if (start < 0) { start = 0; } if (end < 0) { end = 0; } return str.substring(start, end); } /** * 駝峰首字元小寫 */ public static String uncapitalize(String str) { int strLen; if (str == null || (strLen = str.length()) == 0) { return str; } return new StrBuilder(strLen).append(Character.toLowerCase(str.charAt(0))).append(str.substring(1)).toString(); } /** * 下劃線轉駝峰命名 */ public static String toUnderScoreCase(String s) { if (s == null) { return null; } StringBuilder sb = new StringBuilder(); boolean upperCase = false; for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); boolean nextUpperCase = true; if (i < (s.length() - 1)) { nextUpperCase = Character.isUpperCase(s.charAt(i + 1)); } if ((i > 0) && Character.isUpperCase(c)) { if (!upperCase || !nextUpperCase) { sb.append(SEPARATOR); } upperCase = true; } else { upperCase = false; } sb.append(Character.toLowerCase(c)); } return sb.toString(); } /** * 是否包含字串 * * @param str 驗證字串 * @param strs 字串組 * @return 包含返回true */ public static boolean inStringIgnoreCase(String str, String... strs) { if (str != null && strs != null) { for (String s : strs) { if (str.equalsIgnoreCase(trim(s))) { return true; } } } return false; } /** * 將下劃線大寫方式命名的字串轉換為駝峰式。如果轉換前的下劃線大寫方式命名的字串為空,則返回空字串。 例如:HELLO_WORLD->HelloWorld * * @param name 轉換前的下劃線大寫方式命名的字串 * @return 轉換後的駝峰式命名的字串 */ public static String convertToCamelCase(String name) { StringBuilder result = new StringBuilder(); // 快速檢查 if (name == null || name.isEmpty()) { // 沒必要轉換 return ""; } else if (!name.contains("_")) { // 不含下劃線,僅將首字母大寫 return name.substring(0, 1).toUpperCase() + name.substring(1); } // 用下劃線將原始字串分割 String[] camels = name.split("_"); for (String camel : camels) { // 跳過原始字串中開頭、結尾的下換線或雙重下劃線 if (camel.isEmpty()) { continue; } // 首字母大寫 result.append(camel.substring(0, 1).toUpperCase()); result.append(camel.substring(1).toLowerCase()); } return result.toString(); } }
好了,記錄分享完畢。按照這個從零開始手把手配置,絕對能完成!