• 架构探险笔记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对象线程安全即可。

    代码

  • 相关阅读:
    Linux 共享库
    使用Visual Studio(VS)开发Qt程序代码提示功能的实现(转)
    ZOJ 3469 Food Delivery(区间DP)
    POJ 2955 Brackets (区间DP)
    HDU 3555 Bomb(数位DP)
    HDU 2089 不要62(数位DP)
    UESTC 1307 windy数(数位DP)
    HDU 4352 XHXJ's LIS(数位DP)
    POJ 3252 Round Numbers(数位DP)
    HDU 2476 String painter (区间DP)
  • 原文地址:https://www.cnblogs.com/aeolian/p/10238434.html
Copyright © 2020-2023  润新知