详解JAVA Spring 中的事件机制

说到事件机制,可能脑海中最先浮现的就是日常使用的各种 listener,listener去监听事件源,如果被监听的事件有变化就会通知listener,从而针对变化做相应的动作。这些listener是怎么实现的呢?说listener之前,我们先从设计模式开始讲起。

观察者模式

观察者模式一般包含以下几个对象:

  • Subject:被观察的对象。它提供一系列方法来增加和删除观察者对象,同时它定义了通知方法notify()。目标类可以是接口,也可以是抽象类或具体类。
  • ConcreteSubject:具体的观察对象。Subject的具体实现类,在这里实现通知事件。
  • Observer:观察者。这里是抽象的观察者,观察者有一个或者多个。
  • ConcreteObserver:具体的观察者。在这里维护观察对象的具体操作。

按照观察者对象,我们来写一个简单的观察者示例,定义一个气象中心,发布气象信息,观察者是各个电视台,订阅气象中心的信息,有新增的气象信息发布的时候,及时播报。

定义气象中心:

public interface WeatherCenter {

  void publishWeatherInfo();

}

定义观察者对象:

public interface Observer {

  void sendWeatherWarning();
}

定义具体的观察者:

public class BeijingTvObserver implements Observer {

  @Override
  public void sendWeatherWarning(){
    System.out.println("北京卫视天气预报开始了");
  }

}

中央电视台:

public class CCTVObserver implements Observer {

  @Override
  public void sendWeatherWarning(){
    System.out.println("中央电视台天气预报开始了");
  }

}

现在发布北京的气象信息:

public class BeijingWeather implements WeatherCenter {

  private List<Observer> observerArrayList = new ArrayList<>();

  @Override
  public void publishWeatherInfo() {
    for(Observer observer : observerArrayList) {
      observer.sendWeatherWarning();
    }
  }
}

这时候给所有的订阅者推送一条气象发布消息,那么他们就收到最新的气象预报。

总结一下观察者模式的核心就是:事件中心持有所有的订阅者,每当事件发生时循环通知所有的订阅者。

当然上面我写的比较简单,你也可以在事件中心写一个注册订阅者的方法,每当有新的订阅者加入就调用该方法注册。

Java 中的事件机制

Java中提供了基本的事件处理基类:

  1. EventObject:所有事件状态对象都将从其派生的根类;
  2. EventListener:所有事件侦听器接口必须扩展的标记接口;

具体使用方式可以用一个非常经典的开门案例来讲解:

首先创建一个开/关门事件:

import java.util.EventObject;

/**
 * @author rickiyang
 * @date 2019-12-05
 * @Desc TODO
 */
public class DoorEvent extends EventObject {

  private Integer doorStatus;

  public DoorEvent(Object source) {
    super(source);
  }

  public DoorEvent(Object source, Integer status) {
    super(source);
    this.doorStatus = status;
  }

  public void setStatus(Integer status) {
    this.doorStatus = status;
  }

  public Integer getStatus() {
    return doorStatus;
  }

}

所有的事件都继承 EventObject。

然后创建监听器:

public interface DoorListener extends EventListener {

  void DoorEvent(DoorEvent doorEvent);
}

所有的监听器都要实现 EventListener。

继续创建具体的开门/关门的监听器:

public class CloseDoorListener implements DoorListener {

  @Override
  public void DoorEvent(DoorEvent doorEvent) {
    Integer openStatus = doorEvent.getStatus();
    if(0 == openStatus) {
      System.out.println("the door is close");
    }
  }
}

开门:

public class OpenDoorListener implements DoorListener {
  @Override
  public void DoorEvent(DoorEvent doorEvent) {
    Integer openStatus = doorEvent.getStatus();
    if(1 == openStatus) {
      System.out.println("the door is open");
    }
  }
}

有了监听器和事件之后,下一步就是用上他们。还记得上面的观察者模式嘛,同样的使用方式:

		/**
   * 将所有的listener保存起来
   *
   * @return
   */
