为了尽量减少耗时操作对Action执行的影响,使用TaskExecutor线程池来管理耗时任务,作为后台进程执行,从而解决了问题。

场景:
    使用了Struts和Spring,但Struts的Action并未交给Spring容器管理,Spring容器仅仅用来管理Dao。

要求:
    对每个Action,实现向数据库写入Log功能,最好做到不要影响正常的操作流程 。Log的内容是此Action的请求参数,由客户端决定。

分析:
    如何截获或者使得Action执行前后做写入Log动作并非关键,难点在于“不要影响正常的操作流程”。可以将Action交给Spring容器管理,利 用AOP功能将向要执行的动作添加在Action执行前后;或者为所有Action添加一含有写入Log动作的父类。
    不影响正常的操作流程,最理想的状态就是实现导弹发射出去后不管的功能。即在Action执行的某个步骤中,执行一下写入Log的命令,然后继续执行后继 任务,写入Log任务让其自己去执行,无需等待返回信息。所以写入Log功能就不能与Action处于同一进程,否则Log任务会抢占资源而让 Action的后续任务处于等待状态。
    势必需要启动新的进程来执行Log任务。对于一个Action还不算复杂,多个呢?启动的新进程如何管理就成了新的问题。如果解决了这个问题,原来的问题 就显得很容易了。

实现:
    根据以上分析,希望有这么一个进程提供者,有这样的功能:能接受任务;当有可用线程时,任务获得调度执行;无可用资源时,任务处于等待状态;任务执行完成 之后,释放进程资源给进程管理者。这就是一个进程池的基本功能。正好Spring定义了--TaskExecutor线程池(想想Spring还真是无所 不有啊!),为各种进程池提供了统一的用户接口。
    这里就不去分析各种不同线程池的功能了。简单地讲一下对上面提出要求的解决步骤:
    1 定义写入Log任务;
        线程池可接受的任务都必须是实现了Runable接口的类,因此定义一个写入Log的任务如下:
            class LogTask implements Runnable ...{
       
        private AccessLog accessLog = null;
        private AccessLogDao accessLogDao = null;
       
        public LogTask(AccessLog accessLog, AccessLogDao accessLogDao) ...{
            this.accessLog = accessLog;
            this.accessLogDao = accessLogDao;
        }
       
        public void run() ...{
            accessLogDao.addLog(accessLog);
        }
    }其中的AccessLog是一个简单Bean,定义了需要写入的各种信息;AccessDao专门向数据库中写入AccessLog中的内容提供服务。 在new一个LogTask的时候,必须提供这两个参数。
    2 实现用TaskExcutor执行写入Log任务;
        首先指定TaskExcutor接口的实现者。将具体的TaskExcutor用定义的方式配置并由Spring容器管理:
    <bean id="taskExecutor"
        class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
        <property name="corePoolSize" value="2" />
        <property name="keepAliveSeconds" value="200" />
        <property name="maxPoolSize" value="10" />
        <property name="queueCapacity" value="60" />
    </bean>可以看到,具体使用的是ThreadPoolTaskExecutor。这样更换别的线程池也比较方便。
        将任务加入到进程池并执行则比较简单:
        LogTask logTask = new LogTask(accessLog, accessLogDao);
        taskExecutor.execute(logTask);加入任务后,进程池会根据调度算法来执行任务(调度算法也可以自定义)。
    3 定义服务接口,并实现;
       定义一个Action可以方便使用的接口:
public interface AccessLogService ...{

public void writeLog(HttpServletRequest request);
}它接受的参数就是包含了 客户端请求参数的HttpServletRequest。这样就不需要Action去作参数分析了,具体的工作留给AccessLogService完 成。
        实现此接口: public class AccessLogServiceImpl implements AccessLogService ...{
   
    /**//*
     * (non-Javadoc)
     * @see my.test.service.AccessLogService#writeLog(javax.servlet.http.HttpServletRequest)
     */
    public void writeLog(HttpServletRequest request) ...{

//分析request,创建AccessLog对象
        AccessLog accessLog = new AccessLog();
        //以下略

writeLog(accessLog);       
    }
   
   
   
    //-------------------------------------------------------------------------
   
    /**//**
     * 写入Log信息<br>
     *
     * @param accessLog
     */
    private void writeLog(AccessLog accessLog) ...{
       
        LogTask logTask = new LogTask(accessLog, accessLogDao);
        taskExecutor.execute(logTask);
    }
   
    /**//** DAO */
    private AccessLogDao accessLogDao = (AccessLogDao) getDaoByName("accessLogDao");
   
    /**//** 线程池 */
    private TaskExecutor taskExecutor = (TaskExecutor) getDaoByName("taskExecutor");
   
    //-------------------------------------------------------------------------
   
    /**//**
     * 任务定义
     *
     */
    class LogTask implements Runnable ...{
       
        private AccessLog accessLog = null;
        private AccessLogDao accessLogDao = null;
       
        public LogTask(AccessLog accessLog, AccessLogDao accessLogDao) ...{
            this.accessLog = accessLog;
            this.accessLogDao = accessLogDao;
        }
       
        public void run() ...{
            accessLogDao.addLog(accessLog);
        }
    }
}这里的任务被作为内部类定义 的。

