A. JRebel啟動報錯
項目概況:SpringMVC
新接手的項目,安裝JRebel插譽喊遲件(JRebel for IntelliJ v2019.1.1)後,啟動報錯,在此之前是可以正常啟動項目。
報錯核心信息: Exception in thread "Timer-RefreshDataTask" java.lang.IllegalStateException: BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext
報慶李錯詳細信息:
五月 10, 2019 10:37:36 上午 org.apache.catalina.loader.WebappClassLoader clearReferencesJdbc
嚴重: The web application [/edi-web] registered the JDBC driver [com.alibaba.druid.proxy.DruidDriver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.
五月 10, 2019 10:37:36 上滲伏午 org.apache.catalina.loader.WebappClassLoader clearReferencesJdbc
嚴重: The web application [/edi-web] registered the JDBC driver [com.mysql.jdbc.Driver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.
五月 10, 2019 10:37:36 上午 org.apache.catalina.loader.WebappClassLoader clearReferencesJdbc
嚴重: The web application [/edi-web] registered the JDBC driver [oracle.jdbc.driver.OracleDriver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.
五月 10, 2019 10:37:36 上午 org.apache.catalina.loader.WebappClassLoader checkThreadLocalMapForLeaks
嚴重: The web application [/edi-web] created a ThreadLocal with key of type [net.sf.json.AbstractJSON.CycleSet] (value [net.sf.json.AbstractJSON$CycleSet@52abeb56]) and a value of type [java.lang.ref.SoftReference] (value [java.lang.ref.SoftReference@73639524]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
[2019-05-10 10:37:36,507] Artifact xbwl-edi-web:war exploded: Error ring artifact deployment. See server log for details.
log4j:WARN No appenders could be found for logger (org.springframework.scheling.quartz.SchelerFactoryBean).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
五月 10, 2019 10:37:45 上午 org.apache.catalina.loader.WebappClassLoader loadClass
信息: Illegal access: this web application instance has been stopped already. Could not load org.springframework.core.NestedExceptionUtils. The eventual following stack trace is caused by an error thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access, and has no functional impact.
java.lang.IllegalStateException
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1612)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1571)
at org.springframework.core.NestedRuntimeException.<clinit>(NestedRuntimeException.java:45)
at org.springframework.scheling.quartz.SchelerFactoryBean$1.run(SchelerFactoryBean.java:673)
Exception in thread "Quartz Scheler [SYSSTATIC_SCHEDULE]" java.lang.NoClassDefFoundError: org/springframework/core/NestedExceptionUtils
at org.springframework.core.NestedRuntimeException.<clinit>(NestedRuntimeException.java:45)
at org.springframework.scheling.quartz.SchelerFactoryBean$1.run(SchelerFactoryBean.java:673)
Caused by: java.lang.ClassNotFoundException: org.springframework.core.NestedExceptionUtils
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1720)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1571)
... 2 more
Exception in thread "Timer-RefreshDataTask" java.lang.IllegalStateException: BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext
at org.springframework.context.support..getBeanFactory(.java:170)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:985)
at com.xbwl.core.cache.CacheManager.<init>(CacheManager.java:20)
at com.xbwl.core.cache.CacheManager.getInstance(CacheManager.java:54)
at com.xbwl.core.mq.rabbitMq.service.impl.RabbitMqServiceImpl$RefreshDataTask.run(RabbitMqServiceImpl.java:107)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
Exception in thread "Timer-RefreshDataTask" java.lang.IllegalStateException: BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext
at org.springframework.context.support..getBeanFactory(.java:170)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:985)
at com.xbwl.core.cache.CacheManager.<init>(CacheManager.java:20)
at com.xbwl.core.cache.CacheManager.getInstance(CacheManager.java:54)
at com.xbwl.core.mq.MqInterceptor.initMqCache(MqInterceptor.java:70)
at com.xbwl.core.mq.MqInterceptor.access$000(MqInterceptor.java:26)
at com.xbwl.core.mq.MqInterceptor$RefreshDataTask.run(MqInterceptor.java:64)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
Exception in thread "Timer-RefreshDataTask" java.lang.IllegalStateException: BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext
at org.springframework.context.support..getBeanFactory(.java:170)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:985)
at com.xbwl.core.cache.CacheManager.<init>(CacheManager.java:20)
at com.xbwl.core.cache.CacheManager.getInstance(CacheManager.java:54)
at com.xbwl.core.service.impl.BaiWebApiServiceImpl$RefreshDataTask.run(BaiWebApiServiceImpl.java:41)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
暫未解決,待續
B. mob簡訊驗證錯誤碼
錯誤碼參考
SMSSDK for Android API回調中,當result=SMSSDK.RESULT_ERROR,則data的類型為Throwable;如果這個Throwable的message就存放有json數據,你可以從中讀取status的錯誤碼信息。一部分是伺服器返回的。還有一部分是SDK本地生成的。
伺服器錯誤碼:
錯誤碼 描述 說明
400 無效請求 客戶端請求不能被識別。
405 AppKey為空 請求的AppKey為空。
406 AppKey錯誤 請求的AppKey不存在。
407 缺少數據 請求提交的數據缺少必要的數據。
408 無效的參數 無效的請求參數。
418 內部介面調用失敗 內部介面調用失敗。
450 許可權不足 無權執行該操作。
454 數據格式錯誤 請求傳遞的數據格式錯誤,伺服器無法轉換為JSON格式的數據。
455 簽名無效 簽名檢驗。
456 手機號碼為空 提交的手機號碼或者區號為空。
457 手機號碼格式錯誤 提交的手機號格式不正確(包括手機的區號)。
458 手機號碼在黑名單中 手機號碼在發送黑名單中。
459 無appKey的控制數據 獲取appKey控制發送簡訊的數據失敗。
460 無許可權發送簡訊 沒有打開客戶端發送簡訊的開關。
461 不支持該地區發送簡訊 沒有開亂扮通給當前地區發送簡訊的功能。
462 每分鍾發送次數超限 每分鍾發送簡訊的數量超過限制。
463 手機號碼每天發送次數超限 手機號碼在當前APP內每天發送短早陪碼信的次數超出限制。
464 每台手機每天發送次數超限 每台手機每天發送簡訊的次數超限。
465 號碼在App中每天發送簡訊的次數超限 手機號碼在APP中每天發送簡訊的數量超限。
466 校驗的驗證碼為空 提交的校驗驗證碼為空。
467 校驗驗證碼請求頻繁 5分鍾內校驗錯誤超過3次,驗證碼失效。
468 需要校驗的驗證碼錯誤 用戶提交校驗的驗證碼錯誤。
469 未開啟web發送簡訊 沒有打開通過網頁端發送簡訊的開關。
470 賬戶余額不足 賬戶的簡訊余額不足。
471 請求IP錯誤 通過服務端發送或驗證簡訊的IP錯誤
472 客戶端請求發送簡訊驗證過於頻繁 客戶端請求發送簡訊驗證過於頻繁
473 服務端根據id獲取平台錯誤 服務端根據id獲取平台錯誤
474 沒有打開服務端驗證開關 沒有打開服務端驗證開關
475 appKey的應用信息不存在 appKey的應用信息不存在
476 當前appkey發送簡訊的數量超過限額 如果當前appkey對應的包名陸哪沒有通過審核,每天次appkey+包名最多可以發送20條簡訊
477 當前手機號發送簡訊的數量超過限額 當前手機號碼在SMSSDK平台內每天最多可發送簡訊10條,包括客戶端發送和WebApi發送
478 當前手機號在當前應用內發送超過限額 當前手機號碼在當前應用下 12小時內最多可發送文本驗證碼5條
500 伺服器內部錯誤 服務端程序報錯。
本地錯誤碼:
錯誤碼 描述 說明
600 API使用受限制 API使用受限制
601 簡訊發送受限 簡訊發送受限
602 無法發送此地區簡訊 無法發送此地區簡訊
603 請填寫正確的手機號碼 請填寫正確的手機號碼
604 當前服務暫不支持此國家 當前服務暫不支持此國家,請選擇其它國家
C. webapi 返回類型到底應該是什麼才合適,這是個問題
我們知道在WebAPi中對於響應結果我們都是這樣用的:
public HttpResponseMessage GetResult<T>(T t)
{
return Request.CreateResponse<T>(HttpStatusCode.OK, t);
}
在項目中前端為了和其他統一,封裝了一套響應的結果和狀態碼,要求直接返回對象,於是將上述修改成比如如下:
public Result<List<Person>> GetResult()
{
var result = new Result<List<Person>>();
return result;
}
public class Result<T> : BaseResult
{
public T Data;
}
public class BaseResult
{
public string Message;
public int Status;
public ErrorCode ErrorCode;
}
public enum ErrorCode
{
......
}
統觀以上兩種方法,一種是WebAPi內置響應的結果,另外一種則是直接返回自定義響應結果。
於是乎,我開始思索這兩種方法雖然都能得到我們想要的結果,但是有什麼區別沒有呢?說的更加明確一點的是,二者在數據響應上有沒有性能上的差異呢?
WebAPi響應結果和自定義響應結果二者性能差異
以上則是需要返回對象來進行處理,而有些我們則不需要返回任何對象來進行處理例如直接返回void,而在WebAPi中對應需要返回 IHttpActionResult 例如自定義返回則是如下:
public void GetFirst()
{.....}
在WebAPi中則是進行如下返回:
public IHttpActionResult GetSecond()
{
return OK();
}
下面我們在控制台中分別來測試這二者在WebHost以及在SelfHost上的差異,我們如何獲取其差異呢?我們通過對void方法和http方法在控制台中發出1000個請求來獲取其總共花費時間來進行比較。
SelfHost
[HttpGet]
public void GetFirst()
{
StringBuilder stringbuilder = new StringBuilder();
for (int i = 0; i < 20; i++)
{
stringbuilder.Append("something");
}
}
[HttpGet]
public IHttpActionResult GetSecond()
{
StringBuilder stringbuilder = new StringBuilder();
for (int i = 0; i < 20; i++)
{
stringbuilder.Append("something");
}
return Ok();
}
在控制台中方法如下:
private const string voidUrl = "http://localhost:8080/api/home/GetFirst";
private const string httpUrl = "http://localhost:8080/api/home/GetSecond";
private static List<TimeSpan> voidTimes = new List<TimeSpan>();
private static List<TimeSpan> httpTimes = new List<TimeSpan>();
static void Main(string[] args)
{
Console.WriteLine("Start Test....");
for (int i = 0; i < 1000; i++)
{
voidTimes.Add(getResponse(voidUrl));
Thread.Sleep(10);
Console.WriteLine("void Test " + i);
}
Console.WriteLine("Finished Void Test");
for (int i = 0; i < 1000; i++)
{
httpTimes.Add(getResponse(httpUrl));
Thread.Sleep(10);
Console.WriteLine("http Test " + i);
}
Console.WriteLine("Finished Http Test");
var voidTotalTime = voidTimes.Sum(t => t.Milliseconds);
Console.WriteLine("void方法發出1000個請求總共需要時間:" + voidTotalTime);
Console.WriteLine("void方法平均每一個請求需要時間:" + voidTotalTime / 1000.00 + "秒");
var httpTotalTime = httpTimes.Sum(t => t.Milliseconds);
Console.WriteLine("http方法發出1000個請求總共需要時間: " + httpTotalTime);
Console.WriteLine("http方法平均每一個請求需要時間: " + httpTotalTime / 1000.00 + "秒");
Console.Read();
}
static TimeSpan getResponse(string url)
{
var stopWatch = new Stopwatch();
stopWatch.Start();
var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(url);
var task = httpClient.GetAsync(httpClient.BaseAddress).Result;
var result = task.Content.ReadAsAsync(typeof(object));
var timeSpan = stopWatch.Elapsed;
stopWatch.Stop();
return timeSpan;
}
D. 用MVC WebApi寫介面,網閘上了之後,方法訪問不到,埠已經全部開放
VisualStudio為我們提供了專門用於創建ASP.NETWebAPI應用的項目模板,藉助於此項目模板提供的向導,我們可以「一鍵式」創建一個完整的ASP.NETWebAPI項目。在項目創建過程中,VisualStudio會自動為我們添加必要的程序集引用和配置,甚至會為我們自動生成相關的代碼,總之一句話:這種通過向導生成的項目在被創建之後其本身就是一個可執行的應用。對於IDE提供的這種旨在提高生產效率的自動化機制,我個人自然是推崇的,但是我更推薦讀者朋友們去了解一下這些自動化機制具體為我們做了什麼?做這些的目的何在?哪些是必需的,哪些又是不必要的?正是基於這樣的目的,在接下來演示的實例中,我們將摒棄VisualStudio為我們提供的向導,完全在創建的空項目中編寫我們的程序。這些空項目體現在如右圖所示的解決方案結構中。如右圖所示,整個解決方案一共包含6個項目,上面介紹的作為「聯系人管理器」的單頁Web應用對應著項目WebApp,下面的列表給出了包括它在內的所有項目的類型和扮演的角色。·Common:這是一個空的類庫項目,僅僅定義了表示聯系人的數據類型而已。之所以將數據類型定義在獨立的項目中,只要是考慮到它會被多個項目(WebApi和ConsoleApp)所使用。WebApi:這是一個空的類庫項目,表現為HttpController類型的WebAPI就定義在此項目中,它具有對Common的項目引用。WebHost:這是一個空的ASP.NETWeb應用,它實現了針對ASP.NETWebAPI的WebHost寄宿,該項目具有針對WebApi的項目引用。SelfHost:這是一個空的控制台應用,旨在模擬ASP.NETWebAPI的SelfHost寄宿模式,它同樣具有針對WebApi的項目引用。WebApp:這是一個空的ASP.NETWeb應用,代表「聯系人管理器」的網頁就存在於該項目之中,至於具體的聯系人管理功能,自然通過以Ajax的形式調用WebAPI來完成。ConsoleApp:這是一個空的控制台應用,我們用它來模擬如何利用客戶端代理來實現對WebAPI的遠程調用,它具有針對Common的項目引用。二、定義WebAPI在正式定義WebAPI之前,我們需要在項目Common中定義代表聯系人的數據類型Contact。簡單起見,我們僅僅為Contact定義了如下幾個簡單的屬性,它們分別代表聯系人的ID、姓名、聯系電話、電子郵箱和聯系地址。1:publicclassContact2:{3:publicstringId{get;set;}4:publicstringName{get;set;}5:publicstringPhoneNo{get;set;}6:publicstringEmailAddress{get;set;}7:publicstringAddress{get;set;}8:}表現為HttpController的WebAPI定義在WebApi項目之中,我們一般將ApiController作為繼承的基類。ApiController定義在「System.Web.Http.dll」程序集中,我們可以在目錄「%ProgramFiles%\MicrosoftASP.NET\ASP.NETWebStack5\Packages\」中找到這個程序集。具體來說,該程序集存在於子目錄「Microsoft.AspNet.WebApi.Core.5.0.0\lib\net45」中。WebAPI體現在如下所示的ContactsController類型中。在該類型中,我們定義了Get、Post、Put和Delete這4個Action方法,它們分別實現了針對聯系人的查詢、添加、修改和刪除操作。Action方法Get具有一個表示聯系人ID的可預設參數,如果該參數存在則返回對應的聯系人,否則返回整個聯系人列表。由於ASP.NETWebAPI默認實現了Action方法與HTTP方法的映射,所以方法名也體現了它們各自所能處理請求必須採用的HTTP方法。1:publicclassContactsController:ApiController2:{3:staticListcontacts;4:staticintcounter=2;5:6:staticContactsController()7:{8:contacts=newList();9:contacts.Add(newContact{Id="001",Name="張三",10:PhoneNo="0512-12345678",EmailAddress="[email protected]",11:Address="江蘇省蘇州市星湖街328號"});12:contacts.Add(newContact{Id="002",Name="李四",13:PhoneNo="0512-23456789",EmailAddress="[email protected]",14:Address="江蘇省蘇州市金雞湖大道328號"});15:}16:17:publicIEnumerableGet(stringid=null)18:{19:returnfromcontactincontacts20:wherecontact.Id==id||string.IsNullOrEmpty(id)21:selectcontact;22:}23:24:publicvoidPost(Contactcontact)25:{26:Interlocked.Increment(refcounter);27:contact.Id=counter.ToString("D3");28:contacts.Add(contact);29:}30:31:publicvoidPut(Contactcontact)32:{33:contacts.Remove(contacts.First(c=>c.Id==contact.Id));34:contacts.Add(contact);35:}36:37:publicvoidDelete(stringid)38:{39:contacts.Remove(contacts.First(c=>c.Id==id));40:}41:}簡單起見,我們利用一個靜態欄位(contacts)表示存儲的聯系人列表。當ContactsController類型被載入的時候,我們添加了兩個ID分別為「001」和「002」的聯系人記錄。至於實現聯系人CRUD操作的Action方法,我們也省略了必要的驗證,對於本書後續的演示的實例,我們基本上也會採用這種「簡寫」的風格。