㈠ spring mvc 怎樣處理非同步
Spring MVC 3.2開始引入Servlet 3中的基於非同步的處理request.往常是返回一個值,而現在是一個Controller方法可以返回一個java.util.concurrent.Callable對象和從Spring MVC的託管線程生產返回值.同時Servlet容器的主線程退出和釋放,允許處理其他請求。Spring MVC通過TaskExecutor的幫助調用Callable在一個單獨的線程。並且當這個Callable返回時,這個rquest被分配回Servlet容器使用由Callable的返回值繼續處理。
這里有這樣的Controller方法的一個例子:
@RequestMapping(method=RequestMethod.POST)
public Callable<String> processUpload(final MultipartFile file) {
return new Callable<String>() {
public String call() throws Exception {
// ...
return "someView";
}
};
}1234567891011
Controller方法的另外一個選擇是返回一個DeferredResult的實例。在這種情況下,返回值也將由多線程產生.i.e. 一個不是由Spring MVC託管。例如可能產生的結果在應對一些外部事件,比如JMS消息,一個計劃任務等等.這里有這樣的Controller方法的一個例子:
@RequestMapping("/quotes")
@ResponseBody
public DeferredResult<String> quotes() {
DeferredResult<String> deferredResult = new DeferredResult<String>();
// Save the deferredResult somewhere..
return deferredResult;
}
// In some other thread...
deferredResult.setResult(data);12345678910
如果你沒有了解過Servlet 3.0中的非同步處理request的特性,這可能有點難以理解.這肯定會幫助閱讀.這里有一些關於底層機制基本原理:
1、Servlet 3.0 非同步機制
一個ServletRequest可以通過調用 request.startAsync()被放到一個非同步的模塊中.這樣做的主要作用是Servlet,以及任何過濾器,可以退出,但是響應仍將允許開放直到處理完成後。
調用request.startAsync()返回AsyncContext可以用於進一步控制非同步處理。例如,它提供一個方法叫dispatch,這個類似於Servlet API,並且它允許應用程序通過Servlet容器線程繼續請求處理。
ServletRequest提供獲取當前DispatcherType的介面.DispatcherType可以用來區分處理初始請求,一個非同步分發,forward,以及其他分發類型。
記住上面的,下面是通過Callable非同步請求處理事件的序列:
Controller返回一個Callable對象.
Spring MVC開始非同步處理並且提交Callable到TaskExecutor在一個單獨的線程中進行處理。
DispatcherServlet與所有的Filter的Servlet容器線程退出,但Response仍然開放。
Callable產生結果並且Spring MVC分發請求給Servlet容器繼續處理.
DispatcherServlet再次被調用並且繼續非同步的處理由Callable產生的結果
DeferredResult的處理順序與Callable十分相似,由應用程序多線程產生非同步結果:
Controller返回一個DeferredResult對象,並且把它保存在內在隊列當中或者可以訪問它的列表中。
Spring MVC開始非同步處理.
DispatcherServlet與所有的Filter的Servlet容器線程退出,但Response仍然開放。
application通過多線程返回DeferredResult中sets值.並且Spring MVC分發request給Servlet容器.
DispatcherServlet再次被調用並且繼續非同步的處理產生的結果.
為進一步在非同步請求處理動機的背景,並且when或者why使用它請看this blog post series.
2、非同步請求的異常處理
當Callable執行一個Controller方法的時候帶有異常怎麼辦?簡單的回答就是:和當執行一個普通Controller方法帶有異常一樣.它通過有規律的異常處理機制。詳細點來說就是:當Callable帶有異常時,Spring MVC以這個Exception為結果分發給Servlet容器.並且引導帶有Exception處理request而不是Controller方法返回一個值。當使用DeferredResult時,你可以選擇是否把Exception實例通過調用setResult或者setErrorResult進行傳值.
3、攔截非同步請求
一個HandlerInterceptor可樣也可以實現AsyncHandlerInterceptor,通過實現它的方法進行回調.當進行非同步處理的時候,將會調用而不是postHandle和afterCompletion.
一個HandlerInterceptor同樣可以注冊CallableProcessingInterceptor或者一個用於更深度的集成非同步request的生命周期.例如處理一個timeout事件.你可以參看AsyncHandlerInterceptor的Javadoc了解更多細節.
DeferredResult類也提供了方法,像onTimeout(Runnable)和onCompletion(Runnable).可以看DeferredResult了解更多細節.
當使用Callable的時候,你可以通過WebAsyncTask來對它進行包裝.這樣也可以提供注冊timeout與completion方法.
4、HTTP Streaming
一個Controller方法可以通過使用DeferredResult與Callable來非同步的產生它的返回值.並且這個可以被用來實現long polling技術.就是伺服器可以推動事件盡快給客戶端。
如果當你需要在一個HTTP response中放入多個event怎麼辦?這個技術需要」Long Polling」.這就是所謂的」Long Polling」.Spring MVC通過使用ResponseBodyEmitter做為返回值來實現這種功能.這樣可以被用來發送多個Object。而不是使用@ResponseBody這樣。這樣每個對象都可以對應一種HttpMessageConverter被寫入到response中。
下面是一個簡單的例子:
@RequestMapping("/events")
public ResponseBodyEmitter handle() {
ResponseBodyEmitter emitter = new ResponseBodyEmitter();
// Save the emitter somewhere..
return emitter;
}
// In some other thread
emitter.send("Hello once");
// and again later on
emitter.send("Hello again");
// and done at some point
emitter.complete();123456789101112131415
注意:ResponseBodyEmitter同樣也可以用做ResponseEntity中的body,用於定製化response的status與headers.
5、HTTP Streaming With Server-Sent Events
SseEmitter是ResponseBodyEmitter的子類,它提供Server-Sent Events.伺服器事件發送是」HTTP Streaming」的另一個變種技術.只是從伺服器發送的事件按照W3C Server-Sent Events規范來的.
Server-Sent Events能夠來用於它們的預期使用目的,就是從server發送events到clients.在Spring MVC中可以很容易的實現.僅僅需要返回一個SseEmitter類型的值.
注意:IE瀏覽器並不支持Server-Sent Events並且對於更高級的web應用程序消息傳遞場景:例如在線游戲,在線協作,金融應用以及其它.最好考慮使用Spring的WebSocket來支持.它包含SockJS-style WebSocket的模擬可以被廣泛的瀏覽器加高。(包含IE瀏覽器).在一個消息中心架構中通過發布-訂閱模型也能進行更高級別的與客戶消息傳遞模式進行交互.你可以通過看the following blog post了解更多.
6、HTTP Streaming Directly To The OutputStream
ResponseBodyEmitter允許通過HttpMessageConverter把發送的events寫到對象到response中.這可能是最常見的情況。例如寫JSON數據.可是有時候它被用來繞開message轉換直接寫入到response的OutputStream。例如文件下載.這樣可以通過返回StreamingResponseBody類型的值做到.
下面就是一個簡單的例子:
@RequestMapping("/download")
public StreamingResponseBody handle() {
return new StreamingResponseBody() {
@Override
public void writeTo(OutputStream outputStream) throws IOException {
// write...
}
};
}123456789
注意:StreamingResponseBody同樣可以用來作為ResponseEntity中的body用來定製化response的狀態與headers。
7、配置非同步請求
7.1 Servlet容器配置
保證web.xml中application的配置的版本是3.0:
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
...
</web-app>123456789
可以通過web.xml中的子元素<async-supported>true</async-supported>使得DispatcherServlet支持非同步.此外的任何Filter參與非同步語法處理必須配置為支持ASYNC分派器類型。這樣可以確保Spring Framework提供的所有filter都能夠非同步分發.自從它們繼承了OncePerRequestFilter之後.並且在runtime的時候會check filter是否需要被非同步調用分發.
下面是web.xml的配置示例:
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<filter>
<filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
<filter-class>org.springframework.~.OpenEntityManagerInViewFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ASYNC</dispatcher>
</filter-mapping>
</web-app>
如果使用Sevlet3,Java配置可以通過WebApplicationInitializer,你同樣需要像在web.xml中一樣,設置」asyncSupported」標簽為ASYNC.為了簡化這個配置,考慮繼承或者。它們會自動設置這些選項,使它很容易注冊過濾器實例。
7.2. Spring MVC配置
Spring MVC提供Java Config與MVC namespace作為選擇用來配置處理非同步request.WebMvcConfigurer可以通過configureAsyncSupport來進行配置,而xml可以通過子元素來進行配置.
如果你不想依賴Servlet容器(e.g. Tomcat是10)配置的值,允許你配置非同步請求默認的timeout值。你可以配置AsyncTaskExecutor用來包含Callable實例作為controller方法的返回值.強烈建議配置這個屬性,因為在默認情況下Spring MVC使用SimpleAsyncTaskExecutor。Spring MVC中Java配置與namespace允許你注冊CallableProcessingInterceptor與實例.
如果你想覆蓋DeferredResult的默認過期時間,你可以選擇使用合適的構造器.同樣的,對於Callable,你可以通過WebAsyncTask來包裝它並且使用相應的構造器來定製化過期時間.WebAsyncTask的構造器同樣允許你提供一個AsyncTaskExecutor.
因為水平有限,翻譯不足之處還望見諒。
原文地址:spring-framework-reference-4.2.6.RELEASE
㈡ spring async 怎樣檢查任務的狀態
使用ctrl+alt+delete 快捷鍵或者在任務欄中右鍵選擇任務管理器來打開
在進程列表中找到我們想要了解的進程
右鍵選擇
可以看到有如下幾個選項
結束任務
資源值
調試
創建轉儲文件
打開文件所在的位置
在線搜索
屬性
3
選擇屬性
會打開當前進程的屬性窗口
㈢ spring中@async的配置文件怎麼配
內容如下:
# 是否開啟邏輯刪除
del.filter.on=false
domain=http://www.366go.cn/
修改Spring配置文件
之前代碼:
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:dbinfo.properties</value>
</list>
</property>
</bean>
修改後的配置文件
<bean id="propertyConfigurer"
class="com.hisun.core.util.">
<property name="locations">
<list>
<value>classpath:dbinfo.properties</value>
<value>classpath:project.properties</value>
</list>
</property>
</bean>
加入了classpath:project.properties,其為自定義的配置文件
將PropertyPlaceholderConfigurer類修改為自定義類,
PropertyPlaceholderConfigurer類的具體作用可以查資料這塊兒不做詳細介紹
定義類
類的具體內容為下,
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
public class extends PropertyPlaceholderConfigurer {
private static Map ctxPropertiesMap;
@Override
protected void processProperties( beanFactoryToProcess,
Properties props) throws BeansException {
super.processProperties(beanFactoryToProcess, props);
ctxPropertiesMap = new HashMap();
for (Object key : props.keySet()) {
String keyStr = key.toString();
String value = props.getProperty(keyStr);
ctxPropertiesMap.put(keyStr, value);
}
}
public static Object getContextProperty(String name) {
return ctxPropertiesMap.get(name);
}
}
㈣ springboot中如何使用線程池及非同步線程
有一些業務需求,需要是非同步進行的,不能影響當前線程的運行,在spring boot中則能通過註解和配置快速實現這個。
首先寫個非同步線程池配置類,如下:
@Configuration@EnableAsyncpublic class AsyncConfig {
@Value("${async.executor.thread.core_pool_size}") private int corePoolSize; @Value("${async.executor.thread.max_pool_size}") private int maxPoolSize; @Value("${async.executor.thread.queue_capacity}") private int queueCapacity; private String threadNamePrefix = "AsyncExecutorThread-"; @Bean(name = "asyncExecutor") public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setThreadNamePrefix(threadNamePrefix);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize(); return executor;
}
}
然後將需要非同步執行的業務步驟寫成方法用@sync註解即可,如下:
@Componentpublic class AsyncExecutorCommon {
@Async("asyncExecutor") public void loadPic(IfcTingshenJzml tsJzml,
WsTingshenJzmlService wsTingshenJzmlService) {
List<IfcTingshenJzml> tsJzmls = new ArrayList<IfcTingshenJzml>();
tsJzmls.add(tsJzml);
wsTingshenJzmlService.docService(tsJzmls);
}
}12345678910
在業務代碼里直接調用這個方法即可,這個方法的執行就是非同步的。注意,非同步方法和調用非同步的方法不能寫在一個類里,否則會報循環依賴異常,建議另建一個類,只用來放非同步方法。
㈤ spring非同步處理@Async怎麼關聯線程和任務id
這個註解用於標注某個方法或某個類裡面的所有方法都是需要非同步處理的。被註解的方法被調用的時候,會在新線程中執行,而調用它的方法會在原來的線程中執行。這樣可以避免阻塞、以及保證任務的實時性。
㈥ spring中如何配置數組
在使用SpringMVC時,我們想傳遞數組引用類型,SpringMVC對數組傳遞有些限制:
經過測試:SpringMVC支持一維數組的參數傳遞,不支持多維數組的參數傳遞,如果想傳遞多維數組,那麼我們只好改用其他的辦法如:1、將多維數組拆成一維數組;2、將多維數組改為集合傳遞;3、或者改為字元串,接收時間處理一下等等,4、或者將所有數組中的值拼接傳遞(例如:data=1&data=2&data=3,代表三個數組的值)方法很靈活。
1 SpringMVC傳遞一維數組:傳遞數組類型時,需要在@requestParam()中添加value,否則會出現HTTP Status 400 - Required long[] parameter 'data' is not present錯誤。
例如: @RequestParam(value = "data[]")long[] data
前端請求:
var dataArr = new Array();
for(var i = 0; i < 10; i++){
dataArr.push(i);
}
$.ajax({
url : "test/arrayParam.shtml",
data : {
"datas" : dataArr
},
dataType : "json",
success : function(data) {
alert(data);
},
async : false
});
}
後端代碼:
package com.pyc.search.view.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/test")
public class TestController {
@RequestMapping("/arrayParam")
public @ResponseBody
int test1(@RequestParam(value = "datas[]") long[] datas) {
return datas.length;
}
}
2 多維數組可以轉換為一維數組,或者轉換為字元串,在後端接收時處理一下。
㈦ spring async 拋出異常嗎
async是非同步,沒有設置,那麼默認是true,本來就是非同步。 $.ajax全是 沒設置 同步,但是卻執行同步動作。。 不知道你是怎麼判斷的同步,如果return H.promise()在done後執行,這並不能說明就是同步。
㈧ 如何使用Spring配置非同步和同步事件發布
在Spring中使用非同步事件實現同步事務:
結合Scala+Spring,我們將採取一個很簡單的場景:下訂單,然後發送一封電子郵件。
編制一個服務:
@Service
class OrderService @Autowired() (orderDao: OrderDao, mailNotifier: OrderMailNotifier) {
@Transactional
def placeOrder(order: Order) {
orderDao save order //保存訂單
mailNotifier sendMail order //發送郵件
}
}
上面代碼是在保存訂單和發送郵件兩個同步執行,發送郵件需要連接郵件伺服器,比較耗時,拖延了整個性能,我們採取非同步發送電子郵件,利用Spring內置的自定義事件,與JMS或其他生產者 - 消費者類似。
case class OrderPlacedEvent(order: Order) extends ApplicationEvent
@Service
class OrderService @Autowired() (orderDao: OrderDao, eventPublisher: ApplicationEventPublisher) {
@Transactional
def placeOrder(order: Order) {
orderDao save order
eventPublisher publishEvent OrderPlacedEvent(order)
}
}
區別是繼承了ApplicationEvent 之前是直接用 OrderMailNotifier 直接發送,而現在我們使用ApplicationEventPublisher 發送發郵件事件了。
事件監聽者代碼如下:
@Service
class OrderMailNotifier extends ApplicationListener[OrderPlacedEvent] {
def onApplicationEvent(event: OrderPlacedEvent) {
//sending e-mail...
}
}
在監聽者方法中真正實現郵件發送。
但是Spring的ApplicationEvents是同步事件,意味著我們並沒有真正實現非同步,程序還會在這里堵塞,如果希望非同步,我們需要重新定義一個ApplicationEventMulticaster,實現類型和TaskExecutor:
@Bean
def applicationEventMulticaster() = {
val multicaster = new ()
multicaster.setTaskExecutor(taskExecutor())
multicaster
}
@Bean
def taskExecutor() = {
val pool = new ThreadPoolTaskExecutor()
pool.setMaxPoolSize(10)
pool.setCorePoolSize(10)
pool.setThreadNamePrefix("Spring-Async-")
pool
}
Spring通過使用TaskExecutor已經支持廣播事件了,對onApplicationEvent() 標注 @Async
@Async
def onApplicationEvent(event: OrderPlacedEvent) { //...
如果你希望使用@Async,可以編制自己的非同步執行器:
@Configuration
@EnableAsync
class ThreadingConfig extends AsyncConfigurer {
def getAsyncExecutor = taskExecutor()
@Bean
def taskExecutor() = {
val pool = new ThreadPoolTaskExecutor()
pool.setMaxPoolSize(10)
pool.setCorePoolSize(10)
pool.setThreadNamePrefix("Spring-Async-")
pool
}
}
@ EnableAsync是足夠了。,默認情況下,Spring使用SimpleAsyncTaskExecutor類創建新的線程。
以上所有設置暴露一個真正的問題。現在,我們雖然使用其他線程發送一個非同步消息處理。不幸的是,我們引入競爭條件。
開始事務
存儲order到資料庫
發送一個包裝order的消息
確認
非同步線程獲得OrderPlacedEvent並開始處理。現在的問題是,它發生(3)之後,還是(4)之前或者(4)之後?這有一個很大的區別!在前者的情況下,交易也尚未提交訂單所以不存在於資料庫中。另一方面,延遲載入可能已經在工作,致使訂單對象仍然然綁定在 PersistenceContext(預設我們使用JPA)。
解決辦法是使用 .,可以注冊很多監聽者 TransactionSynchronization,它對於事務的提交或回滾都有事件發送。
@Transactional
def placeOrder(order: Order) {
orderDao save order
afterCommit {
eventPublisher publishEvent OrderPlacedEvent(order)
}
}
private def afterCommit[T](fun: => T) {
.registerSynchronization(new {
override def afterCommit() {
fun
}
})
}
當前事務提交後 afterCommit()接受調用,可以安全地調用registerSynchronization()多次 - 監聽器存儲在Set並且本地保存到當前事務中,事務提交後消失。
我們將afterCommit方法單獨抽象成一個類,分離關注。
class (delegate: ApplicationEventPublisher)
extends ApplicationEventPublisher {
override def publishEvent(event: ApplicationEvent) {
if (.isActualTransactionActive) {
.registerSynchronization(
new {
override def afterCommit() {
delegate publishEvent event
}
})
}
else
delegate publishEvent event
}
}
是實現Spring的ApplicationEventPublisher。
我們要將這個新的實現告訴Spring替換掉舊的,用@Primary:
@Resource
val applicationContext: ApplicationContext = null
@Bean
@Primary
def () =
new (applicationContext)
再看看原來的訂單服務:
@Service
class OrderService @Autowired() (orderDao: OrderDao, eventPublisher:ApplicationEventPublisher) {
@Transactional
def placeOrder(order: Order) {
orderDao save order
eventPublisher publishEvent OrderPlacedEvent(order)
}
注意這里ApplicationEventPublisher已經是我們自己實現的,將被自動注入這個服務。
最後,要在真正訂單保存的業務代碼上放置事務:
def placeOrder(order: Order) {
storeOrder(order)
eventPublisher publishEvent OrderPlacedEvent(order)
}
@Transactional
def storeOrder(order: Order) = orderDao save order