黑马程序员技术交流社区

标题: 使用ThreadLocal存取值 [打印本页]

作者: 王超举    时间: 2017-7-31 11:27
标题: 使用ThreadLocal存取值
本帖最后由 王超举 于 2017-7-31 15:01 编辑

    ThreadLocal,本身是一个工具类,用于往当前线程中存取一些数据。他内部调用ThreadLocalMap,一个map集合。其key是ThreadLocal这个工具类的引用,value是我们要往里面保存的值。    如果一个线程需要调用很多个方法,各个方法之间调用会显示的传参,如果要增加参数则需要该方法的参数列表,如果是用ThreadLocal来自定义一个工具类则不需要。
    我们在java的三层框架调用时,如果dao层需要一个数据,一般是从service层传递过来的。如果代码已经写完,并且dao层要修改的方法有很多,这样做就太麻烦了,而且也使得代码有了侵入性
     这时候我们就可以写一个工具类,在工具类中定义一个ThreadLocal类型的变量,将需要传递给dao层,或者service层这样的数据保存在ThreadLocalMap里。在service层或dao层中从工具类中获取。
      当然不仅仅这些操作普通的操作,在面向切面编程时同样会用到。下面看一个具体的案例。
需求:
       在一个项目中,需要对dao层所有的save方法,增加权限校验,如果当前用户是管理员,则允许保存操作,否则不允许。项目简介:在servlet层、service层中调用下一层的对象,都是通过配置文件+工厂模式+反射生成的。
解决思路:
    1、编写MyThreadLocalUtils.java类,用于在当前线程中保存已登陆的用户
[Java] 纯文本查看 复制代码
public class MyThreadLocalUtils {
        private static final ThreadLocal<User> tl = new ThreadLocal<User>();
        public static User getUser(){
                User conn = tl.get();
                return conn;
        }
        public static void setUser(User user){
                tl.set(user);
        }
        
}

    2、在权限过滤的filter类中,调用MyThreadLocalUtils类,存储已登陆用户。ps:代码第20行。

[Java] 纯文本查看 复制代码
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
                HttpServletRequest req = (HttpServletRequest)request;
                HttpServletResponse resp = (HttpServletResponse)response;
                User user = (User) req.getSession().getAttribute("existUser");
                StringBuffer url = req.getRequestURL();
                String contextPath = req.getContextPath();
                String[] split = url.toString().split(contextPath);
                String[] urls = {"/jsp/order_list.jsp","/jsp/order_info.jsp",
                                "/OrderServlet","/CartServlet",
                                "/jsp/cart.jsp"};
                boolean flag = false;
                for (String string : urls) {
                        flag = split[1].startsWith(string);
                        if(flag && user == null ){
                                req.setAttribute("msg", "您还没登录,请先登录");
                                req.getRequestDispatcher("/jsp/msg.jsp").forward(req, resp);
                                return;
                        }
                }
                //往当前线程中存已登陆用户
                User exitUser = (User) req.getSession().getAttribute("existUser");
                MyThreadLocalUtils.setObject(exitUser);
                chain.doFilter(request, response);
        }

    3、在beanFactory工厂类中,对dao层的save方法进行改造,调用MyThreadLocalUtils类,获取登陆的用户。ps:代码第38行。
                    3.1、获取到则执行保存操作
                    3.2、获取不到,不再调用目标的save方法,直接返回null。

[Java] 纯文本查看 复制代码

public class BeanFactory {
        public static Object getBean(String objId){
                try {
                        //读取配置文件
                        SAXReader saxReader = new SAXReader();
                        InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("applicationContext.xml");
                        Document document = saxReader.read(in);
                        //获取配置文件中的对应的class属性值
                        Element ele = (Element) document.selectSingleNode("//bean[@id='"+objId+"']");
                        //根据class属性值获取创建对象
                        String classPath = ele.attributeValue("class");
                        Class clazz = Class.forName(classPath);
                        final Object obj = clazz.newInstance();
                        ClassLoader classLoader = obj.getClass().getClassLoader();
                        Class<?>[] interfaces = obj.getClass().getInterfaces();
                        MyInvocationHandler mih = new MyInvocationHandler(obj);
                        if(objId.endsWith("Dao")){
                                Object proxyInstance = Proxy.newProxyInstance(classLoader,interfaces,mih );
                                return proxyInstance;
                        }
                        return obj;
                } catch (Exception e) {
                        e.printStackTrace();
                }
                return null;
        }
}
class MyInvocationHandler implements InvocationHandler{
        private Object obj;
        public MyInvocationHandler(Object obj) {
                this.obj = obj;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if(method.getName().startsWith("save")){
                        System.out.println("权限校验=================");
//从ThreadLocal中获取User对象
User user = MyThreadLocalUtils.getUser();
                        if(user.getType() == 1){//管理员,可以执行保存操作
                                Object object = method.invoke(obj, args);
System.out.println("管理员,可以执行保存操作****************************");
                                return object;
                        }else{//普通用户,不可以执行保存操作
System.out.println("普通用户,不可以执行保存操作=================");                                
                                return null;
                        }
                        
                }
                Object object = method.invoke(obj, args);
                return object;
        }
}










欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2