今天,我要教妹子学会Spring:Aware、异步编程、计划任务

来源 | 沉默王二

教妹子学 Spring,没见过这么放肆的标题吧?

作者我有一个漂亮如花的妹妹(见封面图,别问我怎么又变了?还不能一天做个梦了?),她叫什么呢?我想聪明的读者能猜得出:沉默王三,没错,年方三六。父母正考虑让她向我学习,做一名正儿八经的 Java 程序员。我一开始是反对的,因为程序员这行业容易掉头发,女生可不适合掉头发。但家命难为啊,与其反对,不如做点更积极的事情,比如说写点有趣的文章教教她。

“二哥,听说今天要学习 Spring 的 Aware、异步编程、计划任务,真的是翘首以盼啊。”

Spring Aware

“二哥,据说 Aware 的目的是让 Bean 获取 Spring 容器的服务,你能给我具体说说吗?”

“没问题啊。”

Bean 一般不需要了解容器的状态和直接使用容器,但是在某些情况下,需要在 Bean 中直接对容器进行操作,这时候,就可以通过特定的 Aware 接口来完成。常见的 Spring Aware 接口有下面这些:

1)BeanNameAware

新建一个 MyBeanName 类,内容如下:

public class MyBeanName implements BeanNameAware {@Overridepublic void setBeanName(String beanName) {System.out.println(beanName);}
}

MyBeanName 实现了 BeanNameAware 接口,并重写了 setBeanName() 方法。beanName 参数表示 Bean 在 Spring 容器中注册的 name。

新建一个 Config 类,内容如下:

@Configuration
public class Config {@Bean(name = "myCustomBeanName")public MyBeanName getMyBeanName() {return new MyBeanName();}
}

@Bean 注解用在 getMyBeanName() 方法上,表明当前方法返回一个 Bean 对象(MyBeanName),并通过 name 属性指定 Bean 的名字为“myCustomBeanName”。

新建 BeanNameMain 类,代码如下:

public class BeanNameMain {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanNameConfig.class);MyBeanName myBeanName = context.getBean(MyBeanName.class);context.close();}
}程序输出的结果如下所示:
myCustomBeanName

如果把 @Bean() 注解中的 (name = "myCustomBeanName)" 去掉的话,程序输出的内容将会是 BeanNameConfig 类的 getMyBeanName() 的方法名“getMyBeanName”。

2)BeanFactoryAware

新建一个 MyBeanFactory 类,内容如下:

public class MyBeanFactory implements BeanFactoryAware {private BeanFactory beanFactory;@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {this.beanFactory = beanFactory;}public void getMyBeanName() {MyBeanName myBeanName = beanFactory.getBean(MyBeanName.class);System.out.println(beanFactory.isSingleton("myCustomBeanName"));System.out.println(beanFactory.isSingleton("getMyBeanFactory"));}
}

借助 setBeanFactory() 方法,可以将容器中的 BeanFactory 赋值给 MyBeanFactory 类的成员变量 beanFactory,这样就可以在 getMyBeanName()方法中使用 BeanFactory 了。

通过 getBean() 方法可以获取 Bean 的实例;通过 isSingleton() 方法判断 Bean 是否为一个单例。

在 Config 类中追加 MyBeanFactory 的 Bean:

@Configuration
public class Config {@Bean(name = "myCustomBeanName")public MyBeanName getMyBeanName() {return new MyBeanName();}@Beanpublic MyBeanFactory getMyBeanFactory() {return new MyBeanFactory();}
}

新建 BeanFactoryMain 类,代码如下:

public class BeanFactoryMain {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);MyBeanFactory myBeanFactory = context.getBean(MyBeanFactory.class);myBeanFactory.getMyBeanName();context.close();}
}

初始化 MyBeanFactory 后就可以调用 getMyBeanName() 方法了,程序输出的结果如下所示:

myCustomBeanName
true
true

结果符合我们的预期:MyBeanName 的名字为“myCustomBeanName”,MyBeanName 和 MyBeanFactory 的 scope 都是 singleton。

3)其他几个 Aware 接口就不再举例说明了。通常情况下,不要实现 Aware 接口,因为它会使 Bean 和 Spring 框架耦合。

异步编程

“二哥,据说 Spring 可以通过 @Async 来实现异步编程,你能给我详细说说吗?”

“没问题啊。”

新建一个 AsyncService 类,内容如下:

public class AsyncService {@Asyncpublic void execute() {System.out.println(Thread.currentThread().getName());}
}

@Async 注解用在 public 方法上,表明 execute() 方法是一个异步方法。

新建一个 AsyncConfig 类,内容如下:

@Configuration
@EnableAsync
public class AsyncConfig {@Beanpublic AsyncService getAsyncService() {return new AsyncService();}
}

在配置类上使用 @EnableAsync 注解用以开启异步编程,否则 @Async 注解不会起作用。

新建一个 AsyncMain 类,内容如下:

public class AsyncMain {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AsyncConfig.class);AsyncService service = context.getBean(AsyncService.class);for (int i = 0; i < 10; i++) {service.execute();}}

程序输出结果如下:

SimpleAsyncTaskExecutor-1
SimpleAsyncTaskExecutor-9
SimpleAsyncTaskExecutor-7
SimpleAsyncTaskExecutor-8
SimpleAsyncTaskExecutor-10
SimpleAsyncTaskExecutor-3
SimpleAsyncTaskExecutor-2
SimpleAsyncTaskExecutor-4
SimpleAsyncTaskExecutor-6
SimpleAsyncTaskExecutor-5

OK,结果符合我们的预期,异步编程实现了。就像你看到的那样,Spring 提供了一个默认的 SimpleAsyncTaskExecutor 用来执行线程,我们也可以在方法级别和应用级别上对执行器进行配置。

1)方法级别

新建 AsyncConfig 类,内容如下:

@Configuration
@EnableAsync
public class AsyncConfig {@Beanpublic AsyncService getAsyncService() {return new AsyncService();}@Bean(name = "threadPoolTaskExecutor")public Executor threadPoolTaskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(5);return executor;}
}

在配置类中创建了一个返回类型为 Executor 的 Bean,其名称定义为“threadPoolTaskExecutor”,并且重新设置了 ThreadPoolTaskExecutor 的核心线程池大小,默认为 1,现在修改为 5。

新进 AsyncService 类,内容如下:

public class AsyncService {@Async("threadPoolTaskExecutor")public void execute() {System.out.println(Thread.currentThread().getName());}
}

@Async 注解上需要指定我们之前配置的线程池执行器“threadPoolTaskExecutor”。

新建 AsyncMain 类,内容如下:

public class AsyncMain {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AsyncConfig.class);AsyncService service = context.getBean(AsyncService.class);for (int i = 0; i < 10; i++) {service.execute();}}
}

程序运行结果如下:

threadPoolTaskExecutor-1
threadPoolTaskExecutor-2
threadPoolTaskExecutor-4
threadPoolTaskExecutor-3
threadPoolTaskExecutor-5
threadPoolTaskExecutor-3
threadPoolTaskExecutor-2
threadPoolTaskExecutor-4
threadPoolTaskExecutor-1
threadPoolTaskExecutor-5

从结果中可以看得出,线程池执行器变成了“threadPoolTaskExecutor”,并且大小为 5。

2)应用级别

新建 AsyncConfig 类,内容如下:

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {@Beanpublic AsyncService getAsyncService() {return new AsyncService();}@Overridepublic Executor getAsyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(3);executor.initialize();return executor;}
}需要实现 AsyncConfigurer 接口,并重写 getAsyncExecutor() 方法,这次设置线程池的大小为 3。注意执行器要执行一次 initialize() 方法。

新进 AsyncService 类,内容如下:

public class AsyncService {@Asyncpublic void execute() {System.out.println(Thread.currentThread().getName());}
}

新建 AsyncMain 类,内容如下:

public class AsyncMain {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AsyncConfig.class);AsyncService service = context.getBean(AsyncService.class);for (int i = 0; i < 10; i++) {service.execute();}}
}

程序运行结果如下:

