SpringWheel框架實(shí)現(xiàn)及原理解析

前言 :本文主要講述SpringWheel框架中的實(shí)現(xiàn)伍纫,閱讀前請(qǐng)先了解SpringBoot和SpringMvc的原理

1胡陪、全局系統(tǒng)出錯(cuò)提示
在spring boot中BasicErrorController對(duì)全局的異常進(jìn)行處理(包括DispatchServlet.java中的異常)类早,在BasicErrotController中

//訪問(wèn)為表單的形式
@RequestMapping(produces = "text/html")
public ModelAndView errorHtml(HttpServletRequest request,      HttpServletResponse response) {  
 response.setStatus(getStatus(request).value());   
Map<String, Object> model = getErrorAttributes(request,         
isIncludeStackTrace(request, MediaType.TEXT_HTML));  
 return new ModelAndView("error", model);
}
//訪問(wèn)為ajax的形式
@RequestMapping
@ResponseBody
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {  
 Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));  
 HttpStatus status = getStatus(request);  
 return new ResponseEntity<Map<String, Object>>(body, status);
}

如果按照默認(rèn)的處理方式顯然就不太符合我們的需求蟆沫,所以在這個(gè)時(shí)候就需要對(duì)BasicErrorController進(jìn)行重寫

public class GlobalExceptionController extends BasicErrorController {    
public GlobalExceptionController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) { 
       super(errorAttributes, errorProperties);    
}    
@RequestMapping(produces = "text/html")    
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { 
        HttpStatus httpStatus = getStatus(request);        //如果是404的錯(cuò)誤
        if (HttpStatus.NOT_FOUND == httpStatus) {            
            return new ModelAndView("error-404");       
        }        
       //代碼異常        
       response.setStatus(getStatus(request).value());        
       Map<String, Object> model = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML));        
       return new ModelAndView("error", model);    
}    
@RequestMapping  
@ResponseBody    
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) { 
       Map<String, Object> body = getErrorAttributes(request,  isIncludeStackTrace(request, MediaType.ALL));       
      HttpStatus status = getStatus(request);        
     return new ResponseEntity<Map<String, Object>>(body, status);    
}
}

然后在將該類添加到spring容器中替換BasicController

2豪治、使用log4jdbc對(duì)慢查詢進(jìn)行監(jiān)控(開發(fā)環(huán)境使用
在開發(fā)過(guò)程中耘婚,如果傳入的參數(shù)很多叼丑,這個(gè)時(shí)候查看sql就變得很麻煩了关翎,log4jdbc通過(guò)重寫驅(qū)動(dòng)類的方式,直接打印出執(zhí)行的sql和sql的執(zhí)行時(shí)間鸠信,大大提升了我們的開發(fā)的效率纵寝。而且其可以對(duì)慢查詢sql進(jìn)行監(jiān)控,如果查詢時(shí)間超過(guò)設(shè)定值就會(huì)打印warn日志星立。但是因?yàn)槠渲貙懥蓑?qū)動(dòng)類爽茴,在生成環(huán)境上還是不要用葬凳。
Step1:引入maven坐標(biāo)

<dependency>    
   <groupId>com.googlecode.log4jdbc</groupId>
   <artifactId>log4jdbc</artifactId>   
   <version>1.2</version>
</dependency>

Step2: 更改驅(qū)動(dòng)類和URL

//將驅(qū)動(dòng)類改為net.sf.log4jdbc.DriverSpy
spring.datasource.driverClassName=net.sf.log4jdbc.DriverSpy
//在URL前面加上jdbc:log4jdbc
spring.datasource.url=jdbc:log4jdbc:mysql://127.0.0.1:3306/yk?useUnicode=true&characterEncoding=utf-8

Step3:在logback.xml文件中配置

<!--SQL日志設(shè)置 -->
<logger name="jdbc.connection" level="OFF" />
<logger name="jdbc.audit" level="OFF" />
<logger name="jdbc.resultset" level="OFF" />
<logger name="jdbc.sqlonly" level="OFF" />
<logger name="jdbc.sqltiming" level="INFO" />

上面的只是一個(gè)簡(jiǎn)單的入門例子,看到一個(gè)帖子講的比較詳細(xì)http://www.360doc.com/content/14/0822/14/8072791_403817030.shtml

3室奏、自定義注解@ApiRequestBody火焰、@ApiRequestBody、@ApiController的思路和實(shí)現(xiàn)
WHY:在一個(gè)項(xiàng)目中胧沫,有的時(shí)候需要對(duì)api的請(qǐng)求參數(shù)和返回參數(shù)進(jìn)行一些處理昌简,如果直接重寫HttpMessageConverter,所有的json請(qǐng)求都會(huì)被處理绒怨。這種做法明顯不符合我們的需求纯赎。這個(gè)時(shí)候比較明智的做法是對(duì)api的方法進(jìn)行單獨(dú)的處理。
SpringMvc請(qǐng)求處理過(guò)程

  • 圖中invokeForRequest中涉及了請(qǐng)求參數(shù)的處理
