最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot ApplicationListener事件監(jiān)聽接口使用問題探究

 更新時(shí)間:2023年04月03日 09:33:10   作者:ForestSpringH  
這篇文章主要介紹了SpringBoot ApplicationListener事件監(jiān)聽接口使用問題,自定義監(jiān)聽器需要實(shí)現(xiàn)ApplicationListener接口,實(shí)現(xiàn)對應(yīng)的方法來完成自己的業(yè)務(wù)邏輯。SpringBoot Application共支持6種事件監(jiān)聽

終日惶惶,不知?dú)w路;一日寫起代碼,突發(fā)奇想,若是在運(yùn)行時(shí)發(fā)現(xiàn)自定義上下文的數(shù)據(jù)丟失,我們該如何解決處理數(shù)據(jù)丟失的問題?

問題復(fù)現(xiàn)一下,大家看下面的代碼,觀察是否有問題,又該如何解決這個(gè)問題:

@RequestMapping("verify")
@RestController
@DependsOn({"DingAppInfoService","CloudChatAppInfoService"})
public class LoginAction {
    @Qualifier("ElderSonService")
    @Autowired
    private ElderSonService elderSonService;
    @Qualifier("EmployeeService")
    @Autowired
    private EmployeeService employeeService;
    @Qualifier("UserThreadPoolTaskExecutor")
    @Autowired
    private ThreadPoolTaskExecutor userThreadPoolTaskExecutor;
    private static AuthRequest ding_request = null;
    private static RongCloud cloud_chat = null;
    private static TokenResult register = null;
    private static final ThreadLocal<String> USER_TYPE = new ThreadLocal<>();
    /**
     * 注意不能在bean的生命周期方法上添注@CheckAppContext注解
     */
    @PostConstruct
    public void beforeVerifySetContext() {
        AppContext.fillLoginContext();
        Assert.hasText(AppContext.getAppLoginDingId(), "初始化app_login_ding_id錯誤");
        Assert.hasText(AppContext.getAppLoginDingSecret(), "初始化app_login_ding_secret錯誤");
        Assert.hasText(AppContext.getAppLoginReturnUrl(), "初始化app_login_return_url錯誤");
        Assert.hasText(AppContext.getCloudChatKey(), "初始化cloud_chat_key錯誤");
        Assert.hasText(AppContext.getCloudChatSecret(), "初始化cloud_chat_secret錯誤");
        if (!(StringUtils.hasText(AppContext.getCloudNetUri()) || StringUtils.hasText(AppContext.getCloudNetUriReserve()))) {
            throw new IllegalArgumentException("初始化cloud_net_uri與cloud_net_uri_reserve錯誤");
        }
        ding_request = new AuthDingTalkRequest(
                AuthConfig.builder().
                        clientId(AppContext.getAppLoginDingId()).
                        clientSecret(AppContext.getAppLoginDingSecret()).
                        redirectUri(AppContext.getAppLoginReturnUrl()).build());
        cloud_chat = RongCloud.getInstance(AppContext.getCloudChatKey(), AppContext.getCloudChatSecret());
    }
.....以下API方法無所影響......
}

其中可能令人不解的是controller組件里初始化方法的代碼:

    public static void fillLoginContext() {
        DingAppInfo appInfo = SpringContextHolder.getBean(DingAppInfoService.class).findAppInfo(APP_CODE);
        setDingVerifyInfo(appInfo);
        CloudChatAppInfo cloudChatAppInfo = SpringContextHolder.getBean(CloudChatAppInfoService.class).findAppInfo(APP_CODE);
        setCloudChatInfo(cloudChatAppInfo);
    }
   public static void setDingVerifyInfo(DingAppInfo dingAppInfo){
        if (dingAppInfo.checkKeyWordIsNotNull(dingAppInfo)) {
            put(APP_LOGIN_DING_ID, dingAppInfo.getApp_id());
            put(APP_LOGIN_DING_SECRET, dingAppInfo.getApp_secret());
            put(APP_LOGIN_RETURN_URL, dingAppInfo.getApp_return_url());
        }
    }
    public static void setCloudChatInfo(CloudChatAppInfo cloudChatAppInfo){
        if (cloudChatAppInfo.checkKeyWordIsNotNull(cloudChatAppInfo)){
            put(CLOUD_CHAT_KEY,cloudChatAppInfo.getCloud_key());
            put(CLOUD_CHAT_SECRET,cloudChatAppInfo.getCloud_secret());
            put(CLOUD_NET_URI,cloudChatAppInfo.getCloud_net_uri());
            put(CLOUD_NET_URI_RESERVE,cloudChatAppInfo.getCloud_net_uri_reserve());
        }
    }

