Heim > Java > javaLernprogramm > So lösen Sie das Problem der Verwendung der SpringBoot ApplicationListener-Ereignisüberwachungsschnittstelle

So lösen Sie das Problem der Verwendung der SpringBoot ApplicationListener-Ereignisüberwachungsschnittstelle

王林
Freigeben: 2023-05-26 10:13:15
nach vorne
1132 Leute haben es durchsucht

Sehen wir uns den folgenden Code an, um zu sehen, ob ein Problem vorliegt und wie man es löst:

@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方法无所影响......
}
Nach dem Login kopieren

Was möglicherweise rätselhaft ist, ist die Initialisierungsmethode im Code der Controller-Komponente:

    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());
        }
    }
Nach dem Login kopieren

Hier finden Sie, dass tatsächlich einige vom Projekt angepasste Daten in den lokalen Thread ThreadLocal>-Objekt unseres statischen benutzerdefinierten Kontexts AppContext gegossen werden, aber wir wissen, dass es sich bei diesem Typ um einen Thread handelt -Isoliert, verschiedene Threads haben unterschiedliche Daten, und jede unserer Anforderungen ist ein Thread, was unweigerlich zu Datenverlust führt. Selbst wenn wir die Daten bei der Initialisierung der Komponente eingeben, wird die nächste Anforderung gemeldet. Eine Ausnahme wird gemeldet eintreten.

Lösung (eigentlich ist dies nicht die Lösung, kann aber auch auf Kosten einer hohen Leistung erfolgen):

Entwerfen Sie einen Listener und einen Herausgeber in der Anforderung. Ausführen Aspektverarbeitung für die Eingabemethode. Wenn es leer ist, wird das Ereignis veröffentlicht. Wenn es nicht leer ist, wird die Methode eingegeben: #🎜🎜 #

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

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();
            }
        }
    }
}
Nach dem Login kopieren

Publisher (Aspektklasse):

@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"));
    }
}
Nach dem Login kopieren

Dann erfasst die AOP-Aspektklasse die Anmerkung:

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

Es ist nicht schwer zu erkennen, dass wir hier sind. Bei den Methoden vor und nach der Erweiterung von Aspekten wird zunächst die Integrität der AppContext-Daten überprüft und dann die Daten gefüllt. Auf diese Weise kann dies erreicht werden, wenn wir @CheckAppContextLogin für jede Anforderungsmethode annotieren. Das Problem besteht jedoch darin, dass andere Daten außer den ausgefüllten Methoden zu schwierig zu verwalten sind, die Kosten für Aspekt-Hijacking-Agenten zu hoch sind und die Häufigkeit von Prüfdaten sind zu hoch.

Richtige Lösung:

Unterteilt nach den Geschäftsfunktionen der Daten, da der Hauptzweck darin besteht, das Füllen von zwei Objekten zu erreichen, auch wenn diese Daten verloren gehen, aber Derselbe Controller Die Mitgliedsvariablen der Komponenten sind alle dasselbe Objekt und werden alle während der Initialisierung initialisiert, sodass nachfolgende Wechselanforderungen ihre Fähigkeit zur Geschäftsimplementierung nicht beeinträchtigen:

 private static AuthRequest ding_request = null;
 private static RongCloud cloud_chat = null;
Nach dem Login kopieren

Wir können es anfordern Der Interceptor Das Frontend übergibt uns den Benutzertyp und die eindeutige Kennung des aktuellen Benutzers, um die benutzerspezifischen Daten für jede Anfrage zu kapseln (um den Abfragevorgang der Methodenkette innerhalb der Anfrage zu reduzieren):

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;
    }
Nach dem Login kopieren

Endlich Vergessen Sie nicht, den Verweis auf ThreadLocal zu entfernen:

 @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        AppContext.clear();
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
Nach dem Login kopieren
Das eigentliche Szenario ist also tatsächlich gelöst, der Kern ist das Geschäft und die Einfachheit des Codes ist nur eine nebensächliche Anforderung.

Das obige ist der detaillierte Inhalt vonSo lösen Sie das Problem der Verwendung der SpringBoot ApplicationListener-Ereignisüberwachungsschnittstelle. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:yisu.com
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage