日常开发使用非常多的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. 开源OA办公平台搭建教程:O2OA+Arduino实现物联网应用(一)

    O2OA平台是一个企业办公类系统的低代码开发平台,更够方便的开发和部署协同办公.流程管理等应用,但它能做的远不止这些,今天这个案例就为大家介绍一下,O2OA可以做的更多. 最近对养鱼产生了浓厚的兴趣, ...

  2. springboot项目启动后tomcat服务器自动关闭 解决方法

    需要在pom.xml中添加 <dependency> <groupId>org.springframework.boot</groupId> <artifac ...

  3. Spring的BeanFactoryPostProcessor接口

    接口简介 BeanFactoryPostProcessor 接口是 Spring 初始化 BeanFactory 时对外暴露的扩展点,Spring IoC 容器允许 BeanFactoryPostPr ...

  4. 京东 Vue3 组件库闪亮登场

    京东零售开源项目 NutUI 是一套京东风格的轻量级移动端 Vue 组件库,是开发和服务于移动 Web 界面的企业级产品.经过长时间的开发与打磨,NutUI 3.0 终于要和大家见面了!3.0 版本在 ...

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

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

  6. LanQiao-297(快速排序)

    快速排序 LanQiao-297 #include<iostream> #include<cstdio> #include<algorithm> #include& ...

  7. HDOJ-6641(欧几里得+异或运算)

    TDL HDOJ-6641 关于题意,就是要找出符合f的第m大的数,而且后面还要满足异或等式. 通过观察题目,可以发现n太大了,所以不能直接枚举.当然因为m比较小,所以可以转换思路k^n,这个数最大不 ...

  8. shell脚本,mysql数据库的备份,并压缩

    db_user="root"db_passwd="xxx"db_host="localhost" databases="xxx-p ...

  9. Vue中去除文本框回车默认事件

    使用v-on:keydown.enter.prevent即可 <input type="password" class="form-control" id ...

  10. AutoPy开发文档

    AutoPy 简介 AutoPy是为python开发者提供的一个安卓插件,由路飞大佬开发维护,主要功能为了实现使用python在安卓端完成一些操作,例如点击,滑动,返回 准备 安装AutoPy.apk ...