接口回调
监听器本质上就是利用回调机制,在某个动作发生前或后,执行我们自己的一些代码。在Java语言中,可以使用接口来实现。
实现一个监听器案例
为了方便,直接在spring环境中定义:以工作(work)为例,定义工作开始时(或结束时)的监听器。
1. 定义回调的接口
1
2
3
4
5
6
7
8
9
|
package com.yawn.demo.listener; /** * @author Created by yawn on 2018-01-21 13:53 */ public interface WorkListener { void onStart(String name); } |
2. 定义动作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
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()方法的适当时机,调用前面定义的接口。此外,在这个动作定义类中,需要提高设置监听器的方法。
3. 监听测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
@RunWith (SpringRunner. class ) @SpringBootTest public class DemoSpringAnnotationApplicationTests { @Resource private MyService myService; @Test public void test1() { // 接口设置监听器 myService.setWorkListener( new WorkListener() { @Override public void onStart(String name) { System.out.println( "Start work for " + name + " !" ); } }); // // lambda 表达式设置监听器 // myService.setWorkListener(name -> System.out.println("Start work for " + name + " !")); // 工作 myService.work( "boss" ); } @Test public void test2() { // 继承实现类设置监听器 myService.setWorkListener( new myWorkListener()); // 工作 myService.work( "boss" ); } class myWorkListener extends WorkListenerAdaptor { @Override public void onStart(String name) { System.out.println( "Start work for " + name + " !" ); } } } |
使用以上两种方法测试,得到了结果为:
1
2
|
Start work for boss ! working hard ... |
说明在动作work发生之前,执行了我们在测试类中写下的监听代码,实现类监听的目的。
使用注解实现监听器
在以上代码中,调用 setWorkListener(WorkListener listener) 方法一般称作设置(注册)监听器,就是将自己写好的监听代码,设置为动作的监听器。然而,在每次注册监听器时,一般需要写一个类,实现定义好的接口或继承实现接口的类,再重写接口定义的方法即可。因此,聪明的程序员就想简化这个过程,所以就想出了使用注解的方法。使用注解,将监听代码段写在一个方法中,使用一个注解标记这个方法即可。
的确,使用变得简单了,但实现却不见得。
1. 定义一个注解
1
2
3
4
5
|
package com.yawn.demo.anno; @Target (ElementType.METHOD) @Retention (RetentionPolicy.RUNTIME) public @interface WorkListener { } |
2. 解析注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
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 需要进行扫描的类级注解类型 * @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; } } |
在注解的解析过程中,设置监听器。
在解析类中,实现了接口ApplicationContextAware,为了在类中拿到ApplicationContext的引用,用于得到 IOC 容器中的 Bean;而实现接口InitializingBean,则是为了在一个合适的时机执行解析注解、设置监听器的代码。 如果不这样做,可以在CommandLineRunner执行时调用解析、设置的代码,而ApplicationContext也可以自动注入。
3. 测试
在执行完以上代码后,监听器就已经设置好了,可以进行测试了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
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 + " !" ); } } |
写一个监听方法,参数类型和个数与接口相同,然后加上自定义的注解即可。当启动环境后,监听器就已经设置好了。
然后通过url调用myService的work()方法,可以看到结果:
1
2
|
Start work for boss ! working hard ... |
已经调用了监听方法。在接下来的开发中,就可以使用这个注解注册监听器了。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://my.oschina.net/silenceyawen/blog/1610900