日常开发使用非常多的Spring,它的设计理念是什么呢?有哪些核心的组件呢?为啥又需要这些组件呢?在Spring中用到了哪些设计模式呢?Spring有哪些高级特性,该如何使用呢?本文将对这些做简要的介绍,希望看完本文您能了解这些知识!

Spring介绍

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构建的数据结构

  1. 在Spring中,Bean才是真正的主角,或者说Spring是面向Bean的编程,Bean在Spring中的作用就像Object对OOP的作用一样,在java中是面向对象的编程,在Spring中是面向Bean的编程,包括Bean的创建、定义、解析等,这些会在后续的文章中说到

  2. 通过IOC容器完成依赖注入机制,构建Bean的生存环境;IOC容器就是被Bean包裹的对象,Spring正是通过把对象包装在Bean中,从而达到对这些对象的管理以及一些列额外操作的目的

  3. 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接口表示抽象策略

  1. CglibAopProxy和JdkDynamicAopProxy分别代表两种策略的实现方式
  2. ProxyFactoryBean就是代表Context角色,它会根据条件选用JDK动态代理方式还是CGLIB方式
  3. 另外的三个类主要是负责创建具体策略对象
  4. ProxyFactoryBean通过依赖关联具体策略对象,通过调用策略对象getProxy(ClassLoader classLoader)方法来完成操作

特性应用

事件驱动编程

事件驱动编程,是基于发布-订阅模式的编程模型,即观察者模式

事件驱动模型的核心构建通常包含了一下几个:

  1. 事件源:负责产生事件的对象,比如页面中常见的按钮,按钮就是一个事件源,可以产生“点击”这个事件
  2. 事件监听器:也叫做事件处理器,负责处理事件的对象
  3. 事件:也可以称作事件对象,是事件源和事件监听器之间的信息桥梁,是整个事件模型驱动的核心

事件驱动模型的实现包含以下几种:

  1. 观察者模式
  2. JDK观察者模式
  3. JavaBean事件驱动
  4. Spring事件驱动

下面主要是Spring事件驱动的示例,由于Spring事件驱动模型原理比较复杂,涉及到的类比较多,下面从一个简单的例子入手,了解Spring事件驱动模型

在日常购物中,当下了一个订单的时候,这个订单的支付状态会发生变化,然后能够通知到库存服务、短信服务、邮件服务等

  1. PaymentEntity类:
/**
* @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 + '\'' +
'}';
}
}
  1. PaymentUpdateStatusEvent类
/**
* @ClassName 支付状态更新的事件,以PaymentEntity作为传输的载体
* @Description: TODO
* @Author TR
* @Date 2021/3/21
* @Version V1.0
*/
public class PaymentUpdateStatusEvent extends ApplicationEvent { public PaymentUpdateStatusEvent(Object source) {
super(source);
}
}
  1. PaymentService类,主要用来发布事件
/**
* @ClassName PaymentService
* @Description: TODO
* @Author TR
* @Date 2021/3/21
* @Version V1.0
*/
@Service
public 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));
}
}
  1. StockPaymentListener事件监听器
/**
* @ClassName StockPaymantListener
* @Description: 无序事件监听器,库存服务监听器
* @Author TR
* @Date 2021/3/21
* @Version V1.0
*/
@Service
public class StockPaymentListener implements ApplicationListener<PaymentUpdateStatusEvent> { @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<? extends ApplicationEvent> eventType) {
return eventType == PaymentUpdateStatusEvent.class;
} /** 事件发生的目标类 */
@Override
public boolean supportsSourceType(Class<?> sourceType) {
return sourceType == PaymentEntity.class;
}
}
  1. SmsPaymentListener事件监听器
/**
* @ClassName SmsPaymentListener
* @Description: 短信监听器
* @Author TR
* @Date 2021/3/21
* @Version V1.0
*/
@Service
public 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);
}
}
  1. MailPaymentListener事件监听器
/**
* @ClassName MailPaymentListener
* @Description: 邮件监听器
* @Author TR
* @Date 2021/3/21
* @Version V1.0
*/
@Service
public 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);
}
}
  1. 测试类
/**
* @ClassName EventTest
* @Description: 测试类
* @Author TR
* @Date 2021/3/21
* @Version V1.0
*/
@SpringBootTest
public class EventTest { @Autowired
PaymentService paymentService; @Test
void pay() {
paymentService.pay(1, "支付成功");
}
}

运行之后的结果:



涉及到的类:

  1. ApplicationEvent
  2. ApplicationListener
  3. SmartApplicationListener
  4. ApplicationContext

可以看到,有序监听器执行是按照优先级执行的,也可以看到,上面执行的线程全部是main线程,当订单很多的时候,只有一个线程来执行,效率会很低,所以引出了下面的内容,异步执行

异步执行

Spring有两种异步执行方式:全局异步、注解式配置异步

一. 全局异步实现

/**
* @ClassName GlobalAsyncConfig
* @Description: TODO
* @Author TR
* @Date 2021/3/21
* @Version V1.0
*/
@Configuration
public 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;
}
}

思考一下,当用到了异步之后,上面的有序监听器还会按照优先级执行吗?下面看下执行结果:



可以看到,当使用异步之后,有序监听器并没有按照优先级执行,具体原因就是不同的线程去执行导致的

全局异步的执行步骤:

  1. 定义并且配置Executor Bean
  2. 配置名为applicationEventMulticaster的SimpleApplicationEventMulticaster Bean
  3. 设置applicationEventMulticaster执行器为第一步的Executor

二. 注解式配置异步实现

/**
* @ClassName AnnotationAsyncConfig
* @Description: TODO
* @Author TR
* @Date 2021/3/21
* @Version V1.0
*/
@Configuration
@EnableAsync
public 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
@Async
public void onApplicationEvent(PaymentUpdateStatusEvent event) {
System.out.println(Thread.currentThread().getName() +
":库存服务,收到了支付状态的更新:" + event);
}

执行结果如下:



注解式配置异步的执行步骤:

  1. 开启异步执行:@EnableAsync
  2. 配置线程池,这个是非必要的,没有的话则使用默认线程池
  3. 在Bean方法上指定为异步:@Async

异步执行的原理本质上是AOP,具体步骤如下:

  1. 初始化线程池和异步处理器
  2. 创建异步方法所在的Bean后,执行Async对应的BeanPostProcessor,创建AOP代理类,代理对象替换原来的对象
  3. 代理对象中,异步方法被动态植入了异步执行方法
  4. 执行异步方法,其实执行的是代理对象里面的方法,从而实现异步,除了Async这个注解,没有任何的入侵

定时任务

一. SpringTask

  1. fixedRate:上一次开始执行时间点之后再执行
  2. fixedDelay:上一次执行完毕时间点之后再执行
  3. initialDelay:第一次延迟后执行,之后按照上面指定的规则执行
  4. 默认的是上一次执行完毕时间点之后再执行

Cron表达式,一个cron表达式有至少6个(也可能7个)有空格分隔的时间元素,按照顺序依次是:

  • 秒(0~59)
  • 分钟(0~59)
  • 小时(0~23)
  • 天(0~31)
  • 月(0~11)
  • 星期(1~7 1=SUN 或 SUN,MON,TUE,WED,THU,FRI,SAT)
  • 年份(1970-2099)

可以使用工具生成,如下图:



Corn表达式生成工具:点击链接

简单示例:

  1. 使用cron表达式
@Component
@EnableScheduling
public 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
@EnableScheduling
public 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
@EnableScheduling
public 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
@EnableScheduling
public 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
*/
@Configuration
public 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(); }
}

运行结果:

本文其他知识点链接:

  1. Tony老师带你来看Java设计模式:代理模式

  2. 诸葛亮的锦囊妙计竟然是大名鼎鼎的Java设计模式:策略模式

  3. 什么?女神发了朋友圈,快来围观之Java设计模式:观察者模式

