在spring中使用自定義注解注冊監(jiān)聽器的方法
接口回調(diào)
監(jiān)聽器本質(zhì)上就是利用回調(diào)機(jī)制,在某個動作發(fā)生前或后,執(zhí)行我們自己的一些代碼。在Java語言中,可以使用接口來實現(xiàn)。
實現(xiàn)一個監(jiān)聽器案例
為了方便,直接在spring環(huán)境中定義:以工作(work)為例,定義工作開始時(或結(jié)束時)的監(jiān)聽器。
1. 定義回調(diào)的接口
package com.yawn.demo.listener; /** * @author Created by yawn on 2018-01-21 13:53 */ public interface WorkListener { void onStart(String name); }
2. 定義動作
package com.yawn.demo.service; import com.yawn.demo.listener.WorkListener; /** * @author Created by yawn on 2018-01-21 13:39 */ @Service public class MyService { @Resource private PersonService personService; private WorkListener listener; public void setWorkListener(WorkListener workListener) { this.listener = workListener; } public void work(String name) { listener.onStart(name); personService.work(); } }
動作work為一個具體的方法,在work()方法的適當(dāng)時機(jī),調(diào)用前面定義的接口。此外,在這個動作定義類中,需要提高設(shè)置監(jiān)聽器的方法。
3. 監(jiān)聽測試
@RunWith(SpringRunner.class) @SpringBootTest public class DemoSpringAnnotationApplicationTests { @Resource private MyService myService; @Test public void test1() { // 接口設(shè)置監(jiān)聽器 myService.setWorkListener(new WorkListener() { @Override public void onStart(String name) { System.out.println("Start work for " + name + " !"); } }); // // lambda 表達(dá)式設(shè)置監(jiān)聽器 // myService.setWorkListener(name -> System.out.println("Start work for " + name + " !")); // 工作 myService.work("boss"); } @Test public void test2() { // 繼承實現(xiàn)類設(shè)置監(jiān)聽器 myService.setWorkListener(new myWorkListener()); // 工作 myService.work("boss"); } class myWorkListener extends WorkListenerAdaptor { @Override public void onStart(String name) { System.out.println("Start work for " + name + " !"); } } }
使用以上兩種方法測試,得到了結(jié)果為:
Start work for boss ! working hard ...
說明在動作work發(fā)生之前,執(zhí)行了我們在測試類中寫下的監(jiān)聽代碼,實現(xiàn)類監(jiān)聽的目的。
使用注解實現(xiàn)監(jiān)聽器
在以上代碼中,調(diào)用 setWorkListener(WorkListener listener) 方法一般稱作設(shè)置(注冊)監(jiān)聽器,就是將自己寫好的監(jiān)聽代碼,設(shè)置為動作的監(jiān)聽器。然而,在每次注冊監(jiān)聽器時,一般需要寫一個類,實現(xiàn)定義好的接口或繼承實現(xiàn)接口的類,再重寫接口定義的方法即可。因此,聰明的程序員就想簡化這個過程,所以就想出了使用注解的方法。使用注解,將監(jiān)聽代碼段寫在一個方法中,使用一個注解標(biāo)記這個方法即可。
的確,使用變得簡單了,但實現(xiàn)卻不見得。
1. 定義一個注解
package com.yawn.demo.anno; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface WorkListener { }
2. 解析注解
package com.yawn.demo.anno; import com.yawn.demo.service.MyService; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.LinkedHashMap; import java.util.Map; /** * @author Created by yawn on 2018-01-21 14:46 */ @Component public class WorkListenerParser implements ApplicationContextAware, InitializingBean { @Resource private MyService myService; private ApplicationContext applicationContext; @Override public void afterPropertiesSet() throws Exception { Map<String, Object> listenerBeans = getExpectListenerBeans(Controller.class, RestController.class, Service.class, Component.class); for (Object listener : listenerBeans.values()) { for (Method method : listener.getClass().getDeclaredMethods()) { if (!method.isAnnotationPresent(WorkListener.class)) { continue; } myService.setWorkListener(name -> { try { method.invoke(listener, name); } catch (Exception e) { e.printStackTrace(); } }); } } } /** * 找到有可能使用注解的bean * @param annotationTypes 需要進(jìn)行掃描的類級注解類型 * @return 掃描到的beans的map */ private Map<String, Object> getExpectListenerBeans(Class<? extends Annotation>... annotationTypes) { Map<String, Object> listenerBeans = new LinkedHashMap<>(); for (Class<? extends Annotation> annotationType : annotationTypes) { Map<String, Object> annotatedBeansMap = applicationContext.getBeansWithAnnotation(annotationType); listenerBeans.putAll(annotatedBeansMap); } return listenerBeans; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }
在注解的解析過程中,設(shè)置監(jiān)聽器。
在解析類中,實現(xiàn)了接口ApplicationContextAware,為了在類中拿到ApplicationContext的引用,用于得到 IOC 容器中的 Bean;而實現(xiàn)接口InitializingBean,則是為了在一個合適的時機(jī)執(zhí)行解析注解、設(shè)置監(jiān)聽器的代碼。 如果不這樣做,可以在CommandLineRunner執(zhí)行時調(diào)用解析、設(shè)置的代碼,而ApplicationContext也可以自動注入。
3. 測試
在執(zhí)行完以上代碼后,監(jiān)聽器就已經(jīng)設(shè)置好了,可以進(jìn)行測試了。
package com.yawn.demo.controller; import com.yawn.demo.anno.WorkListener; import com.yawn.demo.service.MyService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; /** * @author Created by yawn on 2018-01-21 13:28 */ @RestController public class TestController { @Resource private MyService myService; @GetMapping("/work") public Object work() { myService.work("boss"); return "done"; } @WorkListener public void listen(String name) { System.out.println("Start work for " + name + " !"); } }
寫一個監(jiān)聽方法,參數(shù)類型和個數(shù)與接口相同,然后加上自定義的注解即可。當(dāng)啟動環(huán)境后,監(jiān)聽器就已經(jīng)設(shè)置好了。
然后通過url調(diào)用myService的work()方法,可以看到結(jié)果:
Start work for boss ! working hard ...
已經(jīng)調(diào)用了監(jiān)聽方法。在接下來的開發(fā)中,就可以使用這個注解注冊監(jiān)聽器了。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Spring boot通過HttpSessionListener監(jiān)聽器統(tǒng)計在線人數(shù)的實現(xiàn)代碼
- springboot 用監(jiān)聽器統(tǒng)計在線人數(shù)案例分析
- Spring Boot的listener(監(jiān)聽器)簡單使用實例詳解
- SpringBoot定義過濾器、監(jiān)聽器、攔截器的方法
- 監(jiān)聽器獲取Spring配置文件的方法
- Spring Boot中配置定時任務(wù)、線程池與多線程池執(zhí)行的方法
- springboot schedule 解決定時任務(wù)不執(zhí)行的問題
- spring-boot 多線程并發(fā)定時任務(wù)的解決方案
- Spring監(jiān)聽器及定時任務(wù)實現(xiàn)方法詳解
相關(guān)文章
Java 實現(xiàn)倒計時功能(由秒計算天、小時、分鐘、秒)
最近做項目遇到這樣的需求,天、小時、分鐘、秒的數(shù)值都是隔開的,服務(wù)器端只返回一個時間戳長度,怎么實現(xiàn)這樣的功能呢?下面小編給大家?guī)砹薐ava 實現(xiàn)倒計時功能的方案,需要的朋友參考下吧2018-01-01Java實現(xiàn)監(jiān)控多個線程狀態(tài)的簡單實例
下面小編就為大家?guī)硪黄狫ava實現(xiàn)監(jiān)控多個線程狀態(tài)的簡單實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-03-03SpringBoot自動配置特點(diǎn)與原理詳細(xì)分析
這篇文章主要介紹了SpringBoot自動配置原理分析,SpringBoot是我們經(jīng)常使用的框架,那么你能不能針對SpringBoot實現(xiàn)自動配置做一個詳細(xì)的介紹。如果可以的話,能不能畫一下實現(xiàn)自動配置的流程圖。牽扯到哪些關(guān)鍵類,以及哪些關(guān)鍵點(diǎn)2022-08-08理解 MyBatis 是如何在 Spring 容器中初始化的
這篇文章主要介紹了理解 MyBatis 是如何在 Spring 容器中初始化的,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11java開發(fā)gui教程之jframe監(jiān)聽窗體大小變化事件和jframe創(chuàng)建窗體
這篇文章主要介紹了java開發(fā)gui教程中jframe監(jiān)聽窗體大小變化事件和jframe創(chuàng)建窗體的示例,需要的朋友可以參考下2014-03-03java 較大數(shù)據(jù)量取差集,list.removeAll性能優(yōu)化詳解
這篇文章主要介紹了java 較大數(shù)據(jù)量取差集,list.removeAll性能優(yōu)化詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09關(guān)于ThreadLocal對request和response的用法說明
這篇文章主要介紹了關(guān)于ThreadLocal對request和response的用法說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08