検索
ホームページ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 のローカル スレッド ThreadLocal> オブジェクトに流し込んでいることがわかりますが、この型は分離されたスレッドであることがわかります。異なるスレッドには異なるデータがあり、各リクエストはスレッドであるため、必然的にデータの損失が発生するため、コンポーネントの初期化時にデータを入力したとしても、次のリクエストは依然として報告されます。

ソリューションのアイデア (実際には、これは解決策ではありませんが、高いパフォーマンスを犠牲にして実行することもできます):

リクエスト エントリのメソッドでリスナーとパブリッシャーを設計します。アスペクトの実行を実行します。処理中です。アスペクトは 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 サイトの他の関連記事を参照してください。

声明
この記事は亿速云で複製されています。侵害がある場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SecLists

SecLists

SecLists は、セキュリティ テスターの究極の相棒です。これは、セキュリティ評価中に頻繁に使用されるさまざまな種類のリストを 1 か所にまとめたものです。 SecLists は、セキュリティ テスターが必要とする可能性のあるすべてのリストを便利に提供することで、セキュリティ テストをより効率的かつ生産的にするのに役立ちます。リストの種類には、ユーザー名、パスワード、URL、ファジング ペイロード、機密データ パターン、Web シェルなどが含まれます。テスターはこのリポジトリを新しいテスト マシンにプルするだけで、必要なあらゆる種類のリストにアクセスできるようになります。

PhpStorm Mac バージョン

PhpStorm Mac バージョン

最新(2018.2.1)のプロフェッショナル向けPHP統合開発ツール

AtomエディタMac版ダウンロード

AtomエディタMac版ダウンロード

最も人気のあるオープンソースエディター

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強力な PHP 統合開発環境