欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

Spring動態部署Bean/Controller/Groovy Control

系統 1781 0

最近有好幾個咨詢如何動態部署Bean/動態部署Spring mvc 控制器;首先聲明下:基于普通Java/JavaEE環境的不適合做動態部署;如果你有這種需求請考慮使用如Play Framework/Grails這種框架。但是還是有少量朋友會有這種需求:我的應用中只有少量幾個需要動態部署的組件;好吧,那我來寫一個能動態部署Bean/Controller的工具類吧。

?

注意,因為Spring整個框架非常好的遵循開閉原則,所以只能通過反射來操作,而且目前不考慮Spring 3.1版本以下的(或者使用DefaultAnnotationHandlerMapping,從Spring3.1開始使用RequestMappingHandlerMapping,之前實現了對DefaultAnnotationHandlerMapping的支持,但是想了想還是請考慮升級吧,因為spring向下兼容性非常好),如果想在Spring 3.1之前版本使用請考慮自己修改代碼/升級框架。

?

對于動態注冊Groovy腳本,Spring內部提供了支持,使用如<lang:groovy>標簽;但是對于需要動態修改的Controller就不那么完美了;

1、如果開啟其refresh-check-delay(即多久重載一下腳本),這個目前實現很土,即假設我設置為500毫秒,不管文件修改/沒修改都會自動reload,所以請考慮不要使用它的這種刷新腳本機制;我們需要的是檢查如文件修改否再刷新;

2、如果開啟了refresh-check-delay,其內部是通過Aop完成的,如果沒有設置其是proxy-target-class="true",那么它是走JDK動態代理,因為我們大部分控制器是沒有實現接口的,所以即使你注冊到Spring mvc,也會映射不到的,因此請使用CGLIB代理;創建代理是通過ScriptFactoryPostProcessor來完成的;

3、如果你注冊到Spring MVC了,又刷新了腳本,那么它是通過ScriptFactoryPostProcessor注冊到proxy一個RefreshableScriptTargetSource,通過這個TargetSource刷新的;問題來了:

對于Spring mvc進行映射是通過RequestMappingHandlerMapping實現,那么RequestMappingHandlerMapping通過如下字段來保持映射關系的;

?

      private final Map<T, HandlerMethod> handlerMethods = new LinkedHashMap<T, HandlerMethod>(); //RequestMappingInfo--->HandlerMethod(保持了controllerBean method)
private final MultiValueMap<String, T> urlMap = new LinkedMultiValueMap<String, T>(); //url--->RequestMappingInfo

    

因此如果你刷新了腳本,相當于又創建了一個新的controllerBean,因此拿著的是老的controllerBean和Methond(來的controllerBean類的),而當我們調用時會把Method最終綁定到新的controllerBean類上,所以會得到如下異常:

?

寫道
java.lang.ClassCastException: com.sishuok.spring.controller.GroovyController cannot be cast to com.sishuok.spring.controller.GroovyController
at com.sishuok.spring.controller.GroovyController$$FastClassByCGLIB$$bb52fd90.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:713)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:133)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:121)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:646)
at com.sishuok.spring.controller.GroovyController$$EnhancerByCGLIB$$5c30e5e0.hello(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:214)

"GroovyController cannot be cast to?GroovyController",類名一樣,那就是ClassLoader不一樣了,即刷新腳本時又加載了一個GroovyController類。由于Spring mvc實現機制的問題,無法通過框架本身解決,也就是說動態刷新的Groovy腳本不能用作控制器;具體原因請參考: https://jira.springsource.org/browse/SPR-5749 ;怎么辦呢?想到一個辦法就是在反射調用Method之前把老的controllerBean類替換為新的controllerBean類即可:通過修改ScriptFactoryPostProcessor的postProcessBeforeInstantiation方法中調用的createRefreshableProxy方法:為proxyFactory添加一個增強:proxyFactory.addAdvice(new ScriptReplaceClassInfoMethodInterceptor()):

          @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        boolean isCglibMi = mi.getClass().getName().equals("org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation");
        if (isCglibMi && mi.getMethod().getDeclaringClass() != mi.getThis().getClass()) {
            MethodProxy methodProxy = (MethodProxy) ReflectionUtils.getField(methodProxyField, mi);
            Object fastClassInfo = ReflectionUtils.getField(fastClassInfoField, methodProxy);
            ReflectionUtils.setField(fastClassInfoF1Field, fastClassInfo, FastClass.create(mi.getThis().getClass()));
        }
        return mi.proceed();
    }

    