ThreadPoolTaskExecutor-2
ThreadPoolTaskExecutor-2
ThreadPoolTaskExecutor-2
ThreadPoolTaskExecutor-2
ThreadPoolTaskExecutor-2
ThreadPoolTaskExecutor-2
ThreadPoolTaskExecutor-2
ThreadPoolTaskExecutor-2
ThreadPoolTaskExecutor-1
ThreadPoolTaskExecutor-3

从结果中可以看得出,线程池执行器变成了“ThreadPoolTaskExecutor”,并且大小为 3。

计划任务

“二哥,据说 Spring 可以通过 @Scheduled 来实现计划任务,你能给我详细说说怎么实现吗?”

“没问题啊。”

新建一个 ScheduledService 类,内容如下:

@Service
public class ScheduledService {@Scheduled(fixedDelay = 1000)public void scheduleFixedDelayTask() {System.out.println("固定时间段后执行任务 - " + System.currentTimeMillis() / 1000);}@Scheduled(fixedRate = 1000)public void scheduleFixedRateTask() {System.out.println("固定的频率执行任务 - " + System.currentTimeMillis() / 1000);}@Scheduled(cron = "0/2 * * * * ?")public void scheduleTaskUsingCronExpression() {long now = System.currentTimeMillis() / 1000;System.out.println("Cron 表达式执行任务 - " + now);}
}

@Service 注解用于指定 ScheduledService 类为一个业务层的 Bean。@Scheduled 注解用于指定当前方法(返回类型为 void,无参)为一个任务执行方法,常见的用法有以下 3 种:

1)fixedDelay 用于确保任务执行的完成时间与任务下一次执行的开始时间之间存在 n 毫秒的延迟,下一次任务执行前,上一次任务必须执行完。

2)fixedRate 用于确保每 n 毫秒执行一次计划任务,即使最后一次调用可能仍在运行。

3)Cron 表达式比 fixedDelay 和 fixedRate 都要灵活,由 7 个部分组成,各部分之间用空格隔开,其完整的格式如下所示:

Seconds Minutes Hours Day-of-Month Month Day-of-Week Year

单词都很简单,就不用我翻译了。其中 Year 是可选项。常见的范例如下所示:

*/5 * * * * ?  每隔 5 秒执行一次
0 */1 * * * ?  每隔 1 分钟执行一次
0 0 23 * * ?  每天 23 点执行一次
0 0 1 * * ?  每天凌晨 1 点执行一次:
0 0 1 1 * ?  每月 1 号凌晨 1 点执行一次
0 0 23 L * ?  每月最后一天 23 点执行一次
0 0 1 ? * L  每周星期天凌晨 1 点执行一次
0 26,29,33 * * * ?  在 26 分、29 分、33 分执行一次
0 0 0,13,18,21 * * ? 每天的 0 点、13 点、18 点、21 点各执行一次

新建 ScheduledConfig 类,内容如下:

@Configuration
@EnableScheduling
@ComponentScan("high.scheduled")
public class ScheduledConfig {
}@EnableScheduling 注解用于开启计划任务。@ComponentScan 注解用于扫描当前包下的类,如果它使用了注解(比如 @Service),就将其注册成为一个 Bean。

新建 ScheduledMain 类,内容如下:

public class ScheduledMain {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ScheduledConfig.class);}
}

程序运行结果如下:

固定的频率执行任务 - 1584666273
固定时间段后执行任务 - 1584666273
Cron 表达式执行任务 - 1584666274
固定的频率执行任务 - 1584666274
固定时间段后执行任务 - 1584666274
固定的频率执行任务 - 1584666275
固定时间段后执行任务 - 1584666275
Cron 表达式执行任务 - 1584666276

从结果中可以看得出,如果任务之间没有冲突的话,fixedDelay 任务之间的间隔和 fixedRate 任务之间的间隔是相同的,都是 1 秒;Cron 表达式任务与上一次任务之间的间隔为 2 秒。

“二哥,这篇文章中的示例代码你上传到 GitHub 了吗?”

“你到挺贴心啊,三妹。传送门~”

https://github.com/qinggee/SpringDemo

“二哥,你教得真不错,我完全学会了,一点也不枯燥。”

“那必须得啊,就是不知道看这篇文章的他们学会了没?”

