The callback interface

Listeners are essentially executing some of our own code before or after an action, using a callback mechanism. In the Java language, this can be done using interfaces.

Implement a listener case

For convenience, define it directly in the Spring environment: Use work as an example to define listeners when work starts (or ends).

1. Define the interface for the callback

package com.yawn.demo.listener;

/**
 * @author Created by yawn on 2018-01-21 13:53
 */
public interface WorkListener {

    void onStart(String name);
}
Copy the code

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

The action work is a concrete method that calls the previously defined interface at the appropriate time for the work() method. In addition, you need to improve the way you set up listeners in this action definition class.

3. Listening test

@RunWith(SpringRunner.class) @SpringBootTest public class DemoSpringAnnotationApplicationTests { @Resource private MyService myService; @ Test public void test1 () {/ / interface to set the listener myService. SetWorkListener (new WorkListener () {@ Override public void onStart(String name) { System.out.println("Start work for " + name + " !" ); }}); / / / / lambda expressions to set the listener / / myService setWorkListener (name - > System. Out. The println (" Start the work for "+ name +"!" )); / / work myService. Work (" boss "); } @ Test public void test2 () {/ / inheritance implementation class set 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 + " !" ); }}}Copy the code

The above two methods are used to test, and the results are as follows:

Start work for boss !
working hard ...Copy the code

Before the action work occurs, the listener code written in the test class is executed to achieve the purpose of the class listening.

This is an example of Java USES interface callback, I also wrote an essay about in big three callback blog can refer to: my.oschina.net/silenceyawe…

Implement listeners with annotations

In the above code, the call to setWorkListener(WorkListener Listener) method is generally called set (register) listener, is to write their own listener code, set as an action listener. However, each time a listener is registered, it is generally 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. Therefore, smart programmers want to simplify the process and come up with ways to use annotations. With annotations, write the listening snippet in a method that is marked with an annotation.

Yes, it’s easier to use, but not to implement.

1. Define a comment

package com.yawn.demo.anno;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface WorkListener {

}Copy the code

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 the bean that can annotate * @param annotationTypes to scan * @return the map of the bean that can be annotated */ 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; }}Copy the code

During annotation parsing, set up listeners.

In the parse class, the interface ApplicationContextAware is implemented. In order to get the reference of the ApplicationContext in the class, it is used to get the Bean in the IOC container. The interface InitializingBean is implemented to execute the code that parses annotations and sets up listeners at an appropriate time. If you don’t, parsed, set code can be invoked at CommandLineRunner execution, and the ApplicationContext can 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 + " !");
    }
}
Copy the code

Write a listener method with the same type and number of arguments as the interface, and add custom annotations. When the environment is started, listeners are already set up.

We then call the work() method of myService via the URL and see the result:

Start work for boss !
working hard ...Copy the code

The listener method has been called. Later in development, you can use this annotation to register listeners.