吃透Shiro源码2----PrincipalCollection、ThreadState
技术手法
(1)工厂返回单例与多例
创建内部实例对象,如果是单例对象,那么就把创建出来的对象塞到singletonInstance中并返回这个单例对象。如果不是,那么就创建一个新对象给调用者
public abstract class AbstractFactory<T>
implements Factory<T> ...
@Override
public T getInstance() {
T instance;
if (isSingleton()) {
//如果是单例的,创建单例对象
if (this.singletonInstance == null) {
this.singletonInstance = createInstance();
}
instance = this.singletonInstance;
} else {
//如果不是单例的,创建一个实例对象
instance = createInstance();
}
//我自己写的时候,没有想到这一步
//大佬的心思真细腻,再来一次健壮性判断
//异常用了IllegalStateException
if (instance == null) {
String msg = "工厂返回了一个空的对象";
throw new IllegalStateException(msg);
}
return instance;
}
protected abstract T createInstance();
}
(2)备忘录:如何保住旧对象状态?
如何保住对象的状态呢?备忘录设计模式的精髓就在于保住以前对象的状态。这里大佬提供了较为完整的实现代码,学到了。
public interface ThreadState {
/**
* 保存当前线程数据
*/
void bind();
/**
* 恢复数据
*/
void restore();
/**
* 清空数据
*/
void clear();
}
public class SubjectThreadState implements ThreadState ...
/**
* 保存以前的Subject与SecurityManager
* 然后把这个类中的Subject与SecurityManager绑定到当前线程
*/
@Override
public void bind() {
SecurityManager securityManager = this.securityManager;
if (securityManager == null) {
//防止为空,从当前线程中再拿一次
securityManager = ThreadContext.getSecurityManager();
}
//绑定当前线程中的Subject与SecurityManager到originResources
this.originResources = ThreadContext.getResources();
//清空当先线程
ThreadContext.remove();
//重新绑定
ThreadContext.bind(this.subject);
if (securityManager != null) {
ThreadContext.bind(securityManager);
}
}
@Override
public void restore() {
//先清空
ThreadContext.remove();
if(!CollectionUtils.isEmpty(this.originResources)){
//把之前保存的对象再重新设置到当前线程中
ThreadContext.setResources(this.originResources);
}
}
(3)集合如何懒加载
某个对象内部有一个private Map<String, Set> realmPrincipals;属性。需要在第一次添加数据的时候创建Set集合。这里提供了一种懒加载机制,非常值得借鉴
/**
* 创建 realmName - Set 这种对象关联关系并添加进内部Map中
* 最后返回Set对象引用方便操作
* 添加新凭证到Set中
*
* @param realmName realmName
* @return realmName对应的Set对象引用
*/
protected Set getPrincipalsLazy(String realmName) {
Set principalsFromRealm = this.realmPrincipals.get(realmName);
if (principalsFromRealm == null) {
principalsFromRealm = new LinkedHashSet();
this.realmPrincipals.put(realmName, principalsFromRealm);
}
//返回此Set集合引用给调用者
return principalsFromRealm;
}
重点研究类
public interface ThreadState {
/**
* 绑定任何状态,应该可以在一个线程的执行
*/
void bind();
/**
* 恢复一个线程绑定之前其状态
*/
void restore();
/**
* Completely clears/removes the {@code ThreadContext} state
*/
void clear();
}
package com.wise.security.subject.support;
import com.wise.security.mgt.SecurityManager;
import com.wise.security.subject.Subject;
import com.wise.security.util.CollectionUtils;
import com.wise.security.util.ThreadContext;
import com.wise.security.util.ThreadState;
import java.util.Map;
/**
*
* 当前类的作用:
* 首先通过构造函数创建此对象,构造函数封装一个Subject对象
* 通过Subject对象又拿到SecurityManager对象
* 通过bind()将这组对象存放到此此对象的originResources中
* 通过restore()清除ThreadContext中的resources,然后把originResources重新塞到ThreadContext中
* 通过clear()方法删除ThreadContext中的Subject与SecurityManager对象
*
*/
public class SubjectThreadState implements ThreadState {
private final Subject subject;
private SecurityManager securityManager;
private Map<Object, Object> originResources;
public SubjectThreadState(Subject subject) {
if (subject == null) {
throw new IllegalArgumentException("Subject参数不能为空");
}
this.subject = subject;
SecurityManager securityManager = null;
//todo:从Subject中生产SecurityManager
// if ( subject instanceof DelegatingSubject) {
// securityManager = ((DelegatingSubject)subject).getSecurityManager();
// }
if (securityManager == null) {
securityManager = ThreadContext.getSecurityManager();
}
this.securityManager = securityManager;
}
/**
* 保存以前的Subject与SecurityManager
* 然后把这个类中的Subject与SecurityManager绑定到当前线程
*/
@Override
public void bind() {
SecurityManager securityManager = this.securityManager;
if (securityManager == null) {
//防止为空,从当前线程中再拿一次
securityManager = ThreadContext.getSecurityManager();
}
//绑定当前线程中的Subject与SecurityManager到originResources
this.originResources = ThreadContext.getResources();
//清空当先线程
ThreadContext.remove();
//重新绑定
ThreadContext.bind(this.subject);
if (securityManager != null) {
ThreadContext.bind(securityManager);
}
}
/**
* 之前的Subject与SecurityManager被存放到了originResources里面
* 如果没有originResources,说明之前就没调用bind()方法绑定
* @see #bind()
*/
@Override
public void restore() {
//先清空
ThreadContext.remove();
if(!CollectionUtils.isEmpty(this.originResources)){
ThreadContext.setResources(this.originResources);
}
}
/**
* 清除当前线程中存放的SecurityManager与Subject
*/
@Override
public void clear() {
ThreadContext.remove();
}
protected Subject getSubject() {
return this.subject;
}
}
package com.wise.security.subject;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Set;
public interface PrincipalCollection extends Iterable, Serializable {
/**
* 返回主凭证
* <p>
* 返回主应用程序主要用来唯一地标识拥有账户或Subject
*
* @return
*/
Object getPrimaryPrincipal();
/**
* 返回指定类型的第一个凭证,返回指定类型
*
* @param type 指定类型
* @param <T> 泛型
* @return 指定类型的凭证
*/
<T> T oneByType(Class<T> type);
/**
* 返回指定类型的所有凭证
*
* @param type 指定类型
* @param <T> 泛型
* @return 指定类型的所有凭证
*/
<T> Collection<T> byType(Class<T> type);
/**
* 所有凭证作为List
*
* @return 所有凭证
*/
List asList();
/**
* 所有凭证作为Set
*
* @return 所有凭证
*/
Set asSet();
/**
* 从指定Realm中返回主凭证
*
* @param realName realm名称
* @return 主凭证
*/
Collection fromRealm(String realName);
/**
* 返回提供了凭证的Realm对象们的Names
*
* @return 提供了凭证的Realm对象们的Names
*/
Set<String> getRealmNames();
/**
* 是否有凭证
*
* @return 是否有凭证
*/
boolean isEmpty();
}
package com.wise.security.subject;
import java.util.Collection;
/**
* 可变的凭证集合
*/
public interface MutablePrincipalCollection extends PrincipalCollection {
/**
* 添加一个凭证
*
* @param principal 凭证对象
* @param realmName realmName
*/
void add(Object principal, String realmName);
/**
* 添加所有的凭证
*
* @param principals
*/
void addAll(PrincipalCollection principals);
/**
* 添加所有的凭证集合
*
* @param principals 凭证集合
* @param realmName 这些凭证的realm来源
*/
void addAll(Collection principals, String realmName);
/**
* 删除所有凭证
*/
void clear();
}
package com.wise.security.subject;
import com.wise.security.util.CollectionUtils;
import java.util.*;
@SuppressWarnings({"unchecked"})
public class SimplePrincipalCollection implements MutablePrincipalCollection {
/**
* 字符串(realm)对应一批Set (Principal)
*/
private Map<String, Set> realmPrincipals;
private String cachedToString;
public SimplePrincipalCollection() {
//fromRealm()方法可能报空指针,因此用这个
realmPrincipals = new LinkedHashMap<>();
}
public SimplePrincipalCollection(Object principal, String realmName) {
if (principal == null) {
throw new IllegalArgumentException("principal is null");
}
if (realmName == null) {
throw new IllegalArgumentException("realmName is null");
}
add(principal, realmName);
}
public SimplePrincipalCollection(Collection principals, String realmName) {
addAll(principals, realmName);
}
public SimplePrincipalCollection(PrincipalCollection principals) {
addAll(principals);
}
@Override
public Object getPrimaryPrincipal() {
if (isEmpty()) {
return null;
}
return iterator().next();
}
@Override
public <T> T oneByType(Class<T> type) {
if (type == null) {
return null;
}
//获取所有凭证
List principals = asList();
for (Object principal : principals) {
if (type.isAssignableFrom(principal.getClass())) {
return (T) principal;
}
}
return null;
}
@Override
public <T> Collection<T> byType(Class<T> type) {
if (type == null) {
return null;
}
//获取所有凭证
List principals = asList();
LinkedHashSet result = new LinkedHashSet();
for (Object principal : principals) {
if (type.isAssignableFrom(principal.getClass())) {
result.add(principal);
}
}
return result;
}
@Override
public List asList() {
Set allPrincipals = asSet();
if (CollectionUtils.isEmpty(allPrincipals)) {
return Collections.EMPTY_LIST;
}
return Collections.unmodifiableList(new ArrayList(allPrincipals));
}
@Override
public Set asSet() {
if (this.realmPrincipals == null || this.realmPrincipals.isEmpty()) {
return Collections.EMPTY_SET;
}
//获取Map中所有的凭证Set集合
Collection<Set> allPrincipalSet = this.realmPrincipals.values();
Set result = new LinkedHashSet();
for (Set principals : allPrincipalSet) {
result.addAll(principals);
}
if (result.isEmpty()) {
return Collections.EMPTY_SET;
}
//返回不可修改的视图
return Collections.unmodifiableSet(result);
}
@Override
public Collection fromRealm(String realName) {
if (CollectionUtils.isEmpty(this.realmPrincipals)) {
return Collections.EMPTY_SET;
}
Set principals = this.realmPrincipals.get(realName);
//如果是空,返回空集合。
if (CollectionUtils.isEmpty(principals)) {
principals = Collections.EMPTY_SET;
}
//返回一个不可修改的凭证视图
return Collections.unmodifiableSet(principals);
}
@Override
public Set<String> getRealmNames() {
if (CollectionUtils.isEmpty(this.realmPrincipals)) {
return Collections.EMPTY_SET;
}
return Collections.unmodifiableSet(this.realmPrincipals.keySet());
}
@Override
public boolean isEmpty() {
return this.realmPrincipals == null || this.realmPrincipals.isEmpty();
}
@Override
public Iterator iterator() {
return asSet().iterator();
}
@Override
public void add(Object principal, String realmName) {
if (principal == null) {
throw new IllegalArgumentException("principal is null");
}
if (realmName == null) {
throw new IllegalArgumentException("realmName is null");
}
//???
this.cachedToString = null;
//内部的realmPrincipals Map 可能没有初始化.
Set principals = getPrincipalsLazy(realmName);
principals.add(principal);
}
@Override
public void addAll(PrincipalCollection principals) {
//获取所有的realmNames
Set<String> realmNames = principals.getRealmNames();
for (String realmName : realmNames) {
//找到此realm对应的Set凭证
Collection set = fromRealm(realmName);
addAll(set, realmName);
}
}
@Override
public void addAll(Collection principals, String realmName) {
if (realmName == null) {
throw new IllegalArgumentException("realmName is null");
}
if (principals == null) {
throw new IllegalArgumentException("principals is null");
}
if (principals.isEmpty()) {
throw new IllegalArgumentException("principals is empty collection");
}
//???
this.cachedToString = null;
Set principalSet = getPrincipalsLazy(realmName);
principalSet.addAll(principals);
}
@Override
public void clear() {
this.cachedToString = null;
if (!CollectionUtils.isEmpty(this.realmPrincipals)) {
//realmPrincipals是一个map集合,调用clear方法清空数据
this.realmPrincipals.clear();
this.realmPrincipals = null;
}
}
/**
* 创建 realmName - Set 这种对象关联关系并添加进内部Map中
* 最后返回Set对象引用方便操作
* 添加新凭证到Set中
*
* @param realmName realmName
* @return realmName对应的Set对象引用
*/
protected Set getPrincipalsLazy(String realmName) {
Set principalsFromRealm = this.realmPrincipals.get(realmName);
if (principalsFromRealm == null) {
principalsFromRealm = new LinkedHashSet();
this.realmPrincipals.put(realmName, principalsFromRealm);
}
//返回此Set集合引用给调用者
return principalsFromRealm;
}
}