Heim >Java >javaLernprogramm >So lösen Sie das Problem der Verwendung der SpringBoot ApplicationListener-Ereignisüberwachungsschnittstelle
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方法无所影响...... }
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()); } }
Hier finden Sie, dass tatsächlich einige vom Projekt angepasste Daten in den lokalen Thread ThreadLocalb9ce0bbc1c208c036cee4db71a23ea6a>-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); } }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(); } } } }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")); } }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 ""; }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;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; }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); }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!