這里可以發(fā)現(xiàn)其實(shí)就是將一些項(xiàng)目定制的數(shù)據(jù)灌入我們的靜態(tài)自定義上下文AppContext的本地線程ThreadLocal<Map<String,String>>對象中去,但是我們知道這個(gè)類型可是線程隔離的,不同的線程數(shù)據(jù)都不同,而我們的每一個(gè)請求都是一個(gè)線程,勢必會導(dǎo)致數(shù)據(jù)的丟失,所以我們就算是在組件初始化時(shí)將數(shù)據(jù)給進(jìn)去,下一個(gè)請求給進(jìn)來也是會報(bào)出異常的。

解決思路(實(shí)際上不是這么解決的,但是也可以這么做,代價(jià)是性能耗費(fèi)高):

設(shè)計(jì)一個(gè)監(jiān)聽者,一個(gè)發(fā)布者,在請求進(jìn)入的方法上進(jìn)行切面處理,切面檢查AppContext對象數(shù)據(jù),若為空則發(fā)布事件,不為空則進(jìn)入方法:

事件原型:

public class AppContextStatusEvent extends ApplicationEvent {
    public AppContextStatusEvent(Object source) {
        super(source);
    }
    public AppContextStatusEvent(Object source, Clock clock) {
        super(source, clock);
    }
}

監(jiān)聽者:

@Component
public class AppContextListener implements ApplicationListener<AppContextStatusEvent> {
    @Override
    public void onApplicationEvent(AppContextStatusEvent event) {
        if ("FillAppContext".equals(event.getSource())) {
            AppContext.fillLoginContext();
        } else if ("CheckAppContextLogin".equals(event.getSource())) {
            boolean checkContext = AppContext.checkLoginContext();
            if (!checkContext) {
                AppContext.fillLoginContext();
            }
        }
    }
}

發(fā)布者(切面類):

@Aspect
@Component("AppContextAopAutoSetting")
public class AppContextAopAutoSetting {
    @Before("@annotation(com.lww.live.ApplicationListener.CheckAppContextLogin)")
    public void CheckContextIsNull(JoinPoint joinPoint){
        System.out.println("-----------aop---------CheckAppContextLogin---------start-----");
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        boolean value = signature.getMethod().getAnnotation(CheckAppContextLogin.class).value();
        if (value){
            boolean checkContext = AppContext.checkLoginContext();
            if (!checkContext){
                SpringContextHolder.pushEvent(new AppContextStatusEvent("FillAppContext"));
            }
        }
    }
    @After("@annotation(com.lww.live.ApplicationListener.CheckAppContextLogin)")
    public void CheckContextIsNull(){
        System.out.println("-----------aop---------CheckAppContextLogin---------end-----");
        SpringContextHolder.pushEvent(new AppContextStatusEvent("CheckAppContextLogin"));
    }
}

那么AOP切面類捕獲的是注解:

@Inherited
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckAppContextLogin {
    boolean value() default false;
    String info() default "";
}

這里不難發(fā)現(xiàn)我們在切面的前置與后置增強(qiáng)方法里都是先檢查AppContext數(shù)據(jù)的完整性,再進(jìn)行填充數(shù)據(jù)。這樣如果我們每一個(gè)請求方法都打上注解@CheckAppContextLogin也可以實(shí)現(xiàn),但是問題是除填充的方法外其他的數(shù)據(jù)太難維護(hù)且切面劫持代理的代價(jià)太高,檢查數(shù)據(jù)的頻率太高。

正確的解決方案:

根據(jù)數(shù)據(jù)的業(yè)務(wù)功能劃分,因?yàn)橹饕菍?shí)現(xiàn)兩個(gè)對象的填充,哪怕這幾個(gè)數(shù)據(jù)丟失了,但是同一個(gè)controller組件的成員變量都是同一個(gè)對象,且都在初始化的時(shí)候進(jìn)行了初始化,故后續(xù)切換請求了也不影響它們實(shí)現(xiàn)業(yè)務(wù)的能力:

 private static AuthRequest ding_request = null;
 private static RongCloud cloud_chat = null;

