本文共 11878 字,大约阅读时间需要 39 分钟。
Spring是一个Java轻量级的IOC容器,实现了AOP的特性,非侵入性框架。提供了对持久层、事务、Web层等各个方面组件集成与一致性封装。涉及到的组件非常丰富,但核心仍然是Spring Framework。Spring Framework真正的核心组件只有几个。
下面看下Spring框架的总体架构图:可以看到,Spring提供的功能非常多,但核心组件只有三个:Core、Context、Beans;它们构建起了整个Spring的骨骼架构,没有它们就不可能有AOP、Web等上层的特性功能。Spring的设计理念:构建一个数据结构,然后根据这个数据结构设计它的生存环境
就像开发一个系统一样,比如电商系统,需要有用户User,这个User需要一张表,然后根据这个用户去设计他的生存环境,比如用户的订单、购物车等,这些就是这个用户在这个系统中的生存环境,那么Spring的生存环境又是什么呢?
上面说到,Spring的设计理念是构建一个数据结构,那么什么是Spring的数据结构呢?
Spring的三个核心组件中最核心的是Beans组件,Bean则是Spring构建的数据结构
在Spring中,Bean才是真正的主角,或者说Spring是面向Bean的编程,Bean在Spring中的作用就像Object对OOP的作用一样,在java中是面向对象的编程,在Spring中是面向Bean的编程,包括Bean的创建、定义、解析等,这些会在后续的文章中说到
通过IOC容器完成依赖注入机制,构建Bean的生存环境;IOC容器就是被Bean包裹的对象,Spring正是通过把对象包装在Bean中,从而达到对这些对象的管理以及一些列额外操作的目的
Spring框架的设计目标:依赖注入机制,把对象之间的依赖关系用配置文件或者注解来管理
从上面可以知道,Bean是Spring的关键因素,那么Context和Core又有什么作用呢?
如果把Bean比作舞台中的演员的话,那么Context就是这个舞台背景,而Core就是演出的道具Context、Core、Beans关系图:
知道了Bean是Spring的核心,Bean里面包装的是对象,那么Context组件解决了Bean的生存环境问题,就比如没有舞台,演员还怎么演出呢;Context也会去发现每个Bean之间的关系,然后为它们建立维护好Bean关系;所以可以说,Context就是一个Bean关系的集合,这个关系集合又叫做IOC容器,一旦建立起这个IOC容器后Spring就可以工作了Core组件就是发现、建立和维护Bean关系需要的一系列的工具,从这个角度来看的话,Core组件叫做Util更容易理解
Spring AOP中CGLIB、JDK动态代理就是利用代理模式设计实现的
从上图可以看到,Spring除了实现被代理对象的接口,还有SpringProxy和Advised两个接口$Proxy就是创建的代理对象,Subject是抽象主题,代理对象是通过InvocationHandler来持有对目标对象的引用的
在Spring中一个真实的代理对象结构如下:
在Spring中,代理对象的创建就是通过策略模式来实现的
Spring中的代理方式有两个,一个JDK动态代理,一个CGLIB代理。两个代理方式都使用了策略模式,结构图如下:
AopProxy接口表示抽象策略事件驱动编程,是基于发布-订阅模式的编程模型,即观察者模式
事件驱动模型的核心构建通常包含了一下几个:
事件驱动模型的实现包含以下几种:
下面主要是Spring事件驱动的示例,由于Spring事件驱动模型原理比较复杂,涉及到的类比较多,下面从一个简单的例子入手,了解Spring事件驱动模型
在日常购物中,当下了一个订单的时候,这个订单的支付状态会发生变化,然后能够通知到库存服务、短信服务、邮件服务等
/** * @ClassName PaymentEntity * @Description: 支付的实体。作为事件实体 * @Author TR * @Date 2021/3/21 * @Version V1.0 */public class PaymentEntity { /** 订单id */ private int id; /** 订单状态 */ private String status; public PaymentEntity(int id, String status) { this.id = id; this.status = status; } @Override public String toString() { return "PaymentEntity{" + "id=" + id + ", status='" + status + '\'' + '}'; }}
/** * @ClassName 支付状态更新的事件,以PaymentEntity作为传输的载体 * @Description: TODO * @Author TR * @Date 2021/3/21 * @Version V1.0 */public class PaymentUpdateStatusEvent extends ApplicationEvent { public PaymentUpdateStatusEvent(Object source) { super(source); }}
/** * @ClassName PaymentService * @Description: TODO * @Author TR * @Date 2021/3/21 * @Version V1.0 */@Servicepublic class PaymentService { @Autowired private ApplicationContext applicationContext; public void pay(int id, String status) { //TODO 省略的业务代码 PaymentEntity entity = new PaymentEntity(id, status); // 发布事件 applicationContext.publishEvent(new PaymentUpdateStatusEvent(entity)); }}
/** * @ClassName StockPaymantListener * @Description: 无序事件监听器,库存服务监听器 * @Author TR * @Date 2021/3/21 * @Version V1.0 */@Servicepublic class StockPaymentListener implements ApplicationListener{ @Override @Async public void onApplicationEvent(PaymentUpdateStatusEvent event) { System.out.println(Thread.currentThread().getName() + ":库存服务,收到了支付状态的更新:" + event); }}
5.AbstractPaymentListener抽象类,有序监听器
/** * @ClassName SmsPaymentListener * @Description: 有序监听器,抽象类实现事件源以及事件的通用判断 * @Author TR * @Date 2021/3/21 * @Version V1.0 */public abstract class AbstractPaymentListener implements SmartApplicationListener { /** 支持的事件类型 */ @Override public boolean supportsEventType(Class eventType) { return eventType == PaymentUpdateStatusEvent.class; } /** 事件发生的目标类 */ @Override public boolean supportsSourceType(Class sourceType) { return sourceType == PaymentEntity.class; }}
/** * @ClassName SmsPaymentListener * @Description: 短信监听器 * @Author TR * @Date 2021/3/21 * @Version V1.0 */@Servicepublic class SmsPaymentListener extends AbstractPaymentListener implements SmartApplicationListener { /** 排序,数字越小执行的优先级越高 */ @Override public int getOrder() { return 1; } @Override @Async public void onApplicationEvent(ApplicationEvent event) { System.out.println(Thread.currentThread().getName() + ":短信服务,收到了支付状态的更新:" + event); }}
/** * @ClassName MailPaymentListener * @Description: 邮件监听器 * @Author TR * @Date 2021/3/21 * @Version V1.0 */@Servicepublic class MailPaymentListener extends AbstractPaymentListener implements SmartApplicationListener { /** 排序,数字越小执行的优先级越高 */ @Override public int getOrder() { return 2; } @Override @Async public void onApplicationEvent(ApplicationEvent event) { System.out.println(Thread.currentThread().getName() + ":邮件服务,收到了支付状态的更新:" + event); }}
/** * @ClassName EventTest * @Description: 测试类 * @Author TR * @Date 2021/3/21 * @Version V1.0 */@SpringBootTestpublic class EventTest { @Autowired PaymentService paymentService; @Test void pay() { paymentService.pay(1, "支付成功"); }}
运行之后的结果:
涉及到的类:可以看到,有序监听器执行是按照优先级执行的,也可以看到,上面执行的线程全部是main线程,当订单很多的时候,只有一个线程来执行,效率会很低,所以引出了下面的内容,异步执行
Spring有两种异步执行方式:全局异步、注解式配置异步
一. 全局异步实现
/** * @ClassName GlobalAsyncConfig * @Description: TODO * @Author TR * @Date 2021/3/21 * @Version V1.0 */@Configurationpublic class GlobalAsyncConfig { /** 线程池维护线程的最小数量 */ private int minPoolSize = 2; /** 线程池维护线程的最大数量 */ private int maxPoolSize = 2; /** 线程池队列的长度 */ private int queueCapacity = 100; /** 获取异步线程池的执行对象 */ @Bean("asyncExecutor") public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(minPoolSize); executor.setMaxPoolSize(maxPoolSize); executor.setQueueCapacity(queueCapacity); //用来调试 executor.setThreadNamePrefix("GlobalAsyncConfig:"); executor.setWaitForTasksToCompleteOnShutdown(true); //拒绝策略 CallerRunsPolicy 由调用线程处理该任务 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; } /** 名称必须是applicationEventMulticaster,Spring内部通过这个名字来获取Bean */ @Bean("applicationEventMulticaster") public SimpleApplicationEventMulticaster simpleApplicationEventMulticaster(Executor executor) { SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster(); eventMulticaster.setTaskExecutor(executor); return eventMulticaster; }}
思考一下,当用到了异步之后,上面的有序监听器还会按照优先级执行吗?下面看下执行结果:
可以看到,当使用异步之后,有序监听器并没有按照优先级执行,具体原因就是不同的线程去执行导致的全局异步的执行步骤:
二. 注解式配置异步实现
/** * @ClassName AnnotationAsyncConfig * @Description: TODO * @Author TR * @Date 2021/3/21 * @Version V1.0 */@Configuration@EnableAsyncpublic class AnnotationAsyncConfig implements AsyncConfigurer { /** 线程池维护线程的最小数量 */ private int minPoolSize = 2; /** 线程池维护线程的最大数量 */ private int maxPoolSize = 2; /** 线程池队列的长度 */ private int queueCapacity = 100; /** 获取异步线程池的执行对象 */ @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(minPoolSize); executor.setMaxPoolSize(maxPoolSize); executor.setQueueCapacity(queueCapacity); //用来调试 executor.setThreadNamePrefix("AnnotationAsyncConfig:"); executor.setWaitForTasksToCompleteOnShutdown(true); //拒绝策略 CallerRunsPolicy 由调用线程处理该任务 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; }}
//加上@Async注解@Override@Asyncpublic void onApplicationEvent(PaymentUpdateStatusEvent event) { System.out.println(Thread.currentThread().getName() + ":库存服务,收到了支付状态的更新:" + event);}
执行结果如下:
注解式配置异步的执行步骤:异步执行的原理本质上是AOP,具体步骤如下:
一. SpringTask
Cron表达式,一个cron表达式有至少6个(也可能7个)有空格分隔的时间元素,按照顺序依次是:
可以使用工具生成,如下图:
Corn表达式生成工具:简单示例:
@Component@EnableSchedulingpublic class AlarmSpringTask { /** 默认的是fixedDelay,上一次执行完毕时间点之后再执行 */ @Scheduled(cron = "0/5 * * * * *") public void run() throws InterruptedException { Thread.sleep(6000); System.out.println(Thread.currentThread().getName() + "使用cron表达式:" +(System.currentTimeMillis()/1000)); }}
运行结果:
2. 使用fixedRate@Component@EnableSchedulingpublic class AlarmSpringTask { /** fixedRate,上一次开始执行时间点之后5秒再执行 */ @Scheduled(fixedRate = 5000) public void run() throws InterruptedException { Thread.sleep(6000); System.out.println(Thread.currentThread().getName() + "使用fixedRate:" +(System.currentTimeMillis()/1000)); }}
运行结果:
3. 使用fixedDelay@Component@EnableSchedulingpublic class AlarmSpringTask { /** fixedDelay,上一次执行完毕时间点之后5秒再执行 */ @Scheduled(fixedDelay = 5000) public void run() throws InterruptedException { Thread.sleep(6000); System.out.println(Thread.currentThread().getName() + "使用fixedDelay:" +(System.currentTimeMillis()/1000)); }}
运行结果:
4. 使用initialDelay@Component@EnableSchedulingpublic class AlarmSpringTask { /** initialDelay,第一次延迟2s后执行,之后按fixedDelay的规则每5秒执行 一次*/ @Scheduled(initialDelay = 2000, fixedDelay = 5000) public void run() throws InterruptedException { Thread.sleep(6000); System.out.println(Thread.currentThread().getName() + "使用使用initialDelay:" +(System.currentTimeMillis()/1000)); }}
运行结果:
二. Spring集成Quartz/** * @ClassName TestQuartz * @Description: 创建任务类TestQuartz,该类主要是继承了QuartzJobBean * @Author TR * @Date 2021/3/21 * @Version V1.0 */public class TestQuartz extends QuartzJobBean { /** 执行定时任务 */ @Override protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { System.out.println("quartz task "+(System.currentTimeMillis()/1000)); }}
/** * @ClassName QuartzConfig * @Description: 创建配置类QuartzConfig * @Author TR * @Date 2021/3/21 * @Version V1.0 */@Configurationpublic class QuartzConfig { @Bean public JobDetail teatQuartzDetail() { return JobBuilder.newJob(TestQuartz.class).withIdentity("testQuartz").storeDurably().build(); } @Bean public Trigger testQuartzTrigger() { SimpleScheduleBuilder builder = SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(6) //设置时间周期单位:秒 .repeatForever(); return TriggerBuilder.newTrigger().forJob(teatQuartzDetail()) .withIdentity("testQuartz") .withSchedule(builder) .build(); }}
运行结果:
本文其他知识点链接:
转载地址:http://oliuz.baihongyu.com/