緒論
今天就和大家分享一下shiro原始碼裡面使用到的建造者模式。在介紹建造者模式相關知識之前,我們先來看一段例子分析。
從一個簡單的例子說起
我們在使用shiro獲取登入使用者的時候,比如使用ini檔案配置使用者角色許可權,我們可以這樣寫:
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:demo01_getstarted.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject currentUser = SecurityUtils.getSubject();
複製程式碼
SecurityUtils.getSubject()
是獲取當前使用者,我們跳進去看一下,是怎麼獲取的
public static Subject getSubject() {
Subject subject = ThreadContext.getSubject();
if (subject == null) {
subject = (new Subject.Builder()).buildSubject();
ThreadContext.bind(subject);
}
return subject;
}
複製程式碼
上面程式碼,首先從ThreadContext裡面獲取Subject,獲取不到就建立,我們看看建立過程,跳進去看看(new Subject.Builder()).buildSubject()
幹了什麼。
這裡不貼過長程式碼了,Builder是Subject的靜態內部類,用來完成Subject的複雜建立過程,使得呼叫就無需關注Subject怎麼建立的,只要知道怎麼獲取Subject就可以了。
我們看一下,Builder類裡面幹了什麼事情
public static class Builder {
private final SubjectContext subjectContext;
private final SecurityManager securityManager;
public Builder() {
this(SecurityUtils.getSecurityManager());
}
public Builder(SecurityManager securityManager) {
if (securityManager == null) {
throw new NullPointerException("SecurityManager method argument cannot be null.");
}
this.securityManager = securityManager;
this.subjectContext = newSubjectContextInstance();
if (this.subjectContext == null) {
throw new IllegalStateException("Subject instance returned from 'newSubjectContextInstance' " +
"cannot be null.");
}
this.subjectContext.setSecurityManager(securityManager);
}
protected SubjectContext newSubjectContextInstance() {
return new DefaultSubjectContext();
}
protected SubjectContext getSubjectContext() {
return this.subjectContext;
}
public Builder sessionId(Serializable sessionId) {
if (sessionId != null) {
this.subjectContext.setSessionId(sessionId);
}
return this;
}
public Builder host(String host) {
if (StringUtils.hasText(host)) {
this.subjectContext.setHost(host);
}
return this;
}
public Builder session(Session session) {
if (session != null) {
this.subjectContext.setSession(session);
}
return this;
}
public Builder principals(PrincipalCollection principals) {
if (principals != null && !principals.isEmpty()) {
this.subjectContext.setPrincipals(principals);
}
return this;
}
public Builder sessionCreationEnabled(boolean enabled) {
this.subjectContext.setSessionCreationEnabled(enabled);
return this;
}
public Builder authenticated(boolean authenticated) {
this.subjectContext.setAuthenticated(authenticated);
return this;
}
public Builder contextAttribute(String attributeKey, Object attributeValue) {
if (attributeKey == null) {
String msg = "Subject context map key cannot be null.";
throw new IllegalArgumentException(msg);
}
if (attributeValue == null) {
this.subjectContext.remove(attributeKey);
} else {
this.subjectContext.put(attributeKey, attributeValue);
}
return this;
}
// 建立Subject
public Subject buildSubject() {
return this.securityManager.createSubject(this.subjectContext);
}
}
複製程式碼
我們可以看到,Builder完成了Subject一些列的複雜建立過程,包括sesson、sessionid等的設定等過程。最後會呼叫buildSubject()
方法來建立Subject。
我們就不繼續深入看this.securityManager.createSubject(this.subjectContext)
裡面幹了什麼事了。
回到前面,我們可以看到Subject物件會包含許多的當前使用者資訊,但是這些資訊框架底層都替我們設定好了,我們只要獲取Subject物件即可,呼叫Subject currentUser = SecurityUtils.getSubject();
就可以了。
這樣看來,是不是特別方便。這些都歸功於建造這模式的使用。
建造這模式基本概念
通過分析shiro原始碼,我們對建造這模式有了一個初步的感知,目前大家可以理解成建立複雜物件由一個建造類來完成。那麼,我們就來學習一下建造者模式的一些理論知識,相信大家結合上面的例子分析,不會感到吃力乏味的。
什麼是建造者模式
建造者模式又稱為生成器模式。
建造者模式:它可以將複雜物件的建造過程抽象出來(抽象類別),使這個抽象過程的不同實現方法可以構造出不同表現(屬性)的物件。 參考維基百度百科:生成器模式
可能有些讀者看了這句話迷迷糊糊的,那麼這句話是什麼意思呢?結合上面的例子,我們可以知道Subject物件有很多屬性,通過SubjectContext物件來儲存的,最後建立Subject物件的時候,傳遞SubjectContext就可以了。這樣比如,我們建立兩個有不同屬性值的Subject:
Subject user_1 = new Subject.Builder().authenticated(false).buildSubject();
Subject user_2 = new Subject.Builder().authenticated(true).buildSubject();
複製程式碼
我們建立Subject的不同實現方法,比如authenticated()一個設定true,一個設定false,我們就構造出來了有不同表現(屬性)的Subject物件了,一個是授權,一個是未授權。
為什麼使用建造者模式
使用建造者模式可以遮蔽複雜的物件建立過程,物件的建立對呼叫者來說是透明的,這樣就使得程式耦合度降低,程式可讀性和維護性提高了。
結束語
對於設計模式,大家不要機械地學習,個人覺得主要理解其設計思想和好處就可以了。在編碼過程中大家不要刻意去使用它,而是去思考如何運用它更好地設計程式碼。這樣,經過時間的推磨,大家就可以真正做到把設計模式融會貫通,手到拈來。