4 为Action加入写入Log功能。
        直接使用策略模式,为所有Action添加一父类,在父类中调用写入Log功能,而具体的Action执行流程由子类决定。
public abstract class BaseAction extends Action ...{
   
    public ActionForward execute(
            ActionMapping mapping,
            ActionForm actionForm,
            HttpServletRequest request,
            HttpServletResponse response)
        throws Exception ...{

try ...{
            ActionForward forward = _execute(mapping, actionForm, request, response);

} catch (Exception e) ...{
            throw e;

} finally ...{
            AccessLogService als = new AccessLogServiceImpl();
            als.writeLog(request);
        }
    }

//子类必须实现此方法
    public abstract ActionForward _execute( ActionMapping mapping,
            ActionForm actionForm,
            HttpServletRequest request,
            HttpServletResponse response) throws Exception;

}子类中只需实现抽象方法_execute即可。在执行完子类的_execute之后,才会将写入Log的任务放入进程池,然后返回。具体任务何时 被执行,则由进程池管理。
通过分析执行时log可知,Action返回之后,写入Log任务才被执行。也就是说,这样做了之后,很大程度上减少了 对Action执行流程的干扰,同时还完成了比较耗时的写数据库的工作。

总结:这只是为了解决项目中的某个特殊的需求而做的,可能也不是什么好的通用的办法。但它也给我们以后解决比如需要后台执行之类的要求提供了一种可 行的方案。

spring线程池的使用相关推荐

  1. spring 线程池_Spring线程池服务

    spring 线程池 线程池对于执行同步和异步过程非常重要. 本文介绍如何使用Spring开发和监视线程池服务. 创建线程池已通过两种替代方法进行了说明. 二手技术 : JDK 1.6.0_21 春天 ...

  2. Spring线程池开发实战

    Spring线程池开发实战 作者:chszs,转载需注明. 作者博客主页:http://blog.csdn.net/chszs 本文提供了三个Spring多线程开发的例子,由浅入深,由于例子一目了然, ...

  3. spring线程池的理解和使用

    1.spring线程池 <bean id="taskExecutor" class="org.springframework.scheduling.concurre ...

  4. Spring线程池服务

    线程池对于执行同步和异步过程非常重要. 本文介绍如何使用Spring开发和监视线程池服务. 创建线程池已通过两种替代方法进行了说明. 二手技术 : JDK 1.6.0_21 Spring3.0.5 M ...

  5. spring线程池使用

    为何使用多线程 对于复杂的业务逻辑下,有时候需要使用多线程执行,以优化项目的执行速度 单线程同步执行的场景中,如果前边出现异常,会导致所有任务中断,异步执行没有这样的问题 多线程实现方式:线程池 减少 ...

  6. Spring 线程池使用

    Spring 中默认自带线程池_org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor,一般有可以直接使用,这是时候使用的是默 ...

  7. Spring线程池异步传递MDC信息

    目录 1. 什么是MDC 2. 引入MDC打印步骤 2.1 pom依赖 2.2 log4j2打印日志配置文件 3 步骤演示 3.1 单线程业务使用示例 postman查询示例 查询代码 查询日志 3. ...

  8. spring线程池 java_Java 中几种常用的线程池

    概述: 在java内置API中操作线程所用到的类为Thread.创建线程一般有两种方式, 继承Thread方式 实现Runnable方式,并以runnable作为target创建Thread 在And ...

  9. Spring 线程池的使用和配置

    @Configuration @EnableAsync //开启异步调用 public class AsyncExecutorConfig {@Value("${thread.number} ...

最新文章

  1. ARM体系结构及内核回顾总结(一)
  2. php 二叉树判断节点的位置,PHP实现判断二叉树是否对称的方法
  3. Head First JSP---随笔五
  4. intro to cs with python_CS 105 – Intro to Computing Non-Tech Spring 2019
  5. Xcode 升级后,常常遇到的遇到的警告、错误,解决方法(转)
  6. Flutter AnimatedBuilder 的基本使用
  7. 数据仓库流程和代码说明
  8. linux中指令简约版
  9. 宏基笔记本升级bios(2012-12-28-bd 写的日志迁移
  10. plist 文件详解
  11. 程序员慵懒的周末:不想出门还想吃肯德基香辣鸡翅?
  12. 一个大学老师的2020
  13. 最新前端开发面试题集合(非常全面)
  14. 写一个python函数 用cuda加速
  15. Allegro阻抗分析指导书
  16. 办公软件不能打印能打印测试页,excel2010无法打印的解决方法
  17. 基于快速傅里叶变换实现的狗声识别器(人工智能)
  18. java打怪升级地图
  19. 读懂python中的self
  20. 如何科学应对宝宝秋季腹泻?

热门文章

  1. 高中数学知识点;二次函数与幂函数(高考备考)
  2. 如何编译DD-WRT
  3. c++语言 幂指数,C++ pow(指数函数):求x的y次幂的值
  4. JavaScript自用
  5. php——webshell变形总结
  6. mysql update cascade_mysql中On Delete Cascade和On Update Cascade之间的区别
  7. 模仿新浪微博雷达搜索动画效果
  8. js 触发 select onchange事件
  9. c语言:递归求学生年龄问题
  10. Qcom平台 Camera 之常见错误和问题