public static List<DoorListener> getAllListener() {
 List<DoorListener> list = Lists.newArrayList();
 list.add(new OpenDoorListener());
 list.add(new CloseDoorListener());
 return list;
}

public static void main(String[] args) {
 DoorEvent open = new DoorEvent("open", 1);
 List<DoorListener> listeners = getAllListener();
 for (DoorListener listener : listeners) {
  listener.DoorEvent(open);
 }
}

Spring 中的事件机制

在 Spring 容器中通过 ApplicationEvent 类和 ApplicationListener 接口来处理事件,如果某个 bean实现 ApplicationListener 接口并被部署到容器中,那么每次对应的 ApplicationEvent 被发布到容器中都会通知该 bean ,这是典型的观察者模式。

Spring 的事件默认是同步的,即调用 publishEvent 方法发布事件后,它会处于阻塞状态,直到 onApplicationEvent 接收到事件并处理返回之后才继续执行下去,这种单线程同步的好处是可以进行事务管理。

先展示一下使用方式,我们拿用户登录来举例。首先来创建一个事件:

import org.springframework.context.ApplicationEvent;

/**
 * @author rickiyang
 * @date 2019-12-04
 * @Desc TODO
 */
public class UserRegisterEvent extends ApplicationEvent {

  public UserRegisterEvent(Object source) {
    super(source);
  }
}

然后创建监听器去监听这个事件:

import com.alibaba.fastjson.JSON;
import com.rickiyang.learn.entity.User;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
 * @author rickiyang
 * @date 2019-12-05
 * @Desc 插入用户信息
 */
@Component
public class UserInsertListener implements ApplicationListener<UserRegisterEvent> {

  @Override
  public void onApplicationEvent(UserRegisterEvent userRegisterEvent) {
    String source = (String)userRegisterEvent.getSource();
    User user = JSON.parseObject(source, User.class);
    //insert db
  }
}

创建一个用户注册成功之后插入用户信息的监听器。

import com.alibaba.fastjson.JSON;
import com.rickiyang.learn.entity.User;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
 * @author rickiyang
 * @date 2019-12-05
 * @Desc 用户注册成功发送短信
 */
@Component
public class NotifyUserListener implements ApplicationListener<UserRegisterEvent> {

  @Override
  public void onApplicationEvent(UserRegisterEvent userRegisterEvent) {
    String source = (String)userRegisterEvent.getSource();
    User user = JSON.parseObject(source, User.class);
    //send sms
  }
}

创建注册成功发送通知短信的监听器。

import com.alibaba.fastjson.JSON;
import com.rickiyang.learn.entity.User;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
 * @author rickiyang
 * @date 2019-12-05
 * @Desc 用户注册成功给用户生成推荐商品
 */
@Component
public class RecommendListener implements ApplicationListener<UserRegisterEvent> {

  @Override
  public void onApplicationEvent(UserRegisterEvent userRegisterEvent) {
    String source = (String)userRegisterEvent.getSource();
    User user = JSON.parseObject(source, User.class);
    // generate recommend commodity
  }
}

创建用户注册成功之后给用户推荐商品的事件。

用户注册事件的监听器创建完毕,那么接下来就发布事件等待监听器监听就行。在Spring中提供了 ApplicationEventPublisherAware 接口,从名称上看就知道是 ApplicationEventPublisher 的适配器类,用法就是你在业务类中实现该接口,然后使用 ApplicationEventPublisher#publishEvent发布你的事件即可。

package com.rickiyang.learn.controller.test;

import com.alibaba.fastjson.JSON;
import com.rickiyang.learn.entity.User;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;

/**
 * @author rickiyang
 * @date 2019-12-04
 * @Desc TODO
 */
@Service
public class UserRegisterPublisherService implements ApplicationEventPublisherAware {

  private ApplicationEventPublisher applicationEventPublisher;

  @Override
  public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
    this.applicationEventPublisher = applicationEventPublisher;
  }

  public void insert(User user){
    UserRegisterEvent event = new UserRegisterEvent(JSON.toJSONString(user));
    applicationEventPublisher.publishEvent(event);
  }
}

调用insert方法就可以发布事件,写一个test测试一下:

import com.rickiyang.learn.entity.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserRegisterPublisherServiceTest {

  @Resource
  private UserRegisterPublisherService userRegisterPublisherService;

  @Test
  public void test1() {
    User build = User.builder().name("1").sex(1).phone("123456789").build();
    userRegisterPublisherService.insert(build);
  }

}

可以看到3个监听器都打印出来了:

发送短信
商品推荐
插入用户

有个问题不知道大家发现没,监听器的发布顺序是按照 bean 自然装载的顺序执行的,如果我们的bean是有序的应该怎么办呢?别怕,Spring自然考虑到这个问题。

SmartApplicationListener实现有序的监听

SmartApplicationListener 接口继承了 ApplicationListener,使用全局的 ApplicationEvent 作为监听的事件对象。之所以 能提供顺序性,是因为继承了 Ordered 类,实现了排序的逻辑。另外添加了两个方法#supportsEventType、#supportsSourceType 来作为区分是否是我们监听的事件,只有这两个方法同时返回true时才会执行onApplicationEvent方法。

package com.rickiyang.learn.controller.test;

import com.rickiyang.learn.entity.User;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;

/**
 * @author rickiyang
 * @date 2019-12-05
 * @Desc TODO
 */
@Component
public class UserInsert1Listener implements SmartApplicationListener {

  @Override
  public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
    return aClass == UserRegisterEvent.class;
  }

  @Override
  public boolean supportsSourceType(Class<?> sourceType) {
    return sourceType == User.class;
  }

  /**
   * 数字越小优先级越高
   * 默认值为 2147483647
   * @return
   */
  @Override
  public int getOrder() {
    return 8;
  }

  @Override
  public void onApplicationEvent(ApplicationEvent applicationEvent) {
    UserRegisterEvent event = (UserRegisterEvent)applicationEvent;
    // insert to db
  }
}

如果你有对多个监听器做排序的需求,那么你只用在 getOrder 方法中指定当前的排序级别即可。数字越大优先级越低,默认的排序级别是2147483647,你可以自己调整。

Spring 对事件监听机制的注解支持

Spring4.2之后,ApplicationEventPublisher 自动被注入到容器中,不再需要显示实现Aware接口。

import com.alibaba.fastjson.JSON;
import com.rickiyang.learn.entity.User;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * @author rickiyang
 * @date 2019-12-04
 * @Desc TODO
 */
@Service
public class UserRegisterPublisher1Service {

  @Resource
  private ApplicationEventPublisher applicationEventPublisher;

  public void insert(User user){
    UserRegisterEvent event = new UserRegisterEvent(JSON.toJSONString(user));
    applicationEventPublisher.publishEvent(event);
  }
}

创建listener也就不需要显式的继承 ApplicationListener 或者 SmartApplicationListener,使用 @EventListener 注解即可:

import com.alibaba.fastjson.JSON;
import com.rickiyang.learn.entity.User;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;

/**
 * @author rickiyang
 * @date 2019-12-07
 * @Desc TODO
 */
@Service
public class UserInfoCheckListener {

  @Order(8)
  @EventListener(classes = UserRegisterEvent.class)
  public void checkUserInfo(UserRegisterEvent event) {
    String source = (String) event.getSource();
    User user = JSON.parseObject(source, User.class);
    //todo check user info
  }
}

如果你想使用顺序性的listener,那么只需要使用 @Order注解就可以了。

异步事件的支持

上面说过 Spring 事件机制默认是同步阻塞的,如果 ApplicationEventPublisher 发布事件之后他会一直阻塞等待listener 响应,多个 listener 的情况下前面的没有执行完后面的一直被阻塞。如果我们的应用场景是:用户订单完成之后异步发货,检查快递信息,这些操作是没有必要返回结果给用户的。

这种情况下,我们是不是想到可以使用异步线程的方式来处理。你可以把listener中的处理流程做一个异步线程,或者利用 Spring 提供的线程池注解 @Async 来实现异步线程。

要使用 @Async 之前需要先开启线程池,在 启动类上添加 @EnableAsync 注解即可。线程池支持配置模式,如果你不想使用默认的线程池配置,可以手动指定:

package com.rickiyang.learn.controller.test;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.*;

/**
 * @author rickiyang
 * @date 2019-12-07
 * @Desc TODO
 */
@Configuration
@EnableAsync
public class AsyncConfig {

  @Bean("userInfoPool")
  public Executor getExecutor() {
    ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
        .setNameFormat("consumer-queue-thread-%d").build();
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    // 线程池维护线程的最少数量
    executor.setCorePoolSize(5);
    // 线程池维护线程的最大数量
    executor.setMaxPoolSize(10);
    // 缓存队列
    executor.setQueueCapacity(25);
    //线程名
    executor.setThreadFactory(namedThreadFactory);
    // 线程池初始化
    executor.initialize();
    return executor;
  }
}

手动配置一个 bean name 为 userInfoPool 的线程池,接下来使用@Async注解使用线程池:

package com.rickiyang.learn.controller.test;

import com.alibaba.fastjson.JSON;
import com.rickiyang.learn.entity.User;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

/**
 * @author rickiyang
 * @date 2019-12-07
 * @Desc TODO
 */
@Service
public class UserInfoCheckListener {

  @Async("userInfoPool")
  @Order(8)
  @EventListener(classes = UserRegisterEvent.class)
  public void checkUserInfo(UserRegisterEvent event) {
    String source = (String) event.getSource();
    User user = JSON.parseObject(source, User.class);
    System.out.println("async deel");
    //todo check user info
  }
}

这样我们就把 UserInfoCheckListener 变成了异步任务。

Spring中的事件机制分析

上面从基本的发布订阅设计模式到 Java 提供的基本的事件处理基类,再拓展到 Spring 中如何使用事件机制来拓展代码,整条线还是很清晰。讲完了我们应该如何在业务代码中使用发布订阅模式,我们也来分析一下Spring是如何实现发布订阅模式的,看看人家的代码功底。

在Spring 中提供了Event 的基类:ApplicationEvent,如果事件要想被Spring监听那么就必须继承该类,同样该类也继承了 Java 中的事件基类:EventObject。

有了事件源,我们要定义事件监听者用于处理事件,所有的事件监听者都要继承 org.springframework.context.ApplicationListener 接口:

/**
 * Interface to be implemented by application event listeners.
 * Based on the standard {@code java.util.EventListener} interface
 * for the Observer design pattern.
 *
 * <p>As of Spring 3.0, an ApplicationListener can generically declare the event type
 * that it is interested in. When registered with a Spring ApplicationContext, events
 * will be filtered accordingly, with the listener getting invoked for matching event
 * objects only.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @param <E> the specific ApplicationEvent subclass to listen to
 * @see org.springframework.context.event.ApplicationEventMulticaster
 */
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

}

ApplicationListener 提供了 一个基于 ApplicationEvent 的泛型,所以你指定了某个类的监听者只会处理该类型的event。

上面我们说了 Spring 是基于 ApplicationEventPublisher 来发布事件,那么监听器是如何获取到事件呢?

注意到 ApplicationListener 上面的注释写到:@param <E> the specific ApplicationEvent subclass to listen to ApplicationEventMulticaster,从名称上看这个类的作用应该是用于事件广播。

ApplicationEventMulticaster是一个接口,提供了如下方法:

  • addApplicationListener(ApplicationListener<?> listener) :新增一个listener;
  • addApplicationListenerBean(String listenerBeanName):新增一个listener,参数为bean name;
  • removeApplicationListener(ApplicationListener<?> listener):删除listener;
  • removeApplicationListenerBean(String listenerBeanName):根据bean name 删除listener;
  • multicastEvent(ApplicationEvent event):广播事件;
  • multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType):广播事件,指定事件的source类型。

从接口的方法看,该类的作用就是添加监听器然后对所有监听器或者指定监听器发送事件进行处理。

ApplicationEventMulticaster 有两个实现类:

  • SimpleApplicationEventMulticaster
  • AbstractApplicationEventMulticaster

因为 AbstractApplicationEventMulticaster 是一个抽象类,并且 SimpleApplicationEventMulticaster 也继承了了 SimpleApplicationEventMulticaster ,所以我们直接看 SimpleApplicationEventMulticaster:

public abstract class AbstractApplicationEventMulticaster
		implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {

	private final ListenerRetriever defaultRetriever = new ListenerRetriever(false);

	final Map<ListenerCacheKey, ListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);

 @Override
	public void addApplicationListener(ApplicationListener<?> listener) {
		synchronized (this.retrievalMutex) {
			// Explicitly remove target for a proxy, if registered already,
			// in order to avoid double invocations of the same listener.
			Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
			if (singletonTarget instanceof ApplicationListener) {
				this.defaultRetriever.applicationListeners.remove(singletonTarget);
			}
			this.defaultRetriever.applicationListeners.add(listener);
			this.retrieverCache.clear();
		}
	}

 ......
 ......

}

#addApplicationListener 方法用于新增监听器,新增的逻辑主要在这一句:

defaultRetriever.applicationListeners.add(listener);

继续看 ListenerRetriever 的实现:

private class ListenerRetriever {

 public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();

 public final Set<String> applicationListenerBeans = new LinkedHashSet<>();

 private final boolean preFiltered;

 public ListenerRetriever(boolean preFiltered) {
  this.preFiltered = preFiltered;
 }

 public Collection<ApplicationListener<?>> getApplicationListeners() {
  List<ApplicationListener<?>> allListeners = new ArrayList<>(
   this.applicationListeners.size() + this.applicationListenerBeans.size());
  allListeners.addAll(this.applicationListeners);
  if (!this.applicationListenerBeans.isEmpty()) {
   BeanFactory beanFactory = getBeanFactory();
   for (String listenerBeanName : this.applicationListenerBeans) {
    try {
     ApplicationListener<?> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
     if (this.preFiltered || !allListeners.contains(listener)) {
      allListeners.add(listener);
     }
    }
    catch (NoSuchBeanDefinitionException ex) {
     // Singleton listener instance (without backing bean definition) disappeared -
     // probably in the middle of the destruction phase
    }
   }
  }
  if (!this.preFiltered || !this.applicationListenerBeans.isEmpty()) {
   AnnotationAwareOrderComparator.sort(allListeners);
  }
  return allListeners;
 }
}

看到没,最终还是 持有了一个 applicationListeners 的集合,跟我们的发布订阅设计模式一样。

剩下的逻辑就好去解释,顺着咱们前面讲过的发布订阅模式的使用套路撸下去就行,事件广播的方法#multicastEvent不外乎就是遍历所有的监听器进行匹配。

总结

这一篇讲的发布订阅模式以及在Spring中的使用在日常开发中只要稍加注意你就会发现对改善代码流程的影响还是挺大。写代码有90%的时间我们都是在写同步代码,因为不用动脑子,顺着该有的流程撸就完事。这样带来的后果就是你真的只是在搬砖!

有的时候停下来,从业务逻辑跳出来拿半个小时想想你应该如何让这这一次搬砖有点技术含量。或许从此刻开始,搬砖也会与众不同。

