- Spring 监听器listener原理-基本使用(一)
- Spring 监听器listener原理-手写监听器(二)
- Spring 监听器listener原理-spring监听器源码分析(三)
原本计划从0开始,实现spring的扫描注入,然后在自定义我们的监听器。但是spring容器并不是我们关注的重点。所以最终还是决定使用spring框架来探究监听器的实现原理
基于接口的监听器实现原理
接口定义
- 配置类
@ComponentScan({"com.zhu.demo.customer"})
@Configuration
public class CustomerListenerConfig {
}
- 定义事件接口
public interface MyApplicationEvent {
}
- 定义监听器接口
public interface MyApplicationListener<E extends MyApplicationEvent> {
/**
* 用于实现监听逻辑
* @param event
*/
void onEvent(MyApplicationEvent event);
/**
* 判断监听器可以监听的事件类型
* @param eventType
* @return
*/
boolean supportsEventType(Class<?> eventType);
}
接口实现
- 实现事件接口
public class AEvent implements MyApplicationEvent {
//定义发布事件的内容
}
public class BEvent implements MyApplicationEvent {
//定义发布事件的内容
}
- 监听器接口实现
@Component
public class AListener implements MyApplicationListener<AEvent> {
@Override
public void onEvent(MyApplicationEvent event) {
System.out.println(event.getClass());
}
/**
* 判断需要监听何种事件,只是核心方法
* @param eventType
* @return
*/
@Override
public boolean supportsEventType(Class<?> eventType) {
//获得泛型类型
Class annotationClass = (Class) ((ParameterizedType) this.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0];
//父类.class.isAssignableFrom(子类.class)
return annotationClass.isAssignableFrom(eventType);
}
}
supportsEventType
是核心方法,用于判断我们监听器需要监听那些事件。判断的方法时获取监听器上的泛型是否与事件类型一致。如果一致就是我们需要监听的事件。
事件发布器
@Component("myApplicationEventPublisher")
public class MyApplicationEventPublisher {
/**
* 找到我们自定义的所有监听器
*/
@Autowired
private List<MyApplicationListener> applicationListeners ;
public void pushEvent(MyApplicationEvent event){
for (MyApplicationListener applicationListener : applicationListeners) {
//判断是否需要监听的事件
if (applicationListener.supportsEventType(event.getClass())){
applicationListener.onEvent(event);
}
}
}
public List<MyApplicationListener> getApplicationListeners() {
return applicationListeners;
}
public void setApplicationListeners(List<MyApplicationListener> applicationListeners) {
this.applicationListeners = applicationListeners;
}
}
执行发布事件
public class Main {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(CustomerListenerConfig.class);
MyApplicationEventPublisher myApplicationEventPublisher = (MyApplicationEventPublisher) applicationContext.getBean("myApplicationEventPublisher");
MyApplicationEvent Aevent = new AEvent();
MyApplicationEvent Bevent = new BEvent();
myApplicationEventPublisher.pushEvent(Aevent);
myApplicationEventPublisher.pushEvent(Bevent);
}
}
结果
class com.zhu.demo.customer.inter.impl.AEvent
可以看到因为我们只监听了AEvent所以只打印了AEvent。至此我们基于接口的监听器就实现完了。代码的重点是supportsEventType方法。用于判断我们需要监听的类型。后面文章在关于分析spring监听器源码的时候我们也会看到类似的逻辑。
基于注解的监听器实现原理
- 定义注解类
@Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyEventListener {
}
使用注解标注方法
@ComponentScan({"com.zhu.demo.customer"})
@Configuration
public class CustomerListenerConfig {
@MyEventListener
public void test(AEvent aEvent){
System.out.println("使用注解监听器" + aEvent.getClass());
}
}
大家可能有一个疑问,被注解MyEventListener标注的监听器只是一个方法,没有实现MyApplicationListener接口,是否也可以监听MyApplicationEventPublisher 发布的事件或者说如何实现监听MyApplicationEventPublisher 的事件。这里我们引入一个设计模式---适配器模式
- 适配器类
适配器类也实现了MyApplicationListener接口,并且成员属性中包含beanName和method方法。
public class MyApplicationListenerMethodAdapter implements MyApplicationListener<MyApplicationEvent> {
/**
* bean的名字
*/
private final String beanName;
/**
* 被MyEventListener注解标注的方法
*/
private final Method method;
/**
* 用于获取我们执行method方式时获取对象
*/
@Nullable
private ApplicationContext applicationContext;
public MyApplicationListenerMethodAdapter(String beanName, Method method, @Nullable ApplicationContext applicationContext) {
this.beanName = beanName;
this.method = method;
this.applicationContext = applicationContext;
}
protected void doInvoke(Object... args) {
Object bean = applicationContext.getBean(beanName);
// Detect package-protected NullBean instance through equals(null) check
if (bean.equals(null)) {
return;
}
ReflectionUtils.makeAccessible(this.method);
try {
//反射调用MyEventListener注解标注的方法
this.method.invoke(bean, args);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
/**
* 执行事件方法
* @param event
*/
@Override
public void onEvent(MyApplicationEvent event) {
doInvoke(new Object[] {event});
}
/**
* 判断我们方法支持的事件类型
* @param eventType
* @return
*/
@Override
public boolean supportsEventType(Class<?> eventType) {
Class<?>[] getTypeParameters = method.getParameterTypes();
if (getTypeParameters[0].isAssignableFrom(eventType)) {
return true;
}
return false;
}
}
适配器类已经有了,那么如何扫描被MyEventListener注解标注的方法并进行解析呢。我们使用spring提供的一个扩展点SmartInitializingSingleton。实现SmartInitializingSingleton的接口后,当所有单例 bean 都初始化完成以后, Spring的IOC容器会回调该接口的 afterSingletonsInstantiated()方法。我们可以在这个扩展点把所有被MyEventListener标注的方法都找出来。
@Component
public class MyEventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware {
@Nullable
private ConfigurableApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
//获取spring上下文
this.applicationContext = (ConfigurableApplicationContext) applicationContext;
}
@Override
public void afterSingletonsInstantiated() {
//获取beanFactory工厂,beanFactory可以理解为就是spring容器
ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
//获取beanFactory所有的对象
String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
for (String beanName : beanNames) {
Class<?> type = beanFactory.getType(beanName);
//判断类中的方法是否被MyEventListener注解所标识
Map<Method, MyEventListener> annotatedMethods = null;
annotatedMethods = MethodIntrospector.selectMethods(type,
(MethodIntrospector.MetadataLookup<MyEventListener>) method ->
AnnotatedElementUtils.findMergedAnnotation(method, MyEventListener.class));
//没有就直接返回
if (CollectionUtils.isEmpty(annotatedMethods)) {
continue;
}
//获取我们自己的事件发布器
MyApplicationEventPublisher myApplicationEventPublisher = (MyApplicationEventPublisher)beanFactory.getBean("myApplicationEventPublisher");
for (Method method : annotatedMethods.keySet()) {
//如果发现有被MyEventListener注解标注,那么实例化一个适配器类把beanName和method、spring上下文传入。
MyApplicationListenerMethodAdapter myApplicationListenerMethodAdapter = new MyApplicationListenerMethodAdapter(beanName, method, applicationContext);
//把封装的适配器也加入到我们的监听器集合中
myApplicationEventPublisher.getApplicationListeners().add(myApplicationListenerMethodAdapter);
}
}
}
}
执行
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(CustomerListenerConfig.class);
MyApplicationEventPublisher myApplicationEventPublisher = (MyApplicationEventPublisher) applicationContext.getBean("myApplicationEventPublisher");
MyApplicationEvent Aevent = new AEvent();
MyApplicationEvent Bevent = new BEvent();
myApplicationEventPublisher.pushEvent(Aevent);
myApplicationEventPublisher.pushEvent(Bevent);
}
执行结果如下。
class com.zhu.demo.customer.inter.impl.AEvent
使用注解监听器class com.zhu.demo.customer.inter.impl.AEvent
至此我们定义的监听器就已经实现完毕。后面一篇文章将分析spring的实现。