吃透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;
            }
        }
    }


}

posted @ 2022-07-17 12:14  小大宇  阅读(50)  评论(0编辑  收藏  举报