同时,欢迎所有开发者扫描下方二维码填写《开发者与AI大调研》,只需2分钟,便可收获价值299元的「AI开发者万人大会」在线直播门票!

推荐阅读:在容器上构建持续部署及最佳实践初探在Kubernetes上部署一个简单的、类PaaS的平台,原来这么容易!全球呼吸机告急!医疗科技巨头美敦力“开源”设计图和源代码互联网之父确诊新冠,一代传奇:任谷歌副总裁、NASA 访问科学家微软为一人收购一公司?破解索尼程序、写黑客小说,看他彪悍的程序人生!2020年,这20个大家都认识的加密交易所过得怎么样?真香,朕在看了!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/518539.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java-异常03 自定义异常

// 自定义的异常类 public class MyException extends Exception{// 传递数字 > 10private int detail;public MyException(int a) {this.detail a;}// toString: 异常的打印信息Overridepublic String toString() {return "MyException{" detail };} }public …

SLS机器学习最佳实战:批量时序异常检测

0.文章系列链接 SLS机器学习介绍&#xff08;01&#xff09;&#xff1a;时序统计建模SLS机器学习介绍&#xff08;02&#xff09;&#xff1a;时序聚类建模SLS机器学习介绍&#xff08;03&#xff09;&#xff1a;时序异常检测建模SLS机器学习介绍&#xff08;04&#xff09;…

一张900w的数据表,怎么把原先要花费17s执行的SQL优化到300ms?

来源 | 程序员乔戈里 封图| CSDN下载于视觉中国有一张财务流水表&#xff0c;未分库分表&#xff0c;目前的数据量为9555695&#xff0c;分页查询使用到了limit&#xff0c;优化之前的查询耗时16 s 938 ms (execution: 16 s 831 ms, fetching: 107 ms)&#xff0c;按照下文的方…

浅析基于 Serverless 的前后端一体化框架

概述 Serverless 是一种“无服务器架构”模式&#xff0c;它无需关心程序运行环境、资源及数量&#xff0c;只需要将精力聚焦到业务逻辑上的技术。基于 Serverless 开发 web 应用&#xff0c;架构师总是试图把传统的解决方案移植到 Serverless 上&#xff0c;虽然可以做到既拥…

Java-总结

常用类 集合框架 IO流 多线程 网络编程 GUI 注解与反射 https://www.bilibili.com/video/BV12J41137hu?p80

一文告诉你,如何在 Kubernetes 的容器引擎中运行 KVM 和 VMware VM!

作者 | Gilson Melo译者 | 天道酬勤 责编 | 徐威龙封图| CSDN下载于视觉中国随着微服务的出现&#xff0c;人们通常会问&#xff1a;“是否有可能通过Kubernetes上的微服务在基于内核的虚拟机&#xff08;KVM&#xff09;或VMware 中运行我的旧虚拟机&#xff0c;还是需要将它们…

性能压测工具选型对比

本文是《Performance Test Together》&#xff08;简称PTT&#xff09;系列专题分享的第二期&#xff0c;该专题将从性能压测的设计、实现、执行、监控、问题定位和分析、应用场景等多个纬度对性能压测的全过程进行拆解&#xff0c;以帮助大家构建完整的性能压测的理论体系&…

adb-基础命令

查看连接 adb devices --查看连接的手机安装APP adb install -r apk在电脑路径 -- 安装APP adb install -r 替换已存在的应用程序&#xff0c;也就是说强制安装 adb install -g 为应用程序授予所有运行时的权限 -l 锁定该应用程序 -r 替换已存在的应用程序&…

大数据架构如何做到流批一体?

阿里妹导读&#xff1a;大数据与现有的科技手段结合&#xff0c;对大多数产业而言都能产生巨大的经济及社会价值。这也是当下许多企业&#xff0c;在大数据上深耕的原因。大数据分析场景需要解决哪些技术挑战&#xff1f;目前&#xff0c;有哪些主流大数据架构模式及其发展&…

一群阿里人如何用 10 年自研洛神云网络平台?技术架构演进全揭秘!

