91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

SpringBoot?ApplicationListener事件監聽接口使用問題怎么解決

發布時間:2023-04-04 11:39:25 來源:億速云 閱讀:246 作者:iii 欄目:開發技術

這篇文章主要介紹“SpringBoot ApplicationListener事件監聽接口使用問題怎么解決”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“SpringBoot ApplicationListener事件監聽接口使用問題怎么解決”文章能幫助大家解決問題。

問題復現一下,大家看下面的代碼,觀察是否有問題,又該如何解決這個問題:

@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());
        }
    }

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

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

設計一個監聽者,一個發布者,在請求進入的方法上進行切面處理,切面檢查AppContext對象數據,若為空則發布事件,不為空則進入方法:

事件原型:

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

監聽者:

@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();
            }
        }
    }
}

發布者(切面類):

@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 "";
}

這里不難發現我們在切面的前置與后置增強方法里都是先檢查AppContext數據的完整性,再進行填充數據。這樣如果我們每一個請求方法都打上注解@CheckAppContextLogin也可以實現,但是問題是除填充的方法外其他的數據太難維護且切面劫持代理的代價太高,檢查數據的頻率太高。

正確的解決方案:

根據數據的業務功能劃分,因為主要是實現兩個對象的填充,哪怕這幾個數據丟失了,但是同一個controller組件的成員變量都是同一個對象,且都在初始化的時候進行了初始化,故后續切換請求了也不影響它們實現業務的能力:

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

我們可以在攔截器中要求前端給我們傳遞當前用戶的用戶類型與唯一標識,來進行每一次請求的用戶定制數據的封裝(減少請求內調用方法鏈查庫操作):

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);
    }

所以實際場景實際解決,核心是業務,代碼簡潔只是附帶的要求。

關于“SpringBoot ApplicationListener事件監聽接口使用問題怎么解決”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

茂名市| 南靖县| 师宗县| 儋州市| 沙田区| 玛多县| 从化市| 滦南县| 东乡| 古蔺县| 普兰店市| 马关县| 大化| 苍山县| 房产| 游戏| 永州市| 平安县| 克什克腾旗| 江油市| 呼和浩特市| 长顺县| 石嘴山市| 东丰县| 冕宁县| 凌海市| 北海市| 泰和县| 云阳县| 郑州市| 瓦房店市| 古田县| 冀州市| 朝阳市| 永寿县| 祁东县| 横峰县| 囊谦县| 溧阳市| 沅陵县| 扎鲁特旗|