zoukankan      html  css  js  c++  java
  • 架构探险笔记11-与Servlet API解耦

    Servlet API解耦

    为什么需要与Servlet API解耦

    目前在Controller中是无法调用Servlet API的,因为无法获取Request与Response这类对象,我们必须在Dispatcher中将这些对象传递给Controller的Action方法才能拿到这些对象,这显然会增加Controller对Servlet API的耦合。最好能让Controller完全不使用Servlet API就能操作Request与Response对象。

    最容易拿到Request与Response对象的地方就是DispatcherServlet的service方法:

    @WebServlet(urlPatterns = "/*",loadOnStartup = 0)
    public class DispatcherServlet extends HttpServlet {
    
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         ...
        }
    }

    然而,我们又不想把Request和Response对象传递到Controller的Action方法中,所以我们需要提供一个线程安全的对象,通过它来封装Request和Response对象,并提供一系列常用的Servlet API,这样我们就可以在Controller中随时通过该对象来操作Request与Response对象的方法了。需要强调的是,这个对象一定是线程安全的,也就是说每个请求线程独自拥有一份Request与Response对象,不同请求线程间是隔离的

    与Servlet API解耦的实现过程

    一个简单的思路是,编写一个ServletHelper类,让它去封装Request与Response对象,提供常用的ServletAPI工具方法,并利用ThreadLocal技术来保证线程安全,代码如下:

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import javax.servlet.ServletContext;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    
    public class ServletHelper {
        private static final Logger LOGGER = LoggerFactory.getLogger(ServletHelper.class);
    
        /**
         * 使每个线程独自拥有一份ServletHelper实例
         */
        private static final ThreadLocal<ServletHelper> SERVLET_HELPER_HOLDER = new ThreadLocal<ServletHelper>();
    
        private HttpServletRequest request;
        private HttpServletResponse response;
    
        public ServletHelper(HttpServletRequest request, HttpServletResponse response) {
            this.request = request;
            this.response = response;
        }
    
        /**
         * 初始化
         * @param request
         * @param response
         */
        public static void init(HttpServletRequest request,HttpServletResponse response){
            SERVLET_HELPER_HOLDER.set(new ServletHelper(request,response));
        }
    
        /**
         * 销毁
         */
        public static void destroy(){
            SERVLET_HELPER_HOLDER.remove();
        }
    
        /**
         * 获取Request对象
         * @return
         */
        private static HttpServletRequest getRequest(){
            return SERVLET_HELPER_HOLDER.get().request;
        }
    
        /**
         * 获取Response对象
         * @return
         */
        private static HttpServletResponse getResponse(){
            return SERVLET_HELPER_HOLDER.get().response;
        }
    
        /**
         * 获取Session对象
         * @return
         */
        private static HttpSession getSession(){
            return getRequest().getSession();
        }
    
        /**
         * 获取ServletContext对象
         * @return
         */
        private static ServletContext getContext(){
            return getRequest().getServletContext();
        }
    }

    最重要的就是init和destroy方法,我们需要在恰当的地方调用它们,哪里是最恰当的地方呢?当然是上面提到的DispatcherServlet的service方法。此外还提供了一系列私有的getter和setter方法,因为我们需要封装几个常用的Servlet API工具方法:

        /**
         * 将属性放入Request中
         * @param key
         * @param val
         */
        public static void setRequestAttribute(String key,Object val){
            getRequest().setAttribute(key,val);
        }
    
        /**
         * 获取Request中的属性
         * @param key
         * @param <T>
         * @return
         */
        public static <T> T getRequestAttribute(String key){
            return (T) getRequest().getAttribute(key);
        }
    
        /**
         * 从Request中移除属性
         * @param key
         */
        public static void removeRequestAttribute(String key){
            getRequest().removeAttribute(key);
        }
    
        /**
         * 重定向
         * @param location
         */
        public static void sendRedirect(String location){
            try {
                getResponse().sendRedirect(location);
            } catch (IOException e) {
                LOGGER.error("redirect failure",e);
            }
        }
    
        /**
         * 将属性放入Session中
         * @param key
         * @param val
         */
        public static void setSessionAttribute(String key,Object val){
            getSession().setAttribute(key,val);
        }
    
        /**
         * 获取Session中的属性
         * @param key
         * @param <T>
         * @return
         */
        public static <T> T getSessionAttribute(String key){
            return (T) getSession().getAttribute(key);
        }
    
        /**
         * 移除Session中的属性
         * @param key
         */
        public static void removeSessionAttribute(String key){
            getSession().removeAttribute(key);
        }
    
        /**
         * 使Session失效
         */
        public static void invalidateSession(){
            getSession().invalidate();
        }

    以上这些工具方法都是可拓展的,只要是我们认为比较常用的都可以封装起来。

    现在ServletHelper已经开发完毕,是时候将其整合到DispatcherServlet中并初始化Request与Response对象了,实际上就是调用init与destroy方法。

    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            ServletHelper.init(req,resp);   //使每个线程都有独立的request和response
            try {
                  /****/
            }finally {
                ServletHelper.destroy();
            }
        }

    现在就可以在Controller类中随时调用ServletHelper封装的Servlet API了:而且不仅仅可以在Controller类中调用,实际上在Service类中也是可以调用。因为所有调用都来自同一请求线程。DispatcherServlet是请求线程的入口,随后请求线程会先后来到Controller与Service中,我们只需要使用ThreadLocal来确保ServletHelper对象中的Request与Response对象线程安全即可。

    代码

  • 相关阅读:
    sql中的不常见查询
    sql中的常见报错;
    wcf_first
    均方值-数学期望-方差
    转:模块度(Modularity)与Fast Newman算法讲解与代码实现
    转:社区发现评估指标-NMI
    转:模块度(Modularity)与Fast Newman算法讲解与代码实现
    转:聚类评价指标
    转:聚类︱python实现 六大 分群质量评估指标(兰德系数、互信息、轮廓系数)
    转:用K-Means聚类分析做客户分群
  • 原文地址:https://www.cnblogs.com/aeolian/p/10238434.html
Copyright © 2011-2022 走看看