Maison > Java > javaDidacticiel > Comment résoudre le problème de l'utilisation de l'interface d'écoute des événements SpringBoot ApplicationListener

Comment résoudre le problème de l'utilisation de l'interface d'écoute des événements SpringBoot ApplicationListener

王林
Libérer: 2023-05-26 10:13:15
avant
1158 Les gens l'ont consulté

Reproduisons le problème. Regardez le code ci-dessous pour voir s'il y a un problème et comment le résoudre :

@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方法无所影响......
}
Copier après la connexion

Ce qui peut être déroutant, c'est la méthode d'initialisation dans le code du composant du contrôleur :

    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());
        }
    }
Copier après la connexion

Vous pouvez trouver ici que le fait de verser certaines données personnalisées du projet dans le thread local ThreadLocal> -isolés, différents threads ont des données différentes, et chacune de nos requêtes est un thread, ce qui entraînera inévitablement une perte de données, donc même si nous mettons les données lors de l'initialisation du composant, la requête suivante sera une exception sera signalée lorsque entrant.

Solution (en fait ce n'est pas la solution, mais cela peut aussi se faire au détriment de hautes performances) :

Concevoir un auditeur et un éditeur, dans la requête Perform traitement de l'aspect sur la méthode de saisie. L'aspect vérifie les données de l'objet AppContext. S'il est vide, l'événement est publié. S'il n'est pas vide, la méthode est saisie :

Event prototype : #🎜🎜. #

public class AppContextStatusEvent extends ApplicationEvent {
    public AppContextStatusEvent(Object source) {
        super(source);
    }
    public AppContextStatusEvent(Object source, Clock clock) {
        super(source, clock);
    }
}
Copier après la connexion

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();
            }
        }
    }
}
Copier après la connexion

Publisher (classe d'aspect) :

@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"));
    }
}
Copier après la connexion

Ensuite, la classe d'aspect AOP capture l'annotation :

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

Il n'est pas difficile de constater que nous sommes ici. Dans les méthodes de pré- et post-amélioration des aspects, l'intégrité des données AppContext est d'abord vérifiée, puis les données sont remplies. De cette façon, cela peut être réalisé si nous annotons @CheckAppContextLogin sur chaque méthode de requête. Cependant, le problème est que les autres données, à l'exception des méthodes remplies, sont trop difficiles à conserver, le coût des agents de détournement d'aspect est trop élevé et la fréquence de leur traitement. la vérification des données est trop élevée.

Solution correcte :

Réparties selon les fonctions métier des données, car le but principal est de réaliser le remplissage de deux objets, même si ces données sont perdues, mais le même contrôleur Les variables membres des composants sont toutes le même objet, et elles sont toutes initialisées lors de l'initialisation, donc les demandes de changement ultérieures n'affecteront pas leur capacité à mettre en œuvre les affaires :

 private static AuthRequest ding_request = null;
 private static RongCloud cloud_chat = null;
Copier après la connexion

Nous pouvons le demander dans l'intercepteur Le front-end nous transmet le type d'utilisateur et l'identifiant unique de l'utilisateur actuel pour encapsuler les données personnalisées par l'utilisateur pour chaque requête (afin de réduire l'opération de requête en chaîne de méthodes dans la requête) :

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;
    }
Copier après la connexion

Enfin , n'oubliez pas Supprimez la référence à ThreadLocal :

 @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        AppContext.clear();
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
Copier après la connexion
Donc, le scénario réel est réellement résolu, le cœur est l'activité et la simplicité du code n'est qu'une exigence accessoire.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:yisu.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal