吃透Shiro源码1-----MapContext、ThreadContext
今天第一次用markdown编辑器,希望用的顺手。不是,目录在哪里生成呢,我擦。。。
技术手法
(1)组合手法
实现某个接口,然后内部组装一个已经实现此接口的类。类似于适配器模式一样。这个解决了我一直的困惑,如何继承一个final类,原来组合就能完美解决。曾经用过一个叫做JsonResponse的类,现在想来,当初如果直接组合这个类,而不是重新弄一个类似的类,或许更好
public class MapContext implements Map<String,Object> ..
private Map<String,Object> backingMap = new HashMap<String,Object>();
public Object get(Object key) {
return this.backingMap.get(key);
}
(2)集合框架返回不可修改的视图
public Collection<Object> values(){
return Collections.unmodifiableSet(this.backingMap.values());
}
(3)泛型与类型判断
某个Object对象,判断它是不是某个Class类的。Class类的一个方法,叫做 isAssignableFrom方法。早就希望能自己实现这个方法,无奈做不到,毕竟涉及到字节码。终于见到大佬写的方法了。
protected <E> E get(String key, Class<E> type) {
Object value = get(key);
if (value == null) {
return null;
}
//匹配类型
if (type.isAssignableFrom(value.getClass())) {
return (E) value;
}
throw new IllegalArgumentException("返回值的类型与接收类型不匹配,实际的值的类型:"
+ value.getClass());
}
(4)如何从子线程拿到父线程的值
继承InheritableThreadLocal类,重写childValue方法。一年前我就希望能够实现从某个线程开始,携带着一批数据,到这个线程结束,穿越了无数的类后,依旧能访问道最初的那批那一批数据 。这个功能当时遇到的问题是,因为在Web中穿越了无数的Filter,假设每个过滤器都要给一个关键数据,不能每个局部变量都用一个全局Map吧。这个ThreadContext类写的太棒了!解惑了,解惑了。
private static final class InheritableThreadLocalMap<T extends Map<Object, Object>>
extends InheritableThreadLocal<Map<Object, Object>> {
......
@SuppressWarnings({"unchecked"})
@Override
protected Map<Object, Object> childValue(Map<Object, Object> parentValue) {
//为什么要从新创建Map呢?是希望互不干扰父线程中的数据吗?结论是 是的
if (parentValue != null) {
//先转成HashMap作clone,然后再转为Map
return (Map<Object, Object>) ((HashMap<Object, Object>) parentValue).clone();
} else {
return null;
}
}
}
我写了一个简单的例子,才明白为什么大佬要重写chilidValue方法。原来,大佬的目的就是在子线程继承到父线程的所有数据,但是又不影响父线程的数据。比如在下面的代码中,我在父线程中创建了一个Map并将其绑定到这个线程中。然后父线程又开启另外一个子线程。线程局部变量用的是继承了InheritableThreadLocal类,重写了childValue方法,这会把父线程中所有数据都拷贝了一份。然后下面的main函数代码中,即使子线程clear了这个Map,但是也不会影响到父线程中绑定的线程局部变量。全部代码参考最下面的第二个类,如何继承InheritableThreadLocal并重写childValue方法。
public static void main(String[] args) {
new Thread(() -> {
//write your task here
Map<Object,Object> parentMap = new HashMap<>();
parentMap.put("a",1);
parentMap.put("b",2);
ThreadContext.setResources(parentMap);
new Thread(() -> {
//子线程开始
System.out.println(ThreadContext.get("a"));
Map<Object,Object> map = ThreadContext.getResources();
map.clear();
//子线程结束
}).start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ThreadContext.get("a"));
}).start();
}
运行结果:
1 #子线程的1
1 #父线程的1
重点研究类
import java.io.Serializable;
import java.util.*;
public class MapContext implements Map<String, Object>, Serializable {
private final Map<String, Object> backingMap;
public MapContext() {
this.backingMap = new HashMap<String, Object>();
}
public MapContext(Map<String, Object> map) {
//初始化内部map
this();
//加入
if (!CollectionUtils.isEmpty(map)) {
this.backingMap.putAll(map);
}
}
@SuppressWarnings({"unchecked"})
protected <E> E get(String key, Class<E> type) {
Object value = get(key);
if (value == null) {
return null;
}
//匹配类型
if (type.isAssignableFrom(value.getClass())) {
return (E) value;
}
throw new IllegalArgumentException("返回值的类型与接收类型不匹配,实际的值的类型:" + value.getClass());
}
/**
* 只能存入不为null的value到内部HashMap中
*/
protected void nullSafePut(String key, Object value) {
if (value != null) {
put(key, value);
}
}
//------------------------------------组合手法开始------------------------------------------
public int size() {
return backingMap.size();
}
public boolean isEmpty() {
return backingMap.isEmpty();
}
public boolean containsKey(Object key) {
return backingMap.containsKey(key);
}
public boolean containsValue(Object value) {
return backingMap.containsValue(value);
}
public Object get(Object key) {
return backingMap.get(key);
}
public Object put(String key, Object value) {
return backingMap.put(key, value);
}
public Object remove(Object key) {
return backingMap.remove(key);
}
public void putAll(Map<? extends String, ?> map) {
backingMap.putAll(map);
}
public void clear() {
backingMap.clear();
}
public Set<String> keySet() {
//返回无法修改的Set视图
return Collections.unmodifiableSet(backingMap.keySet());
}
public Collection<Object> values() {
return Collections.unmodifiableCollection(backingMap.values());
}
public Set<Entry<String, Object>> entrySet() {
return Collections.unmodifiableSet(backingMap.entrySet());
}
//------------------------------------组合手法结束------------------------------------------
public static void main(String[] args) {
MapContext context = new MapContext();
context.put("username","value");
Integer value = context.get("username",Integer.class);
System.out.println(value);
}
}
import com.wise.security.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
/**
* ThreadContext提供了一种基于键/值对将对象绑定和取消绑定到当前线程的方法。
*/
public abstract class ThreadContext {
private static final Logger LOGGER = LoggerFactory.getLogger(ThreadContext.class);
/**
* 全限定名_SECURITY_MANAGER_KEY
*/
public static final String SECURITY_MANAGER_KEY =
ThreadContext.class.getName() + "_SECURITY_MANAGER_KEY";
public static final String SUBJECT_KEY =
ThreadContext.class.getName() + "_SUBJECT_KEY";
/**
* InheritableThreadLocal可以让子线程从父线程的键值对中取得值。
* A线程启动了B线程,那么A线程是B线程的父线程。
* 也就是A线程中的值,子线程B也能获取到A线程中的键值对的值,而不是重新计算表达式。
* 通过重写InheritableThreadLocal的childValue(parentValue)方法,重新定义MyInheritableThreadLocal的键值对。
* A线程启动了B线程,那么A线程是父线程,B线程是A线程的子线程。
* B线程此刻访问自己的键值对的时候,拿的是父类的键值对的值。
* 当然,如果重写了childValue()方法,那么拿的就是自己的值了。
* ————————————————
* 版权声明:本文为CSDN博主「小大宇」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
* 原文链接:https://blog.csdn.net/yanluandai1985/article/details/81195485
*/
private static final ThreadLocal<Map<Object, Object>> resources
= new InheritableThreadLocalMap<Map<Object, Object>>();
protected ThreadContext() {
}
public static Map<Object, Object> getResources() {
//这里为什么要重新创建HashMap呢?是因为创建一份副本给开发者
//防止干扰当前线程中的Map局部变量
//修改此变量必须走 remove() 、 put(key,value)方法
return new HashMap<Object, Object>(resources.get());
}
public static void setResources(Map<Object, Object> newResources) {
if (CollectionUtils.isEmpty(newResources)) {
return;
}
//获取内部map并清空
resources.get().clear();
resources.get().putAll(newResources);
}
public static Object get(Object key) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("取当前线程中数据,当前线程:{}",
Thread.currentThread().getName());
LOGGER.debug("取当前线程中数据,key:{}", key);
}
return getValue(key);
}
public static void put(Object key, Object value) {
if (key == null) {
throw new IllegalArgumentException("key 为 空");
}
if (value == null) {
//空值就删除key
remove(key);
}
resources.get().put(key, value);
}
public static Object remove(Object key) {
return resources.get().remove(key);
}
/**
* 清空当前线程内部所有值
*/
public static void remove() {
resources.remove();
}
private static Object getValue(Object key) {
return resources.get().get(key);
}
//-------------------绑定Subject到当前线程开始---------------------
public static void bind(Subject subject) {
if (subject != null) {
put(SUBJECT_KEY, subject);
}
}
public static void unbindSubject() {
remove(SUBJECT_KEY);
}
public static Subject getSubject() {
return (Subject) get(SUBJECT_KEY);
}
//-------------------绑定Subject到当前线程结束---------------------
//-------------------绑定SecurityManager到当前线程开始---------------------
public static void bind(SecurityManager securityManager) {
if (securityManager != null) {
put(SECURITY_MANAGER_KEY, securityManager);
}
}
public static void unbindSecurityManager() {
remove(SECURITY_MANAGER_KEY);
}
public static SecurityManager getSecurityManager() {
return (SecurityManager) get(SECURITY_MANAGER_KEY);
}
//-------------------绑定SecurityManager到当前线程结束---------------------
/**
* 可以直接从父线程中获取 resources 值
*
* @param <T>
*/
private static final class
InheritableThreadLocalMap<T extends Map<Object, Object>>
extends InheritableThreadLocal<Map<Object, Object>> {
@Override
protected Map<Object, Object> initialValue() {
return new HashMap<Object, Object>();
}
@SuppressWarnings({"unchecked"})
@Override
protected Map<Object, Object>
childValue(Map<Object, Object> parentValue) {
//为什么要从新创建Map呢?是希望互不干扰父线程中的数据
if (parentValue != null) {
//先转成HashMap作clone,然后再转为Map
return (Map<Object, Object>) ((HashMap<Object, Object>) parentValue).clone();
} else {
return null;
}
}
}
}