【CSDN 编者按】逍遥子曾说&#xff0c;对阿里人来说「打仗是最好的团建&#xff0c;参加过双 11 的叫战友」&#xff0c;同样&#xff0c;参加过多次双 11 考验的系统技术都会成为真正意义上的基础设施&#xff0c;阿里云飞天系统的云网络平台洛神就是如此。本文作者 —— 阿里…

数据人看Feed流-架构实践

背景 Feed流&#xff1a;可以理解为信息流&#xff0c;解决的是信息生产者与信息消费者之间的信息传递问题。 我们常见的Feed流场景有&#xff1a; 1 手淘&#xff0c;微淘提供给消费者的首页商品信息&#xff0c;用户关注店铺的新消息等 2 微信朋友圈&#xff0c;及时获取朋友…

Java-端口

windows 查看端口 netstat -ano # 查看所有端口 netstat -ano | findstr "8080" # 查看含有8080的行&#xff0c;查看指定端口 tasklist | findstr "20100" # 查看指定端口的进程 Ctrl Shift Esc # 打开任务管理器public class TestInetSocketAddr…

“出道” 5 年采用率达 78%,Kubernetes 的成功秘诀是什么?

作者 | Matt Asay译者 | 天道酬勤 责编 | 徐威龙封图| CSDN下载于视觉中国很难相信Kubernetes直到2015年中期&#xff08;首次提交后的一年&#xff09;才达到1.0版本&#xff0c;因为容器编排平台目前已经在云原生计算基金会&#xff08;CNCF&#xff09;调查的企业中生产了78…

Java-内置注解

注解&#xff1a; Java.Annotation (注释 comment) // 什么是注解 public class Test01 extends Object{// Override 重写的注解Overridepublic String toString() {return super.toString();} }// SuppressWarnings 镇压警告&#xff0c;写了就没有 变量没有使用的警告了Sup…

使用Quick BI连接AnalyticDB for PostgreSQL数据源

本文介绍如何通过阿里云Quick BI连接AnalyticDB for PostgreSQL数据库。 在Quick BI中新建AnalyticDB for PostgreSQL数据源 登录Quick BI控制台。单击工作空间>数据源&#xff0c;进入数据源管理页面。单击新建数据源>AnalyticDB for PostgreSQL。在添加AnalyticDB fo…

开源背后 | 面对端侧推理引擎的挑战,阿里工程师如何应对?

阿里妹导读&#xff1a;MNN&#xff08;Mobile Neural Network&#xff09;已于今年5月7日在 Github 上正式开源。淘宝无线开发专家——陈以鎏&#xff08;离青&#xff09;在 GMTC 全球大前端技术大会为大家分享了 MNN 开发、开源中的思考与总结&#xff0c;通过淘宝在移动 AI…

财报上新不久的Mobvista,还内藏何种“惊艳”?

本文为CSDN博主「L-JingJing」的原创文章 原文链接&#xff1a;https://blog.csdn.net/sch881226/article/details/105362679 就在不久前&#xff0c;Mobvista刚刚发布了2019年的财报数据。我们观察到其程序化广告收入已高达22.3亿元&#xff0c;同比增长40.8%&#xff1b;经…

Java-元注解

// 测试 元注解 public class Test02 {MyAnnotationpublic void test(){} } // 定义一个注解 // Target 表示注解可以用在哪些地方 ElementType.METHOD 是方法 ElementType.TYPE是类 Target(value {ElementType.METHOD, ElementType.TYPE})// Retention 表示注解在什么地方有…

容器十年 ——一部软件交付编年史

作者| 张磊&#xff0c;阿里云容器平台高级技术专家&#xff0c;CNCF Ambassador &#xff08;CNCF 官方大使&#xff09;&#xff0c;Kubernetes 项目资深成员与维护者&#xff0c;曾就职于 Hyper、微软研究院&#xff08;MSR&#xff09;&#xff0c;现在负责 Kubernetes 技术…

Java-自定义注解

// 自定义注解 public class Test03 {// 注解可以显示赋值&#xff0c; 如果没有默认值&#xff0c;我们就必须给注解赋值MyAnnotation2(name"wang")public void test1(){}// 当只有 一个值 为value 时&#xff0c; 可以不用写 value""MyAnnotation3("…