private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,      Object... providedArgs) throws Exception {
      if (this.argumentResolvers.supportsParameter(parameter)) {
        ...
        args[i] = this.argumentResolvers.resolveArgument(      parameter, mavContainer, request, this.dataBinderFactory);
        ...
        }
}

我們?cè)倏纯碼rgumentResolvers的實(shí)現(xiàn)吧南蹂,例如RequestResponseBodyMethodProcessor

@Override
public boolean supportsParameter(MethodParameter parameter) {   
     return parameter.hasParameterAnnotation(RequestBody.class);
}

從以上代碼可以看出犬金,只要controller的方法上面有@RequestBody的注解,請(qǐng)求就會(huì)進(jìn)入RequestResponseBodyMethodProcessor進(jìn)行處理(主要是List<HttpMessageConverter> messgeConverters的處理)碎紊。

  • 圖中handleReturnValue方式對(duì)返回的參數(shù)進(jìn)行了處理佑附,同樣的套路
private HandlerMethodReturnValueHandler selectHandler(Object value, MethodParameter returnType) {
       for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
           ...
           if (handler.supportsReturnType(returnType)) {   
                return handler;
           }
       }
}

同樣在RequestResponseBodyMethodProcessor中

@Override
public boolean supportsReturnType(MethodParameter returnType) {   
        return (AnnotationUtils.findAnnotation(returnType.getContainingClass(), ResponseBody.class) != null ||         returnType.getMethodAnnotation(ResponseBody.class) != null);
}

從以上代碼可以看出樊诺,只要controller的方法上面有@ResponseBody的注解仗考,請(qǐng)求就會(huì)進(jìn)入RequestResponseBodyMethodProcessor進(jìn)行處理(主要是List<HttpMessageConverter> messgeConverters的處理)
自定義實(shí)現(xiàn)
了解了原理之后,發(fā)現(xiàn)我們完全可以按照SpringMVC的套路來(lái)實(shí)現(xiàn)自己的需求
Step1:定義三個(gè)注解@ApiReqeustBody词爬,@ApiResponseBody秃嗜,@ApiController(參照@RequestBody、@ResponseBody顿膨、@RestController
Step2:定義參數(shù)處理類ApiRequestResponseMethodProcessor(參照ReqeustResponseBodyMethodProcessor
Step3:定義ApiHttpMessageConverter(參照AbstractGenericHttpMessageConverter的實(shí)現(xiàn)
Step4:將ApiRequestResponseMethodProcessor放到argumentResolvers(入?yún)⑻幚韑ist)returnValueHandlers(返回參數(shù)處理list)锅锨。

  • 在SpringBoot中:
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { 
                    List<HttpMessageConverter<?>> messageConverters = getHttpMessageConverters(); 
                    argumentResolvers.add(new ApiRequestResponseBodyMethodProcessor(messageConverters));
}
@Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {   
                   List<HttpMessageConverter<?>> messageConverters = getHttpMessageConverters();  
                   returnValueHandlers.add(new ApiRequestResponseBodyMethodProcessor(messageConverters));
}
private List<HttpMessageConverter<?>> getHttpMessageConverters(){   
                  List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();    
                  ApiHttpMessageConverter apiHttpMessageConverter = new ApiHttpMessageConverter(); 
                  messageConverters.add(apiHttpMessageConverter);    
                  return messageConverters;
}
  • 在SpringMvc中:
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
      <property name="customArgumentResolvers">
         <list>
             <bean class="com.springwheel.common.annotation.ApiRequestResponseBodyMethodProcessor"/>
         </list>
       </property>
       <property name="customReturnValueHandlers">
         <list>
             <bean class="com.springwheel.common.annotation.ApiRequestResponseBodyMethodProcessor"/>
         </list>
       </property>
</bean>

4、api請(qǐng)求處理過(guò)程詳解


ApiRequestFilter

  • 驗(yàn)簽
  • 權(quán)限檢驗(yàn)

ValidationInterceptor

  • 參數(shù)檢驗(yàn)

碰見的問(wèn)題
1)Filter中讀取Request中的流后, 然后再Control中讀取不到的做法
解決方法:實(shí)現(xiàn)一個(gè)Wrapper 恋沃,可參照:http://my.oschina.net/vernon/blog/363693
2)ValidationInterceptor中無(wú)法獲取參數(shù)值
解決方法:在ValidationInterceptor調(diào)用ArgumentResolver必搞,對(duì)參數(shù)進(jìn)行處理。參照:http://my.oschina.net/FengJ/blog/223727

