iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >SpringBootApplicationListener事件监听接口使用问题探究
  • 178
分享到

SpringBootApplicationListener事件监听接口使用问题探究

SpringBootApplicationListenerSpringBoot事件监听接口 2023-05-14 08:05:32 178人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

终日惶惶,不知归路;一日写起代码,突发奇想,若是在运行时发现自定义上下文的数据丢失,我们该如何解决处理数据丢失的问题? 问题复现一下,大家看下面的代码,观察是否有问题,又该如何解决这

终日惶惶,不知归路;一日写起代码,突发奇想,若是在运行时发现自定义上下文的数据丢失,我们该如何解决处理数据丢失的问题?

问题复现一下,大家看下面的代码,观察是否有问题,又该如何解决这个问题:

@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<>();
    
    @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方法无所影响......
}

其中可能令人不解的是controller组件里初始化方法的代码:

    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<Map<String,String>>对象中去,但是我们知道这个类型可是线程隔离的,不同的线程数据都不同,而我们的每一个请求都是一个线程,势必会导致数据的丢失,所以我们就算是在组件初始化时将数据给进去,下一个请求给进来也是会报出异常的。

解决思路(实际上不是这么解决的,但是也可以这么做,代价是性能耗费高):

设计一个监听者,一个发布者,在请求进入的方法上进行切面处理,切面检查AppContext对象数据,若为空则发布事件,不为空则进入方法:

事件原型:

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

监听者:

@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 "";
}

这里不难发现我们在切面的前置与后置增强方法里都是先检查AppContext数据的完整性,再进行填充数据。这样如果我们每一个请求方法都打上注解@CheckAppContextLogin也可以实现,但是问题是除填充的方法外其他的数据太难维护且切面劫持代理的代价太高,检查数据的频率太高。

正确的解决方案:

根据数据的业务功能划分,因为主要是实现两个对象的填充,哪怕这几个数据丢失了,但是同一个controller组件的成员变量都是同一个对象,且都在初始化的时候进行了初始化,故后续切换请求了也不影响它们实现业务的能力:

 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;
    }

最后千万不要忘记remove一下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事件监听接口使用问题探究的文章就介绍到这了,更多相关SpringBoot ApplicationListener内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: SpringBootApplicationListener事件监听接口使用问题探究

