ホームページ >Java >&#&チュートリアル >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方法无所影响...... }
不可解なのは、コントローラー コンポーネントの初期化メソッドのコードです。 ##
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 のローカル スレッド ThreadLocalb9ce0bbc1c208c036cee4db71a23ea6a> オブジェクトに流し込んでいることがわかりますが、この型は分離されたスレッドであることがわかります。異なるスレッドには異なるデータがあり、各リクエストはスレッドであるため、必然的にデータの損失が発生するため、コンポーネントの初期化時にデータを入力したとしても、次のリクエストは依然として報告されます。 ソリューションのアイデア (実際には、これは解決策ではありませんが、高いパフォーマンスを犠牲にして実行することもできます): リクエスト エントリのメソッドでリスナーとパブリッシャーを設計します。アスペクトの実行を実行します。処理中です。アスペクトは AppContext オブジェクト データをチェックします。それが空の場合は、イベントが発行されます。空でない場合は、メソッドを入力します: イベント プロトタイプ:
public class AppContextStatusEvent extends ApplicationEvent { public AppContextStatusEvent(Object source) { super(source); } public AppContextStatusEvent(Object source, Clock clock) { super(source, clock); } }Listener:
@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 ""; }pre-and で最初にチェックすることを見つけるのは難しくありません。アスペクトの拡張後のメソッド AppContext データの整合性を確認し、データを入力します。このように、すべてのリクエストメソッドに @CheckAppContextLogin のアノテーションを付ければ実現できますが、埋められたメソッド以外のデータの維持が困難であること、アスペクトハイジャックエージェントのコストが高すぎること、アスペクトハイジャックエージェントの頻度が高すぎることが問題です。データのチェックが高すぎます。 正解: 主に 2 つのオブジェクトの充填を実現するため、データのビジネス機能に応じて分割します。これらのデータが失われた場合でも、同じコントローラー コンポーネントのメンバー変数は保持されます。これらはすべて同じオブジェクトであり、初期化中にすべて初期化されるため、後続の切り替えリクエストはビジネスを実装する能力に影響を与えません。
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; }最後に、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 イベント・リスニング・インターフェースの使用の問題を解決する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。