5囊咏、使用profile方便線上環(huán)境配置
我想有做過(guò)發(fā)布的一定配到過(guò)這樣的問(wèn)題恕洲,本地開發(fā)用的配置文件跟線上開發(fā)的配置文件不一樣。一般如果使用jenkens進(jìn)行自動(dòng)部署的會(huì)在copy的時(shí)候?qū)ε渲梦募M(jìn)行exclude梅割,但是這樣往往會(huì)有以下幾個(gè)缺點(diǎn):

  • 線上配置文件跟本地不一致霜第,如果本地增加了配置,但是線上沒(méi)有的話户辞,項(xiàng)目啟動(dòng)就會(huì)報(bào)錯(cuò)
  • 運(yùn)維一般都會(huì)控制寫入權(quán)限泌类,所以線上的配置文件開發(fā)沒(méi)有權(quán)限更改,需要增加或者更改配置的話需要通知運(yùn)維(增加了溝通成本和時(shí)間成本)
  • 每個(gè)人本地的環(huán)境都不一樣底燎,如果每次從svn或者git上面更新文件都要更改本地配置

spring boot多profile配置
pom.xml

<!--根據(jù)環(huán)境配置不同的profiles -->
<profiles>    
           <profile>        
                  <id>dev</id>        
                  <properties>            
                       <profileActive>dev</profileActive>        
                  </properties>        
                  <activation>            
                      <activeByDefault>true</activeByDefault>        
                  </activation>    
             </profile>    
             <profile>        
                   <id>cte</id>        
                   <properties>            
                        <profileActive>cte</profileActive>        
                    </properties>    
               </profile>    
               <profile>        
                    <id>cte2</id>        
                     <properties>            
                         <profileActive>cte2</profileActive>        
                     </properties>    
                </profile>
</profiles>

<!--打包的時(shí)候?qū)esources的文件進(jìn)行攔截 (根據(jù)maven傳入的不同的環(huán)境參數(shù)打包的時(shí)候會(huì)對(duì)application的配置文件進(jìn)行攔截)-->
<resources>  
           <resource>   
                  <directory>src/main/resources</directory>    
                  <filtering>true</filtering>    
                  <excludes>      
                          <exclude>application.properties</exclude>      
                          <exclude>application-dev.properties</exclude>      
                          <exclude>application-cte.properties</exclude>    
                </excludes>  
             </resource>  
              <resource>    
                    <directory>src/main/resources</directory>    
                    <filtering>true</filtering>    
                    <includes>      
                         <include>application.properties</include>      
                         <include>application-${profileActive}.properties</include>    
                     </includes>  
              </resource>
</resources>

application.properties

#profile
spring.profiles.active=@profileActive@

打包時(shí)指定環(huán)境(如果不指定刃榨,起作用的為dev
例如:dev環(huán)境
mvn clean install -Dmaven.test.skip=true -P dev


可以看到弹砚,打包的時(shí)候只會(huì)打入application.properties和application-dev.properties這兩個(gè)文件。
地址:https://github.com/hjm017/SpringWheel
官網(wǎng):http://hjm017.github.io/SpringWheel/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末喇澡,一起剝皮案震驚了整個(gè)濱河市迅栅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌晴玖,老刑警劉巖读存,帶你破解...
    沈念sama閱讀 212,542評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異呕屎,居然都是意外死亡让簿,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門秀睛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)尔当,“玉大人,你說(shuō)我怎么就攤上這事蹂安⊥钟” “怎么了?”我有些...
    開封第一講書人閱讀 158,021評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵田盈,是天一觀的道長(zhǎng)畜号。 經(jīng)常有香客問(wèn)我,道長(zhǎng)允瞧,這世上最難降的妖魔是什么简软? 我笑而不...
    開封第一講書人閱讀 56,682評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮述暂,結(jié)果婚禮上痹升,老公的妹妹穿的比我還像新娘。我一直安慰自己畦韭,他們只是感情好疼蛾,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著艺配,像睡著了一般察郁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上妒挎,一...
    開封第一講書人閱讀 49,985評(píng)論 1 291
  • 那天绳锅,我揣著相機(jī)與錄音,去河邊找鬼酝掩。 笑死鳞芙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播原朝,決...
    沈念sama閱讀 39,107評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼驯嘱,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了喳坠?” 一聲冷哼從身側(cè)響起鞠评,我...
    開封第一講書人閱讀 37,845評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎壕鹉,沒(méi)想到半個(gè)月后剃幌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,299評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡晾浴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評(píng)論 2 327
  • 正文 我和宋清朗相戀三年负乡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片脊凰。...
    茶點(diǎn)故事閱讀 38,747評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡抖棘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出狸涌,到底是詐尸還是另有隱情切省,我是刑警寧澤,帶...
    沈念sama閱讀 34,441評(píng)論 4 333
  • 正文 年R本政府宣布帕胆,位于F島的核電站朝捆,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏惶楼。R本人自食惡果不足惜右蹦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評(píng)論 3 317
  • 文/蒙蒙 一诊杆、第九天 我趴在偏房一處隱蔽的房頂上張望歼捐。 院中可真熱鬧,春花似錦晨汹、人聲如沸豹储。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)剥扣。三九已至,卻和暖如春铝穷,著一層夾襖步出監(jiān)牢的瞬間钠怯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工曙聂, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留晦炊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,545評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像断国,于是被迫代替她去往敵國(guó)和親贤姆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容