嗨,你知道吗,Spring还有这些高级特性!的更多相关文章

  1. Spring框架学习[IoC容器高级特性]

    1.通过前面4篇文章对Spring IoC容器的源码分析,我们已经基本上了解了Spring IoC容器对Bean定义资源的定位.读入和解析过程,同时也清楚了当用户通过getBean方法向IoC容器获取 ...

  2. 第07章-Spring MVC 的高级技术

    Spring MVC 的高级技术 1. Spring MVC配置的替代方案 1.1 自定义DispatcherServlet配置 AbstractAnnotationConfigDispatcherS ...

  3. Spring 事务管理高级应用难点剖析: 第 3 部分

    本文是“Spring 事务管理高级应用难点剖析” 系列文章的第 3 部分,作者将继续深入剖析在实际 Spring 事务管理应用中容易遇见的一些难点,包括在使用 Spring JDBC 时如果直接获取 ...

  4. Spring 事务管理高级应用难点剖析: 第 2 部分

    本文是“Spring 事务管理高级应用难点剖析” 系列文章的第 2 部分,作者将继续深入剖析在实际 Spring 事务管理应用中容易遇见的一些难点,包括混合使用多种数据访问技术(如 Spring JD ...

  5. mybatis 高级映射和spring整合之高级映射(4)

    mybatis 高级映射和spring整合之高级映射 ----------------学习结构-------------------- 0.0 对订单商品数据模型进行分析 1.0 高级映射 1.1 一 ...

  6. 跟我学SpringCloud | 第十四篇:Spring Cloud Gateway高级应用

    SpringCloud系列教程 | 第十四篇:Spring Cloud Gateway高级应用 Springboot: 2.1.6.RELEASE SpringCloud: Greenwich.SR1 ...

  7. Redis基础、高级特性与性能调优

    本文将从Redis的基本特性入手,通过讲述Redis的数据结构和主要命令对Redis的基本能力进行直观介绍.之后概览Redis提供的高级能力,并在部署.维护.性能调优等多个方面进行更深入的介绍和指导. ...

  8. Redis 基础、高级特性与性能调优

    本文将从Redis的基本特性入手,通过讲述Redis的数据结构和主要命令对Redis的基本能力进行直观介绍.之后概览Redis提供的高级能力,并在部署.维护.性能调优等多个方面进行更深入的介绍和指导. ...

  9. Spring Boot提供的特性

    一.导览 本文主要按以下模块介绍spring Boot(1.3.6.RELEASE)提供的特性. SpringApplication类 外部化配置 Profiles 日志 开发WEB应用 Securi ...

随机推荐

  1. net面试总结的题目

    准备的面试题目. 1.private.protected.public.internal的访问权限? private : 私有成员,在类的内部才可以访问. protected :保护成员,该类内部和继 ...

  2. 喜忧参半的SQL Server触发器

    SQL Server触发器在非常有争议的主题.它们能以较低的成本提供便利,但经常被开发人员.DBA误用,导致性能瓶颈或维护性挑战. 本文简要回顾了触发器,并深入讨论了如何有效地使用触发器,以及何时触发 ...

  3. Mysql 高可用(MHA)-读写分离(Atlas)-分布式架构(Mycat)

    Mysql 高可用(MHA)-读写分离(Atlas) 1. 搭建主从复制(一主两从) 1.1 准备环境 1 主库:10.0.0.51/db01 2 从库:10.0.0.52/db02,10.0.0.5 ...

  4. Codeblocks支持语法着色

  5. 后端程序员之路 35、Index搜索引擎实现分析4-最终的正排索引与倒排索引

    # index_box 提供搜索功能的实现- 持有std::vector<ITEM> _buffer; 存储所有文章信息- 持有ForwardIndex _forward_index;  ...

  6. 医学图像 | DualGAN与儿科超声心动图分割 | MICCAI

    文章转自微信公众号:「机器学习炼丹术」 作者:炼丹兄(已授权) 联系方式:微信cyx645016617(欢迎交流共同进步) 论文名称:"Dual Network Generative Adv ...

  7. DatePicker 多时间粒度选择器组件

    使用方法: 在.vue文件引入 import ruiDatePicker from '@/components/rattenking-dtpicker/rattenking-dtpicker.vue' ...

  8. APICloud Avm.js跨端框架的优势

    AVM(Application-View-Model)是APICloud推出的一个跨端的高性能 JavaScript框架,更趋近于原生的编程体验,它提供简洁的模型来分离应用的用户界面.业务逻辑和数据模 ...

  9. OSI协议简述版

    OSI简介 OSI只是计算机网络中的一种协议名称缩写,它只是电脑间传输数据的协议,并不代表具体的物理设备,并且这种协议,只是被人为的划分为五层:物理层.数据链路层.网络层.传输层.应用层.记住,它只是 ...

  10. 漏洞复现-ActiveMq反序列化漏洞(CVE-2015-5254)

          0x00 实验环境 攻击机:Win 10 靶机也可作为攻击机:Ubuntu18 (docker搭建的vulhub靶场) 0x01 影响版本 Apache ActiveMQ 5.13.0之前 ...