Rumah >Java >javaTutorial >Bagaimana untuk menyelesaikan masalah menggunakan antara muka mendengar acara SpringBoot ApplicationListener

Bagaimana untuk menyelesaikan masalah menggunakan antara muka mendengar acara SpringBoot ApplicationListener

王林
王林ke hadapan
2023-05-26 10:13:151129semak imbas

Untuk menghasilkan semula masalah, lihat kod di bawah untuk melihat sama ada terdapat masalah dan cara menyelesaikannya:

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

Apa yang mungkin membingungkan ialah kod kaedah permulaan dalam komponen pengawal :

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

Di sini anda boleh mendapati bahawa sesetengah data tersuai projek sebenarnya dituangkan ke dalam utas tempatan ThreadLocalb9ce0bbc1c208c036cee4db71a23ea6a> objek konteks tersuai statik kami, tetapi kami tahu bahawa jenis ini ialah a utas Diasingkan, utas yang berbeza mempunyai data yang berbeza, dan setiap permintaan kami adalah urutan, yang pasti akan membawa kepada kehilangan data, jadi walaupun kami memasukkan data semasa komponen dimulakan, permintaan seterusnya akan tetap dilaporkan.

Idea penyelesaian (sebenarnya ini bukan penyelesaian, tetapi ia juga boleh dilakukan dengan kos prestasi tinggi):

Reka bentuk pendengar dan penerbit dalam kaedah kemasukan permintaan Laksanakan aspek pemprosesan. Aspek menyemak data objek AppContext Jika ia kosong, acara akan diterbitkan Jika ia tidak kosong, masukkan kaedah:

Prototaip acara:

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

Pendengar: <.>

@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();
            }
        }
    }
}

Penerbit (kelas aspek):

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

Kemudian kelas aspek AOP menangkap anotasi:

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

Tidak sukar untuk mencari bahawa kami menyemak dahulu dalam pra dan kaedah pasca peningkatan aspek integriti data AppContext, dan kemudian isikan data. Dengan cara ini, ia boleh dicapai jika kita memberi anotasi @CheckAppContextLogin pada setiap kaedah permintaan Walau bagaimanapun, masalahnya ialah data lain kecuali kaedah yang diisi terlalu sukar untuk diselenggara, kos ejen rampasan aspek terlalu tinggi, dan kekerapan bagi. menyemak data terlalu tinggi.

Penyelesaian yang betul:

Bahagikan mengikut fungsi perniagaan data, kerana ia merealisasikan pengisian dua objek Walaupun data ini hilang, pembolehubah ahli pengawal yang sama komponen Mereka semua adalah objek yang sama, dan mereka semua dimulakan semasa pemulaan, jadi permintaan penukaran berikutnya tidak akan menjejaskan keupayaan mereka untuk melaksanakan perniagaan:

 private static AuthRequest ding_request = null;
 private static RongCloud cloud_chat = null;

Kami boleh meminta bahagian hadapan memberikan kami pengguna semasa dalam pemintas Jenis pengguna dan pengecam unik digunakan untuk merangkum data tersuai pengguna bagi setiap permintaan (untuk mengurangkan operasi semakan perpustakaan rantai kaedah yang dipanggil dalam permintaan):

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

Akhir sekali, jangan lupa untuk mengalih keluar Rujukan ThreadLocal:

 @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        AppContext.clear();
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }

Jadi penyelesaian sebenar kepada senario sebenar adalah teras perniagaan, dan kesederhanaan kod hanyalah keperluan sampingan.

Atas ialah kandungan terperinci Bagaimana untuk menyelesaikan masalah menggunakan antara muka mendengar acara SpringBoot ApplicationListener. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam