我们知道,在实际开发中为了解耦,或者提高用户体验,都会采用到异步的方式。这里举个简单的例子,在用户注册的sh时候,一般我们都会要求手机验证码验证,邮箱验证,而这都依赖于第三方。这种情况下,我们一般会通过新起一个线程或者消息中间件的方式去处理。

其实在spring中,本身就提供了publish-event-listener机制还有异步任务机制。通过这两种机制,能很好的帮助我们解耦我们的应用跟代码的管理


publish-event-listener机制:

我们先看代码:

/**
* 配置类,主要为了扫面组件
*/
@Configuration
@ComponentScan("com.spring.publishevent")
public class Config {
} /**
*
* @Description: 事件类,必须实现ApplicationEvent
*
*/
public class MyEvent extends ApplicationEvent { private String msg; public MyEvent(Object source, String msg) {
super(source);
this.msg = msg;
} public String getMsg() {
return msg;
}
} /**
* @Author: dmz
* @Description: 观察者,必须实现ApplicationListener,并指定泛型为我们的自定义事件
* @Date: Create in 0:23 2019/3/15
*/
@Component
public class MyListener implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
System.out.println(event.getMsg());
}
} /**
* @Author: dmz
* @Description: 发布者,直接用我们的上下文对象进行发布,它继承了ApplicationEventPublisher
* @Date: Create in 0:42 2019/3/15
*/
@Component
public class MyPublisher {
@Autowired
private ApplicationContext applicationContext; public void publish() {
applicationContext.publishEvent(new MyEvent(this, "hello event"));
}
}

运行结果:

原理分析:我们debug追踪下代码

核心代码就是上面这一句:

先看第一个方法:getApplicationEventMulticaster()

它其实就是返回当前类持有的ApplicationEventMulticaster这个接口的实现的引用,我们看下这个接口上的注释

/**
* Interface to be implemented by objects that can manage a number of
* {@link ApplicationListener} objects, and publish events to them.
...........

英文稍微好些的同学就能知道,这个接口其实就是管理了我们的listener,并且可以向它们发布事件

现在我们知道了getApplicationEventMulticaster()返回的其实一个发布者,我们的容器applicationContext其实就是调用这个对象去发布事件

并且它管理了我们所有的listener。

接下来我们继续看:multicastEvent(......)这个方法

getApplicationListeners(event, type),其实就是返回了当前对象所管理的监听了当前事件的listener,之后在调用listener的invokeListener(listener, event)方法。

这段代码我们暂时忽略,稍后再讲

现在我们继续跟进invokeListener(....)方法,发现最终会调用到

也就是我们实现了applicationListener接口所实现的方法,不难发现整个过程是同步的。

不过虽然是同步的,也能解耦我们的代码,这并不冲突。不仅如此,还能提高我们代码的复用性

在spring4.2以后,有一种更优雅的方式实现发布监听模式,不再需要我们去实现那么多接口,采用注解即可实现,代码如下:(配置类代码不变)

/**
* @Author: dmz
* @Description:
* @Date: Create in 0:20 2019/3/15
*/
public class MyEvent { private String msg; public MyEvent(String msg) {
this.msg = msg;
} public String getMsg() {
return msg;
}
} /**
* @Author: dmz
* @Description:
* @Date: Create in 1:32 2019/3/15
*/
@Data
@AllArgsConstructor
public class MySecondEvent {
private String msg;
}

/**
* @Author: dmz
* @Description:
* @Date: Create in 0:23 2019/3/15
*/
@Component
public class MyListener{
@EventListener
public void onApplicationEvent(MyEvent event) {
System.out.println(event.getMsg());
} @EventListener
public void onApplicationEvent(MySecondEvent event) {
System.out.println(event.getMsg());
} }
/**
* @Author: dmz
* @Description:
* @Date: Create in 0:32 2019/3/15
*/
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(Config.class);
applicationContext.publishEvent(new MyEvent("hello event"));
applicationContext.publishEvent(new MySecondEvent("hello secondEvent"));
}
}

运行结果如下:

原理不再分析了,其实跟第一种差不多

异步处理机制(多线程):

先看代码:

package com.spring.asycn.config;

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.lang.reflect.Method;
import java.util.concurrent.Executor; /**
* @Author: dmz
* @Description:
* @Date: Create in 0:23 2019/3/16
*/
@Configuration
@EnableAsync
@ComponentScan("com.spring.asycn.service")
public class AsyncConfig implements AsyncConfigurer { @Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(10);
threadPoolTaskExecutor.setMaxPoolSize(50);
threadPoolTaskExecutor.setQueueCapacity(5);
threadPoolTaskExecutor.setKeepAliveSeconds(1);
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
} @Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncUncaughtExceptionHandler() {
@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
System.out.println("出现异常啦~~~~~~");
}
};
}
} package com.spring.asycn.service; import org.springframework.stereotype.Component; /**
* @Author: dmz
* @Description:
* @Date: Create in 0:24 2019/3/16
*/
@Component
public class SyncService {
//@Async
public void test(int i) {
System.out.println(Thread.currentThread().getName() + "执行方法______________"+i);
}
} package com.spring.asycn; import com.spring.asycn.config.AsyncConfig;
import com.spring.asycn.service.SyncService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; /**
* @Author: dmz
* @Description:
* @Date: Create in 0:23 2019/3/16
*/
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(AsyncConfig.class);
SyncService bean = applicationContext.getBean(SyncService.class);
for (int i = 0; i < 100; i++) {
bean.test(i);
}
}
}

我们将test方法上的@Async注解打开跟注释分别执行可以得到如下结果:

这个注释掉的结果:

这是打开的结果:

很明显,一个是单线程,一个是多线程并发执行,线程池就是我们配置的线程池

spring学习笔记(二)spring中的事件及多线程的更多相关文章

  1. spring学习笔记二:spring使用构造方法注入(set方式注入)

    项目目录树: 1.spring的依赖包配置 * SPRING_HOME/dist/spring.jar * SPRING_HOME/lib/log4j/log4j-1.2.14.jar * SPRIN ...

  2. spring学习笔记(一) Spring概述

    博主Spring学习笔记整理大部分内容来自Spring实战(第四版)这本书.  强烈建议新手购入或者需要电子书的留言. 在学习Spring之前,我们要了解这么几个问题:什么是Spring?Spring ...

  3. Java架构师之路 Spring学习笔记(一) Spring介绍

    前言 这是一篇原创的Spring学习笔记.主要记录我学习Spring4.0的过程.本人有四年的Java Web开发经验,最近在面试中遇到面试官总会问一些简单但我不会的Java问题,让我觉得有必要重新审 ...

  4. Spring 学习笔记(2) Spring Bean

    一.IoC 容器 IoC 容器是 Spring 的核心,Spring 通过 IoC 容器来管理对象的实例化和初始化(这些对象就是 Spring Bean),以及对象从创建到销毁的整个生命周期.也就是管 ...

  5. Spring学习笔记之五----Spring MVC

    Spring MVC通常的执行流程是:当一个Web请求被发送给Spring MVC Application,Dispatcher Servlet接收到这个请求,通过HandlerMapping找到Co ...

  6. jQuery学习笔记(二)jQuery中DOM操作

    目录 DOM操作分类 jQuery中的各种DOM操作 查找节点 创建节点 删除节点 复制节点 替换节点 包裹节点 属性操作 样式操作 对HTML.文本和值的操作 遍历节点 CSS-DOM操作 小结 本 ...

  7. Spring学习笔记(二)之装配Bean

    一,介绍Bean的装配机制 在Spring中,容器负责对象的创建并通过DI来协调对象之间的关系.但是我们要告诉Spring创建哪些Bean并且如何将其装配在一起.,装配wiring就是DI依赖注入的本 ...

  8. Spring学习笔记之 Spring IOC容器(二) 之注入参数值,自动组件扫描方式,控制Bean实例化方式,使用注解方式

     本节主要内容:    1. 给MessageBean注入参数值    2. 测试Spring自动组件扫描方式    3. 如何控制ExampleBean实例化方式    4. 使用注解方式重构Jdb ...

  9. spring学习笔记二 注解及AOP

    本节需要导入spring-aop包 注解 使用注解的目的是为了代替配置,在使用注解时,省略键时,则是为value赋值. 扫描某个包下的所有类中的注解 <?xml version="1. ...

  10. Spring学习笔记二:注入方式

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6774608.html  我们说,IOC的实现方式是依赖注入,也就是把被依赖对象赋值到依赖对象的成员属性.怎么做 ...

随机推荐

  1. 【小学数学】算术口诀 独立音频MP3

    算术口诀 独立音频MP3 原文载于本人个人网站:http://www.unlimitedbladeworks.cc/writing_202004_01_sskj 特点 加法口诀 乘法口诀 独立音频 m ...

  2. Python—一个简单搜索引擎索引库

    因为课业要求,搭建一个简单的搜索引擎,找了一些相关资料并进行了部分优化(坑有点多) 一.数据 数据是网络上爬取的旅游相关的攻略页面 这个是travels表,在索引中主要用到id和url两个字段. 页面 ...

  3. Python-气象-大气科学-可视化绘图系列(二)——利用basemap叠加地图,并添加白化效果(代码+示例)

    本文为原创链接: https:////www.cnblogs.com/zhanling/p/12193031.html 白化单图代码: import numpy as np import xarray ...

  4. SPFA()判环

    1 SPFA()判负环 SPFA()判负环的原理就是在求最短路的过程中,如果存在负环,比如说要求从A到a的最短距离,设为s,但是经过a->c->b->a可以更短,所以如果一直经过a- ...

  5. F. Count Prime Pairs

    单点时限: 2.0 sec 内存限制: 512 MB 对于数组a,如果i≠j并且ai+aj是一个质数,那么我们就称(i,j)为质数对,计算数组中质数对的个数. 输入格式 第一行输入一个n,表示数组的长 ...

  6. 2020.4面试分享(7面收5个offer)

    都说金三银四是找工作的最佳时节,由于本人的个人职业规划跟目前工作内容不太相符(具体原因就不透露了,领导平时也要来这里逛,哈哈),四月份挑选了10多家公司投递简历(公司规模从几十人到上万人都有),参加了 ...

  7. [linux][MongoDB] mongodb学习(二):命令使用数据库

    使用数据库 # 查看数据库 > show dbs admin 0.000GB local 0.000GB # 查看表(集合) > show tables # 删除集合 > db.us ...

  8. [腾讯云]简单在腾讯云 CenTOS7.0 安装Nginx,Mysql(MariaDB),Memcache,解析PHP!

    1.安装LNMP之前要安装EPEL,以便安装源以外的软件,如Nginx,phpMyAdmin等. yum install epel-release 2.安装Nginx a) yum install n ...

  9. 【题解】P2480 [SDOI2010]古代猪文 - 卢卡斯定理 - 中国剩余定理

    P2480 [SDOI2010]古代猪文 声明:本博客所有题解都参照了网络资料或其他博客,仅为博主想加深理解而写,如有疑问欢迎与博主讨论✧。٩(ˊᗜˋ)و✧*。 题目描述 猪王国的文明源远流长,博大精 ...

  10. 重装anaconda的记录,包含设置jupyter kernel

    anaconda安装记录 官网下载最新版 linux:sh xx.sh 注意不要敲太多回车,容易错过配置bash的部分,还要手动添加 (vim ~/.bashrc 手动添加新bash,卸载时也要删掉此 ...