本文链接: https://www.lsjlt.com/news/202897.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • SpringBootApplicationListener事件监听接口使用问题探究
    终日惶惶,不知归路;一日写起代码,突发奇想,若是在运行时发现自定义上下文的数据丢失,我们该如何解决处理数据丢失的问题? 问题复现一下,大家看下面的代码,观察是否有问题,又该如何解决这...
    99+
    2023-05-14
    SpringBoot ApplicationListener SpringBoot事件监听接口
  • SpringBoot ApplicationListener事件监听接口使用问题怎么解决
    这篇文章主要介绍“SpringBoot ApplicationListener事件监听接口使用问题怎么解决”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“SpringBoot Ap...
    99+
    2023-07-05
  • 使用Spring怎么监听事件
    使用Spring怎么监听事件?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。一、观察者模式先来看下观察者模式,举个例子警察和军人是观察者,犯罪嫌疑人是被观察者代码实现:定义被观察...
    99+
    2023-06-15
  • vue中的事件触发(emit)及监听(on)问题
    目录vue事件触发(emit)及监听(on)每个 vue 实例都实现了事件接口案例vue emit事件无法触发问题vue事件触发(emit)及监听(on) 每个 vue 实例都实现了...
    99+
    2024-04-02
  • springboot事件监听器怎么使用
    本篇内容介绍了“springboot事件监听器怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!引导案例下面看一个简单的案例,@Conf...
    99+
    2023-07-02
  • 关于vue路由监听事件跳转的问题
    目录vue路由监听事件跳转1.监听路由触发事件的语法2.可能遇到的问题vue路由监听不到怎么办方法一方法二方法三vue路由监听事件跳转 1.监听路由触发事件的语法  watch: {...
    99+
    2022-11-13
    vue路由监听 vue路由跳转 vue路由监听事件
  • vue中监听scroll事件失效的问题及解决
    目录vue监听scroll事件失效问题下面附实现成功的代码vue监听scroll事件vue监听scroll事件失效问题 vue项目中遇到需要监听页面某个元素距顶部距离实现吸顶效果,正...
    99+
    2024-04-02
  • Vue的addEventListener()监听事件怎么使用
    本篇内容介绍了“Vue的addEventListener()监听事件怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、语法eleme...
    99+
    2023-07-04
  • vue如何使用监听事件 v-on
    这篇文章主要介绍了vue如何使用监听事件 v-on,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。代码:问题:如何将button和counte...
    99+
    2024-04-02
  • 探究实现Aware接口的原理及使用
    目录前言设计&实现Aware 感知接口提供具体能力的接口测试前言 spring 对bean的创建过程做了很完整的封装。但是提供了非常多的扩展接口,供我们使用。这一节主要是实现...
    99+
    2023-05-18
    Aware接口原理使用 Aware接口实现
  • 详解Vue3中Watch监听事件的使用
    目录一、watch的使用1、引入watch2、多个数据源监听3、监听数组变化4、侦听对象5、结论一、watch的使用 watch(WatcherSource, Callback, [...
    99+
    2023-02-10
    Vue3 Watch监听事件使用 Vue3 Watch监听 Vue3 Watch
  • 微信小程序使用picker组件出现的问题探究
    目录一、picker基本概念二、遇到的问题三、如何解决四、延伸五、效果图一、picker基本概念 先来看一下官方文档中picker的基本概念: 从底部弹起的滚动选择器,现支持三种选择...
    99+
    2023-01-12
    小程序picker组件 小程序picker 微信小程序picker
  • 使用layui监听器监听select下拉框,事件绑定不成功怎么办
    这篇文章主要为大家展示了“使用layui监听器监听select下拉框,事件绑定不成功怎么办”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“使用layui监听器监听...
    99+
    2024-04-02
  • thinkphp的事件绑定、监听和订阅怎么使用
    这篇文章主要介绍了thinkphp的事件绑定、监听和订阅怎么使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇thinkphp的事件绑定、监听和订阅怎么使用文章都会有所收获,下面我们一起来看看吧。事件是什么事件...
    99+
    2023-06-30
  • vue中对监听esc事件和退出全屏问题的解决方案
    目录对监听esc事件和退出全屏问题的解决下面是全屏的完整代码element+vue全屏与退出全屏(监听ESC改样式)一、效果二、代码对监听esc事件和退出全屏问题的解决 vue 的项...
    99+
    2022-11-13
    vue监听 监听esc事件 vue退出全屏
  • Spring事件监听机制主要涉及到的核心类和接口有哪些
    这篇文章主要介绍“Spring事件监听机制主要涉及到的核心类和接口有哪些”,在日常操作中,相信很多人在Spring事件监听机制主要涉及到的核心类和接口有哪些问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Spr...
    99+
    2023-06-15
  • 探究Python并发编程中使用接口优化的最佳实践。
    Python并发编程是一种以提高程序运行效率为目标的编程方式,因为它可以让程序同时处理多个任务。然而,并发编程也带来了一些问题,例如线程安全、死锁等。为了解决这些问题,我们可以使用接口优化并发编程。本文将介绍Python并发编程中使用接口优...
    99+
    2023-05-26
  • vue使用$emit时父组件无法监听到子组件事件怎么办
    这篇文章主要介绍vue使用$emit时父组件无法监听到子组件事件怎么办,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!vue使用$emit时,父组件无法监听到子组件的事件的原因是$em...
    99+
    2024-04-02
  • Java Web3J :使用web3j监听、查询、订阅智能合约的事件
    前面有文章写如何使用Docker-compose方式部署blockscout浏览器+charts图表,区块链浏览器已经部署成功了,同时我们在链上增加了治理投票流程,如何实时的把治理事件快速同步到浏...
    99+
    2023-09-10
    java web3 智能合约 websocket 笔记 经验分享 maven
  • vue使用element-ui的el-input监听不了回车事件怎么办
    小编给大家分享一下vue使用element-ui的el-input监听不了回车事件怎么办,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作