該增強通過反射替換老的controllerBean類為新的controllerBean類即可,這也是沒有辦法的辦法 皺眉

?

4、如果你的Groovy Controller又有依賴注入,如@Autowired private UserController userController;又完蛋了,因為對于@Autowired注解是通過AutowiredAnnotationBeanPostProcessor實現,而其又緩存了注入信息;如果刷新了腳本就會得到如下異常:

寫道
java.lang.IllegalArgumentException: Can not set com.sishuok.spring.controller.UserController field com.sishuok.spring.controller.GroovyController.userController to com.sishuok.spring.controller.GroovyController

原因和之前的類似,因為AutowiredAnnotationBeanPostProcessor緩存了InjectionMetadata,即注入的元數據;而這些元數據又存儲了目標類、注入的字段/方法信息;所以會得到如上信息;只能通過Hack清除緩存信息了;通過重載RefreshableScriptTargetSource得到一個ReplaceAndRefreshableScriptTargetSource:然后在其刷新時調用的方法obtainFreshBean中調用removeInjectCache(beanFactory, beanName)清除注入元數據緩存即可完美工作了。

?

涉及的類:

DynamicDeployBeans2.java

ScriptFactoryPostProcessor.java?

ScriptReplaceClassInfoMethodInterceptor.java?

ReplaceAndRefreshableScriptTargetSource.java?

?

?

這種方式不推薦使用:

需要覆蓋重寫其ScriptFactoryPostProcessor,如果未來發生變化需要跟著維護;

如果在Groovy Controller里添加新的方法是無法注冊到RequestMappingHandlerMapping中的;還需要自己手工注冊一遍;

?

所以以上Hack意義不是特別大了,接下來再給大家另一種比較完美的方案。即完全自己定制注冊邏輯,不依賴于Spring相關的基礎組件:

?

DynamicDeployBeans.java

      dynamicDeployBeans.registerBean(DynamicService1.class); //注冊一般的Class類
dynamicDeployBeans.registerBean(DynamicService2.class); //注冊一般的Class類 注意DynamicService2依賴于DynamicService1

dynamicDeployBeans.registerController(DynamicController.class); //注冊一般的控制器(可以重復注冊)

dynamicDeployBeans2.registerGroovyController("classpath:com/sishuok/spring/dynamic/GroovyController.groovy"); //注冊Groovy Controller 注冊后根據scriptCheckInterval會定期檢查腳本有沒有更新
    

?

這種方式可以對控制器的動態修改提供更好的支持:

動態修改代碼;

動態增/刪/改方法,即可以刪除一個已有的映射,或者添加一個新的映射,不會拋出映射二義性錯誤;

依賴注入的支持。

?

具體請參考我的github

https://github.com/zhangkaitao/spring4-showcase/tree/master/spring-dynamic ?

?

如無必要請不要這樣用,請盡量考慮動態腳本語言/框架。

?

?

Spring動態部署Bean/Controller/Groovy Controller


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 欧美第一视频 | 香蕉视频在线观看免费国产婷婷 | 亚洲精品国偷拍自产在线观看 | 久碰香蕉精品视频在线观看 | 亚洲一区二区色 | 91视频观看 | 黄色成年在线观看 | 精品一区二区久久久久久久网站 | 日本国产最新一区二区三区 | 日韩在线播放网址 | 国产精品久久婷婷六月丁香 | 欧美一区二区三区在线播放 | 国产成人精品一区二区三在线观看 | 免费看污又色又爽又黄视频 | 免费欧美黄色 | 久久精品视频18 | 黄视频网站 | 日韩欧美色| 欧美日本一 | 在线不欧美 | 欧美成人精品一区二区三区 | 欧美日韩精品一区二区三区视频 | 久久久久久久av | 午夜小视频免费观看 | 国产亚洲精品高清在线 | 国产成人av在线 | 免费一级片观看 | 国产精品综合网 | 玖玖爱365 | 天天躁夜夜躁很很躁麻豆 | 成人一级黄色大片 | 国产熟妇另类久久久久XYZ | 免费在线国产视频 | 丁香六月婷婷在线 | 久草欧美 | 挑战者联盟第一季免费观看完整版 | 黄色影片在线免费观看 | 午夜资源站 | 精品久久中文久久久 | 成人日韩在线观看 | 一级做a爰片久久毛片看看 欧美日韩精品国产一区二区 |