以上就是详解JAVA Spring 中的事件机制的详细内容,更多关于JAVA Spring 事件机制的资料请关注我们其它相关文章!

(0)

相关推荐

  • Java事件机制要素及实例详解

    java事件机制中包含下述三要素: 1.事件,发生了什么事,比如用户在界面上的一个操作(手势滑动屏幕),当一个事件发生的时候,该事件用一个事件对象表示,每一个事件对象都有其对应的事件类. Java中事件一般继承自java.util.EventObject类,封装了事件源对象,以及事件的相关信息. 每一类事件有一个相应的事件监听器接口,该接口定义了接收和处理事件的抽象方法.实现该接口的类,就是监听器类.其对象可作为监听器对象向相应的组件注册.事件的类名通常为:XxxEvent ,比如下面实例中的C

  • Java利用Sping框架编写RPC远程过程调用服务的教程

    RPC,即 Remote Procedure Call(远程过程调用),说得通俗一点就是:调用远程计算机上的服务,就像调用本地服务一样. RPC 可基于 HTTP 或 TCP 协议,Web Service 就是基于 HTTP 协议的 RPC,它具有良好的跨平台性,但其性能却不如基于 TCP 协议的 RPC.会两方面会直接影响 RPC 的性能,一是传输方式,二是序列化. 众所周知,TCP 是传输层协议,HTTP 是应用层协议,而传输层较应用层更加底层,在数据传输方面,越底层越快,因此,在一般情况下

  • Java Spring动态生成Mysql存储过程详解

    一. 背景 由于公司业务需要动态配置一些存储过程来生成数据,之前尝试过使用jpa来完成,或多或少都存在一些问题,最后使用了spring的Jdbctemplate. 二. 环境 1.此随笔内容基于spring boot项目 2.数据库为mysql 5.7.9版本 3.jdk 版本为1.8 三. 说明 说明:为方便表示,下列存储过程在代码中的表示我称之为接口配置 四. 内容 1.定义接口和接口参数bean: 1)接口配置bean: @Entity @Table(name="qt_interface&

  • Java spring boot 实现支付宝支付功能的示例代码

    一.准备工作: 1.登陆支付宝开发者中心,申请一个开发者账号. 地址:https://openhome.alipay.com/ 2.进入研发服务: 3.点击链接进入工具下载页面: 4.点击下载对应版本的RSA公钥生成器: 5.生成公钥密钥(记录你的应用私钥): 6.在支付宝配置公钥(点击保存): 二.搭建demo 1.引入jia包: <dependency> <groupId>com.alipay.sdk</groupId> <artifactId>alip

  • 详解JAVA Spring 中的事件机制

    说到事件机制,可能脑海中最先浮现的就是日常使用的各种 listener,listener去监听事件源,如果被监听的事件有变化就会通知listener,从而针对变化做相应的动作.这些listener是怎么实现的呢?说listener之前,我们先从设计模式开始讲起. 观察者模式 观察者模式一般包含以下几个对象: Subject:被观察的对象.它提供一系列方法来增加和删除观察者对象,同时它定义了通知方法notify().目标类可以是接口,也可以是抽象类或具体类. ConcreteSubject:具体的

  • 详解Node.js中的事件机制

    前言 在前端编程中,事件的应用十分广泛,DOM上的各种事件.在Ajax大规模应用之后,异步请求更得到广泛的认同,而Ajax亦是基于事件机制的. 通常js给我们的第一印象就是运行在客户端浏览器上面的脚本,通过node.js我们可以在服务端运行javascript. node.js是基于单线程无阻塞异步式的I/O,异步式的I/O指的是当遇到I/O操作的时候,线程不阻塞而是进行下面的操作,那么I/O操作完成之后,线程时如何知道该操作完成的呢? 当操作完成耗时的I/O操作之后,会以事件的形式通知I/O操

  • Tomcat和Spring中的事件机制深入讲解

    引言 最近在看tomcat源码,源码中出现了大量事件消息,可以说整个tomcat的启动流程都可以通过事件派发机制串起来,研究透了tomcat的各种事件消息,基本上对tomcat的启动流程也就有了一个整体的认识.在这一基础上,联想到之前在看spring源码过程中也存在不少事件相关知识,于是想对这两个框架中的事件派发机制做一个简单的总结,加深理解. 事件机制原理其实比较简单,抽象来看的话,设计模式中的观察者模式可以说是最经典的事件驱动机制的体现了,观察者和被观察者就体现了事件监听和事件派发的角色.还

  • 详解java代码中init method和destroy method的三种使用方式

    在java的实际开发过程中,我们可能常常需要使用到init method和destroy method,比如初始化一个对象(bean)后立即初始化(加载)一些数据,在销毁一个对象之前进行垃圾回收等等. 周末对这两个方法进行了一点学习和整理,倒也不是专门为了这两个方法,而是在巩固spring相关知识的时候提到了,然后感觉自己并不是很熟悉这个,便好好的了解一下. 根据特意的去了解后,发现实际上可以有三种方式来实现init method和destroy method. 要用这两个方法,自然先要知道这两

  • 详解在spring中使用JdbcTemplate操作数据库的几种方式

    使用JdbcTemplate的步骤 1.设置spring-jdbc和spring-tx的坐标(也就是导入依赖) <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.7.RELEASE</version> </dependency> <dependency&

  • 详解Java 类的加载机制

    一.类的加载机制 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构.类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接

  • 详解Java分布式系统中一致性哈希算法

    业务场景 近年来B2C.O2O等商业概念的提出和移动端的发展,使得分布式系统流行了起来.分布式系统相对于单系统,解决了流量大.系统高可用和高容错等问题.功能强大也意味着实现起来需要更多技术的支持.例如系统访问层的负载均衡,缓存层的多实例主从复制备份,数据层的分库分表等. 我们以负载均衡为例,常见的负载均衡方法有很多,但是它们的优缺点也都很明显: 随机访问策略.系统随机访问,缺点:可能造成服务器负载压力不均衡,俗话讲就是撑的撑死,饿的饿死. 轮询策略.请求均匀分配,如果服务器有性能差异,则无法实现

  • 详解Java的Proxy动态代理机制

    一.Jvm加载对象 在说Java动态代理之前,还是要说一下Jvm加载对象的过程,这个依旧是理解动态代理的基础性原理: Java类即源代码程序.java类型文件,经过编译器编译之后就被转换成字节代码.class类型文件,类加载器负责读取字节代码,并转换成java.lang.Class对象,描述类在元数据空间的数据结构,类被实例化时,堆中存储实例化的对象信息,并且通过对象类型数据的指针找到类. 过程描述:源码->.java文件->.class文件->Class对象->实例对象 所以通过

  • 一文详解Java线程中的安全策略

    目录 一.不可变对象 二.线程封闭 三.线程不安全类与写法 四.线程安全-同步容器 1. ArrayList -> Vector, Stack 2. HashMap -> HashTable(Key, Value都不能为null) 3. Collections.synchronizedXXX(List.Set.Map) 五.线程安全-并发容器J.U.C 1. ArrayList -> CopyOnWriteArrayList 2.HashSet.TreeSet -> CopyOnW

  • 详解Monaco Editor中的Keybinding机制

    目录 一.前言 二.举个 三.原理机制 1. 概览 2. 初始化 3. 注册 keybindings 4. key的转换 5.执行 6.卸载 四.结语 一.前言 前段时间碰到了一个 Keybinding 相关的问题,于是探究了一番,首先大家可能会有两个问题:Monaco Editor 是啥?Keybinding 又是啥? Monaco Editor: 微软开源的一个代码编辑器,为 VS Code 的编辑器提供支持,Monaco Editor 核心代码与 VS Code 是共用的(都在 VS Co

随机推荐