我們可以在攔截器中要求前端給我們傳遞當(dāng)前用戶的用戶類型與唯一標(biāo)識,來進(jìn)行每一次請求的用戶定制數(shù)據(jù)的封裝(減少請求內(nèi)調(diào)用方法鏈查庫操作):

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = (String) request.getSession().getAttribute("token");
        String user_type = (String) request.getSession().getAttribute("user_type");
        if (StringUtils.hasText(token) && StringUtils.hasText(user_type)) {
            Context context = new Context();
            if (Objects.equals(user_type, "elder_son")) {
                ElderSon elderSon = elderSonService.getElderSonByElderSonId(token);
                context.setContextByElderSon(elderSon);
                return true;
            } else if (Objects.equals(user_type, "employee")) {
                Employee employee = employeeService.getEmployeeById(token);
                context.setContextByEmployee(employee);
                return true;
            }
        } else if (StringUtils.hasText(user_type)) {
            response.sendRedirect("/verify/login?user_type=" + user_type);
            return false;
        }
        return false;
    }

最后千萬不要忘記remove一下ThreadLocal的引用:

 @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        AppContext.clear();
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }

所以實(shí)際場景實(shí)際解決,核心是業(yè)務(wù),代碼簡潔只是附帶的要求。

到此這篇關(guān)于SpringBoot ApplicationListener事件監(jiān)聽接口使用問題探究的文章就介紹到這了,更多相關(guān)SpringBoot ApplicationListener內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java中對象為null時(shí)的打印輸出方式

    java中對象為null時(shí)的打印輸出方式

    這篇文章主要介紹了java中對象為null時(shí)的打印輸出方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • java單例模式實(shí)現(xiàn)面板切換

    java單例模式實(shí)現(xiàn)面板切換

    這篇文章主要為大家詳細(xì)介紹了java單例模式實(shí)現(xiàn)面板切換,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • JAVA多線程搶紅包的實(shí)現(xiàn)示例

    JAVA多線程搶紅包的實(shí)現(xiàn)示例

    這篇文章主要介紹了JAVA多線程搶紅包的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • Java多線程生產(chǎn)者消費(fèi)者模式實(shí)現(xiàn)過程解析

    Java多線程生產(chǎn)者消費(fèi)者模式實(shí)現(xiàn)過程解析

    這篇文章主要介紹了Java多線程生產(chǎn)者消費(fèi)者模式實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • Java CharacterEncodingFilter案例詳解

    Java CharacterEncodingFilter案例詳解

    這篇文章主要介紹了Java CharacterEncodingFilter案例詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • IDEA高效使用設(shè)置指南

    IDEA高效使用設(shè)置指南

    本文主要為大家介紹了關(guān)于IDEA高效的設(shè)置指南,其中包含必備的一些插件推薦以及主題優(yōu)化還有IDEA源碼的閱讀技巧,干貨滿滿,有需要的朋友可以借鑒參考下
    2022-01-01
  • SpringBoot+thymeleaf+Echarts+Mysql 實(shí)現(xiàn)數(shù)據(jù)可視化讀取的示例

    SpringBoot+thymeleaf+Echarts+Mysql 實(shí)現(xiàn)數(shù)據(jù)可視化讀取的示例

    本文主要介紹了SpringBoot+thymeleaf+Echarts+Mysql 實(shí)現(xiàn)數(shù)據(jù)可視化讀取的示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • SpringCloudAlibaba Nacos開啟鑒權(quán)解決跳過登錄頁面問題

    SpringCloudAlibaba Nacos開啟鑒權(quán)解決跳過登錄頁面問題

    對于Nacos,如果需要開啟權(quán)限控制,可以在 Nacos 控制臺上進(jìn)行配置,本文主要介紹了SpringCloudAlibaba Nacos開啟鑒權(quán)解決跳過登錄頁面問題,感興趣的可以了解一下
    2023-10-10
  • 記一次在idea離線使用maven問題(推薦)

    記一次在idea離線使用maven問題(推薦)

    這篇文章主要介紹了記一次在idea離線使用maven問題,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • 淺析Java 反射機(jī)制的用途和缺點(diǎn)

    淺析Java 反射機(jī)制的用途和缺點(diǎn)

    這篇文章給大家分析了Java 反射機(jī)制的用途和缺點(diǎn)以及相關(guān)知識點(diǎn)內(nèi)容,有興趣的朋友可以參考學(xué)習(xí)下。
    2018-07-07

最新評論

林口县| 石嘴山市| 资源县| 秦皇岛市| 威海市| 玉山县| 武穴市| 舒兰市| 潼南县| 谷城县| 太仓市| 乌恰县| 舒城县| 平陆县| 北川| 鄂州市| 杭锦后旗| 保山市| 慈溪市| 镇雄县| 岱山县| 长治市| 东方市| 定州市| 五河县| 兴文县| 屏东市| 黎川县| 射洪县| 鲜城| 龙游县| 神农架林区| 西贡区| 绍兴市| 来宾市| 萨迦县| 扶余县| 鹰潭市| 汝阳县| 上林县| 新津县|