为了尽量减少耗时操作对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. `corePoolSize`:核心线程数,即线程池维护线程的最少数量。即使在空闲时,线程池也会保留这些线程,不进行销毁。 2. `maximumPoolSize`:最大线程数,线程池能容纳...
总结,使用Spring MVC和Spring线程池处理HTTP并发请求,能够有效提升系统的并发处理能力。同时,通过数据同步控制策略,可以确保在多线程环境下数据的正确性和一致性。在实际开发中,我们需要结合业务需求,灵活运用...
标题中的“Spring线程池”指的是Spring框架中提供的线程池实现,它是Spring对Java的ExecutorService接口的一个封装,提供了更高级别的抽象和扩展性。线程池在多线程编程中扮演着至关重要的角色,它能有效地管理和...
ActiveMQ与Spring线程池整合的一个实例。 lib库没有上传。 对于实例的讲解,在竹子的论坛有我对这个实例的帖子(http://www.java2000.net/viewthread.jsp?tid=1167) lib中包含: apache-activemq-4.1.1.jar ...
以下是关于Spring线程池(同步、异步)的详细解析: 一、Spring异步线程池类图 Spring提供了一系列的`TaskExecutor`实现,它们根据不同的需求和场景有不同的特性和行为。这些实现包括但不限于: 1. `...
数据库连接是一种关键的有限的昂贵的资源,这一点在多用户网页应用程序中体现的尤为突出。对数据库连接的管理能显著影响到整个应用的伸缩性和健壮性,影响到程序的性能指标。
在Java的多线程编程中,Spring框架提供了一种便捷的方式来管理和配置线程池,这就是`ThreadPoolTaskExecutor`。这个类是Spring对Java内置的`java.util.concurrent.ThreadPoolExecutor`的封装,允许开发者在Spring...
总结来说,这篇博文探讨了如何在Spring中使用线程池执行定时任务,包括配置线程池、创建定时任务、异常处理和线程安全。同时,提到了Spring MVC在Web开发中的角色,以及它如何与任务调度协同工作。理解和掌握这些...
JDK线程池和Spring线程池的使用实例解析 JDK线程池和Spring线程池是两种常用的线程池实现,它们都提供了线程池的功能,但它们在使用和配置上有所不同。下面我们将详细介绍JDK线程池和Spring线程池的使用实例解析。 ...
本文将详细探讨在Spring框架中如何配置和使用线程池,并通过一个简单的示例——"Spring线程池demo"来展示其实现过程。 首先,我们需要了解线程池的基本原理。线程池通过预先创建一定数量的线程,当有任务需要执行时...
在 Spring Boot 中使用 Java 线程池 ExecutorService 的讲解 Spring Boot 作为一个流行的 Java 框架,提供了许多便捷的功能来帮助开发者快速构建应用程序。其中之一就是使用 Java 线程池 ExecutorService 来管理...
### Spring3.2.6定时任务...#### 二、Spring线程池配置 Spring框架提供了一个强大的任务调度模块,其中包含了对线程池的支持。下面通过一个具体的示例来展示如何在Spring3.2.6中配置线程池: ```xml <!-- 线程池 --> ...
使用spring线程池多线程并发处理大批量数据,解决IO效率问题。
Spring Boot 整合 FTPClient 线程池的实现示例 在本文中,我们将探讨如何在 Spring Boot 项目中整合 FTPClient 线程池的实现示例。FTPClient 是一个常用的 FTP 客户端库,而线程池则可以帮助我们减少频繁创建和销毁...
Spring线程池ThreadPoolTaskExecutor配置详情 Spring线程池ThreadPoolTaskExecutor是Spring Framework提供的一种线程池实现,用于管理和执行异步任务。本文将详细介绍ThreadPoolTaskExecutor的配置详情,并提供一...
本篇文章将深入探讨如何在使用线程池的情况下,利用Spring AOP来增强事务处理。 首先,让我们理解问题的核心。在给定的示例中,一个服务类`AAAService`有一个`doJob`方法,它会将`job`方法提交给线程池执行。由于`...
面试中可能会问到如何使用反射创建和调用对象,以及反射在Spring中的应用。 BeanFactory和ApplicationContext的区别是面试常考问题。BeanFactory主要关注于对象的创建和管理,而ApplicationContext则提供了更全面的...
Spring Boot 使用 Spring 的异步线程池的实现 在软件开发中,异步线程池是一种非常重要的技术,能够帮助我们更好地处理系统性能和大用户量请求之间的矛盾。Spring Boot 提供了异步线程池的实现,可以帮助我们更好地...
Spring线程池ThreadPoolExecutor配置并且得到任务执行的结果 在Java中,线程池是一种非常重要的技术,能够帮助我们更好地管理线程资源,提高系统的性能和可扩展性。其中,ThreadPoolExecutor是Java中的一种线程池...
在Spring Boot应用中,使用线程池和异步处理是提高系统并发性能和响应速度的重要手段。`@Async`注解是Spring提供的一个特性,用于标记方法为异步执行,这样可以避免主线程等待耗时的操作,提高系统效率。本文将详细...