Use custom annotations in spring to register listeners
- 2021-01-02 21:50:00
- OfStack
The callback interface
Listeners essentially use callback mechanisms to execute some of our own code before or after an action occurs. In the Java language, you can do this using an interface.
Implement 1 listener case
For convenience, define directly in the spring environment: In the case of work (work), define listeners at the beginning (or end) of the work.
1. Define the callback interface
package com.yawn.demo.listener;
/**
* @author Created by yawn on 2018-01-21 13:53
*/
public interface WorkListener {
void onStart(String name);
}
2. Define actions
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();
}
}
Action work is a concrete method that invokes the previously defined interface at the appropriate time for the work() method. In addition, in this action definition class, you need to improve the way the listener is set up.
3. Listen for tests
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoSpringAnnotationApplicationTests {
@Resource
private MyService myService;
@Test
public void test1() {
// Interface Settings listener
myService.setWorkListener(new WorkListener() {
@Override
public void onStart(String name) {
System.out.println("Start work for " + name + " !");
}
});
// // lambda Expression sets the listener
// myService.setWorkListener(name -> System.out.println("Start work for " + name + " !"));
// work
myService.work("boss");
}
@Test
public void test2() {
// The inherited implementation class sets the listener
myService.setWorkListener(new myWorkListener());
// work
myService.work("boss");
}
class myWorkListener extends WorkListenerAdaptor {
@Override
public void onStart(String name) {
System.out.println("Start work for " + name + " !");
}
}
}
By using the above two methods, the results are as follows:
Start work for boss !
working hard ...
Explains that before action work occurs, we execute the listening code we wrote in the test class to realize the purpose of class listening.
Implement listeners with annotations
In the above code, calling setWorkListener(WorkListener listener) method 1 is commonly called setting (registering) listeners, which is the code you write to listen to the action. However, each time a listener is registered, it is usually necessary to write a class that implements the defined interface or inherits the class that implements the interface, and then override the methods defined by the interface. So clever programmers want to simplify the process and come up with ways to use annotations. Using annotations, write the listening code snippet in 1 method and use 1 annotation to mark this method.
True, usage has become easier, but implementation has not.
1. Define a comment
package com.yawn.demo.anno;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface WorkListener {
}
2. Parse annotations
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();
}
});
}
}
}
/**
* Find those that might use annotations bean
* @param annotationTypes Class-level annotation types that need to be scanned
* @return Scan the beans the 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;
}
}
During annotation parsing, set listeners.
In the parse class, the interface ApplicationContextAware is implemented. In order to get the reference of ApplicationContext in the class, it is used to get Bean in the IOC container. The interface InitializingBean is implemented to parse the annotation and set up the listener code at an appropriate time. If you don't, you can call the parsed, set code when CommandLineRunner executes, and ApplicationContext can also be injected automatically.
3. The test
After executing the above code, the listener is set up and ready to test.
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 + " !");
}
}
Write 1 listener method with the same type and number of arguments as the interface, and then add custom annotations. When the environment is started, the listener is set up.
Then call the work() method of myService via url, and you can see the result:
Start work for boss !
working hard ...
The listener method has been called. In future development, you can use this annotation to register listeners.